Skip to content
Travis Goodspeed edited this page Nov 8, 2019 · 1 revision

The masked ROM of the RF430FRL152H contains eight kilobytes that implement a minimal NFC-V tag, complete with support for sensor readings and over the air reprogramming. This page contains some notes on the ROM, coming from its documentation and reverse engineering of the ROM binary.

The RF430TAL152H ROM is related, but quite different, and none of the pointers from this page will be valid on that chip.

General Use, Layout

Firmware engineers are expected to make a patch against the ROM, so that a tiny bit of code can extend the functionality of the pre-existing ROM application. This code must be tiny, as it and all nonvolatile data storage must fit into 2kB of FRAM.

Dump and Annotations

A dump of the ROM is available in /rom/RF430FRL152H-ROM-4400.bin, with a GHIDRA ZIP of the same, including SRAM and FRAM, in RF430FRL152H-ROM-4400.gzf. rom/symbols.ld is a linker script containing some useful functions from the ROM, but for now it is manually maintained rather than automatically exported from GHIDRA.

ISO 15693 Commands

Commands consist of a byte of flags followed by a verb, then an optional vendor ID (depending on the verb) and an optional address (depending on the flags) before the arguments of that verb. Blocks are either eight or four bytes, as configured in block 0xFF, which is virtual and not at the address it expects.

Verbs less than 0xA0 are standard ones, and they do not include a vendor ID.

01 -- Inventory
02 -- Stay Quiet
20 -- Read Single Block
21 -- Write Single Block
22 -- Lock Block
23 -- Read Multiple Blocks
24 -- Write Multiple Blocks
25 -- Select
26 -- Reset to Ready
27 -- Write AFI
28 -- Lock AFI
29 -- Write DSFID
2A -- Lock DSFID
2B -- Get System Information
2C -- Get Multiple Blocks Security Status

Verbs starting with 0xA0 are vendor commands, and they must be followed by the vendor ID of 0x07. For the RF430FRL152H, they begin with 0xC0 and mostly exist to get around the standard command limitation of a single block address.

C0 -- Custom Read Single w/ 16 bit block number
C1 -- Custom Write Single w/ 16 bit block number
C2 -- Custom Lock Block w/ 16 bit block number
C3 -- Custom Read Multiple w/ 16 bit block number
C4 -- Custom Write Multiple w/ 16 bit block number

Using these custom commands in 8-byte mode, blocks 0x0000 to 0x00F3 expose FRAM memory where address=(block*8)+0xF868. All of SRAM is exposed in a region from 0x0600 to 0x07FF. Single special blocks are available at 0x00FB and 0x00FF.

Other TI HI-Tag devices have different hidden commands. They don't seem to exist in the RF430FR152, but might be expected by host software.

A2 -- Write 2 Blocks
A3 -- Lock 2 Blocks
A4 -- Kill
A5 -- Write Single Block PWD

FRAM Virtual Registers

The FRAM contains a number of virtual registers, just before the FRAM logging space. Their bit fields are documented in SLAU603B.

F867 -- Firmware System Control Byte

F868 -- General Control Register
F869 -- Firmware Status Register
F86A -- Sensor Control Register
F86B -- Frequency Register
F86C -- Number of Passes Register
F86D -- Averaging Register
F86E -- Interrupt Control Register
F86F -- Error Control Register

F870 -- Reference-ADC1 Sensor Skip Count Register
F871 -- Thermistor-ADC2 Sensor Skip Count Register
F872 -- ADC0 Sensor Skip Count Register
F873 -- Internal Sensor Skip Count Register
F874 -- Digital Sensor 1 Skip Count Register
F875 -- Digital Sensor 2 Skip Count Register
F876 -- Digital Sensor 3 Skip Count Register
F877 -- Number of Blocks Received Register

F878 -- Reference-ADC1 Configuration Register
F879 -- Thermistor-ADC2 Configuration Register
F87A -- ADC0 Sensor Configuration Register
F87B -- Internal Sensor Configuration Register
F87C -- Initial Delay Period Setup Register
F87D -- JTAG Enable Password Register (0x39 enables)
F87E -- Initial Delay Period Register (16-bit)

F880 -- Custom Timer Value Register (32-bit)
F884 -- Low Threshold Reference-ADC1 Sensor Register (16-bit)
F886 -- High Threshold Reference-ADC1 Sensor Register
F888 -- Low Threshold Thermistor-ADC2 Sensor Register
F88A -- High Threshold Thermistor-ADC2 Sensor Register
F88C -- Low Threshold ADC0 Sensor Register
F88E -- High Threshold ADC0 Sensor Register
F890 -- Low Threshold Internal Temperature Sensor Register
F892 -- High Threshold Internal Temperature Sensor Register
F894 -- Low Threshold Digital1 Sensor Register
F896 -- High Threshold Digital1 Sensor Register
F898 -- Low Threshold Digital1 Sensor Register
F89A -- High Threshold Digital2 Sensor Register
F89C -- Low Threshold Digital3 Sensor Register
F89E -- High Threshold Digital3 Sensor Register

