Skip to content

Commit 4e0254d

Browse files
committed
Merge branch 'abortautoenter'
2 parents 2c07fc7 + a75f984 commit 4e0254d

File tree

6 files changed

+207
-34
lines changed

6 files changed

+207
-34
lines changed

src/memory/spi_mem.c

Lines changed: 140 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#include "spi_mem.h"
1616
#include "bitbox02_pins.h"
17+
#include "random.h"
18+
#include "screen.h"
1719
#include "util.h"
1820
#include <hal_delay.h>
1921
#include <spi_lite.h>
@@ -24,23 +26,33 @@
2426
#define SECTOR_MASK 0xFFFFF000
2527
#define MEMORY_LIMIT (SPI_MEM_MEMORY_SIZE - 1)
2628
#define SR_WIP 0x01
29+
#define SR_PROTECT_BITS_MASK 0x3C
30+
#define SR_PROTECT_BITS_SHIFT 2
31+
#define SR_PROTECT_BITS (SPI_MEM_PROTECTED_BLOCKS << SR_PROTECT_BITS_SHIFT)
32+
#define CR1_TB_BIT_BOTTOM 0x8
33+
#define CR1_TB_BIT_MASK 0x8
2734
#define CMD_READ 0x03
2835
#define CMD_WREN 0x06
36+
#define CMD_WRSR 0x01
2937
#define CMD_SE 0x20
3038
#define CMD_PP 0x02
3139
#define CMD_RDSR 0x05
40+
#define CMD_RDCR 0x15
3241
#define CMD_CE 0x60
3342

43+
// Drives the chip select pin low
3444
static void _spi_mem_cs_low(void)
3545
{
3646
gpio_set_pin_level(PIN_MEM_CS, 0);
3747
}
3848

49+
// Drives the chip select pin high
3950
static void _spi_mem_cs_high(void)
4051
{
4152
gpio_set_pin_level(PIN_MEM_CS, 1);
4253
}
4354

55+
// Reads the status register
4456
static uint8_t _spi_mem_read_sr(void)
4557
{
4658
uint8_t buffer[2] = {0};
@@ -51,19 +63,19 @@ static uint8_t _spi_mem_read_sr(void)
5163
return buffer[1];
5264
}
5365

54-
static void _spi_mem_read(uint32_t address, size_t size, uint8_t* buffer)
66+
// Reads the configuration register
67+
static void _spi_mem_read_cr(uint8_t* data_out)
5568
{
56-
buffer[0] = CMD_READ;
57-
buffer[1] = (address >> 16) & 0xFF;
58-
buffer[2] = (address >> 8) & 0xFF;
59-
buffer[3] = address & 0xFF;
60-
memset(&buffer[4], 0x00, size);
61-
69+
uint8_t buffer[3] = {0};
70+
buffer[0] = CMD_RDCR;
6271
_spi_mem_cs_low();
63-
SPI_MEM_exchange_block(buffer, size + 4);
72+
SPI_MEM_exchange_block(buffer, 3);
6473
_spi_mem_cs_high();
74+
75+
memcpy(data_out, &buffer[1], 2);
6576
}
6677

78+
// Waits until the WIP bits goes low
6779
static void _spi_mem_wait(void)
6880
{
6981
uint8_t status;
@@ -72,23 +84,67 @@ static void _spi_mem_wait(void)
7284
} while (status & SR_WIP);
7385
}
7486

