Skip to content

Commit 6171d8d

Browse files
committed
pbsys/program_load: Split header from map.
This lets us figure out what the maximum program size can be.
1 parent 57d1e9a commit 6171d8d

File tree

2 files changed

+80
-45
lines changed

2 files changed

+80
-45
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2022 The Pybricks Authors
3+
4+
/**
5+
* @addtogroup SysProgramLoad System: Load user programs.
6+
*
7+
* Configuration for loading data.
8+
*
9+
* @{
10+
*/
11+
12+
#ifndef _PBSYS_PROGRAM_LOAD_H_
13+
#define _PBSYS_PROGRAM_LOAD_H_
14+
15+
#include <stdint.h>
16+
17+
#include <pbsys/config.h>
18+
19+
#if PBSYS_CONFIG_PROGRAM_LOAD
20+
21+
// Sanity check that application RAM is enough to load ROM and still do something useful
22+
#if PBSYS_CONFIG_PROGRAM_LOAD_RAM_SIZE < PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE + 2048
23+
#error "Application RAM must be at least ROM size + 2K."
24+
#endif
25+
26+
/**
27+
* Header of loaded data. All data types are little-endian.
28+
*/
29+
typedef struct _pbsys_program_load_data_header_t {
30+
/**
31+
* How much to write on shutdown. This is reset to 0 on load, and should be
32+
* set whenever any data is updated. This must always remain the first
33+
* element of this structure.
34+
*/
35+
uint32_t write_size;
36+
#if PBSYS_CONFIG_PROGRAM_LOAD_OVERLAPS_BOOTLOADER_CHECKSUM
37+
/**
38+
* Checksum complement to satisfy bootloader requirements. This ensures
39+
* that words in the scanned area still add up to precisely 0 after user
40+
* data was written.
41+
*/
42+
volatile uint32_t checksum_complement;
43+
#endif
44+
/**
45+
* Size of the application program (size of code only).
46+
*/
47+
uint32_t program_size;
48+
} pbsys_program_load_data_header_t;
49+
50+
#define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE - sizeof(pbsys_program_load_data_header_t))
51+
52+
#else
53+
54+
#define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (0)
55+
56+
#endif // PBSYS_CONFIG_PROGRAM_LOAD
57+
58+
#endif // _PBSYS_PROGRAM_LOAD_H_
59+
60+
/** @} */

lib/pbio/sys/program_load.c

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,23 @@
1515
#include <pbio/main.h>
1616
#include <pbio/protocol.h>
1717
#include <pbsys/main.h>
18+
#include <pbsys/program_load.h>
1819
#include <pbsys/status.h>
1920

2021
#include "core.h"
2122

22-
// Sanity check that application RAM is enough to load ROM and still do something useful
23-
#if PBSYS_CONFIG_PROGRAM_LOAD_RAM_SIZE < PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE + 2048
24-
#error "Application RAM must be at least ROM size + 2K."
25-
#endif
26-
2723
/**
28-
* Map of loaded data. All data types are little-endian.
24+
* Map of loaded data.
2925
*/
3026
typedef struct {
3127
/**
32-
* How much to write on shutdown. This is reset to 0 on load, and should be
33-
* set whenever any data is updated. This must always remain the first
34-
* element of this structure.
35-
*/
36-
uint32_t write_size;
37-
#if PBSYS_CONFIG_PROGRAM_LOAD_OVERLAPS_BOOTLOADER_CHECKSUM
38-
/**
39-
* Checksum complement to satisfy bootloader requirements. This ensures
40-
* that words in the scanned area still add up to precisely 0 after user
41-
* data was written.
42-
*/
43-
volatile uint32_t checksum_complement;
44-
#endif
45-
/**
46-
* Size of the application program (size of code only).
28+
* User data header.
4729
*/
48-
uint32_t program_size;
30+
pbsys_program_load_data_header_t header;
4931
/**
5032
* Data of the application program (code + heap).
5133
*/
52-
uint8_t program_data[PBSYS_CONFIG_PROGRAM_LOAD_RAM_SIZE] __attribute__((aligned(sizeof(void *))));
34+
uint8_t program_data[PBSYS_CONFIG_PROGRAM_LOAD_RAM_SIZE - sizeof(pbsys_program_load_data_header_t)] __attribute__((aligned(sizeof(void *))));
5335
} data_map_t;
5436

5537
// The data map sits at the start of user RAM.
@@ -59,16 +41,14 @@ static data_map_t *map = &pbsys_user_ram_data_map;
5941
static bool pbsys_program_load_start_user_program_requested;
6042
static bool pbsys_program_load_start_repl_requested;
6143

