Skip to content

Commit fa774da

Browse files
authored
Merge pull request #1479 from NickeZ/nickez/merge-master
Nickez/merge master
2 parents 95ec42a + b72d3a8 commit fa774da

File tree

8 files changed

+254
-42
lines changed

8 files changed

+254
-42
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately.
99
### [Unreleased]
1010
- Ethereum: add confirmation screen for known networks, change base unit to ETH for Arbitrum and Optimism
1111
- Ethereum: add Base and Gnosis Chain to known networks
12+
- Ethereum: allow fetching xpub at m/44'/60'/0' and m/44'/1'/0'
1213
- Bitcoin: enable message signing on testnet and regtest
1314
- Enable Litecoin in the simulator
1415

src/memory/spi_mem.c

Lines changed: 165 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)
7689
{
77-
uint8_t buffer[2];
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+
}
78105

79-
// --- Enable Write ---
80-
buffer[0] = CMD_WREN;
106+
// Write the status and configuration registers
107+
static void _spi_mem_write_sr(uint8_t* data_in)
108+
{
109+
_spi_mem_write_enable();
110+
uint8_t buffer[4] = {0};
111+
buffer[0] = CMD_WRSR;
112+
memcpy(&buffer[1], data_in, 3);
81113
_spi_mem_cs_low();
82-
SPI_MEM_exchange_block(buffer, 1);
114+
SPI_MEM_exchange_block(buffer, 4);
83115
_spi_mem_cs_high();
116+
_spi_mem_wait();
117+
}
84118

85-
// --- Chip Erase ---
86-
buffer[0] = CMD_CE;
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);
127+
128+
_spi_mem_cs_low();
129+
SPI_MEM_exchange_block(buffer, size + 4);
130+
_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();
140+
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;
@@ -118,6 +174,21 @@ bool spi_mem_sector_erase(uint32_t sector_addr)
118174
// --- Wait for write to end ---
119175
_spi_mem_wait();
120176

177+
// --- Check that sector has been actually erased ---
178+
uint8_t page_data[SPI_MEM_PAGE_SIZE];
179+
for (size_t i = 0; i < SPI_MEM_SECTOR_SIZE / SPI_MEM_PAGE_SIZE; i++) {
180+
uint32_t page_addr = sector_addr + i * SPI_MEM_PAGE_SIZE;
181+
if (!spi_mem_page_read(page_addr, page_data)) {
182+
util_log("Read after sector erase at %p failed", (void*)(uintptr_t)page_addr);
183+
return false;
184+
}
185+
for (size_t j = 0; j < SPI_MEM_PAGE_SIZE; j++) {
186+
if (page_data[j] != 0xFF) {
187+
util_log("Sector erase at %p failed", (void*)(uintptr_t)(page_addr + j));
188+
return false;
189+
}
190+
}
191+
}
121192
return true;
122193
}
123194

@@ -156,21 +227,23 @@ uint8_t* spi_mem_read(uint32_t address, size_t size)
156227
return buffer;
157228
}
158229

230+
// Writes SPI_MEM_PAGE_SIZE bytes from `input` at `page_addr`
159231
static bool _spi_mem_page_write(uint32_t page_addr, const uint8_t* input)
160232
{
161233
if (page_addr % SPI_MEM_PAGE_SIZE != 0) {
162234
util_log("Invalid page write address %p", (void*)(uintptr_t)page_addr);
163235
return false;
164236
}
165237

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();
238+
if (_spi_mem_verify_address_protected(page_addr)) {
239+
util_log("Page address %p protected", (void*)(uintptr_t)page_addr);
240+
return false;
241+
}
242+
243+
_spi_mem_write_enable();
172244

173245
// --- Page Program (write 4 bytes) ---
246+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
174247
buffer[0] = CMD_PP;
175248
buffer[1] = (page_addr >> 16) & 0xFF;
176249
buffer[2] = (page_addr >> 8) & 0xFF;
@@ -184,6 +257,16 @@ static bool _spi_mem_page_write(uint32_t page_addr, const uint8_t* input)
184257
// --- Wait for write to end ---
185258
_spi_mem_wait();
186259

260+
// --- Check that input data has been properly written ---
261+
uint8_t read_data[SPI_MEM_PAGE_SIZE];
262+
if (!spi_mem_page_read(page_addr, read_data)) {
263+
util_log("Read after page write at %p failed", (void*)(uintptr_t)page_addr);
264+
return false;
265+
}
266+
if (memcmp(read_data, input, SPI_MEM_PAGE_SIZE) != 0) {
267+
util_log("Write page at %p failed", (void*)(uintptr_t)page_addr);
268+
return false;
269+
}
187270
return true;
188271
}
189272

