1
0
Fork 0
forked from len0rd/rockbox

disk: Support GUID Partition Tables (GPT)

Notes:

 * Currently limited to 32-bit sector addresses due to internal Rockbox
   APIs. So this means a practical limit of 2TiB per drive.
 * Only 'General Data' GPT partition type is recognised, as that's
   what SD cards seem to use for exFAT/FAT32.

Note that _booting_ off GPT-partitioned drive will require rebuilding
the various rockbox bootloaders, and even then there may be platform
limitations that preclude this.

Change-Id: Ibfaae1960adcb1e81976d4b60dd596c6d16318e4
This commit is contained in:
Solomon Peachy 2023-05-19 08:56:49 -04:00
parent 8fbd44a3d3
commit 5dc0e4e0bc
2 changed files with 132 additions and 10 deletions

View file

@ -44,7 +44,7 @@
#define disk_writer_lock() file_internal_lock_WRITER()
#define disk_writer_unlock() file_internal_unlock_WRITER()
/* Partition table entry layout:
/* "MBR" Partition table entry layout:
-----------------------
0: 0x80 - active
1: starting head
@ -58,6 +58,16 @@
12-15: nr of sectors in partition
*/
#define BYTES2INT64(array, pos) \
(((uint64_t)array[pos+0] << 0) | \
((uint64_t)array[pos+1] << 8) | \
((uint64_t)array[pos+2] << 16) | \
((uint64_t)array[pos+3] << 24) | \
((uint64_t)array[pos+4] << 32) | \
((uint64_t)array[pos+5] << 40) | \
((uint64_t)array[pos+6] << 48) | \
((uint64_t)array[pos+7] << 56) )
#define BYTES2INT32(array, pos) \
(((uint32_t)array[pos+0] << 0) | \
((uint32_t)array[pos+1] << 8) | \
@ -65,11 +75,11 @@
((uint32_t)array[pos+3] << 24))
#define BYTES2INT16(array, pos) \
(((uint32_t)array[pos+0] << 0) | \
((uint32_t)array[pos+1] << 8))
(((uint16_t)array[pos+0] << 0) | \
((uint16_t)array[pos+1] << 8))
/* space for 4 partitions on 2 drives */
static struct partinfo part[NUM_DRIVES*4];
static struct partinfo part[NUM_DRIVES*MAX_PARTITIONS_PER_DRIVE];
/* mounted to which drive (-1 if none) */
static int vol_drive[NUM_VOLUMES];
@ -120,12 +130,13 @@ bool disk_init(IF_MD_NONVOID(int drive))
/* For each drive, start at a different position, in order not to
destroy the first entry of drive 0. That one is needed to calculate
config sector position. */
struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4];
struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
uint8_t is_gpt = 0;
disk_writer_lock();
/* parse partitions */
for (int i = 0; i < 4; i++)
for (int i = 0; i < MAX_PARTITIONS_PER_DRIVE && i < 4; i++)
{
unsigned char* ptr = sector + 0x1be + 16*i;
pinfo[i].type = ptr[4];
@ -140,8 +151,115 @@ bool disk_init(IF_MD_NONVOID(int drive))
{
/* not handled yet */
}
}
if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
is_gpt = 1;
}
}
while (is_gpt) {
/* Re-start partition parsing using GPT */
uint64_t part_lba;
uint32_t part_entries;
uint32_t part_entry_size;
unsigned char* ptr = sector;
storage_read_sectors(IF_MD(drive,) 1, 1, sector);
part_lba = BYTES2INT64(ptr, 0);
if (part_lba != 0x5452415020494645ULL) {
DEBUGF("GPT: Invalid signature\n");
break;
}
part_entry_size = BYTES2INT32(ptr, 8);
if (part_entry_size != 0x00010000) {
DEBUGF("GPT: Invalid version\n");
break;
}
part_entry_size = BYTES2INT32(ptr, 12);
if (part_entry_size != 0x5c) {
DEBUGF("GPT: Invalid header size\n");
break;
}
// XXX checksum header -- u32 @ offset 16
part_entry_size = BYTES2INT32(ptr, 24);
if (part_entry_size != 1) {
DEBUGF("GPT: Invalid header LBA\n");
break;
}
part_lba = BYTES2INT64(ptr, 72);
part_entries = BYTES2INT32(ptr, 80);
part_entry_size = BYTES2INT32(ptr, 84);
int part = 0;
reload:
storage_read_sectors(IF_MD(drive,) part_lba, 1, sector);
uint8_t *pptr = ptr;
while (part < MAX_PARTITIONS_PER_DRIVE && part_entries) {
if (pptr - ptr >= SECTOR_SIZE) {
part_lba++;
goto reload;
}
/* Parse GPT entry. We only care about the "General Data" type, ie:
EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
LE32 LE16 LE16 BE16 BE16
*/
uint64_t tmp;
tmp = BYTES2INT32(pptr, 0);
if (tmp != 0xEBD0A0A2)
goto skip;
tmp = BYTES2INT16(pptr, 4);
if (tmp != 0xB9E5)
goto skip;
tmp = BYTES2INT16(pptr, 6);
if (tmp != 0x4433)
goto skip;
if (pptr[8] != 0x87 || pptr[9] != 0xc0)
goto skip;
if (pptr[10] != 0x68 || pptr[11] != 0xb6 || pptr[12] != 0xb7 ||
pptr[13] != 0x26 || pptr[14] != 0x99 || pptr[15] != 0xc7)
goto skip;
tmp = BYTES2INT64(pptr, 48); /* Flags */
if (tmp) {
DEBUGF("GPT: Skip parition with flags\n");
goto skip; /* Any flag makes us ignore this */
}
tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
DEBUGF("GPT: partition starts after 2GiB mark\n");
goto skip;
}
if (tmp < 34) {
DEBUGF("GPT: Invalid start LBA\n");
goto skip;
}
pinfo[part].start = tmp;
tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
DEBUGF("GPT: partition ends after 2GiB mark\n");
goto skip;
}
if (tmp <= pinfo[part].start) {
DEBUGF("GPT: Invalid end LBA\n");
goto skip;
}
pinfo[part].size = tmp - pinfo[part].start + 1;
pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
DEBUGF("GPart%d: start: %08lx size: %08lx\n",
part,pinfo[part].start,pinfo[part].size);
part++;
skip:
pptr += part_entry_size;
part_entries--;
}
is_gpt = 0; /* To break out of the loop */
}
disk_writer_unlock();
init = true;
@ -192,8 +310,7 @@ int disk_mount(int drive)
disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
#endif
/* try "superfloppy" mode */
/* try "superfloppy" mode */
DEBUGF("Trying to mount sector 0.\n");
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
@ -210,12 +327,14 @@ int disk_mount(int drive)
if (mounted == 0 && volume != -1) /* not a "superfloppy"? */
{
for (int i = CONFIG_DEFAULT_PARTNUM;
volume != -1 && i < 4 && mounted < NUM_VOLUMES_PER_DRIVE;
volume != -1 && i < MAX_PARTITIONS_PER_DRIVE && mounted < NUM_VOLUMES_PER_DRIVE;
i++)
{
if (pinfo[i].type == 0 || pinfo[i].type == 5)
continue; /* skip free/extended partitions */
DEBUGF("Trying to mount partition %d.\n", i);
#ifdef MAX_LOG_SECTOR_SIZE
for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
{

View file

@ -35,6 +35,9 @@ struct partinfo
#define PARTITION_TYPE_FAT32_LBA 0x0c
#define PARTITION_TYPE_FAT16 0x06
#define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84
#define PARTITION_TYPE_GPT_GUARD 0xee
#define MAX_PARTITIONS_PER_DRIVE 4 /* Needs to be at least 4 */
bool disk_init(IF_MD_NONVOID(int drive));
bool disk_partinfo(int partition, struct partinfo *info);