Files
n_signer/firmware/teensy41/signer/imxrt1062_t41_flashmem.ld
T
Laan Tungir ac2e6347a2 v0.1.6 - Teensy 4.1 SD-card OTP pad: real implementation working on hardware
- otppad_embedded: bit-compatible port of libotppad (2386/2386 host tests pass)
- otp_pad_sd: SdFat-direct SD card pad reader (FAT-only, ASCII armor + binary .otp)
- pad_gen.ino: TRNG-sourced 1 MB pad generator using i.MX RT1062 TRNG registers
- Linker script: moved .rodata from DTCM to FLASH (EXCLUDE_FILE ed25519),
  reclaiming 124 KB DTCM, free stack 5.9 KB -> 130.9 KB
- check_stack.sh: build-time FlexRAM stack gauge, wired into build_signer.sh
- test_otp_sd.py: 8/9 hardware tests pass (ASCII + binary round-trips,
  offset advance, tamper detection; 10 KB plaintext times out on perf)
- test_classical.py: 16/16 pass with new memory layout (ed25519 OK)
- Memory evaluation document: plans/teensy41_memory_evaluation.md
2026-07-30 17:10:50 -04:00

233 lines
6.5 KiB
Plaintext

MEMORY
{
ITCM (rx): ORIGIN = 0x00000000, LENGTH = 512K
DTCM (rw): ORIGIN = 0x20000000, LENGTH = 512K
RAM (rw): ORIGIN = 0x20200000, LENGTH = 512K
FLASH (rx): ORIGIN = 0x60000000, LENGTH = 7936K
ERAM (rw): ORIGIN = 0x70000000, LENGTH = 32768K
}
ENTRY(ImageVectorTable)
SECTIONS
{
.text.headers : {
KEEP(*(.flashconfig))
FILL(0xFF)
. = ORIGIN(FLASH) + 0x1000;
KEEP(*(.ivt))
KEEP(*(.bootdata))
. = ALIGN(1024);
} > FLASH
.text.code : {
KEEP(*(.startup))
*(.flashmem*)
/* Route the heavy crypto libraries (secp256k1, PQClean, ed25519,
* nostr_utils) into FLASH (.text.code) instead of ITCM (.text.itcm).
* This frees up RAM1 (ITCM) so the DTCM stack is larger, avoiding
* stack overflows in the ed25519/x25519/PQ key-derivation paths
* whose SHA-512 W[80] + HMAC ikey/okey buffers are ~2-3 KB on the
* stack. The crypto code runs from flash (slightly slower) but the
* stack headroom is the critical constraint on the Teensy 4.1. */
*secp256k1.c.o(.text*)
*secp256k1.c.o(.rodata*)
/* Note: ed25519.c .text is already in .flashmem (functions are
* marked __attribute__((section(".flashmem")))). We do NOT move
* ed25519.c.o(.rodata*) to flash — the ed_K SHA-512 round constants
* and ed_X/ed_Y base point must stay in DTCM (.data) for correct
* access. Moving them to flash produced an all-zeros pubkey. */
*nostr_utils.c.o(.text*)
*nostr_utils.c.o(.rodata*)
*nostr_secp256k1.c.o(.text*)
*nostr_secp256k1.c.o(.rodata*)
*cbd.c.o(.text*)
*cbd.c.o(.rodata*)
*indcpa.c.o(.text*)
*indcpa.c.o(.rodata*)
*kem.c.o(.text*)
*kem.c.o(.rodata*)
*mlkem768_ntt.c.o(.text*)
*mlkem768_ntt.c.o(.rodata*)
*mlkem768_poly.c.o(.text*)
*mlkem768_poly.c.o(.rodata*)
*reduce.c.o(.text*)
*reduce.c.o(.rodata*)
*symmetric.c.o(.text*)
*symmetric.c.o(.rodata*)
*verify.c.o(.text*)
*verify.c.o(.rodata*)
*mldsa65_ntt.c.o(.text*)
*mldsa65_ntt.c.o(.rodata*)
*mldsa65_poly.c.o(.text*)
*mldsa65_poly.c.o(.rodata*)
*mldsa65_sign.c.o(.text*)
*mldsa65_sign.c.o(.rodata*)
*address.c.o(.text*)
*address.c.o(.rodata*)
*fors.c.o(.text*)
*fors.c.o(.rodata*)
*hash.c.o(.text*)
*hash.c.o(.rodata*)
*slhdsa128s_sign.c.o(.text*)
*slhdsa128s_sign.c.o(.rodata*)
*slhdsa128s_utils.c.o(.text*)
*slhdsa128s_utils.c.o(.rodata*)
*thash.c.o(.text*)
*thash.c.o(.rodata*)
*wots.c.o(.text*)
*wots.c.o(.rodata*)
/* Route the SdFat library (SD card access) into FLASH too, so it
* doesn't consume ITCM and overflow the flexRAM DTCM partition.
* Without this, including <SdFat.h> crashes the device before
* setup() runs (ITCM grows past 12 blocks, reducing DTCM below
* what .data needs). See plans/teensy41_otp_sd_pad.md §BLOCKER. */
*FatFile.cpp.o(.text*)
*FatFile.cpp.o(.rodata*)
*FatFileLFN.cpp.o(.text*)
*FatFileLFN.cpp.o(.rodata*)
*FatFileSFN.cpp.o(.text*)
*FatFileSFN.cpp.o(.rodata*)
*FatFilePrint.cpp.o(.text*)
*FatFilePrint.cpp.o(.rodata*)
*FatPartition.cpp.o(.text*)
*FatPartition.cpp.o(.rodata*)
*FatVolume.cpp.o(.text*)
*FatVolume.cpp.o(.rodata*)
*FatName.cpp.o(.text*)
*FatName.cpp.o(.rodata*)
*FatFormatter.cpp.o(.text*)
*FatFormatter.cpp.o(.rodata*)
*FatDbg.cpp.o(.text*)
*FatDbg.cpp.o(.rodata*)
*FsCache.cpp.o(.text*)
*FsCache.cpp.o(.rodata*)
*FsFile.cpp.o(.text*)
*FsFile.cpp.o(.rodata*)
*FsVolume.cpp.o(.text*)
*FsVolume.cpp.o(.rodata*)
*FsName.cpp.o(.text*)
*FsName.cpp.o(.rodata*)
*FsStructs.cpp.o(.text*)
*FsStructs.cpp.o(.rodata*)
*FsNew.cpp.o(.text*)
*FsNew.cpp.o(.rodata*)
*FsUtf.cpp.o(.text*)
*FsUtf.cpp.o(.rodata*)
*FsDateTime.cpp.o(.text*)
*FsDateTime.cpp.o(.rodata*)
*FmtNumber.cpp.o(.text*)
*FmtNumber.cpp.o(.rodata*)
*FreeStack.cpp.o(.text*)
*FreeStack.cpp.o(.rodata*)
*MinimumSerial.cpp.o(.text*)
*MinimumSerial.cpp.o(.rodata*)
*istream.cpp.o(.text*)
*istream.cpp.o(.rodata*)
*ostream.cpp.o(.text*)
*ostream.cpp.o(.rodata*)
/* SDIO driver stays in ITCM (not FLASH) for fast interrupt response.
* Only the FAT filesystem layer goes to FLASH. */
/* Move ALL .rodata to FLASH (it's read-only, cache-friendly from
* FLASH via the D-cache) to free up DTCM for stack. This reclaims
* an estimated 40-90 KB of DTCM. The ed25519.c.o rodata is excluded
* because ed_K/ed_X/ed_Y base point constants must stay in DTCM
* (moving them to FLASH produced an all-zeros pubkey — see the
* note at the top of this file). */
*(EXCLUDE_FILE(*ed25519.c.o) .rodata*)
. = ALIGN(4);
KEEP(*(.init))
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
__init_array_start = .;
KEEP (*(.init_array))
__init_array_end = .;
. = ALIGN(4);
} > FLASH
.text.progmem : {
*(.progmem*)
. = ALIGN(4);
} > FLASH
.text.itcm : {
. = . + 32; /* MPU to trap NULL pointer deref */
*(.fastrun)
*(.text*)
. = ALIGN(16);
} > ITCM AT> FLASH
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx* .ARM.extab.text* .gnu.linkonce.armexidx.*)
__exidx_end = .;
} > ITCM AT> FLASH
.data : {
*(.endpoint_queue)
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.data*)))
KEEP(*(.vectorsram))
} > DTCM AT> FLASH
.bss ALIGN(4) : {
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(COMMON)))
. = ALIGN(32);
. = . + 32; /* MPU to trap stack overflow */
} > DTCM
.bss.dma (NOLOAD) : {
*(.hab_log)
*(.dmabuffers)
. = ALIGN(32);
} > RAM
.bss.extram (NOLOAD) : {
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.externalram)))
. = ALIGN(32);
} > ERAM
.text.csf : {
FILL(0xFF)
. = ALIGN(1024);
KEEP(*(.csf))
__text_csf_end = .;
} > FLASH
_stext = ADDR(.text.itcm);
_etext = ADDR(.text.itcm) + SIZEOF(.text.itcm) + SIZEOF(.ARM.exidx);
_stextload = LOADADDR(.text.itcm);
_sdata = ADDR(.data);
_edata = ADDR(.data) + SIZEOF(.data);
_sdataload = LOADADDR(.data);
_sbss = ADDR(.bss);
_ebss = ADDR(.bss) + SIZEOF(.bss);
_heap_start = ADDR(.bss.dma) + SIZEOF(.bss.dma);
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
_extram_start = ADDR(.bss.extram);
_extram_end = ADDR(.bss.extram) + SIZEOF(.bss.extram);
_itcm_block_count = (SIZEOF(.text.itcm) + SIZEOF(.ARM.exidx) + 0x7FFF) >> 15;
_flexram_bank_config = 0xAAAAAAAA | ((1 << (_itcm_block_count * 2)) - 1);
_estack = ORIGIN(DTCM) + ((16 - _itcm_block_count) << 15);
_flashimagelen = __text_csf_end - ORIGIN(FLASH);
_teensy_model_identifier = 0x25;
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
}