Skip to content

Commit 28dc7c4

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 28dc7c4

File tree

3 files changed

+275
-29
lines changed

3 files changed

+275
-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/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,6 +341,7 @@ 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;
@@ -324,7 +398,6 @@ void spi_mem_test(void)
324398
}
325399

326400
// === Test 3: Full sector write and read ===
327-
uint32_t sector_addr = 0x00020000;
328401
uint8_t sector_write_data[SPI_MEM_SECTOR_SIZE];
329402
uint8_t* sector_read_data;
330403

@@ -628,6 +701,148 @@ void spi_mem_test(void)
628701
util_log("Test 10.6: Zero-size read correctly rejected");
629702
}
630703

704+
// === Test 11: Lock/Unlock Protection Behavior ===
705+
util_log("Test 11: Lock/Unlock Protection Verification");
706+
707+
// Backup original SR and CR
708+
uint8_t sr_before = _spi_mem_read_sr();
709+
uint8_t cr_before[2];
710+
_spi_mem_read_cr(cr_before);
711+
712+
// unlock memory if it was already locked
713+
if ((sr_before & SR_PROTECT_BITS_MASK) != 0) {
714+
_spi_mem_set_protection(0);
715+
sr_before = _spi_mem_read_sr();
716+
}
717+
718+
// 11.1 - Lock memory
719+
spi_mem_protected_area_lock();
720+
uint8_t sr_locked = _spi_mem_read_sr();
721+
uint8_t cr_locked[2];
722+
_spi_mem_read_cr(cr_locked);
723+
724+
// Verify that the protection bits were set
725+
if ((sr_locked & SR_PROTECT_BITS_MASK) != SR_PROTECT_BITS) {
726+
util_log("Test 11.1: Lock bits not set correctly");
727+
success = false;
728+
}
729+
730+
// Verify that the remaining part of the SR register didn't change
731+
if ((sr_locked & ~SR_PROTECT_BITS_MASK) != (sr_before & ~SR_PROTECT_BITS_MASK)) {
732+
util_log("Test 11.1: Status register corrupted");
733+
success = false;
734+
}
735+
736+
// Verify that the TB bit was set
737+
if ((cr_locked[0] & CR1_TB_BIT_MASK) != CR1_TB_BIT_BOTTOM) {
738+
util_log("Test 11.1: Lock bits not set correctly");
739+
success = false;
740+
}
741+
742+
// Verify that the remaining part of the CR register didn't change
743+
bool cr1_corrupted = (cr_before[0] & ~CR1_TB_BIT_MASK) != (cr_locked[0] & ~CR1_TB_BIT_MASK);
744+
if (cr1_corrupted || (cr_locked[1] != cr_before[1])) {
745+
util_log("Test 11.1: CR register changed unexpectedly during lock");
746+
success = false;
747+
}
748+
749+
// 11.2 - Verify that writing locked memory doesn't change the data
750+
uint8_t* read_page = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
751+
if (!read_page) {
752+
success = false;
753+
}
754+
if (!spi_mem_write(sector_addr, write_data, SPI_MEM_PAGE_SIZE)) {
755+
success = false;
756+
}
757+
uint8_t* read_page2 = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
758+
if (!read_page2) {
759+
success = false;
760+
}
761+
if (memcmp(read_page, read_page2, SPI_MEM_PAGE_SIZE) != 0) {
762+
util_log("Test 11.2: Write to protected area incorrectly allowed");
763+
success = false;
764+
}
765+
free(read_page2);
766+
767+
// 11.3 - Verify that we can still write unprotected sectors
768+
uint32_t unprotected_sector = 0x001D0000; // block 29
769+
uint8_t* read_unprotected_page = spi_mem_read(unprotected_sector, SPI_MEM_PAGE_SIZE);
770+
if (!read_page) {
771+
success = false;
772+
}
773+
if (!spi_mem_write(unprotected_sector, write_data, SPI_MEM_PAGE_SIZE)) {
774+
success = false;
775+
}
776+
read_page2 = spi_mem_read(unprotected_sector, SPI_MEM_PAGE_SIZE);
777+
if (!read_page2) {
778+
success = false;
779+
}
780+
if (memcmp(read_unprotected_page, read_page2, SPI_MEM_PAGE_SIZE) == 0) {
781+
util_log("Test 11.3: Write to unprotected area failed");
782+
success = false;
783+
}
784+
free(read_page2);
785+
free(read_unprotected_page);
786+
787+
// 11.4 - Verify that writing locked memory with write_locked changes the data
788+
if (!spi_mem_protected_area_write(sector_addr, write_data, SPI_MEM_PAGE_SIZE)) {
789+
success = false;
790+
}
791+
read_page2 = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
792+
if (!read_page2) {
793+
success = false;
794+
}
795+
if (memcmp(read_page, read_page2, SPI_MEM_PAGE_SIZE) == 0) {
796+
util_log("Test 11.4: Write to protected area failed");
797+
success = false;
798+
}
799+
free(read_page2);
800+
801+
// 11.5 - Verify that protected area is still locked after write
802+
sr_locked = _spi_mem_read_sr();
803+
if ((sr_locked & SR_PROTECT_BITS_MASK) != SR_PROTECT_BITS) {
804+
util_log("Test 11.5: Lock bits not set correctly");
805+
success = false;
806+
}
807+
// 11.6 - Unlock the memory and write back the initial data, with normal write
808+
spi_mem_protected_area_unlock();
809+
if (!spi_mem_write(sector_addr, read_page, SPI_MEM_PAGE_SIZE)) {
810+
success = false;
811+
}
812+
read_page2 = spi_mem_read(sector_addr, SPI_MEM_PAGE_SIZE);
813+
if (!read_page2) {
814+
success = false;
815+
}
816+
if (memcmp(read_page, read_page2, SPI_MEM_PAGE_SIZE) != 0) {
817+
util_log("Test 11.6: Write to unprotected area failed");
818+
success = false;
819+
}
820+
free(read_page2);
821+
free(read_page);
822+
823+
// 11.7 - Verify SR are restored or unchanged in unexpected ways
824+
uint8_t sr_final = _spi_mem_read_sr();
825+
uint8_t cr_final[2];
826+
_spi_mem_read_cr(cr_final);
827+
828+
if (sr_final != sr_before) {
829+
util_log("Test 11.7: Status register corrupted during unlock");
830+
success = false;
831+
}
832+
833+
// Verify that the remaining part of the CR register didn't change
834+
cr1_corrupted = (cr_before[0] & ~CR1_TB_BIT_MASK) != (cr_final[0] & ~CR1_TB_BIT_MASK);
835+
if (cr1_corrupted || (cr_locked[1] != cr_before[1])) {
836+
util_log("Test 11.7: CR register changed unexpectedly during unlock");
837+
success = false;
838+
}
839+
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)