learnings.md 10 KB

Learnings — guomi-smart-card

Architecture

  • C*Core C0 architecture (NOT ARM), little endian
  • Bare-metal while(1) super-loop, no RTOS
  • eFlash 512KB @ 0x00400000, RAM 64KB @ 0x00800000
  • SPI FIFO only 8 bytes — MUST use DMA for any real data transfer

Crypto API Conventions

  • Gen2 _2 APIs (byte-buffer based) preferred over Gen1 struct-based
  • EDMAC clock discipline: ModuleClk_On before crypto, ModuleClk_Off after
  • TRNG must init first before any SM2 key generation
  • SM2/SM4/SHA share EDMAC channel — do not nest crypto operations

SPI/DMA Critical Details (VERIFIED via .map file)

  • dma_spitran() binten hardcoded FALSE — interrupt branch is DEAD CODE
  • Must use dma_spi_LLIReceive() for continuous async receive
  • LLI next_lli points to itself for auto-loop
  • dmac_isr() from dmac_drv.c is DISCARDED by linker — NOT in binary
  • Actual IRQ4 handler = DMA_IRQHandler from libCUni360S_mcc.a(mcc_bsp.o) at 0x0041b2ac
  • MUST modify vector_table.h ISR24 to repoint IRQ4 to our own DMA handler
  • Vector table is void *const in flashinterrupt_setup() only sets EIC priority, NOT the vector
  • SPI1+SPI2+PIT32 share IRQ3 (ISR23 = PIT32_SPI1_SPI2_ISR)
  • DMA completion interrupt goes through IRQ4 (ISR24)
  • DMAC_INT_NUM = 0x24 = VecTable[36] = ISR24
  • SPI1 base = 0x70000000, SPIDR offset = 0x12 (verified in spi_reg.h and dmac_drv.c)
  • SPI instances spaced 0x10000 apart: SPI1=0x70000000, SPI2=0x70010000, SPI3=0x70020000

CRC Hardware (VERIFIED)

  • Hardware CRC engine does NOT support configurable polynomials
  • CRC_CR has NO polynomial field — only mode (CRC-8/16/32), source, endianness
  • CRC16-CCITT (0x1021) MUST be implemented in SOFTWARE
  • CRC0_BASE_ADDR = 0x78000000, CRC1_BASE_ADDR = 0x7c000000

RAM Budget (VERIFIED via .map file)

  • .data = 2,992 bytes (0xBB0) at 0x00800000-0x00800BB0
  • .bss = 6,740 bytes (0x1A54) at 0x00800BB0-0x00802604
  • Total used: 9,732 bytes (14.8%)
  • Free heap: 47,608 bytes (0xB9F8) from 0x00802604 to 0x0080DFFC
  • Stack: 8,192 bytes from 0x0080DFFC to 0x0080FFFC
  • _bss_end = 0x00802604, _end = 0x0080DFFC, STACK_LOCATION = 0x0080FFFC

eFlash Budget (VERIFIED via .map file)

  • Firmware end: 0x0041C098 (.text_entry + .rodata + .text + .data LMA)
  • .bin size = 114,840 bytes = 0x1C098 (matches)
  • Key storage 0x00470000-0x00470800 = 100% free, page-aligned (pages 896-899)
  • EFLASH_END per alg_drv.h = 0x0047F000 (4KB reserved at top)
  • 343KB gap between firmware end and key storage region

eFlash Key Storage

  • EFLASH_SetWritePermission() is GLOBAL — no per-page protection
  • Must verify address range BEFORE enabling write, clear IMMEDIATELY after
  • 512 bytes per page, log-structured wear leveling across 4 pages

Git Strategy

GMSP Transport Layer (Task 3+4) �� Implementation Details

DMA LLI Ping-Pong Pattern

  • Existing dma_spi_LLIReceive() creates a SELF-LOOP LLI (next points to itself)
  • For Ping-Pong, need TWO LLI nodes: node[0].next_lli = &node[1], node[1].next_lli = &node[0]
  • DMA_LLI struct has 7 fields but hardware only reads first 5 (src_addr, dst_addr, next_lli, control0, len)
  • Extra fields (hs_sel, per) are software metadata �� NOT loaded by DMA hardware
  • CFG and CFG_HIGH registers persist across LLI transitions (set once, not per-node)
  • control0 value: DMA_IE | SNC | DI | LLP_DST_EN | LLP_SRC_EN | P2M_DMA = 0x18200401

