Skip to content

Commit 6f2aac1

Browse files
oyamawill-v-pikilograham
authored
Add a sample to read the partition table (#627)
* Add an example of extract and enumerate partition table information * update comments * Remove redundant attribute specifiers * Rename structs, specify partition family, remove magic numbers * Changed partition table reads to be split into fixed-length field sections and variable-length field sections * Update flash/partition_info/partition_info.c Co-authored-by: will-v-pi <[email protected]> * panic if the table cannot be retrieved Co-authored-by: will-v-pi <[email protected]> * fixed TYPO `patitions` Co-authored-by: will-v-pi <[email protected]> * rename to match the partition struct Co-authored-by: will-v-pi <[email protected]> * remove WORD_SIZE Co-authored-by: will-v-pi <[email protected]> * update comment Co-authored-by: will-v-pi <[email protected]> * remove unused define Co-authored-by: will-v-pi <[email protected]> * remove unused defined WORD_SIZE Co-authored-by: will-v-pi <[email protected]> * Name unified to flags_and_permissions * Unify pico-sdk header file includes to "" * Fix typo in comment: change "Reads a fixed size fields" to "Reads fixed size fields" * Rename the position field in the partition table to “pos" * Changed function names to prevent misleading as if they were SDK APIs. * Initialize `pt->partition_count = 0` * The fields of the partition table read are added `assert()` * Check the return code of `open_partition_table` * Define and use SECTOR_SIZE * Fix partition name work buffer length * Mask the first bit of the partition name length field * Add `has_partition` flag * rename `open_partition_table` to `read_partition_table` * Use the predefined `FLASH_SECTOR_SIZE` * rename `has_partition` to `has_partition_table` * Skip printing empty partitions * Added printing of extra family IDs * Add `p->has_name` * Changed default_family to be registered at `new` * Unify alloc/free hierarchy * Move `PARTITION_EXTRA_FAMILY_ID_MAX` to uf2_family_ids.h * Change `pos` to `pos_` in the block to avoid conflicts * Fixed typo from `extra_family_id_and_name` to `extra_family_ids_and_name` * Removed redundant brackets * Move comment for `PARTITION_NAME_MAX` * Utilize `p->has_name`. Add `p->has_id` * Add sample extra family id to `pt.json` * Apply suggestions from code review Co-authored-by: will-v-pi <[email protected]> --------- Co-authored-by: will-v-pi <[email protected]> Co-authored-by: Graham Sanderson <[email protected]>
1 parent 9169685 commit 6f2aac1

File tree

7 files changed

+386
-1
lines changed

7 files changed

+386
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ App|Description
113113
[xip_stream](flash/xip_stream) | Stream data using the XIP stream hardware, which allows data to be DMA'd in the background whilst executing code from flash.
114114
[ssi_dma](flash/ssi_dma) | DMA directly from the flash interface (continuous SCK clocking) for maximum bulk read performance.
115115
[runtime_flash_permissions](flash/runtime_flash_permissions) | Demonstrates adding partitions at runtime to change the flash permissions
116+
[partition_info](flash/partition_info) | Extract and enumerate partition information (address ranges, permissions, IDs, and names) from the partition table.
116117

117118
### FreeRTOS
118119

flash/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ if (TARGET hardware_flash)
55
add_subdirectory_exclude_platforms(ssi_dma "rp2350.*")
66
add_subdirectory_exclude_platforms(xip_stream)
77
add_subdirectory_exclude_platforms(runtime_flash_permissions rp2040)
8+
add_subdirectory_exclude_platforms(partition_info rp2040)
89
else()
910
message("Skipping flash examples as hardware_flash is unavailable on this platform")
10-
endif()
11+
endif()

flash/partition_info/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
add_executable(partition_info partition_info.c uf2_family_ids.c)
2+
3+
target_link_libraries(partition_info PRIVATE
4+
pico_stdlib
5+
pico_bootrom
6+
hardware_flash
7+
boot_uf2_headers
8+
)
9+
10+
# add a partition table
11+
pico_embed_pt_in_binary(partition_info ${CMAKE_CURRENT_LIST_DIR}/pt.json)
12+
13+
# create map/bin/hex/uf2 file etc.
14+
pico_add_extra_outputs(partition_info)
15+
16+
# add url via pico_set_program_url
17+
example_auto_set_url(partition_info)

flash/partition_info/partition_info.c

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/**
2+
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
#include <assert.h>
7+
#include <stdio.h>
8+
#include "pico/stdlib.h"
9+
#include "pico/bootrom.h"
10+
#include "boot/picobin.h"
11+
#include "hardware/flash.h"
12+
#include "uf2_family_ids.h"
13+
14+
#define PART_LOC_FIRST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB )
15+
#define PART_LOC_LAST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB )
16+
17+
#define PARTITION_LOCATION_AND_FLAGS_SIZE 2
18+
#define PARTITION_ID_SIZE 2
19+
#define PARTITION_NAME_MAX 127 // name length is indicated by 7 bits
20+
#define PARTITION_TABLE_FIXED_INFO_SIZE (4 + PARTITION_TABLE_MAX_PARTITIONS * (PARTITION_LOCATION_AND_FLAGS_SIZE + PARTITION_ID_SIZE))
21+
22+
/*
23+
* Stores partition table information and data read status
24+
*/
25+
typedef struct {
26+
uint32_t table[PARTITION_TABLE_FIXED_INFO_SIZE];
27+
uint32_t fields;
28+
bool has_partition_table;
29+
int partition_count;
30+
uint32_t unpartitioned_space_first_sector;
31+
uint32_t unpartitioned_space_last_sector;
32+
uint32_t flags_and_permissions;
33+
int current_partition;
34+
size_t pos;
35+
int status;
36+
} pico_partition_table_t;
37+
38+
/*
39+
* Stores information on each partition
40+
*/
41+
typedef struct {
42+
uint32_t first_sector;
43+
uint32_t last_sector;
44+
uint32_t flags_and_permissions;
45+
bool has_id;
46+
uint64_t partition_id;
47+
bool has_name;
48+
char name[PARTITION_NAME_MAX + 1];
49+
uint32_t extra_family_id_count;
50+
uint32_t extra_family_ids[PARTITION_EXTRA_FAMILY_ID_MAX];
51+
} pico_partition_t;
52+
53+
54+
/*
55+
* Read the partition table information.
56+
*
57+
* See the RP2350 datasheet 5.1.2, 5.4.8.16 for flags and structures that can be specified.
58+
*/
59+
int read_partition_table(pico_partition_table_t *pt) {
60+
// Reads fixed size fields
61+
uint32_t flags = PT_INFO_PT_INFO | PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID;
62+
int rc = rom_get_partition_table_info(pt->table, sizeof(pt->table), flags);
63+
if (rc < 0) {
64+
pt->partition_count = 0;
65+
pt->status = rc;
66+
return rc;
67+
}
68+
69+
size_t pos = 0;
70+
pt->fields = pt->table[pos++];
71+
assert(pt->fields == flags);
72+
pt->partition_count = pt->table[pos] & 0x000000FF;
73+
pt->has_partition_table = pt->table[pos] & 0x00000100;
74+
pos++;
75+
uint32_t location = pt->table[pos++];
76+
pt->unpartitioned_space_first_sector = PART_LOC_FIRST(location);
77+
pt->unpartitioned_space_last_sector = PART_LOC_LAST(location);
78+
pt->flags_and_permissions = pt->table[pos++];
79+
pt->current_partition = 0;
80+
pt->pos = pos;
81+
pt->status = 0;
82+
83+
return 0;
84+
}
85+
86+
/*
87+
* Extract each partition information
88+
*/
89+
bool read_next_partition(pico_partition_table_t *pt, pico_partition_t *p) {
90+
if (pt->current_partition >= pt->partition_count) {
91+
return false;
92+
}
93+
94+
size_t pos = pt->pos;
95+
uint32_t location = pt->table[pos++];
96+
p->first_sector = PART_LOC_FIRST(location);
97+
p->last_sector = PART_LOC_LAST(location);
98+
p->flags_and_permissions = pt->table[pos++];
99+
p->has_name = p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_NAME_BITS;
100+
p->has_id = p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS;
101+
102+
if (p->has_id) {
103+
uint32_t id_low = pt->table[pos++];
104+
uint32_t id_high = pt->table[pos++];
105+
p->partition_id = ((uint64_t)id_high << 32) | id_low;
106+
} else {
107+
p->partition_id = 0;
108+
}
109+
pt->pos = pos;
110+
111+
p->extra_family_id_count = (p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS)
112+
>> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB;
113+
if (p->extra_family_id_count | p->has_name) {
114+
// Read variable length fields
115+
uint32_t extra_family_ids_and_name[PARTITION_EXTRA_FAMILY_ID_MAX + (((PARTITION_NAME_MAX + 1) / sizeof(uint32_t)) + 1)];
116+
uint32_t flags = PT_INFO_SINGLE_PARTITION | PT_INFO_PARTITION_FAMILY_IDS | PT_INFO_PARTITION_NAME;
117+
int rc = rom_get_partition_table_info(extra_family_ids_and_name, sizeof(extra_family_ids_and_name),
118+
(pt->current_partition << 24 | flags));
119+
if (rc < 0) {
120+
pt->status = rc;
121+
return false;
122+
}
123+
size_t pos_ = 0;
124+
uint32_t __attribute__((unused)) fields = extra_family_ids_and_name[pos_++];
125+
assert(fields == flags);
126+
for (size_t i = 0; i < p->extra_family_id_count; i++, pos_++) {
127+
p->extra_family_ids[i] = extra_family_ids_and_name[pos_];
128+
}
129+
130+
if (p->has_name) {
131+
uint8_t *name_buf = (uint8_t *)&extra_family_ids_and_name[pos_];
132+
uint8_t name_length = *name_buf++ & 0x7F;
133+
memcpy(p->name, name_buf, name_length);
134+
p->name[name_length] = '\0';
135+
}
136+
}
137+
if (!p->has_name)
138+
p->name[0] = '\0';
139+
140+
pt->current_partition++;
141+
return true;
142+
}
143+
144+
int main() {
145+
stdio_init_all();
146+
147+
pico_partition_table_t pt;
148+
int rc;
149+
rc = read_partition_table(&pt);
150+
if (rc != 0) {
151+
panic("rom_get_partition_table_info returned %d", pt.status);
152+
}
153+
if (!pt.has_partition_table) {
154+
printf("there is no partition table\n");
155+
} else if (pt.partition_count == 0) {
156+
printf("the partition table is empty\n");
157+
}
158+
159+
uf2_family_ids_t *family_ids = uf2_family_ids_new(pt.flags_and_permissions);
160+
char *str_family_ids = uf2_family_ids_join(family_ids, ", ");
161+
printf("un-partitioned_space: S(%s%s) NSBOOT(%s%s) NS(%s%s) uf2 { %s }\n",
162+
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_R_BITS ? "r" : ""),
163+
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_W_BITS ? "w" : ""),
164+
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_R_BITS ? "r" : ""),
165+
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_W_BITS ? "w" : ""),
166+
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_R_BITS ? "r" : ""),
167+
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_W_BITS ? "w" : ""),
168+
str_family_ids);
169+
free(str_family_ids);
170+
uf2_family_ids_free(family_ids);
171+
172+
if (pt.partition_count == 0) {
173+
return 0;
174+
}
175+
printf("partitions:\n");
176+
pico_partition_t p;
177+
while (read_next_partition(&pt, &p)) {
178+
printf("%3d:", pt.current_partition - 1);
179+
180+
printf(" %08x->%08x S(%s%s) NSBOOT(%s%s) NS(%s%s)",
181+
p.first_sector * FLASH_SECTOR_SIZE, (p.last_sector + 1) * FLASH_SECTOR_SIZE,
182+
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_R_BITS ? "r" : ""),
183+
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_W_BITS ? "w" : ""),
184+
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_R_BITS ? "r" : ""),
185+
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_W_BITS ? "w" : ""),
186+
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_R_BITS ? "r" : ""),
187+
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_W_BITS ? "w" : ""));
188+
if (p.has_id) {
189+
printf(", id=%016llx", p.partition_id);
190+
}
191+
if (p.has_name) {
192+
printf(", \"%s\"", p.name);
193+
}
194+
195+
// print UF2 family ID
196+
family_ids = uf2_family_ids_new(p.flags_and_permissions);
197+
for (size_t i = 0; i < p.extra_family_id_count; i++) {
198+
uf2_family_ids_add_extra_family_id(family_ids, p.extra_family_ids[i]);
199+
}
200+
str_family_ids = uf2_family_ids_join(family_ids, ", ");
201+
printf(", uf2 { %s }", str_family_ids);
202+
free(str_family_ids);
203+
uf2_family_ids_free(family_ids);
204+
205+
printf("\n");
206+
}
207+
if (pt.status != 0) {
208+
panic("rom_get_partition_table_info returned %d", pt.status);
209+
}
210+
211+
return 0;
212+
}

