Skip to content

Add a sample to read the partition table #627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 40 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
01001fa
Add an example of extract and enumerate partition table information
oyama Mar 17, 2025
a0ceb2b
update comments
oyama Mar 17, 2025
d2883d1
Remove redundant attribute specifiers
oyama Mar 17, 2025
1015067
Rename structs, specify partition family, remove magic numbers
oyama Mar 19, 2025
8b87449
Changed partition table reads to be split into fixed-length field sec…
oyama Mar 19, 2025
c5aa4c1
Update flash/partition_info/partition_info.c
oyama Mar 21, 2025
2496fe7
panic if the table cannot be retrieved
oyama Mar 21, 2025
5681d71
fixed TYPO `patitions`
oyama Mar 21, 2025
0f96a36
rename to match the partition struct
oyama Mar 21, 2025
9bdba25
remove WORD_SIZE
oyama Mar 21, 2025
7d9edfa
update comment
oyama Mar 21, 2025
87eddec
remove unused define
oyama Mar 21, 2025
bbb1656
remove unused defined WORD_SIZE
oyama Mar 21, 2025
8f3564d
Name unified to flags_and_permissions
oyama Mar 21, 2025
ee6d8d6
Unify pico-sdk header file includes to ""
oyama Mar 25, 2025
b463a69
Fix typo in comment: change "Reads a fixed size fields" to "Reads fix…
oyama Mar 25, 2025
555ded5
Rename the position field in the partition table to “pos"
oyama Mar 25, 2025
9bce48c
Changed function names to prevent misleading as if they were SDK APIs.
oyama Mar 25, 2025
b5c2fbe
Initialize `pt->partition_count = 0`
oyama Mar 25, 2025
0889622
The fields of the partition table read are added `assert()`
oyama Mar 25, 2025
8dd6b0e
Check the return code of `open_partition_table`
oyama Mar 26, 2025
b53dfbc
Define and use SECTOR_SIZE
oyama Mar 26, 2025
4822ae3
Fix partition name work buffer length
oyama Mar 26, 2025
c5fd635
Mask the first bit of the partition name length field
oyama Mar 26, 2025
c88a23c
Add `has_partition` flag
oyama Mar 26, 2025
869bf75
rename `open_partition_table` to `read_partition_table`
oyama Mar 26, 2025
4005c6f
Use the predefined `FLASH_SECTOR_SIZE`
oyama Mar 26, 2025
8d1d467
rename `has_partition` to `has_partition_table`
oyama Mar 26, 2025
ce5b409
Skip printing empty partitions
oyama Mar 26, 2025
d9909f9
Added printing of extra family IDs
oyama Mar 27, 2025
ce6dda8
Add `p->has_name`
oyama Mar 27, 2025
7edee22
Changed default_family to be registered at `new`
oyama Mar 27, 2025
1066f75
Unify alloc/free hierarchy
oyama Mar 27, 2025
b118961
Move `PARTITION_EXTRA_FAMILY_ID_MAX` to uf2_family_ids.h
oyama Mar 27, 2025
c37dcbe
Change `pos` to `pos_` in the block to avoid conflicts
oyama Mar 27, 2025
9c19b3c
Fixed typo from `extra_family_id_and_name` to `extra_family_ids_and_n…
oyama Mar 27, 2025
cda403e
Removed redundant brackets
oyama Mar 27, 2025
a0e81bd
Move comment for `PARTITION_NAME_MAX`
oyama Mar 28, 2025
ff8d2b4
Utilize `p->has_name`. Add `p->has_id`
oyama Mar 28, 2025
9eb7c33
Add sample extra family id to `pt.json`
oyama Mar 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ App|Description
[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.
[ssi_dma](flash/ssi_dma) | DMA directly from the flash interface (continuous SCK clocking) for maximum bulk read performance.
[runtime_flash_permissions](flash/runtime_flash_permissions) | Demonstrates adding partitions at runtime to change the flash permissions
[partition_info](flash/partition_info) | Extract and enumerate partition information (address ranges, permissions, IDs, and names) from the partition table.

### FreeRTOS

Expand Down
3 changes: 2 additions & 1 deletion flash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ if (TARGET hardware_flash)
add_subdirectory_exclude_platforms(ssi_dma "rp2350.*")
add_subdirectory_exclude_platforms(xip_stream)
add_subdirectory_exclude_platforms(runtime_flash_permissions rp2040)
add_subdirectory_exclude_platforms(partition_info rp2040)
else()
message("Skipping flash examples as hardware_flash is unavailable on this platform")
endif()
endif()
16 changes: 16 additions & 0 deletions flash/partition_info/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
add_executable(partition_info partition_info.c uf2_family_ids.c)

target_link_libraries(partition_info PRIVATE
pico_stdlib
pico_bootrom
hardware_flash
)

# add a partition table
pico_embed_pt_in_binary(partition_info ${CMAKE_CURRENT_LIST_DIR}/pt.json)

# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(partition_info)

# add url via pico_set_program_url
example_auto_set_url(partition_info)
212 changes: 212 additions & 0 deletions flash/partition_info/partition_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/bootrom.h"
#include "boot/picobin.h"
#include "hardware/flash.h"
#include "uf2_family_ids.h"

#define PART_LOC_FIRST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB )
#define PART_LOC_LAST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB )

#define PARTITION_LOCATION_AND_FLAGS_SIZE 2
#define PARTITION_ID_SIZE 2
#define PARTITION_NAME_MAX 127 // name length is indicated by 7 bits
#define PARTITION_TABLE_FIXED_INFO_SIZE (4 + PARTITION_TABLE_MAX_PARTITIONS * (PARTITION_LOCATION_AND_FLAGS_SIZE + PARTITION_ID_SIZE))

/*
* Stores partition table information and data read status
*/
typedef struct {
uint32_t table[PARTITION_TABLE_FIXED_INFO_SIZE];
uint32_t fields;
bool has_partition_table;
int partition_count;
uint32_t unpartitioned_space_first_sector;
uint32_t unpartitioned_space_last_sector;
uint32_t flags_and_permissions;
int current_partition;
size_t pos;
int status;
} pico_partition_table_t;

/*
* Stores information on each partition
*/
typedef struct {
uint32_t first_sector;
uint32_t last_sector;
uint32_t flags_and_permissions;
bool has_id;
uint64_t partition_id;
bool has_name;
char name[PARTITION_NAME_MAX + 1];
uint32_t extra_family_id_count;
uint32_t extra_family_ids[PARTITION_EXTRA_FAMILY_ID_MAX];
} pico_partition_t;


/*
* Read the partition table information.
*
* See the RP2350 datasheet 5.1.2, 5.4.8.16 for flags and structures that can be specified.
*/
int read_partition_table(pico_partition_table_t *pt) {
// Reads fixed size fields
uint32_t flags = PT_INFO_PT_INFO | PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID;
int rc = rom_get_partition_table_info(pt->table, sizeof(pt->table), flags);
if (rc < 0) {
pt->partition_count = 0;
pt->status = rc;
return rc;
}

size_t pos = 0;
pt->fields = pt->table[pos++];
assert(pt->fields == flags);
pt->partition_count = pt->table[pos] & 0x000000FF;
pt->has_partition_table = pt->table[pos] & 0x00000100;
pos++;
uint32_t location = pt->table[pos++];
pt->unpartitioned_space_first_sector = PART_LOC_FIRST(location);
pt->unpartitioned_space_last_sector = PART_LOC_LAST(location);
pt->flags_and_permissions = pt->table[pos++];
pt->current_partition = 0;
pt->pos = pos;
pt->status = 0;

return 0;
}

/*
* Extract each partition information
*/
bool read_next_partition(pico_partition_table_t *pt, pico_partition_t *p) {
if (pt->current_partition >= pt->partition_count) {
return false;
}

size_t pos = pt->pos;
uint32_t location = pt->table[pos++];
p->first_sector = PART_LOC_FIRST(location);
p->last_sector = PART_LOC_LAST(location);
p->flags_and_permissions = pt->table[pos++];
p->has_name = p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_NAME_BITS;
p->has_id = p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS;

if (p->has_id) {
uint32_t id_low = pt->table[pos++];
uint32_t id_high = pt->table[pos++];
p->partition_id = ((uint64_t)id_high << 32) | id_low;
} else {
p->partition_id = 0;
}
pt->pos = pos;

p->extra_family_id_count = (p->flags_and_permissions & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS)
>> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB;
if (p->extra_family_id_count | p->has_name) {
// Read variable length fields
uint32_t extra_family_ids_and_name[PARTITION_EXTRA_FAMILY_ID_MAX + (((PARTITION_NAME_MAX + 1) / sizeof(uint32_t)) + 1)];
uint32_t flags = PT_INFO_SINGLE_PARTITION | PT_INFO_PARTITION_FAMILY_IDS | PT_INFO_PARTITION_NAME;
int rc = rom_get_partition_table_info(extra_family_ids_and_name, sizeof(extra_family_ids_and_name),
(pt->current_partition << 24 | flags));
if (rc < 0) {
pt->status = rc;
return false;
}
size_t pos_ = 0;
uint32_t __attribute__((unused)) fields = extra_family_ids_and_name[pos_++];
assert(fields == flags);
for (size_t i = 0; i < p->extra_family_id_count; i++, pos_++) {
p->extra_family_ids[i] = extra_family_ids_and_name[pos_];
}

if (p->has_name) {
uint8_t *name_buf = (uint8_t *)&extra_family_ids_and_name[pos_];
uint8_t name_length = *name_buf++ & 0x7F;
memcpy(p->name, name_buf, name_length);
p->name[name_length] = '\0';
}
}
if (!p->has_name)
p->name[0] = '\0';

pt->current_partition++;
return true;
}

int main() {
stdio_init_all();

pico_partition_table_t pt;
int rc;
rc = read_partition_table(&pt);
if (rc != 0) {
panic("rom_get_partition_table_info returned %d", pt.status);
}
if (!pt.has_partition_table) {
printf("there is no partition table\n");
} else if (pt.partition_count == 0) {
printf("the partition table is empty\n");
}

uf2_family_ids_t *family_ids = uf2_family_ids_new(pt.flags_and_permissions);
char *str_family_ids = uf2_family_ids_join(family_ids, ", ");
printf("un-partitioned_space: S(%s%s) NSBOOT(%s%s) NS(%s%s) uf2 { %s }\n",
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_R_BITS ? "r" : ""),
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_W_BITS ? "w" : ""),
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_R_BITS ? "r" : ""),
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_W_BITS ? "w" : ""),
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_R_BITS ? "r" : ""),
(pt.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_W_BITS ? "w" : ""),
Comment on lines +161 to +167
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to how you have a function (uf2_family_ids_join) for creating the formatted family-ids string, I wonder if it might be worth pulling out a separate function for creating the S(%s%s) NSBOOT(%s%s) NS(%s%s) part of this string? (which I believe you could then also use in the per-partition loop?) 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, I was about to suggest that you could use %c instead of %s in your printf formatting, but then I realised that you can't do "empty string" when using char 😂

str_family_ids);
free(str_family_ids);
uf2_family_ids_free(family_ids);

if (pt.partition_count == 0) {
return 0;
}
printf("partitions:\n");
pico_partition_t p;
while (read_next_partition(&pt, &p)) {
printf("%3d:", pt.current_partition - 1);

printf(" %08x->%08x S(%s%s) NSBOOT(%s%s) NS(%s%s)",
p.first_sector * FLASH_SECTOR_SIZE, (p.last_sector + 1) * FLASH_SECTOR_SIZE,
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_R_BITS ? "r" : ""),
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_S_W_BITS ? "w" : ""),
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_R_BITS ? "r" : ""),
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NSBOOT_W_BITS ? "w" : ""),
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_R_BITS ? "r" : ""),
(p.flags_and_permissions & PICOBIN_PARTITION_PERMISSION_NS_W_BITS ? "w" : ""));
if (p.has_id) {
printf(", id=%016llx", p.partition_id);
}
if (p.has_name) {
printf(", \"%s\"", p.name);
}

// print UF2 family ID
family_ids = uf2_family_ids_new(p.flags_and_permissions);
for (size_t i = 0; i < p.extra_family_id_count; i++) {
uf2_family_ids_add_extra_family_id(family_ids, p.extra_family_ids[i]);
}
str_family_ids = uf2_family_ids_join(family_ids, ", ");
printf(", uf2 { %s }", str_family_ids);
free(str_family_ids);
uf2_family_ids_free(family_ids);

printf("\n");
}
if (pt.status != 0) {
panic("rom_get_partition_table_info returned %d", pt.status);
}

return 0;
}
47 changes: 47 additions & 0 deletions flash/partition_info/pt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"version": [1, 0],
"unpartitioned": {
"families": ["absolute"],
"permissions": {
"secure": "rw",
"nonsecure": "rw",
"bootloader": "rw"
}
},
"partitions": [
{
"name": "Firmware",
"id": 1,
"start": 0,
"size": "512K",
"families": ["rp2350-arm-ns", "rp2350-arm-s", "rp2350-riscv", "0x12345678"],
"permissions": {
"secure": "rw",
"nonsecure": "rw",
"bootloader": "rw"
}
},
{
"name": "Data",
"id": 2,
"size": "512K",
"families": ["data"],
"permissions": {
"secure": "rw",
"nonsecure": "rw",
"bootloader": "rw"
}
},
{
"name": "Read only Data",
"id": 3,
"size": "512K",
"families": ["data"],
"permissions": {
"secure": "r",
"nonsecure": "r",
"bootloader": "r"
}
}
]
}
76 changes: 76 additions & 0 deletions flash/partition_info/uf2_family_ids.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "uf2_family_ids.h"

#define UF2_FAMILY_ID_HEX_SIZE (2 + 8 * 2 + 1)

static void _add(uf2_family_ids_t *ids, const char *str) {
ids->items = realloc(ids->items, (ids->count + 1) * sizeof(char *));
ids->items[ids->count] = strdup(str);
if (ids->items[ids->count] == NULL) {
perror("strdup");
return;
}
ids->count++;
}

static void _add_default_families(uf2_family_ids_t *ids, uint32_t flags) {
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_ABSOLUTE_BITS)
_add(ids, "absolute");
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2040_BITS)
_add(ids, "rp2040");
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_ARM_S_BITS)
_add(ids, "rp2350-arm-s");
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_ARM_NS_BITS)
_add(ids, "rp2350-arm-ns");
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_RP2350_RISCV_BITS)
_add(ids, "rp2350-riscv");
if (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_DATA_BITS)
_add(ids, "data");
}

uf2_family_ids_t *uf2_family_ids_new(uint32_t flags) {
uf2_family_ids_t *ids = malloc(sizeof(uf2_family_ids_t));
ids->count = 0;
ids->items = NULL;
_add_default_families(ids, flags);
return ids;
}

char *uf2_family_ids_join(const uf2_family_ids_t *ids, const char *sep) {
size_t total_length = 0;
size_t sep_length = strlen(sep);

for (size_t i = 0; i < ids->count; i++) {
total_length += strlen(ids->items[i]);
if (i < ids->count - 1)
total_length += sep_length;
}

char *result = calloc(1, total_length + 1);
if (!result) {
perror("calloc");
return NULL;
}

result[0] = '\0';
for (size_t i = 0; i < ids->count; i++) {
strcat(result, ids->items[i]);
if (i < ids->count - 1)
strcat(result, sep);
}

return result;
}

void uf2_family_ids_free(uf2_family_ids_t *ids) {
for (size_t i = 0; i < ids->count; i++) {
free(ids->items[i]);
}
free(ids->items);
free(ids);
}

void uf2_family_ids_add_extra_family_id(uf2_family_ids_t *ids, uint32_t family_id) {
char hex_id[UF2_FAMILY_ID_HEX_SIZE];
sprintf(hex_id, "0x%08x", family_id);
_add(ids, hex_id);
}
23 changes: 23 additions & 0 deletions flash/partition_info/uf2_family_ids.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pico/stdlib.h"
#include "boot/picobin.h"


#define PARTITION_EXTRA_FAMILY_ID_MAX 3

typedef struct {
size_t count;
char **items;
} uf2_family_ids_t;


uf2_family_ids_t *uf2_family_ids_new(uint32_t flags);
char *uf2_family_ids_join(const uf2_family_ids_t *ids, const char *sep);
void uf2_family_ids_free(uf2_family_ids_t *ids);

void uf2_family_ids_add_extra_family_id(uf2_family_ids_t *ids, uint32_t family_id);