75-
void spi_mem_full_erase(void)
87+
// Set write enable bit
88+
static void _spi_mem_write_enable(void)
89+
{
90+
uint8_t cmd = CMD_WREN;
91+
_spi_mem_cs_low();
92+
SPI_MEM_exchange_block(&cmd, 1);
93+
_spi_mem_cs_high();
94+
}
95+
96+
// Verify if the given address is protected
97+
static bool _spi_mem_verify_address_protected(uint32_t address)
98+
{
99+
uint8_t protected_blocks = (_spi_mem_read_sr() & SR_PROTECT_BITS_MASK) >> SR_PROTECT_BITS_SHIFT;
100+
if (address < (protected_blocks * SPI_MEM_BLOCK_SIZE)) {
101+
return true;
102+
}
103+
return false;
104+
}
105+
106+
// Write the status and configuration registers
107+
static void _spi_mem_write_sr(uint8_t* data_in)
76108
{
77-
uint8_t buffer[2];
109+
_spi_mem_write_enable();
110+
uint8_t buffer[4] = {0};
111+
buffer[0] = CMD_WRSR;
112+
memcpy(&buffer[1], data_in, 3);
113+
_spi_mem_cs_low();
114+
SPI_MEM_exchange_block(buffer, 4);
115+
_spi_mem_cs_high();
116+
_spi_mem_wait();
117+
}
118+
119+
// Reads `size` bytes starting from `address` and writes the data into `buffer`
120+
static void _spi_mem_read(uint32_t address, size_t size, uint8_t* buffer)
121+
{
122+
buffer[0] = CMD_READ;
123+
buffer[1] = (address >> 16) & 0xFF;
124+
buffer[2] = (address >> 8) & 0xFF;
125+
buffer[3] = address & 0xFF;
126+
memset(&buffer[4], 0x00, size);
78127

79-
// --- Enable Write ---
80-
buffer[0] = CMD_WREN;
81128
_spi_mem_cs_low();
82-
SPI_MEM_exchange_block(buffer, 1);
129+
SPI_MEM_exchange_block(buffer, size + 4);
83130
_spi_mem_cs_high();
131+
}
132+
133+
bool spi_mem_full_erase(void)
134+
{
135+
if (_spi_mem_read_sr() & SR_PROTECT_BITS_MASK) {
136+
util_log("Cannot erase with protected area locked.");
137+
return false;
138+
}
139+
_spi_mem_write_enable();
84140

85-
// --- Chip Erase ---
86-
buffer[0] = CMD_CE;
141+
uint8_t cmd = CMD_CE;
87142
_spi_mem_cs_low();
88-
SPI_MEM_exchange_block(buffer, 1);
143+
SPI_MEM_exchange_block(&cmd, 1);
89144
_spi_mem_cs_high();
90145

91146
_spi_mem_wait();
147+
return true;
92148
}
93149

94150
bool spi_mem_sector_erase(uint32_t sector_addr)
@@ -97,15 +153,15 @@ bool spi_mem_sector_erase(uint32_t sector_addr)
97153
util_log("Invalid sector address %p", (void*)(uintptr_t)sector_addr);
98154
return false;
99155
}
156+
if (_spi_mem_verify_address_protected(sector_addr)) {
157+
util_log("Sector address %p protected", (void*)(uintptr_t)sector_addr);
158+
return false;
159+
}
100160

101-
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
102-
// --- Enable Write ---
103-
buffer[0] = CMD_WREN;
104-
_spi_mem_cs_low();
105-
SPI_MEM_exchange_block(buffer, 1);
106-
_spi_mem_cs_high();
161+
_spi_mem_write_enable();
107162

108163
// --- Sector Erase (write 4 bytes) ---
164+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
109165
buffer[0] = CMD_SE;
110166
buffer[1] = (sector_addr >> 16) & 0xFF;
111167
buffer[2] = (sector_addr >> 8) & 0xFF;
@@ -156,21 +212,23 @@ uint8_t* spi_mem_read(uint32_t address, size_t size)
156212
return buffer;
157213
}
158214

215+
// Writes SPI_MEM_PAGE_SIZE bytes from `input` at `page_addr`
159216
static bool _spi_mem_page_write(uint32_t page_addr, const uint8_t* input)
160217
{
161218
if (page_addr % SPI_MEM_PAGE_SIZE != 0) {
162219
util_log("Invalid page write address %p", (void*)(uintptr_t)page_addr);
163220
return false;
164221
}
165222

166-
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
167-
// --- Enable Write ---
168-
buffer[0] = CMD_WREN;
169-
_spi_mem_cs_low();
170-
SPI_MEM_exchange_block(buffer, 1);
171-
_spi_mem_cs_high();
223+
if (_spi_mem_verify_address_protected(page_addr)) {
224+
util_log("Page address %p protected", (void*)(uintptr_t)page_addr);
225+
return false;
226+
}
227+
228+
_spi_mem_write_enable();
172229

173230
// --- Page Program (write 4 bytes) ---
231+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
174232
buffer[0] = CMD_PP;
175233
buffer[1] = (page_addr >> 16) & 0xFF;
176234
buffer[2] = (page_addr >> 8) & 0xFF;
@@ -194,6 +252,11 @@ bool spi_mem_write(uint32_t address, const uint8_t* input, size_t size)
194252
return false;
195253
}
196254

