Skip to content

Commit 950a314

Browse files
committed
memory/spi-mem: add spi memory api calls to protect the memory
This adds api calls to lock/unlock and write a protected area of the memory.
1 parent ccc4bea commit 950a314

File tree

4 files changed

+276
-29
lines changed

4 files changed

+276
-29
lines changed

src/bootloader/startup.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ int main(void)
5555
#ifdef BOOTLOADER_DEVDEVICE
5656
qtouch_init();
5757
#endif
58-
spi_mem_test();
58+
spi_mem_protected_area_lock();
5959
bootloader_jump();
6060

6161
// If did not jump to firmware code, begin USB processing

src/firmware.c

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ int main(void)
3838
common_main();
3939
bitbox02_smarteeprom_init();
4040
spi_mem_test();
41+
spi_mem_protected_area_lock();
4142
firmware_main_loop();
4243
return 0;
4344
}

src/memory/spi_mem.c

+243-28
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,36 @@
2727
#define SECTOR_MASK 0xFFFFF000
2828
#define MEMORY_LIMIT (SPI_MEM_MEMORY_SIZE - 1)
2929
#define SR_WIP 0x01
30+
#define SR_PROTECT_BITS_MASK 0x3C
31+
#define SR_PROTECT_BITS 0xC // protect the first 2 blocks - 128KB
32+
#define CR1_TB_BIT_BOTTOM 0x8
33+
#define CR1_TB_BIT_MASK 0x8
3034
#define CMD_READ 0x03
3135
#define CMD_WREN 0x06
36+
#define CMD_WRSR 0x01
3237
#define CMD_SE 0x20
3338
#define CMD_PP 0x02
3439
#define CMD_RDSR 0x05
40+
#define CMD_RDCR 0x15
3541
#define CMD_CE 0x60
3642

43+
// Drives the chip select pin low
3744
static void _spi_mem_cs_low(void)
3845
{
3946
#ifndef TESTING
4047
gpio_set_pin_level(PIN_MEM_CS, 0);
4148
#endif
4249
}
4350

51+
// Drives the chip select pin high
4452
static void _spi_mem_cs_high(void)
4553
{
4654
#ifndef TESTING
4755
gpio_set_pin_level(PIN_MEM_CS, 1);
4856
#endif
4957
}
5058

59+
// Reads the status register
5160
static uint8_t _spi_mem_read_sr(void)
5261
{
5362
uint8_t buffer[2] = {0};
@@ -58,19 +67,19 @@ static uint8_t _spi_mem_read_sr(void)
5867
return buffer[1];
5968
}
6069

61-
static void _spi_mem_read(uint32_t address, size_t size, uint8_t* buffer)
70+
// Reads the configuration register
71+
static void _spi_mem_read_cr(uint8_t* data_out)
6272
{
63-
buffer[0] = CMD_READ;
64-
buffer[1] = (address >> 16) & 0xFF;
65-
buffer[2] = (address >> 8) & 0xFF;
66-
buffer[3] = address & 0xFF;
67-
memset(&buffer[4], 0x00, size);
68-
73+
uint8_t buffer[3] = {0};
74+
buffer[0] = CMD_RDCR;
6975
_spi_mem_cs_low();
70-
SPI_MEM_exchange_block(buffer, size + 4);
76+
SPI_MEM_exchange_block(buffer, 3);
7177
_spi_mem_cs_high();
78+
79+
memcpy(data_out, &buffer[1], 2);
7280
}
7381

82+
// Waits until the WIP bits goes low
7483
static void _spi_mem_wait(void)
7584
{
7685
uint8_t status;
@@ -79,20 +88,50 @@ static void _spi_mem_wait(void)
7988
} while (status & SR_WIP);
8089
}
8190

