Skip to content

Commit 867ba49

Browse files
committed
feat: Add explicit MBR partition set and MBR gaps removing functions
To be squashed
1 parent 5b12c98 commit 867ba49

File tree

4 files changed

+216
-34
lines changed

4 files changed

+216
-34
lines changed

esp_ext_part_tables/include/esp_ext_part_tables.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ typedef enum {
3535
} esp_ext_part_align_t;
3636

3737
typedef enum {
38-
ESP_EXT_PART_TYPE_UNKNOWN = 0,
38+
ESP_EXT_PART_TYPE_NONE = 0x00,
3939
ESP_EXT_PART_TYPE_FAT_ANY, /*!< FAT partition (any type) */
4040
ESP_EXT_PART_TYPE_FAT12,
4141
ESP_EXT_PART_TYPE_FAT16, /*!< FAT16 with LBA addressing */

esp_ext_part_tables/include/esp_mbr.h

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,54 @@ esp_err_t esp_mbr_parse(void* mbr_buf,
117117
* @param[in] extra_args Optional extra arguments for generation (can be NULL for defaults).
118118
*
119119
* @return
120-
* - ESP_OK: Generation was successful.
121-
* - ESP_ERR_INVALID_ARG: Invalid arguments were provided.
120+
* - ESP_OK: Generation was successful.
121+
* - ESP_ERR_INVALID_ARG: Invalid arguments were provided.
122122
* - ESP_ERR_INVALID_STATE: Error filling partition entry.
123123
* - ESP_ERR_NOT_SUPPORTED: Partition address or size (sector count) exceeds 32-bit limit of MBR.
124-
* - Other error codes from `esp_ext_part_list_insert` or related routines.
124+
* - Other error codes from `esp_ext_part_list_signature_get` or `esp_mbr_partition_set`.
125125
*/
126126
esp_err_t esp_mbr_generate(mbr_t* mbr,
127127
esp_ext_part_list_t* part_list,
128128
esp_mbr_generate_extra_args_t* extra_args);
129129

130+
/**
131+
* @brief Sets a partition entry in the MBR (Master Boot Record).
132+
*
133+
* This function updates the specified partition entry in the provided MBR structure
134+
* with the information from the given partition list item. Additional arguments for
135+
* partition generation must be supplied via the extra_args parameter.
136+
*
137+
* @note This function is not thread-safe.
138+
*
139+
* @warning If the partition entry is empty (i.e., `item->info.type` is `ESP_EXT_PART_TYPE_NONE`), it will be cleared in the MBR.
140+
* If there is an empty gap between partition entries, partition entries after the gap will most likely be ignored when the MBR is parsed (MBR does not allow gaps in the partition table).
141+
* To avoid this, you can use `esp_mbr_remove_gaps_between_partiton_entries()` function to remove gaps in the MBR partition table.
142+
*
143+
* @param[in,out] mbr Pointer to the MBR structure to be updated.
144+
* @param[in] partition_index Index of the partition entry to set (0-3).
145+
* @param[in] item Pointer to the partition list item structure containing partition information.
146+
* @param[in] extra_args Extra arguments for partiton entry setting (required).
147+
*
148+
* @return
149+
* - ESP_OK: Success.
150+
* - ESP_ERR_INVALID_ARG: Invalid arguments were provided.
151+
* - ESP_ERR_INVALID_STATE: Error filling partition entry.
152+
* - ESP_ERR_NOT_SUPPORTED: Partition address or size (sector count) exceeds 32-bit limit of MBR.
153+
*/
154+
esp_err_t esp_mbr_partition_set(mbr_t* mbr, uint8_t partition_index, esp_ext_part_list_item_t* item, esp_mbr_generate_extra_args_t* extra_args);
155+
156+
/**
157+
* @brief Removes gaps in the MBR partition table by shifting partitions to left.
158+
*
159+
* @note This function is not thread-safe.
160+
*
161+
* @param[in,out] mbr Pointer to the MBR structure to be updated.
162+
* @return
163+
* - ESP_OK: Success.
164+
* - ESP_ERR_INVALID_ARG: Invalid pointer to MBR structure.
165+
*/
166+
esp_err_t esp_mbr_remove_gaps_between_partiton_entries(mbr_t* mbr);
167+
130168
#ifdef __cplusplus
131169
}
132170
#endif

esp_ext_part_tables/src/esp_mbr.c

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ static uint8_t ext_part_type_known_to_mbr_type(esp_ext_part_type_known_t type)
117117
static bool default_known_supported_partition_types(uint8_t type, esp_ext_part_type_known_t* out_type_parsed)
118118
{
119119
bool supported = true;
120-
esp_ext_part_type_known_t parsed_type = ESP_EXT_PART_TYPE_UNKNOWN;
120+
esp_ext_part_type_known_t parsed_type = ESP_EXT_PART_TYPE_NONE;
121121
switch (type) {
122122
// Supported types:
123123
case 0x01: // FAT12
@@ -162,9 +162,9 @@ static bool default_known_supported_partition_types(uint8_t type, esp_ext_part_t
162162
return supported;
163163
}
164164

165-
bool esp_mbr_default_supported_partition_types(uint8_t type, esp_ext_part_type_known_t* out_type_parsed)
165+
static bool esp_mbr_default_supported_partition_types(uint8_t type, esp_ext_part_type_known_t* out_type_parsed)
166166
{
167-
esp_ext_part_type_known_t parsed_type = ESP_EXT_PART_TYPE_UNKNOWN;
167+
esp_ext_part_type_known_t parsed_type = ESP_EXT_PART_TYPE_NONE;
168168
bool is_supported = default_known_supported_partition_types(type, &parsed_type);
169169

170170
if (out_type_parsed != NULL) {
@@ -234,7 +234,7 @@ esp_err_t esp_mbr_parse(void* mbr_buf,
234234
}
235235

236236
// If the partition entry is not supported, skip it as well
237-
esp_ext_part_type_known_t parsed_type = ESP_EXT_PART_TYPE_UNKNOWN;
237+
esp_ext_part_type_known_t parsed_type = ESP_EXT_PART_TYPE_NONE;
238238
bool is_supported = esp_mbr_default_supported_partition_types(partition->type, &parsed_type);
239239
if (!is_supported) {
240240
continue;
@@ -303,13 +303,53 @@ static bool mbr_partition_fill(mbr_partition_t* partition, esp_ext_part_list_ite
303303
return true; // OK
304304
}
305305

306+
esp_err_t esp_mbr_partition_set(mbr_t* mbr, uint8_t partition_index, esp_ext_part_list_item_t* item, esp_mbr_generate_extra_args_t* extra_args)
307+
{
308+
if (mbr == NULL || partition_index >= 4 || item == NULL || extra_args == NULL) {
309+
return ESP_ERR_INVALID_ARG;
310+
}
311+
312+
mbr_partition_t* partition = &mbr->partition_table[partition_index];
313+
314+
// Check if the partition entry is empty and if so, skip it
315+
if (item->info.type == ESP_EXT_PART_TYPE_NONE) {
316+
memset(partition, 0, sizeof(mbr_partition_t));
317+
return ESP_OK; // No partition to set
318+
}
319+
320+
// Check if we have enough space in the MBR partition table
321+
uint64_t first_sector_address = esp_ext_part_bytes_to_sector_count(item->info.address, extra_args->sector_size);
322+
uint64_t sector_count = esp_ext_part_bytes_to_sector_count(item->info.size, extra_args->sector_size);
323+
if (first_sector_address > UINT32_MAX || sector_count > UINT32_MAX) {
324+
ESP_LOGE(TAG, "Partition address or size exceeds 32-bit limit of MBR");
325+
return ESP_ERR_NOT_SUPPORTED; // Address or size too large for MBR
326+
}
327+
328+
// Set the partition info
329+
if (item->info.flags & ESP_EXT_PART_FLAG_ACTIVE) {
330+
partition->status = MBR_PARTITION_STATUS_ACTIVE;
331+
}
332+
partition->lba_start = esp_mbr_lba_align((uint32_t) first_sector_address, extra_args->sector_size, extra_args->alignment);
333+
partition->sector_count = (uint32_t) sector_count;
334+
partition->type = ext_part_type_known_to_mbr_type(item->info.type);
335+
336+
337+
if (mbr_partition_fill(partition, item) == false) {
338+
return ESP_ERR_INVALID_STATE; // Error filling partition
339+
}
340+
341+
return ESP_OK;
342+
}
343+
344+
306345
esp_err_t esp_mbr_generate(mbr_t* mbr,
307346
esp_ext_part_list_t* part_list,
308347
esp_mbr_generate_extra_args_t* extra_args)
309348
{
310349
if (mbr == NULL || part_list == NULL) {
311350
return ESP_ERR_INVALID_ARG;
312351
}
352+
esp_err_t err = ESP_OK;
313353

314354
// Set default arguments for MBR generation
315355
esp_mbr_generate_extra_args_t args = {
@@ -332,7 +372,7 @@ esp_err_t esp_mbr_generate(mbr_t* mbr,
332372
mbr->boot_signature = MBR_SIGNATURE;
333373
if (args.keep_signature) {
334374
// Use the disk signature from the partition list
335-
esp_err_t err = esp_ext_part_list_signature_get(part_list, &mbr->disk_signature);
375+
err = esp_ext_part_list_signature_get(part_list, &mbr->disk_signature);
336376
if (err != ESP_OK) {
337377
ESP_LOGE(TAG, "Failed to get disk signature from partition list");
338378
return err;
@@ -345,33 +385,41 @@ esp_err_t esp_mbr_generate(mbr_t* mbr,
345385
mbr->copy_protected = MBR_COPY_PROTECTED;
346386
}
347387

348-
mbr_partition_t* partition = NULL;
349388
esp_ext_part_list_item_t *it = NULL;
350389
int i = 0;
351-
uint64_t first_sector_address, sector_count;
352390
SLIST_FOREACH(it, &part_list->head, next) {
353-
// Check if we have enough space in the MBR partition table
354-
first_sector_address = esp_ext_part_bytes_to_sector_count(it->info.address, args.sector_size);
355-
sector_count = esp_ext_part_bytes_to_sector_count(it->info.size, args.sector_size);
356-
if (first_sector_address > UINT32_MAX || sector_count > UINT32_MAX) {
357-
ESP_LOGE(TAG, "Partition address or size exceeds 32-bit limit of MBR");
358-
return ESP_ERR_NOT_SUPPORTED; // Address or size too large for MBR
391+
err = esp_mbr_partition_set(mbr, i, it, &args);
392+
if (err != ESP_OK) {
393+
ESP_LOGE(TAG, "Failed to set partition %d: %s", i, esp_err_to_name(err));
394+
return err; // Error setting partition
359395
}
360-
361-
partition = (mbr_partition_t*) &mbr->partition_table[i];
362396
i += 1;
397+
}
363398

364-
if (it->info.flags & ESP_EXT_PART_FLAG_ACTIVE) {
365-
partition->status = MBR_PARTITION_STATUS_ACTIVE;
366-
}
367-
partition->lba_start = esp_mbr_lba_align((uint32_t) first_sector_address, args.sector_size, args.alignment);
368-
partition->sector_count = (uint32_t) sector_count;
369-
partition->type = ext_part_type_known_to_mbr_type(it->info.type);
399+
return ESP_OK;
400+
}
370401

371-
if (mbr_partition_fill(partition, it) == false) {
372-
return ESP_ERR_INVALID_STATE; // Error filling partition
402+
esp_err_t esp_mbr_remove_gaps_between_partiton_entries(mbr_t* mbr)
403+
{
404+
if (mbr == NULL) {
405+
return ESP_ERR_INVALID_ARG; // Invalid MBR pointer
406+
}
407+
408+
// Iterate through the partition table and remove gaps
409+
mbr_partition_t* partition;
410+
uint8_t gap_index = 0; // Next index to fill
411+
for (int i = 0; i < 4; i++) {
412+
partition = &mbr->partition_table[i];
413+
if (partition->type == 0x00) {
414+
continue; // Skip empty entries
373415
}
416+
if (gap_index != i) {
417+
// Move the partition to the next available index
418+
memcpy(&mbr->partition_table[gap_index], partition, sizeof(mbr_partition_t));
419+
memset(partition, 0, sizeof(mbr_partition_t)); // Clear the old entry
420+
}
421+
gap_index++;
374422
}
375423

376424
return ESP_OK;
377-
}
425+
}

esp_ext_part_tables/test_apps/main/test_esp_ext_part.c

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ uint8_t mbr_bin[512] = {
2929

3030
unsigned int mbr_bin_len = 512;
3131

32-
void print_esp_ext_part_list_item(esp_ext_part_list_item_t* head)
32+
static void print_esp_ext_part_list_items(esp_ext_part_list_item_t* head)
3333
{
3434
esp_ext_part_list_item_t* it = head;
3535
int i = 0;
@@ -59,7 +59,7 @@ TEST_CASE("Test esp_mbr_parse", "[esp_ext_part_table]")
5959
esp_ext_part_list_item_t* it = esp_ext_part_list_item_head(&part_list);
6060
TEST_ASSERT_NOT_NULL(it);
6161

62-
print_esp_ext_part_list_item(it);
62+
print_esp_ext_part_list_items(it);
6363
fflush(stdout);
6464

6565
do {
@@ -125,8 +125,8 @@ TEST_CASE("Test esp_mbr_generate generates the (almost) same MBR as the original
125125
esp_ext_part_list_item_t* it2 = esp_ext_part_list_item_head(&part_list2);
126126
TEST_ASSERT_NOT_NULL(it2);
127127

128-
print_esp_ext_part_list_item(it1);
129-
print_esp_ext_part_list_item(it2);
128+
print_esp_ext_part_list_items(it1);
129+
print_esp_ext_part_list_items(it2);
130130
fflush(stdout);
131131

132132
uint8_t* mbr_bin_from_part_table = (uint8_t*) mbr_bin + MBR_PARTITION_TABLE_OFFSET;
@@ -156,7 +156,7 @@ TEST_CASE("Test esp_mbr_generate with esp_mbr_parse", "[esp_ext_part_table]")
156156
TEST_ASSERT_NOT_NULL(it);
157157

158158
// Print the partition list
159-
print_esp_ext_part_list_item(it);
159+
print_esp_ext_part_list_items(it);
160160
fflush(stdout);
161161

162162
do {
@@ -214,7 +214,7 @@ TEST_CASE("Test esp_mbr_generate with esp_mbr_parse", "[esp_ext_part_table]")
214214
// Print the partition list
215215
it = esp_ext_part_list_item_head(&part_list);
216216
TEST_ASSERT_NOT_NULL(it);
217-
print_esp_ext_part_list_item(it);
217+
print_esp_ext_part_list_items(it);
218218
fflush(stdout);
219219

220220
// Deinitialize the part list
@@ -241,6 +241,102 @@ TEST_CASE("Test esp_ext_part_list_signature_t get and set", "[esp_ext_part_table
241241
TEST_ESP_OK(esp_ext_part_list_deinit(&part_list));
242242
}
243243

244+
TEST_CASE("Test esp_mbr_partition_set and esp_mbr_remove_gaps_between_partiton_entries", "[esp_ext_part_table]")
245+
{
246+
esp_ext_part_list_t part_list = {0};
247+
248+
esp_mbr_generate_extra_args_t mbr_args = {
249+
.sector_size = ESP_EXT_PART_SECTOR_SIZE_512B,
250+
.alignment = ESP_EXT_PART_ALIGN_1MiB
251+
};
252+
253+
// 4 FAT12 partitions
254+
esp_ext_part_list_item_t item = {
255+
.info = {
256+
.size = 10 * 1024 * 1024, // 10 MiB
257+
.type = ESP_EXT_PART_TYPE_FAT12,
258+
}
259+
};
260+
261+
for (int i = 0; i < 4; i++) {
262+
item.info.address = 1024 * 1024 + i * item.info.size; // First partition starts at 1 MiB offset, next partitions are 10 MiB apart
263+
TEST_ESP_OK(esp_ext_part_list_insert(&part_list, &item));
264+
}
265+
266+
printf("Partition list after creation:\n");
267+
esp_ext_part_list_item_t* it = esp_ext_part_list_item_head(&part_list);
268+
TEST_ASSERT_NOT_NULL(it);
269+
print_esp_ext_part_list_items(it);
270+
fflush(stdout);
271+
272+
// Generate the MBR
273+
mbr_t* mbr = (mbr_t*) calloc(1, sizeof(mbr_t));
274+
TEST_ASSERT_NOT_NULL(mbr);
275+
276+
TEST_ESP_OK(esp_mbr_generate(mbr, &part_list, &mbr_args));
277+
// Deinitialize the part list
278+
TEST_ESP_OK(esp_ext_part_list_deinit(&part_list));
279+
280+
// Create gaps in MBR at index 1 and 2
281+
// This will remove the second and third partitions from the MBR
282+
esp_ext_part_list_item_t empty_item = {
283+
.info = {
284+
.type = ESP_EXT_PART_TYPE_NONE, // No type
285+
}
286+
};
287+
esp_mbr_partition_set(mbr, 1, &empty_item, &mbr_args);
288+
esp_mbr_partition_set(mbr, 2, &empty_item, &mbr_args);
289+
printf("Partition 1 and 2 removed, 0 and 3 remained, gaps created\n\n");
290+
291+
// Parse the MBR to get the partition list without removing the gaps
292+
esp_ext_part_list_t part_list_from_mbr = {0};
293+
TEST_ESP_OK(esp_mbr_parse((void*) mbr, &part_list_from_mbr, NULL));
294+
295+
it = esp_ext_part_list_item_head(&part_list_from_mbr);
296+
TEST_ASSERT_NOT_NULL(it);
297+
int partition_count = 1;
298+
while ((it = esp_ext_part_list_item_next(it)) != NULL) {
299+
partition_count++;
300+
}
301+
TEST_ASSERT_EQUAL(partition_count, 1);
302+
303+
// Print the partition list
304+
printf("Partition list after creating gaps (partition 3 is missing because the gaps were created and not shifted out):\n");
305+
it = esp_ext_part_list_item_head(&part_list_from_mbr);
306+
TEST_ASSERT_NOT_NULL(it);
307+
print_esp_ext_part_list_items(it);
308+
fflush(stdout);
309+
310+
// Deinitialize the part list
311+
TEST_ESP_OK(esp_ext_part_list_deinit(&part_list_from_mbr));
312+
313+
// Now remove the gaps between partition entries
314+
esp_mbr_remove_gaps_between_partiton_entries(mbr);
315+
// Parse the MBR to get the partition list with gaps removed
316+
esp_ext_part_list_t part_list_from_mbr_correct = {0};
317+
TEST_ESP_OK(esp_mbr_parse((void*) mbr, &part_list_from_mbr_correct, NULL));
318+
free(mbr);
319+
320+
// Now the partition list should contain 2 partitions (originally partition 0 and 3, now partition 0 and 1)
321+
it = esp_ext_part_list_item_head(&part_list_from_mbr_correct);
322+
TEST_ASSERT_NOT_NULL(it);
323+
partition_count = 1;
324+
while ((it = esp_ext_part_list_item_next(it)) != NULL) {
325+
partition_count++;
326+
}
327+
TEST_ASSERT_EQUAL(partition_count, 2);
328+
329+
// Print the partition list
330+
printf("Partition list after removing gaps (partition 0 stayed the same, partition 3 was shifted and now is partition 1):\n");
331+
it = esp_ext_part_list_item_head(&part_list_from_mbr_correct);
332+
TEST_ASSERT_NOT_NULL(it);
333+
print_esp_ext_part_list_items(it);
334+
fflush(stdout);
335+
336+
// Deinitialize the part list
337+
TEST_ESP_OK(esp_ext_part_list_deinit(&part_list_from_mbr_correct));
338+
}
339+
244340
void app_main(void)
245341
{
246342
unity_run_menu();

0 commit comments

Comments
 (0)