F8A0 -- Reference or ADC1 Alarm Configuration Register
F8A1 -- Thermistor and ADC2 Alarm Configuration Register
F8A2 -- ADC0 Alarm Configuration Register
F8A3 -- Internal Alarm Configuration Register
F8A4 -- Digital1 Alarm Configuration Register
F8A5 -- Digital2 Alarm Configuration Register
F8A6 -- Digital3 Alarm Configuration Register
F8A7 -- ?????

F8A8 -- Logging Memory Size Register (16-bit)
F8AA -- Total Number of Stores Register
F8AC -- Last Logged Index Register
F8AE -- FRAM Data Valid Register  (0xA3A6)

Interrupts

The native FRAM interrupt table from 0xFFE0 to 0xFFFF is used to directly handle interrupts from the host. These should be redirected to the ROM's own handlers, except when they need to be hooked to a patch's own functions.

Default values are provided in the linker scripts for TI's code examples, and also in the default images of a virgin tag.

//ROM ISRs, starting points, lengths not correct,
//needed to force correct compilation
RFPMM_ROM_ISR      : origin = 0x5BB8, length = 0x0002
PORT1_ROM_ISR      : origin = 0x59C0, length = 0x0002
SD_14_ROM_ISR      : origin = 0x5A06, length = 0x0002
USCI_B0_ROM_ISR    : origin = 0x488A, length = 0x0002
RF13M_ROM_ISR      : origin = 0x54D0, length = 0x0002
WDT_ROM_ISR        : origin = 0x5E48, length = 0x0002
TIMER_A1_ROM_ISR   : origin = 0x5E2A, length = 0x0002
TIMER_A0_ROM_ISR   : origin = 0x4E76, length = 0x0002
UNMI_ROM_ISR       : origin = 0x5E42, length = 0x0002
SYSNMI_ROM_ISR     : origin = 0x5E44, length = 0x0002
RESET_ROM_ISR      : origin = 0x5012, length = 0x0002

SRAM Structures

The linker scripts of TI's examples declare RAM to begin at 0x1C00 and continue for 0x1000 (4,096) bytes. This is true, but it neglects that the code in ROM also uses SRAM structures, and you must be careful not to overwrite or overlap them! Everything above 0x1E02 ought to be safe to use, as all global variables and the call stack are before this in memory.

1C0A -- Command Handler Vector Table
1C69 -- End of Command Table

FRAM Structures

FRAM contains a number of structures. The general rule is that 0xF800 to 0xF867 is reserved for ROM's usage, leaving 0x0768 bytes from 0xF868 to 0xFFCF for application code.

F850 -- Lock blocks memory table.
F867 -- Table ends.

F868 -- Virtual registers.  (Also beginning of ISO 15693 blocks!)
F8AF -- Virtual registers end.

FFD0 -- JTAG Signature, FFFF for unlocked.
FFD4 -- BSL Signature, FFFF for unlocked.

FFCE -- Handler Patch Table (See Handler Vector Table.)

FFE0 -- Interrupt table, stretching to end of memory.

Handler Vector Table

NFC functions are kept in a 96-byte structure of 48 function handler addresses in SRAM at 0x1C0A. This structure is initialized by copying a static version from ROM at 0x5E4E, then altering that structure from a patch table in FRAM at 0xFFCE.

When a command arrives, the command number and optional fields, such as the vendor ID and the address, are verified by rom_handle_cmd() at 0x4590, which then dispatches handling of the command body to the appropriate function from the table.

See Shellcode for an example of how to patch this table in a running device in order to run code from SRAM or FRAM.

Patch Table Format

A patch table begins at ffce with a magic word of cece, then grows downward as pairs of 16-bit words until it ends with cece. Each pair has a vendor command word in little endian (a0 00 for the A0 command) and the address of the handler function.

The handler function is called just after the command byte (a0 or whatever) and the vendor byte (07 for Texas Instruments) have been received, and the very next byte read from RF13MRXF will be the first parameter byte after the vendor ID.

Most handlers don't seem to bother with checksums on the receiving end, and the output checksum is applied by the ROM in its dispatch function. Some functions kindly send an error message on failure, but many will just silently fail to respond.

More details are in SLAU603B on page 58.

Clone this wiki locally