flash/partition_info/pt.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"version": [1, 0],
3+
"unpartitioned": {
4+
"families": ["absolute"],
5+
"permissions": {
6+
"secure": "rw",
7+
"nonsecure": "rw",
8+
"bootloader": "rw"
9+
}
10+
},
11+
"partitions": [
12+
{
13+
"name": "Firmware",
14+
"id": 1,
15+
"start": 0,
16+
"size": "512K",
17+
"families": ["rp2350-arm-ns", "rp2350-arm-s", "rp2350-riscv", "0x12345678"],
18+
"permissions": {
19+
"secure": "rw",
20+
"nonsecure": "rw",
21+
"bootloader": "rw"
22+
}
23+
},
24+
{
25+
"name": "Data",
26+
"id": 2,
27+
"size": "512K",
28+
"families": ["data"],
29+
"permissions": {
30+
"secure": "rw",
31+
"nonsecure": "rw",
32+
"bootloader": "rw"
33+
}
34+
},
35+
{
36+
"name": "Read only Data",
37+
"id": 3,
38+
"size": "512K",
39+
"families": ["data"],
40+
"permissions": {
41+
"secure": "r",
42+
"nonsecure": "r",
43+
"bootloader": "r"
44+
}
45+
}
46+
]
47+
}

flash/partition_info/uf2_family_ids.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include "uf2_family_ids.h"
2+
3+
#define UF2_FAMILY_ID_HEX_SIZE (2 + 8 * 2 + 1)
4+
5+
static void _add(uf2_family_ids_t *ids, const char *str) {
6+
ids->items = realloc(ids->items, (ids->count + 1) * sizeof(char *));
7+
ids->items[ids->count] = strdup(str);
8+
if (ids->items[ids->count] == NULL) {
9+
perror("strdup");
10+
return;
11+
}
12+
ids->count++;
13+
}
14+
15+
static void _add_default_families(uf2_family_ids_t *ids, uint32_t flags) {
16+
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_ABSOLUTE_BITS)
17+
_add(ids, "absolute");
18+
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2040_BITS)
19+
_add(ids, "rp2040");
20+
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_ARM_S_BITS)
21+
_add(ids, "rp2350-arm-s");
22+
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_ARM_NS_BITS)
23+
_add(ids, "rp2350-arm-ns");
24+
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_RISCV_BITS)
25+
_add(ids, "rp2350-riscv");
26+
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_DATA_BITS)
27+
_add(ids, "data");
28+
}
29+
30+
uf2_family_ids_t *uf2_family_ids_new(uint32_t flags) {
31+
uf2_family_ids_t *ids = malloc(sizeof(uf2_family_ids_t));
32+
ids->count = 0;
33+
ids->items = NULL;
34+
_add_default_families(ids, flags);
35+
return ids;
36+
}
37+
38+
char *uf2_family_ids_join(const uf2_family_ids_t *ids, const char *sep) {
39+
size_t total_length = 0;
40+
size_t sep_length = strlen(sep);
41+
42+
for (size_t i = 0; i < ids->count; i++) {
43+
total_length += strlen(ids->items[i]);
44+
if (i < ids->count - 1)
45+
total_length += sep_length;
46+
}
47+
48+
char *result = calloc(1, total_length + 1);
49+
if (!result) {
50+
perror("calloc");
51+
return NULL;
52+
}
53+
54+
result[0] = '\0';
55+
for (size_t i = 0; i < ids->count; i++) {
56+
strcat(result, ids->items[i]);
57+
if (i < ids->count - 1)
58+
strcat(result, sep);
59+
}
60+
61+
return result;
62+
}
63+
64+
void uf2_family_ids_free(uf2_family_ids_t *ids) {
65+
for (size_t i = 0; i < ids->count; i++) {
66+
free(ids->items[i]);
67+
}
68+
free(ids->items);
69+
free(ids);
70+
}
71+
72+
void uf2_family_ids_add_extra_family_id(uf2_family_ids_t *ids, uint32_t family_id) {
73+
char hex_id[UF2_FAMILY_ID_HEX_SIZE];
74+
switch (family_id) {
75+
case CYW43_FIRMWARE_FAMILY_ID:
76+
_add(ids, "cyw43-firmware");
77+
break;
78+
default:
79+
sprintf(hex_id, "0x%08x", family_id);
80+
_add(ids, hex_id);
81+
break;
82+
}
83+
}

0 commit comments

Comments
 (0)