82-
void spi_mem_full_erase(void)
91+
// Set write enable bit
92+
static void _spi_mem_write_enable(void)
93+
{
94+
uint8_t cmd = CMD_WREN;
95+
_spi_mem_cs_low();
96+
SPI_MEM_exchange_block(&cmd, 1);
97+
_spi_mem_cs_high();
98+
}
99+
100+
// Write the status and configuration registers
101+
static void _spi_mem_write_sr(uint8_t* data_in)
83102
{
84-
uint8_t buffer[2];
103+
_spi_mem_write_enable();
104+
uint8_t buffer[4] = {0};
105+
buffer[0] = CMD_WRSR;
106+
memcpy(&buffer[1], data_in, 3);
107+
_spi_mem_cs_low();
108+
SPI_MEM_exchange_block(buffer, 4);
109+
_spi_mem_cs_high();
110+
_spi_mem_wait();
111+
}
112+
113+
// Reads `size` bytes starting from `address` and writes the data into `buffer`
114+
static void _spi_mem_read(uint32_t address, size_t size, uint8_t* buffer)
115+
{
116+
buffer[0] = CMD_READ;
117+
buffer[1] = (address >> 16) & 0xFF;
118+
buffer[2] = (address >> 8) & 0xFF;
119+
buffer[3] = address & 0xFF;
120+
memset(&buffer[4], 0x00, size);
85121

86-
// --- Enable Write ---
87-
buffer[0] = CMD_WREN;
88122
_spi_mem_cs_low();
89-
SPI_MEM_exchange_block(buffer, 1);
123+
SPI_MEM_exchange_block(buffer, size + 4);
90124
_spi_mem_cs_high();
125+
}
126+
127+
void spi_mem_full_erase(void)
128+
{
129+
_spi_mem_write_enable();
91130

92131
// --- Chip Erase ---
93-
buffer[0] = CMD_CE;
132+
uint8_t cmd = CMD_CE;
94133
_spi_mem_cs_low();
95-
SPI_MEM_exchange_block(buffer, 1);
134+
SPI_MEM_exchange_block(&cmd, 1);
96135
_spi_mem_cs_high();
97136

98137
_spi_mem_wait();
@@ -105,14 +144,10 @@ bool spi_mem_sector_erase(uint32_t sector_addr)
105144
return false;
106145
}
107146

108-
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
109-
// --- Enable Write ---
110-
buffer[0] = CMD_WREN;
111-
_spi_mem_cs_low();
112-
SPI_MEM_exchange_block(buffer, 1);
113-
_spi_mem_cs_high();
147+
_spi_mem_write_enable();
114148

115149
// --- Sector Erase (write 4 bytes) ---
150+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
116151
buffer[0] = CMD_SE;
117152
buffer[1] = (sector_addr >> 16) & 0xFF;
118153
buffer[2] = (sector_addr >> 8) & 0xFF;
@@ -163,21 +198,18 @@ uint8_t* spi_mem_read(uint32_t address, size_t size)
163198
return buffer;
164199
}
165200

201+
// Writes SPI_MEM_PAGE_SIZE bytes from `input` at `page_addr`
166202
static bool _spi_mem_page_write(uint32_t page_addr, const uint8_t* input)
167203
{
168204
if (page_addr % SPI_MEM_PAGE_SIZE != 0) {
169205
util_log("Invalid page write address %p", (void*)(uintptr_t)page_addr);
170206
return false;
171207
}
172208

173-
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
174-
// --- Enable Write ---
175-
buffer[0] = CMD_WREN;
176-
_spi_mem_cs_low();
177-
SPI_MEM_exchange_block(buffer, 1);
178-
_spi_mem_cs_high();
209+
_spi_mem_write_enable();
179210

180211
// --- Page Program (write 4 bytes) ---
212+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
181213
buffer[0] = CMD_PP;
182214
buffer[1] = (page_addr >> 16) & 0xFF;
183215
buffer[2] = (page_addr >> 8) & 0xFF;
@@ -258,6 +290,47 @@ int32_t spi_mem_smart_erase(void)
258290
return erased_sectors;
259291
}
260292