255+
if (_spi_mem_verify_address_protected(address)) {
256+
util_log("Address %p protected", (void*)(uintptr_t)address);
257+
return false;
258+
}
259+
197260
uint32_t initial_sector_addr = address & SECTOR_MASK;
198261
uint32_t final_sector_addr = ((address + size - 1) & SECTOR_MASK) + SPI_MEM_SECTOR_SIZE;
199262
uint16_t sectors = (final_sector_addr - initial_sector_addr) / SPI_MEM_SECTOR_SIZE;
@@ -250,3 +313,52 @@ int32_t spi_mem_smart_erase(void)
250313

251314
return erased_sectors;
252315
}
316+
317+
// Writes the `protection` bits into the status register and sets the
318+
// Top/Bottom bit to bottom in the configuration register.
319+
static void _spi_mem_set_protection(uint8_t protection)
320+
{
321+
uint8_t reg[3];
322+
reg[0] = _spi_mem_read_sr();
323+
_spi_mem_read_cr(&reg[1]);
324+
325+
// clean and update status register with protection bits
326+
reg[0] &= ~SR_PROTECT_BITS_MASK;
327+
reg[0] |= protection & SR_PROTECT_BITS_MASK;
328+
329+
// set the top/bottom protection bit.
330+
// This is an OTP bit,so the write will have an effect
331+
// only the first time.
332+
reg[1] = reg[1] | CR1_TB_BIT_BOTTOM;
333+
334+
_spi_mem_write_sr(reg);
335+
}
336+
337+
void spi_mem_protected_area_lock(void)
338+
{
339+
_spi_mem_set_protection(SR_PROTECT_BITS);
340+
}
341+
342+
void spi_mem_protected_area_unlock(void)
343+
{
344+
_spi_mem_set_protection(0x0);
345+
}
346+
347+
bool spi_mem_protected_area_write(uint32_t address, const uint8_t* input, size_t size)
348+
{
349+
// Additional assert to simplify debug.
350+
ASSERT(_spi_mem_verify_address_protected(address + size));
351+
if (!_spi_mem_verify_address_protected(address + size)) {
352+
util_log(
353+
"Write address %p and size %i outside protected area",
354+
(void*)(uintptr_t)address,
355+
(int)size);
356+
return false;
357+
}
358+
uint8_t protection = _spi_mem_read_sr() & SR_PROTECT_BITS_MASK;
359+
_spi_mem_set_protection(0x0);
360+
bool result = spi_mem_write(address, input, size);
361+
_spi_mem_set_protection(protection);
362+
363+
return result;
364+
}

src/memory/spi_mem.h

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@
2222

2323
#define SPI_MEM_PAGE_SIZE 0x100 // 256
2424
#define SPI_MEM_SECTOR_SIZE 0x1000 // 4k
25-
#define SPI_MEM_BLOCK_SIZE 0x8000 // 32k
25+
#define SPI_MEM_BLOCK_SIZE 0x10000 // 64k
2626
#define SPI_MEM_MEMORY_SIZE 0x200000 // 2M
27+
#define SPI_MEM_PROTECTED_BLOCKS 2 // First 2 blocks - 128KB
2728

2829
/**
2930
* @brief Erase the entire flash memory chip.
3031
*
3132
* This operation is blocking and will take 30-60 seconds.
33+
*
34+
* @return true on success, false on error.
3235
*/
33-
void spi_mem_full_erase(void);
36+
bool spi_mem_full_erase(void);
3437

