---
title: Safety Configuration
description: Understanding and configuring mcjtag's safety rails
sidebar:
  order: 3
---

mcjtag is designed for use alongside safety-critical firmware — avionics, medical
devices, automotive. It ships with conservative defaults that prevent accidental
damage to your target. Every safety mechanism is configurable, because the right
level of protection depends on what you are doing.

## Layer 1: Safe write ranges

By default, `write_memory()` only allows writes to SRAM (0x20000000-0x20100000).
Writes to any other address are rejected with an error before reaching the target.

This prevents:

- **Accidental flash corruption** — writing to 0x08000000+ would brick the device
- **Unexpected peripheral behavior** — writing to 0x40000000+ could reconfigure
  hardware (toggle GPIOs, change clock trees, disable interrupts)
- **System register writes** — writing to 0xE0000000+ could lock out the debugger
  or alter the MPU/NVIC configuration

Configure with the `MCJTAG_SAFE_WRITE_RANGES` environment variable:

```bash
# Default: SRAM only
MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20100000"

# Add CCM RAM (STM32F4 has core-coupled memory at 0x10000000)
MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20020000,0x10000000-0x10010000"

# Allow peripheral writes too (for register configuration)
MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20020000,0x40000000-0x50000000"

# Unrestricted — you know what you are doing
MCJTAG_SAFE_WRITE_RANGES="none"
```

The format is `start-end` pairs in hex, comma-separated. Each range is inclusive of
`start` and exclusive of `end`. The write must fall entirely within at least one range.

Setting the value to `none` (case-insensitive) disables the check entirely.

## Layer 2: Raw command deny-list

`raw_command()` provides direct access to OpenOCD's TCL interface, but blocks
commands that have dedicated, safer tools:

| Blocked pattern | Reason | Use instead |
|---|---|---|
| `flash write_image`, `flash erase`, `flash protect`, `flash fill` | Flash modification | `flash_program()` — validates files before writing |
| `mww`, `mwh`, `mwb`, `write_memory` | Memory writes | `write_memory()` — checks safe write ranges |
| `reset` | Target reset | `target_control("reset_halt")` or `target_control("reset_run")` |
| `shutdown`, `exit` | Terminates OpenOCD | `disconnect()` — cleanly closes the session |
| `reg` | Register writes | `write_register()` |
| `eval`, `subst`, `uplevel` | TCL metaprogramming | Blocks patterns that could bypass the deny-list |

Override with:

```bash
MCJTAG_ALLOW_RAW=true
```

This disables the entire deny-list. All OpenOCD TCL commands are passed through
without filtering.

**Important caveat**: The deny-list is best-effort pattern matching against the
command string. It is not a sandbox. TCL metaprogramming (string concatenation,
variable expansion) could theoretically construct a blocked command at runtime.
The deny-list is a safety net for accidental invocations, not a security boundary.

## Layer 3: Flash programming validation

`flash_program()` validates the firmware image before writing:

| Check | Behavior |
|---|---|
| File existence | Rejects if the path does not exist |
| File extension | Accepts `.bin`, `.hex`, `.elf`, `.ihex`, `.srec`, `.s19` only |
| File size | Must be non-zero and under 16 MB |
| Empty file | Rejects zero-byte files |

Progress is reported through three phases — erase, write, and verify — so the
LLM (and you) can track what is happening.

There is no environment variable to disable these checks. If you need to program
a file that does not meet these criteria, use `raw_command()` with
`MCJTAG_ALLOW_RAW=true`.

## Layer 4: Input validation

All tool inputs are validated before reaching OpenOCD:

| Input | Validation |
|---|---|
| Hex addresses | Must parse as valid hex. Negative values rejected. Accepts with or without `0x` prefix. |
| Alignment | Warns on unaligned reads (may fault on Cortex-M0). Rejects unaligned writes. |
| Value ranges | Values must fit in the specified width (8-bit max 0xFF, 16-bit max 0xFFFF, 32-bit max 0xFFFFFFFF). |
| Read count | Maximum 4096 elements per `read_memory()` call. |
| Search range | Maximum 1 MB per `search_memory()` call (configurable via `MCJTAG_MAX_SEARCH_RANGE`). |
| SVD files | Must have `.svd` extension and be under 50 MB. |
| Resume/step addresses | Must be 2-byte aligned (ARM Thumb requirement). |

## Recommended configurations

| Use case | Write ranges | Raw command | Notes |
|---|---|---|---|
| Learning / exploring | Default (SRAM) | Deny-list on | Safest. Cannot accidentally damage the target. |
| Firmware development | SRAM + your chip's RAM | Deny-list on | Normal daily use. Expand the SRAM range to match your chip. |
| Peripheral bring-up | SRAM + peripheral region | Deny-list on | Allows writing to GPIO, UART, SPI registers. Be careful with clock and power config. |
| Automated testing | Chip-specific ranges | Deny-list on | Tailor ranges to exactly what your test suite needs. |
| Power user / automation | `none` | `MCJTAG_ALLOW_RAW=true` | Full access. No guardrails. You own the consequences. |

## Environment variable reference

| Variable | Default | Description |
|---|---|---|
| `MCJTAG_SAFE_WRITE_RANGES` | `0x20000000-0x20100000` | Comma-separated hex ranges for `write_memory()`. Set to `none` for unrestricted. |
| `MCJTAG_ALLOW_RAW` | `false` | Set to `true`, `1`, or `yes` to disable the raw command deny-list. |
| `MCJTAG_MAX_SEARCH_RANGE` | `1048576` (1 MB) | Maximum byte range for `search_memory()`. Integer. |
| `OPENOCD_CONFIG` | (none) | OpenOCD config file path. If set, mcjtag auto-spawns OpenOCD on startup. |
| `OPENOCD_HOST` | `localhost` | OpenOCD TCL RPC hostname (when not auto-spawning). |
| `OPENOCD_PORT` | `6666` | OpenOCD TCL RPC port. |
| `OPENOCD_SVD` | (none) | SVD file path. Auto-loaded on startup if a connection is established. |