293+
// Writes the `protection` bits into the status register and sets the
294+
// Top/Bottom bit to bottom in the configuration register.
295+
static void _spi_mem_set_protection(uint8_t protection)
296+
{
297+
uint8_t reg[3];
298+
reg[0] = _spi_mem_read_sr();
299+
_spi_mem_read_cr(&reg[1]);
300+
301+
// clean and update status register with protection bits
302+
reg[0] &= ~SR_PROTECT_BITS_MASK;
303+
reg[0] |= protection & SR_PROTECT_BITS_MASK;
304+
305+
// set the top/bottom protection bit.
306+
// This is an OTP bit,so the write will have an effect
307+
// only the first time.
308+
reg[1] = reg[1] | CR1_TB_BIT_BOTTOM;
309+
310+
_spi_mem_write_sr(reg);
311+
}
312+
313+
void spi_mem_protected_area_lock(void)
314+
{
315+
_spi_mem_set_protection(SR_PROTECT_BITS);
316+
}
317+
318+
void spi_mem_protected_area_unlock(void)
319+
{
320+
_spi_mem_set_protection(0x0);
321+
}
322+
323+
bool spi_mem_protected_area_write(uint32_t address, const uint8_t* input, size_t size)
324+
{
325+
bool result;
326+
uint8_t protection = _spi_mem_read_sr() & SR_PROTECT_BITS_MASK;
327+
_spi_mem_set_protection(0x0);
328+
result = spi_mem_write(address, input, size);
329+
_spi_mem_set_protection(protection);
330+
331+
return result;
332+
}
333+
261334
// This is an utility function, useful to test the code, but not to be merged.
262335
void spi_mem_test(void)
263336
{
@@ -268,11 +341,148 @@ void spi_mem_test(void)
268341
// --- Setup test buffers ---
269342
uint8_t write_data[SPI_MEM_PAGE_SIZE];
270343
uint8_t read_data[SPI_MEM_PAGE_SIZE];
344+
uint32_t sector_addr = 0x00010000; // block 01
271345

272346
for (size_t i = 0; i < SPI_MEM_PAGE_SIZE; i++) {
273347
write_data[i] = (uint8_t)i;
274348
}
275349

350+
// === Test 0: Lock/Unlock Protection Behavior ===
351+
util_log("Test 0: Lock/Unlock Protection Verification");
352+
353+
// Backup original SR and CR
354+
uint8_t sr_before = _spi_mem_read_sr();
355+
uint8_t cr_before[2];
356+
_spi_mem_read_cr(cr_before);
357+
358+
// unlock memory if it was already locked
359+
if ((sr_before & SR_PROTECT_BITS_MASK) != 0) {
360+
_spi_mem_set_protection(0);
361+
sr_before = _spi_mem_read_sr();
362+
}
363+
364+
// 0.1 - Lock memory
365+
spi_mem_protected_area_lock();
366+
uint8_t sr_locked = _spi_mem_read_sr();
367+
uint8_t cr_locked[2];
368+
_spi_mem_read_cr(cr_locked);
369+
370+
// Verify that the protection bits were set
371+
if ((sr_locked & SR_PROTECT_BITS_MASK) != SR_PROTECT_BITS) {
372+
util_log("Test 0.1: Lock bits not set correctly");
373+
success = false;
374+
}
375+
376+
// Verify that the remaining part of the SR register didn't change
377+
if ((sr_locked & ~SR_PROTECT_BITS_MASK) != (sr_before & ~SR_PROTECT_BITS_MASK)) {
378+
util_log("Test 0.1: Status register corrupted");
379+
success = false;
380+
}
381+
382+
// Verify that the TB bit was set
383+
if ((cr_locked[0] & CR1_TB_BIT_MASK) != CR1_TB_BIT_BOTTOM) {
384+
util_log("Test 0.1: Lock bits not set correctly");
385+
success = false;
386+
}
387+
388+
// Verify that the remaining part of the CR register didn't change
389+
bool cr1_corrupted = (cr_before[0] & ~CR1_TB_BIT_MASK) != (cr_locked[0] & ~CR1_TB_BIT_MASK);
390+
if (cr1_corrupted || (cr_locked[1] != cr_before[1])) {
391+
util_log("Test 0.1: CR register changed unexpectedly during lock");
392+
success = false;
393+
}
394+
395+
// 0.2 - Verify that writing locked memory doesn't change the data
396+
uint8_t* read_page = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
397+
if (!read_page) {
398+
success = false;
399+
}
400+
if (!spi_mem_write(sector_addr, write_data, SPI_MEM_PAGE_SIZE)) {
401+
success = false;
402+
}
403+
uint8_t* read_page2 = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
404+
if (!read_page2) {
405+
success = false;
406+
}
407+
if (memcmp(read_page, read_page2, SPI_MEM_PAGE_SIZE) != 0) {
408+
util_log("Test 0.2: Write to protected area incorrectly allowed");
409+
success = false;
410+
}
411+
free(read_page2);
412+
413+
// 0.3 - Verify that we can still write unprotected sectors
414+
uint32_t unprotected_sector = 0x001D0000; // block 29
415+
uint8_t* read_unprotected_page = spi_mem_read(unprotected_sector, SPI_MEM_PAGE_SIZE);
416+
if (!read_page) {
417+
success = false;
418+
}
419+
if (!spi_mem_write(unprotected_sector, write_data, SPI_MEM_PAGE_SIZE)) {
420+
success = false;
421+
}
422+
read_page2 = spi_mem_read(unprotected_sector, SPI_MEM_PAGE_SIZE);
423+
if (!read_page2) {
424+
success = false;
425+
}
426+
if (memcmp(read_unprotected_page, read_page2, SPI_MEM_PAGE_SIZE) == 0) {
427+
util_log("Test 0.3: Write to unprotected area failed");
428+
success = false;
429+
}
430+
free(read_page2);
431+
free(read_unprotected_page);
432+
433+
// 0.4 - Verify that writing locked memory with write_locked changes the data
434+
if (!spi_mem_protected_area_write(sector_addr, write_data, SPI_MEM_PAGE_SIZE)) {
435+
success = false;
436+
}
437+
read_page2 = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
438+
if (!read_page2) {
439+
success = false;
440+
}
441+
if (memcmp(read_page, read_page2, SPI_MEM_PAGE_SIZE) == 0) {
442+
util_log("Test 0.4: Write to protected area failed");
443+
success = false;
444+
}
445+
free(read_page2);
446+
447+
// 0.5 - Verify that protected area is still locked after write
448+
sr_locked = _spi_mem_read_sr();
449+
if ((sr_locked & SR_PROTECT_BITS_MASK) != SR_PROTECT_BITS) {
450+
util_log("Test 0.5: Lock bits not set correctly");
451+
success = false;
452+
}
453+
// 0.6 - Unlock the memory and write back the initial data, with normal write
454+
spi_mem_protected_area_unlock();
455+
if (!spi_mem_write(sector_addr, read_page, SPI_MEM_PAGE_SIZE)) {
456+
success = false;
457+
}
458+
read_page2 = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
459+
if (!read_page2) {
460+
success = false;
461+
}
462+
if (memcmp(read_page, read_page2, SPI_MEM_PAGE_SIZE) != 0) {
463+
util_log("Test 0.6: Write to unprotected area failed");
464+
success = false;
465+
}
466+
free(read_page2);
467+
free(read_page);
468+
469+
// 0.7 - Verify SR are restored or unchanged in unexpected ways
470+
uint8_t sr_final = _spi_mem_read_sr();
471+
uint8_t cr_final[2];
472+
_spi_mem_read_cr(cr_final);
473+
474+
if (sr_final != sr_before) {
475+
util_log("Test 0.7: Status register corrupted during unlock");
476+
success = false;
477+
}
478+
479+
// Verify that the remaining part of the CR register didn't change
480+
cr1_corrupted = (cr_before[0] & ~CR1_TB_BIT_MASK) != (cr_final[0] & ~CR1_TB_BIT_MASK);
481+
if (cr1_corrupted || (cr_locked[1] != cr_before[1])) {
482+
util_log("Test 0.7: CR register changed unexpectedly during unlock");
483+
success = false;
484+
}
485+
276486
// === Test 1: Valid page write/read at address 0 ===
277487
uint32_t addr_start = 0x00000000;
278488
if (!spi_mem_sector_erase(addr_start)) {
@@ -324,7 +534,6 @@ void spi_mem_test(void)
324534
}
325535

326536
// === Test 3: Full sector write and read ===
327-
uint32_t sector_addr = 0x00020000;
328537
uint8_t sector_write_data[SPI_MEM_SECTOR_SIZE];
329538
uint8_t* sector_read_data;
330539

@@ -628,6 +837,12 @@ void spi_mem_test(void)
628837
util_log("Test 10.6: Zero-size read correctly rejected");
629838
}
630839

840+
// Finally clean up the memory
841+
if (!spi_mem_smart_erase()) {
842+
util_log("Final smart erased failed");
843+
success = false;
844+
}
845+
631846
util_log("==== spi_mem_test %s ====", success ? "PASSED ✅" : "FAILED ❌");
632847
screen_sprintf_debug(10000, "Test result: %s", success ? "OK" : "FAILED");
633848
}

0 commit comments

Comments
 (0)