@@ -194,6 +277,11 @@ bool spi_mem_write(uint32_t address, const uint8_t* input, size_t size)
194277
return false;
195278
}
196279

280+
if (_spi_mem_verify_address_protected(address)) {
281+
util_log("Address %p protected", (void*)(uintptr_t)address);
282+
return false;
283+
}
284+
197285
uint32_t initial_sector_addr = address & SECTOR_MASK;
198286
uint32_t final_sector_addr = ((address + size - 1) & SECTOR_MASK) + SPI_MEM_SECTOR_SIZE;
199287
uint16_t sectors = (final_sector_addr - initial_sector_addr) / SPI_MEM_SECTOR_SIZE;
@@ -250,3 +338,52 @@ int32_t spi_mem_smart_erase(void)
250338

251339
return erased_sectors;
252340
}
341+
342+
// Writes the `protection` bits into the status register and sets the
343+
// Top/Bottom bit to bottom in the configuration register.
344+
static void _spi_mem_set_protection(uint8_t protection)
345+
{
346+
uint8_t reg[3];
347+
reg[0] = _spi_mem_read_sr();
348+
_spi_mem_read_cr(&reg[1]);
349+
350+
// clean and update status register with protection bits
351+
reg[0] &= ~SR_PROTECT_BITS_MASK;
352+
reg[0] |= protection & SR_PROTECT_BITS_MASK;
353+
354+
// set the top/bottom protection bit.
355+
// This is an OTP bit,so the write will have an effect
356+
// only the first time.
357+
reg[1] = reg[1] | CR1_TB_BIT_BOTTOM;
358+
359+
_spi_mem_write_sr(reg);
360+
}
361+
362+
void spi_mem_protected_area_lock(void)
363+
{
364+
_spi_mem_set_protection(SR_PROTECT_BITS);
365+
}
366+
367+
void spi_mem_protected_area_unlock(void)
368+
{
369+
_spi_mem_set_protection(0x0);
370+
}
371+
372+
bool spi_mem_protected_area_write(uint32_t address, const uint8_t* input, size_t size)
373+
{
374+
// Additional assert to simplify debug.
375+
ASSERT(_spi_mem_verify_address_protected(address + size));
376+
if (!_spi_mem_verify_address_protected(address + size)) {
377+
util_log(
378+
"Write address %p and size %i outside protected area",
379+
(void*)(uintptr_t)address,
380+
(int)size);
381+
return false;
382+
}
383+
uint8_t protection = _spi_mem_read_sr() & SR_PROTECT_BITS_MASK;
384+
_spi_mem_set_protection(0x0);
385+
bool result = spi_mem_write(address, input, size);
386+
_spi_mem_set_protection(protection);
387+
388+
return result;
389+
}

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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "platform_init.h"
1616
#include "memory/memory_shared.h"
17+
#include "memory/spi_mem.h"
1718
#include <driver_init.h>
1819
#include <ui/oled/oled.h>
1920
#if !defined(BOOTLOADER)
@@ -48,4 +49,7 @@ void platform_init(void)
4849
#if !defined(BOOTLOADER)
4950
sd_mmc_start();
5051
#endif
52+
if (memory_get_platform() == MEMORY_PLATFORM_BITBOX02_PLUS) {
53+
spi_mem_protected_area_lock();
54+
}
5155
}

0 commit comments

Comments
 (0)