Buffer Completion Tracking

  • Cannot reliably determine completed buffer from DMA_DADDR (race with ongoing transfer)
  • Cannot read DMA_LLP register (may not update during LLI operation)
  • SOLUTION: toggle counter gmsp_currently_filling �� starts at 0 (buf_A), XOR'd in ISR after each completion
  • gmsp_completed_buf = gmsp_currently_filling captures the just-finished buffer before toggling

ISR Variable Scope

  • ALL ISR-shared variables MUST be non-static if accessed from a different .c file
  • gmsp_currently_filling was initially static volatile �� broke extern in gmsp_isr.c
  • Pattern: define in spi_slave.c, extern volatile in gmsp_isr.c

Response Path Design

  • Short responses (<=8 bytes = SPI_FIFO_SIZE): SPI_SlaveSendData (CPU register loop)
  • Long responses (>8 bytes): dma_spitran(0, resp, dummy, len, FALSE) �� blocking DMA
  • dma_spitran uses BOTH CH0 (TX) and CH1 (RX) �� must stop LLI RX on CH1 first
  • After response: caller must call gmsp_transport_rearm() to restart LLI
  • dma_spitran disables DMAC (DMA_CONFIG=0, DMA_CHEN=0) at end �� rearm must re-enable

IRQ3/IRQ4 Vector Table Repointing

  • ISR23 (IRQ3): was PIT32_SPI1_SPI2_ISR �� now gmsp_irq3_handler
  • ISR24 (IRQ4): was DMA_IRQHandler (from .a lib) �� now gmsp_dma_irq_handler
  • Must add extern void declarations in vector_table.h for new handlers
  • Original PIT32_SPI1_SPI2_ISR in pit32_drv.c still exists but is no longer called
  • Original DMA_IRQHandler in libCUni360S_mcc.a still exists but vector no longer points to it

PIT32 Interrupt Flag Clearing

  • PIT32 PCSR register: PCSR_PIF (0x04) = interrupt pending flag
  • Clear by: PIT32->PCSR |= PIT_PIF (write 1 to clear, same as original handler)
  • PIT_PIF = (1<<2) = 0x04, same as PCSR_PIF

SPI Status Register Access

  • SPISR is at offset 0x16 in SPI_TypeDef, union with SPISRHW (16-bit) at same offset
  • Read SPI1->SPISRHW for full 16-bit status in IRQ3 handler
  • Error bits: SPISR_RXFOVF(0x0400), SPISR_RXFUDF(0x0200), SPISR_TXFOVF(0x4000), SPISR_TXFUDF(0x2000), SPISR_MODF(0x0010), SPISR_FLOST(0x0040)
  • In DMA mode, SPI interrupt enables should be off �� IRQ3 only fires for PIT32

LSP Diagnostics

  • clangd cannot resolve Eclipse CDT include paths �� ALL source files show "file not found" for type.h, memmap.h etc.
  • This is project-wide, NOT a code error. Same errors exist on dmac_drv.c, spi_demo.c etc.
  • Verification: compare new file errors against existing file errors �� if same pattern, code is correct

GMSP Protocol Layer (Task 5+6) �� Implementation Details

CRC16-CCITT Software Implementation

  • Hardware CRC engine CANNOT do CRC16-CCITT (poly 0x1021) �� confirmed again
  • 256-entry lookup table (512 bytes const in flash) is pre-computed, stored in .rodata
  • Table generation: for each i in 0..255, crc = i<<8, then 8 rounds of (crc<<1 ^ 0x1021 if MSB set)
  • Algorithm per byte: idx = (crc >> 8) ^ data[i]; crc = (crc << 8) ^ table[idx];
  • Test vector verified: ASCII "123456789" �� 0x29B1
  • Incremental API (init/update/final) uses a static context variable g_crc_ctx
  • gmsp_crc16_calc() is a one-shot wrapper: init + update + final