3538
/**
3639
* @brief Erase a single SECTOR_SIZE sector from flash memory.
@@ -88,4 +91,37 @@ USE_RESULT bool spi_mem_write(uint32_t address, const uint8_t* input, size_t siz
8891
*/
8992
USE_RESULT int32_t spi_mem_smart_erase(void);
9093

94+
/**
95+
* @brief Enables write protection for the first SPI_MEM_PROTECTED_BLOCKS of the memory.
96+
*
97+
* Sets the protection bits in the status register to lock the configured protected
98+
* memory region, preventing accidental writes or erases.
99+
*
100+
*/
101+
void spi_mem_protected_area_lock(void);
102+
103+
/**
104+
* @brief Disables write protection for the protected memory region.
105+
*
106+
* Clears the protection bits in the status register, allowing writes and erases
107+
* to proceed in the previously locked memory area.
108+
*
109+
*/
110+
void spi_mem_protected_area_unlock(void);
111+
112+
/**
113+
* @brief Temporarily unlocks and writes to a protected flash memory region.
114+
*
115+
* This function reads the current protection configuration, disables protection,
116+
* writes the specified data, and restores the previous protection settings.
117+
*
118+
* @param[in] address Start address to write to.
119+
* @param[in] input Pointer to the data to write.
120+
* @param[in] size Number of bytes to write.
121+
* @return true if the write operation succeeds, false otherwise.
122+
*/
123+
USE_RESULT bool spi_mem_protected_area_write(uint32_t address, const uint8_t* input, size_t size);
124+
125+
void spi_mem_test(void);
126+
91127
#endif // _SPI_MEM_H

src/platform/platform_init.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
// limitations under the License.
1414

1515
#include "platform_init.h"
16+
#include "memory/memory_shared.h"
17+
#include "memory/spi_mem.h"
1618
#include <driver_init.h>
1719
#include <ui/oled/oled.h>
1820
#if !defined(BOOTLOADER)
@@ -38,4 +40,7 @@ void platform_init(void)
3840
#if !defined(BOOTLOADER)
3941
sd_mmc_start();
4042
#endif
43+
if (memory_get_platform() == MEMORY_PLATFORM_BITBOX02_PLUS) {
44+
spi_mem_protected_area_lock();
45+
}
4146
}

src/usb/usb_processing.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ void usb_processing_init(void)
421421
void usb_processing_lock(struct usb_processing* ctx)
422422
{
423423
if (_usb_state.blocking_ctx) {
424-
Abort("Tried to lock the USB stack while locked.");
424+
AbortAutoenter("Tried to lock the USB stack while locked.");
425425
}
426426
_usb_state.blocking_ctx = ctx;
427427
}
@@ -434,7 +434,7 @@ void usb_processing_timeout_reset(void)
434434
void usb_processing_unlock(void)
435435
{
436436
if (!_usb_state.blocking_ctx) {
437-
Abort("Tried to unlock the USB stack while not locked.");
437+
AbortAutoenter("Tried to unlock the USB stack while not locked.");
438438
}
439439
_usb_state.blocking_ctx = NULL;
440440
}

test/simulator/framework/mock_spi_mem.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818

1919
__extension__ static uint8_t _memory[] = {[0 ... SPI_MEM_MEMORY_SIZE] = 0xFF};
2020

21-
void spi_mem_full_erase(void)
21+
bool spi_mem_full_erase(void)
2222
{
2323
memset(_memory, 0xFF, sizeof(_memory));
24+
return true;
2425
}
2526

2627
bool spi_mem_write(uint32_t address, const uint8_t* input, size_t size)
@@ -38,3 +39,12 @@ uint8_t* spi_mem_read(uint32_t address, size_t size)
3839
memcpy(result, &_memory[address], size);
3940
return result;
4041
}
42+
43+
void spi_mem_protected_area_lock(void) {}
44+
45+
void spi_mem_protected_area_unlock(void) {}
46+
47+
bool spi_mem_protected_area_write(uint32_t address, const uint8_t* input, size_t size)
48+
{
49+
return spi_mem_write(address, input, size);
50+
}

0 commit comments

Comments
 (0)