62-
#define MAP_HEADER_SIZE (sizeof(*map) - sizeof(map->program_data))
63-
6444
#if PBSYS_CONFIG_PROGRAM_LOAD_OVERLAPS_BOOTLOADER_CHECKSUM
6545
// Updates checksum in data map to satisfy bootloader requirements.
6646
static void pbsys_program_load_update_checksum(void) {
6747

6848
// Align writable data by a double word, to simplify checksum
6949
// computation and storage drivers that write double words.
70-
while (map->write_size % 8) {
71-
*((uint8_t *)map + map->write_size++) = 0;
50+
while (map->header.write_size % 8) {
51+
*((uint8_t *)map + map->header.write_size++) = 0;
7252
}
7353

7454
// The area scanned by the bootloader adds up to 0 when all user data
@@ -79,25 +59,20 @@ static void pbsys_program_load_update_checksum(void) {
7959
uint32_t checksum = checksize / sizeof(uint32_t);
8060

8161
// Don't count existing value.
82-
map->checksum_complement = 0;
62+
map->header.checksum_complement = 0;
8363

8464
// Add checksum for each word in the written data and empty checked size.
8565
for (uint32_t offset = 0; offset < checksize; offset += sizeof(uint32_t)) {
8666
uint32_t *word = (uint32_t *)((uint8_t *)map + offset);
8767
// Assume that everything after written data is erased.
88-
checksum += offset < map->write_size ? *word : 0xFFFFFFFF;
68+
checksum += offset < map->header.write_size ? *word : 0xFFFFFFFF;
8969
}
9070

9171
// Set the checksum complement to cancel out user data checksum.
92-
map->checksum_complement = 0xFFFFFFFF - checksum + 1;
72+
map->header.checksum_complement = 0xFFFFFFFF - checksum + 1;
9373
}
9474
#endif // PBSYS_CONFIG_PROGRAM_LOAD_OVERLAPS_BOOTLOADER_CHECKSUM
9575

96-
// Gets the (constant) maximum program size.
97-
static inline uint32_t pbsys_program_load_get_max_program_size() {
98-
return PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE - MAP_HEADER_SIZE;
99-
}
100-
10176
/**
10277
* Writes the user program metadata.
10378
*
@@ -112,9 +87,9 @@ pbio_error_t pbsys_program_load_set_program_size(uint32_t size) {
11287
return PBIO_ERROR_BUSY;
11388
}
11489

115-
map->program_size = size;
90+
map->header.program_size = size;
11691
// Data was updated, so set the write size.
117-
map->write_size = size + MAP_HEADER_SIZE;
92+
map->header.write_size = size + sizeof(pbsys_program_load_data_header_t);
11893

11994
return PBIO_SUCCESS;
12095
}
@@ -162,7 +137,7 @@ pbio_error_t pbsys_program_load_start_user_program(void) {
162137
}
163138

164139
// Don't run invalid programs.
165-
if (map->program_size == 0 || map->program_size > pbsys_program_load_get_max_program_size()) {
140+
if (map->header.program_size == 0 || map->header.program_size > PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE) {
166141
// TODO: Validate the data beyond just size.
167142
return PBIO_ERROR_INVALID_ARG;
168143
}
@@ -218,16 +193,16 @@ PROCESS_THREAD(pbsys_program_load_process, ev, data) {
218193
PROCESS_BEGIN();
219194

220195
// Read size of stored data.
221-
PROCESS_PT_SPAWN(&pt, pbdrv_block_device_read(&pt, 0, (uint8_t *)map, sizeof(map->write_size), &err));
196+
PROCESS_PT_SPAWN(&pt, pbdrv_block_device_read(&pt, 0, (uint8_t *)map, sizeof(map->header.write_size), &err));
222197

223198
// Read the available data into RAM.
224-
PROCESS_PT_SPAWN(&pt, pbdrv_block_device_read(&pt, 0, (uint8_t *)map, map->write_size, &err));
199+
PROCESS_PT_SPAWN(&pt, pbdrv_block_device_read(&pt, 0, (uint8_t *)map, map->header.write_size, &err));
225200
if (err != PBIO_SUCCESS) {
226-
map->program_size = 0;
201+
map->header.program_size = 0;
227202
}
228203

229204
// Reset write size, so we don't write data if nothing changed.
230-
map->write_size = 0;
205+
map->header.write_size = 0;
231206

232207
// Initialization done.
233208
pbsys_init_busy_down();
@@ -236,14 +211,14 @@ PROCESS_THREAD(pbsys_program_load_process, ev, data) {
236211
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_CONTINUE);
237212

238213
// Write data to storage if it was updated.
239-
if (map->write_size) {
214+
if (map->header.write_size) {
240215

241216
#if PBSYS_CONFIG_PROGRAM_LOAD_OVERLAPS_BOOTLOADER_CHECKSUM
242217
pbsys_program_load_update_checksum();
243218
#endif
244219

245220
// Write the data.
246-
PROCESS_PT_SPAWN(&pt, pbdrv_block_device_store(&pt, (uint8_t *)map, map->write_size, &err));
221+
PROCESS_PT_SPAWN(&pt, pbdrv_block_device_store(&pt, (uint8_t *)map, map->header.write_size, &err));
247222
}
248223

249224
// Deinitialization done.
@@ -288,7 +263,7 @@ pbio_error_t pbsys_program_load_wait_command(pbsys_main_program_t *program) {
288263

289264
// REPL can also use user program (e.g. in MicroPython, import user modules)
290265
program->code_start = map->program_data;
291-
program->code_end = map->program_data + map->program_size;
266+
program->code_end = map->program_data + map->header.program_size;
292267
program->data_end = map->program_data + sizeof(map->program_data);
293268

294269
return PBIO_SUCCESS;

0 commit comments

Comments
 (0)