Skip to content

Commit e1e46e9

Browse files
committed
pbio/sys: Move block device read to driver level.
This moves the platform-specific checksum and saving logic to the driver level. This is a better fit generally, and it is needed for EV3 where the flash shares the SPI bus with the ADC driver. When both reside at the pbdrv level, they can run sequentially without holding up other drivers. This also renames the public functions to more accurately represent what they do. This can only be used to save the disk in one go, so we can't call them generic storage functions. Fixes pybricks/support#2264
1 parent 6f88e2d commit e1e46e9

27 files changed

+292
-239
lines changed

lib/pbio/drv/block_device/block_device_ev3.c

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,34 @@
3232
#include <pbio/error.h>
3333
#include <pbio/int_math.h>
3434

35+
static pbio_os_process_t pbdrv_block_device_ev3_init_process;
36+
37+
static struct {
38+
/**
39+
* How much data to write on shutdown and load on the next boot. Includes
40+
* the size of this field, because it is also saved.
41+
*/
42+
uint32_t saved_size;
43+
/**
44+
* A copy of the data loaded from flash and application heap. The first
45+
* portion of this, up to pbdrv_block_device_get_writable_size() bytes,
46+
* gets saved to flash at shutdown.
47+
*/
48+
uint8_t data[PBDRV_CONFIG_BLOCK_DEVICE_RAM_SIZE];
49+
} ramdisk __attribute__((section(".noinit"), used));
50+
51+
uint32_t pbdrv_block_device_get_writable_size(void) {
52+
return PBDRV_CONFIG_BLOCK_DEVICE_EV3_SIZE - sizeof(ramdisk.saved_size);
53+
}
54+
55+
pbio_error_t pbdrv_block_device_get_data(uint8_t **data) {
56+
*data = ramdisk.data;
57+
58+
// Higher level code can use the ramdisk data if initialization completed
59+
// successfully. Otherwise it should reset to factory default data.
60+
return pbdrv_block_device_ev3_init_process.err;
61+
}
62+
3563
/**
3664
* SPI bus state.
3765
*/
@@ -522,7 +550,7 @@ static uint8_t write_address[4] = {FLASH_CMD_WRITE_DATA};
522550
// Request sector erase at address. Buffer: erase command + address.
523551
static uint8_t erase_address[4] = {FLASH_CMD_ERASE_BLOCK};
524552

