# 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 flash** — `interrupt_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 - Remote: http://gogs.weclouds.xyz:3000/kennyh/encryption_sm.git - Branch: main - Commit + push after each task completion ## 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)