Frame Protocol Design Decisions

  • Frame format: SYNC(0xAA,0x55) + LEN(big-endian, 2B) + CMD/STATUS(1B) + PAYLOAD(N) + CRC16(big-endian, 2B)
  • LEN = payload length only (NOT including CMD/STATUS or CRC)
  • Total frame size = LEN + 7 (SYNC2 + LEN2 + CMD/STATUS1 + CRC2)
  • CRC coverage: LEN(2) + CMD/STATUS(1) + PAYLOAD/RESPONSE(N) = everything between SYNC and CRC
  • CRC is computed incrementally as bytes arrive through the state machine
  • Both LEN and CRC are big-endian (MSB first on wire), matching SYNC byte order

State Machine Parser

  • States: IDLE �� SYNC1 �� SYNC2 �� LEN_HI �� LEN_LO �� CMD �� PAYLOAD �� CRC_HI �� CRC_LO �� DONE
  • State semantics: "what was the last significant byte received"
  • LEN_LO state processes CMD byte; CMD state processes first payload byte (or CRC MSB if len==0)
  • Parser uses static variables for multi-buffer resilience (though frames fit in single 256B buffer)
  • g_payload_ptr points directly into raw_buf �� NO memory copy/allocation
  • On CRC mismatch: state resets to IDLE and continues scanning remaining bytes in buffer
  • SYNC1 state handles repeated 0xAA bytes (stays in SYNC1 for re-sync)

Response Builder

  • Response frame: SYNC + LEN + STATUS + DATA + CRC (same layout, STATUS replaces CMD)
  • cmd parameter is accepted but NOT written to frame (per spec: no CMD echo in response)
  • CRC covers LEN(2) + STATUS(1) + DATA(resp_len) = resp_len + 3 bytes
  • Returns total frame length = resp_len + 7

Verification Notes

  • LSP diagnostics show ONLY type.h-not-found errors (same as all project files)
  • No structural/syntax errors in any of the 4 new files
  • Round-trip test: build frame �� parse frame �� verify CRC and fields �� PASS

GMSP Crypto Service Layer (Tasks 7-9, 11)

SM2 Service (Task 7)

  • sm2_svc.c uses Gen2 _2 APIs exclusively: SM2GenerateKeyPair_2, SM2Signature_2, SM2Verification_2, SM2Encrypt_2, SM2Decrypt_2
  • Private key stored in static g_sm2_privkey[32] -- NEVER exposed via API
  • g_sm2_initialized flag prevents double-init and Init_Trng() calls
  • sm2_svc_init() calls Init_Trng() once, enables TRNG+DMA+CRYPTO clocks
  • sm2_svc_keygen/sign/verify: enable TRNG_IPG_CLK + DMA_HCLK + CRYPTO before, disable after
  • sm2_svc_encrypt/decrypt: enable DMA_HCLK + SHA + CRYPTO (encrypt uses SM3 internally)
  • SM2Signature_2() does NOT accept IDA parameter -- uses default user ID internally
  • SM2Verification_2() returns SUCCESS_ALG=1 if verified, FAIL_ALG=0 if not

SM3 Service (Task 8)

  • SM3_CTX struct: { hash_mode (UINT8), hash_state (Hash_tx) }
  • SHA_Init_2(ctx, HASH_SM3) -> SHA_Update_2(ctx, data, len) -> SHA_Final_2(ctx, digest)
  • sm3_svc_hash() is one-shot with auto clock management
  • Streaming API: caller manages clocks (sm3_svc_init enables them once)

SM4 Service (Task 9)

  • SMS4_EnDecrypt_2(encde_mode, ecb_cbc_mode, key, iv, src, out, data_len)
  • encde_mode: 0=encrypt, 1=decrypt; ecb_cbc_mode: 0=ECB, 1=CBC
  • For ECB mode, iv parameter can be NULL
  • Clock: ModuleClk_On(MODULE_CLK_SMS4) + MODULE_CLK_DMA_HCLK before, Off after
  • Data length MUST be multiple of 16

Keystore (Task 11)

  • 4 pages x 512B at 0x00470000, log-structured wear leveling
  • Page record: magic(0xA5C3,2B)+counter(4B big-endian)+privkey(64B)+CRC16(2B)=72B
  • EFLASH_SetWritePermission() -> WordsProg(18 words) -> EFLASH_ClrWritePermission() MINIMAL window
  • IS_KEY_ADDR(addr) validates all address operations

Include Path Convention

  • Eclipse CDT has -I"src" and -I"include" configured
  • sm4_svc.c uses relative paths for driver includes
  • LSP diagnostics are false positives (Eclipse CDT specific config)