525-
pbio_error_t pbdrv_block_device_read(pbio_os_state_t *state, uint32_t offset, uint8_t *buffer, uint32_t size) {
553+
static pbio_error_t pbdrv_block_device_read(pbio_os_state_t *state, uint32_t offset, uint8_t *buffer, uint32_t size) {
526554

527555
static uint32_t size_done;
528556
static uint32_t size_now;
@@ -573,21 +601,29 @@ static pbio_error_t flash_wait_write(pbio_os_state_t *state) {
573601
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
574602
}
575603

576-
pbio_error_t pbdrv_block_device_store(pbio_os_state_t *state, uint8_t *buffer, uint32_t size) {
604+
pbio_error_t pbdrv_block_device_write_all(pbio_os_state_t *state, uint32_t used_data_size) {
577605

578606
static pbio_os_state_t sub;
579607
static uint32_t offset;
580608
static uint32_t size_now;
581609
static uint32_t size_done;
582610
pbio_error_t err;
583611

612+
// We're going to write the used portion of the ramdisk to flash. Includes
613+
// the size field itself, so add it to the total write size.
614+
uint8_t *buffer = (void *)&ramdisk;
615+
uint32_t size = used_data_size + sizeof(ramdisk.saved_size);
616+
584617
PBIO_OS_ASYNC_BEGIN(state);
585618

586619
// Exit on invalid size.
587620
if (size == 0 || size > PBDRV_CONFIG_BLOCK_DEVICE_EV3_SIZE) {
588621
return PBIO_ERROR_INVALID_ARG;
589622
}
590623

624+
// Store the new size so we know how much to load on next boot.
625+
ramdisk.saved_size = size;
626+
591627
#if PBDRV_CONFIG_ADC_EV3
592628
// HACK
593629
// We only store on shutdown. Block ADC.
@@ -648,10 +684,9 @@ pbio_error_t pbdrv_block_device_store(pbio_os_state_t *state, uint8_t *buffer, u
648684
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
649685
}
650686

651-
static pbio_os_process_t pbdrv_block_device_ev3_init_process;
652-
653687
pbio_error_t pbdrv_block_device_ev3_init_process_thread(pbio_os_state_t *state, void *context) {
654688
pbio_error_t err;
689+
static pbio_os_state_t sub;
655690

656691
PBIO_OS_ASYNC_BEGIN(state);
657692

@@ -667,10 +702,24 @@ pbio_error_t pbdrv_block_device_ev3_init_process_thread(pbio_os_state_t *state,
667702
return PBIO_ERROR_FAILED;
668703
}
669704

670-
// Initialization done.
705+
// Read size of stored data.
706+
PBIO_OS_AWAIT(state, &sub, err = pbdrv_block_device_read(&sub, 0, (uint8_t *)&ramdisk.saved_size, sizeof(ramdisk.saved_size)));
707+
if (err != PBIO_SUCCESS) {
708+
return err;
709+
}
710+
711+
// Read the available data into RAM.
712+
PBIO_OS_AWAIT(state, &sub, err = pbdrv_block_device_read(&sub, 0, (uint8_t *)&ramdisk, ramdisk.saved_size));
713+
714+
// Reading may fail with PBIO_ERROR_INVALID_ARG if the size is too big.
715+
// This happens when the size value was uninitialized or another firmware
716+
// was used before. We still want to proceed with the boot process. The
717+
// higher level code sees this error when requesting the RAM disk. On
718+
// failure, it can reset the user data to factory defaults, and save it
719+
// properly on shutdown.
671720
pbdrv_init_busy_down();
672721

673-
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
722+
PBIO_OS_ASYNC_END(err);
674723
}
675724

676725
void pbdrv_block_device_init(void) {

lib/pbio/drv/block_device/block_device_flash_stm32.c

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,104 @@
2121

2222
#include STM32_HAL_H
2323

24-
void pbdrv_block_device_init(void) {
24+
extern uint8_t _pbdrv_block_device_storage_start[];
25+
26+
static struct {
27+
/**
28+
* How much data to write on shutdown and load on the next boot. Includes
29+
* the size of this field, because it is also saved.
30+
*/
31+
uint32_t saved_size;
32+
/**
33+
* Checksum complement to satisfy bootloader requirements. This ensures
34+
* that words in the scanned area still add up to precisely 0 after user
35+
* data was written. Needs to be volatile as it gets optimized out when it
36+
* is never read by our code.
37+
*/
38+
volatile uint32_t checksum_complement;
39+
/**
40+
* A copy of the data loaded from flash and application heap. The first
41+
* portion of this, up to pbdrv_block_device_get_writable_size() bytes,
42+
* gets saved to flash at shutdown.
43+
*/
44+
uint8_t data[PBDRV_CONFIG_BLOCK_DEVICE_RAM_SIZE];
45+
} ramdisk __attribute__((section(".noinit"), used));
46+
47+
const uint32_t header_size = sizeof(ramdisk.saved_size) + sizeof(ramdisk.checksum_complement);
48+
49+
uint32_t pbdrv_block_device_get_writable_size(void) {
50+
return PBDRV_CONFIG_BLOCK_DEVICE_FLASH_STM32_SIZE - header_size;
2551
}
2652

27-
extern uint8_t _pbdrv_block_device_storage_start[];
53+
static pbio_error_t init_err;
2854

29-
pbio_error_t pbdrv_block_device_read(pbio_os_state_t *state, uint32_t offset, uint8_t *buffer, uint32_t size) {
55+
void pbdrv_block_device_init(void) {
3056

31-
// NB: This function is called as an awaitable for compatibility with other
32-
// external storage mediums. It blocks during the memcpy, but we only use
33-
// this at boot.
57+
uint32_t size;
58+
59+
memcpy((void *)&size, _pbdrv_block_device_storage_start, sizeof(size));
3460

3561
// Exit on invalid size.
36-
if (size == 0 || offset + size > PBDRV_CONFIG_BLOCK_DEVICE_FLASH_STM32_SIZE) {
37-
return PBIO_ERROR_INVALID_ARG;
62+
if (size == 0 || size > PBDRV_CONFIG_BLOCK_DEVICE_FLASH_STM32_SIZE) {
63+
// This error will be retrieved when higher level code requests the
64+
// ramdisk, so that it can reset data to firmware defaults.
65+
init_err = PBIO_ERROR_INVALID_ARG;
66+
return;
3867
}
3968

40-
// Copy requested data to RAM.
41-
memcpy(buffer, _pbdrv_block_device_storage_start + offset, size);
69+
// Load requested amount of data to RAM. Also re-reads the size value.
70+
memcpy((void *)&ramdisk, _pbdrv_block_device_storage_start, size);
71+
}
4272

43-
return PBIO_SUCCESS;
73+
pbio_error_t pbdrv_block_device_get_data(uint8_t **data) {
74+
*data = ramdisk.data;
75+
return init_err;
4476
}
4577

46-
typedef union _double_word_t {
47-
uint8_t data[8];
48-
uint64_t dword;
49-
} double_word_t;
78+
// Updates checksum in data map to satisfy bootloader requirements.
79+
static void pbdrv_block_device_update_ramdisk_size_and_checksum(uint32_t used_data_size) {
80+
81+
// Total size includes used data and header.
82+
ramdisk.saved_size = used_data_size + header_size;
5083

51-
pbio_error_t pbdrv_block_device_store(pbio_os_state_t *state, uint8_t *buffer, uint32_t size) {
84+
// Align writable data by a double word, to simplify checksum
85+
// computation and storage drivers that write double words.
86+
while (ramdisk.saved_size % 8) {
87+
*((uint8_t *)&ramdisk + ramdisk.saved_size++) = 0;
88+
}
89+
90+
// The area scanned by the bootloader adds up to 0 when all user data
91+
// is 0xFFFFFFFF. So the bootloader value up until just before the user
92+
// data is always 0 + the number of words in the scanned user data.
93+
extern uint32_t _pbsys_storage_checked_size;
94+
uint32_t checksize = (uint32_t)&_pbsys_storage_checked_size;
95+
uint32_t checksum = checksize / sizeof(uint32_t);
96+
97+
// Don't count existing value.
98+
ramdisk.checksum_complement = 0;
99+
100+
// Add checksum for each word in the written data and empty checked size.
101+
for (uint32_t offset = 0; offset < checksize; offset += sizeof(uint32_t)) {
102+
uint32_t *word = (uint32_t *)((uint8_t *)&ramdisk + offset);
103+
// Assume that everything after written data is erased by the block
104+
// device driver prior to writing.
105+
checksum += offset < ramdisk.saved_size ? *word : 0xFFFFFFFF;
106+
}
107+
108+
// Set the checksum complement to cancel out user data checksum.
109+
ramdisk.checksum_complement = 0xFFFFFFFF - checksum + 1;
110+
}
111+
112+
pbio_error_t pbdrv_block_device_write_all(pbio_os_state_t *state, uint32_t used_data_size) {
52113

53114
// NB: This function is called as an awaitable for compatibility with other
54115
// external storage mediums. This implementation is blocking, but we only
55116
// use it during shutdown so this is acceptable.
56117

118+
// Account for header size and make valid checksum.
119+
pbdrv_block_device_update_ramdisk_size_and_checksum(used_data_size);
120+
uint32_t size = ramdisk.saved_size;
121+
57122
static const uint32_t base_address = (uint32_t)(&_pbdrv_block_device_storage_start[0]);
58123

59124
// Exit if size is 0, too big, or not a multiple of double-word size.
@@ -103,15 +168,15 @@ pbio_error_t pbdrv_block_device_store(pbio_os_state_t *state, uint8_t *buffer, u
103168
__disable_irq();
104169

105170
// Write the data and re-enable interrupts.
106-
hal_err = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, base_address + done, *(uint64_t *)(buffer + done));
171+
hal_err = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, base_address + done, *(uint64_t *)((void *)&ramdisk + done));
107172
__set_PRIMASK(irq);
108173
if (hal_err != HAL_OK) {
109174
HAL_FLASH_Lock();
110175
return PBIO_ERROR_IO;
111176
}
112177

113178
// Update write progress.
114-
done += sizeof(double_word_t);
179+
done += sizeof(uint32_t) * 2;
115180
}
116181

117182
// Lock flash on completion.

lib/pbio/drv/block_device/block_device_test.c

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@
1010
#include <stdint.h>
1111
#include <string.h>
1212

13-
#include <contiki.h>
14-
1513
#include <pbdrv/block_device.h>
1614

1715
#include <pbio/version.h>
1816

19-
#include <pbsys/storage.h>
20-
2117
/**
2218
The following script is compiled using pybricksdev compile hello.py in MULTI_MPY_V6.
2319
@@ -58,43 +54,42 @@ static const uint8_t _program_data[] = {
5854
0x63,
5955
};
6056

57+
// Information from MicroPython should not be used in the pbdrv drivers but it
58+
// is permissible for this test. It ensures we can place the expected git
59+
// version at the right place. FIXME: Move the git version to pybricks build
60+
// system, since it isn't actually the micropython git version.
61+
#include "genhdr/mpversion.h"
62+
#include <pbsys/storage.h>
63+
6164
static struct {
62-
uint32_t write_size;
6365
uint8_t user_data[PBSYS_CONFIG_STORAGE_USER_DATA_SIZE];
6466
char stored_firmware_hash[8];
6567
pbsys_storage_settings_t settings;
6668
uint32_t program_offset;
6769
uint32_t program_size;
6870
uint8_t program_data[sizeof(_program_data)];
69-
} blockdev = { 0 };
71+
} ramdisk __attribute__((section(".noinit"), used)) = { 0 };
7072

71-
// Information from MicroPython should not be used in the pbdrv drivers but it
72-
// is permissible for this test. It ensures we can place the expected git
73-
// version at the right place. FIXME: Move the git version to pybricks build
74-
// system, since it isn't actually the micropython git version.
75-
#include "genhdr/mpversion.h"
76-
77-
void pbdrv_block_device_init(void) {
78-
blockdev.write_size = sizeof(blockdev) + sizeof(_program_data);
79-
blockdev.program_size = sizeof(_program_data);
80-
memcpy(&blockdev.stored_firmware_hash[0], MICROPY_GIT_HASH, sizeof(blockdev.stored_firmware_hash));
81-
memcpy(&blockdev.program_data[0], _program_data, sizeof(_program_data));
73+
uint32_t pbdrv_block_device_get_writable_size(void) {
74+
return 0;
8275
}
8376

84-
pbio_error_t pbdrv_block_device_read(pbio_os_state_t *state, uint32_t offset, uint8_t *buffer, uint32_t size) {
85-
86-
// Exit on invalid size.
87-
if (size == 0 || offset + size > PBDRV_CONFIG_BLOCK_DEVICE_TEST_SIZE) {
88-
return PBIO_ERROR_INVALID_ARG;
89-
}
77+
pbio_error_t pbdrv_block_device_get_data(uint8_t **data) {
78+
*data = (void *)&ramdisk;
9079

91-
// Copy requested data to RAM.
92-
memcpy(buffer, (uint8_t *)&blockdev + offset, size);
80+
// Higher level code can use the ramdisk data if initialization completed
81+
// successfully. Otherwise it should reset to factory default data.
9382
return PBIO_SUCCESS;
9483
}
9584

85+
void pbdrv_block_device_init(void) {
86+
ramdisk.program_size = sizeof(_program_data);
87+
memcpy(&ramdisk.stored_firmware_hash[0], MICROPY_GIT_HASH, sizeof(ramdisk.stored_firmware_hash));
88+
memcpy(&ramdisk.program_data[0], _program_data, sizeof(_program_data));
89+
}
90+
9691
// Don't store any data in this implementation.
97-
pbio_error_t pbdrv_block_device_store(pbio_os_state_t *state, uint8_t *buffer, uint32_t size) {
92+
pbio_error_t pbdrv_block_device_write_all(pbio_os_state_t *state, uint32_t used_data_size) {
9893
return PBIO_ERROR_NOT_IMPLEMENTED;
9994
}
10095

0 commit comments

Comments
 (0)