summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2023-05-19 08:56:49 -0400
committerSolomon Peachy <pizza@shaftnet.org>2023-05-21 20:34:03 -0400
commit5dc0e4e0bc8011918b58baf0d8b03a3840a7b213 (patch)
tree145848015c29853e0a0437e9731cd8235f0f22c7
parent8fbd44a3d37fddf323dea36e8f52193fd007bd75 (diff)
downloadrockbox-5dc0e4e0bc8011918b58baf0d8b03a3840a7b213.tar.gz
rockbox-5dc0e4e0bc8011918b58baf0d8b03a3840a7b213.zip
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
-rw-r--r--firmware/common/disk.c139
-rw-r--r--firmware/export/disk.h3
2 files changed, 132 insertions, 10 deletions
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index e94e161d44..554ee3cd41 100644
--- a/firmware/common/disk.c
+++ b/firmware/common/disk.c
@@ -44,7 +44,7 @@
44#define disk_writer_lock() file_internal_lock_WRITER() 44#define disk_writer_lock() file_internal_lock_WRITER()
45#define disk_writer_unlock() file_internal_unlock_WRITER() 45#define disk_writer_unlock() file_internal_unlock_WRITER()
46 46
47/* Partition table entry layout: 47/* "MBR" Partition table entry layout:
48 ----------------------- 48 -----------------------
49 0: 0x80 - active 49 0: 0x80 - active
50 1: starting head 50 1: starting head
@@ -58,6 +58,16 @@
58 12-15: nr of sectors in partition 58 12-15: nr of sectors in partition
59*/ 59*/
60 60
61#define BYTES2INT64(array, pos) \
62 (((uint64_t)array[pos+0] << 0) | \
63 ((uint64_t)array[pos+1] << 8) | \
64 ((uint64_t)array[pos+2] << 16) | \
65 ((uint64_t)array[pos+3] << 24) | \
66 ((uint64_t)array[pos+4] << 32) | \
67 ((uint64_t)array[pos+5] << 40) | \
68 ((uint64_t)array[pos+6] << 48) | \
69 ((uint64_t)array[pos+7] << 56) )
70
61#define BYTES2INT32(array, pos) \ 71#define BYTES2INT32(array, pos) \
62 (((uint32_t)array[pos+0] << 0) | \ 72 (((uint32_t)array[pos+0] << 0) | \
63 ((uint32_t)array[pos+1] << 8) | \ 73 ((uint32_t)array[pos+1] << 8) | \
@@ -65,11 +75,11 @@
65 ((uint32_t)array[pos+3] << 24)) 75 ((uint32_t)array[pos+3] << 24))
66 76
67#define BYTES2INT16(array, pos) \ 77#define BYTES2INT16(array, pos) \
68 (((uint32_t)array[pos+0] << 0) | \ 78 (((uint16_t)array[pos+0] << 0) | \
69 ((uint32_t)array[pos+1] << 8)) 79 ((uint16_t)array[pos+1] << 8))
70 80
71/* space for 4 partitions on 2 drives */ 81/* space for 4 partitions on 2 drives */
72static struct partinfo part[NUM_DRIVES*4]; 82static struct partinfo part[NUM_DRIVES*MAX_PARTITIONS_PER_DRIVE];
73/* mounted to which drive (-1 if none) */ 83/* mounted to which drive (-1 if none) */
74static int vol_drive[NUM_VOLUMES]; 84static int vol_drive[NUM_VOLUMES];
75 85
@@ -120,12 +130,13 @@ bool disk_init(IF_MD_NONVOID(int drive))
120 /* For each drive, start at a different position, in order not to 130 /* For each drive, start at a different position, in order not to
121 destroy the first entry of drive 0. That one is needed to calculate 131 destroy the first entry of drive 0. That one is needed to calculate
122 config sector position. */ 132 config sector position. */
123 struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4]; 133 struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
134 uint8_t is_gpt = 0;
124 135
125 disk_writer_lock(); 136 disk_writer_lock();
126 137
127 /* parse partitions */ 138 /* parse partitions */
128 for (int i = 0; i < 4; i++) 139 for (int i = 0; i < MAX_PARTITIONS_PER_DRIVE && i < 4; i++)
129 { 140 {
130 unsigned char* ptr = sector + 0x1be + 16*i; 141 unsigned char* ptr = sector + 0x1be + 16*i;
131 pinfo[i].type = ptr[4]; 142 pinfo[i].type = ptr[4];
@@ -140,8 +151,115 @@ bool disk_init(IF_MD_NONVOID(int drive))
140 { 151 {
141 /* not handled yet */ 152 /* not handled yet */
142 } 153 }
143 }
144 154
155 if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
156 is_gpt = 1;
157 }
158 }
159
160 while (is_gpt) {
161 /* Re-start partition parsing using GPT */
162 uint64_t part_lba;
163 uint32_t part_entries;
164 uint32_t part_entry_size;
165 unsigned char* ptr = sector;
166
167 storage_read_sectors(IF_MD(drive,) 1, 1, sector);
168
169 part_lba = BYTES2INT64(ptr, 0);
170 if (part_lba != 0x5452415020494645ULL) {
171 DEBUGF("GPT: Invalid signature\n");
172 break;
173 }
174 part_entry_size = BYTES2INT32(ptr, 8);
175 if (part_entry_size != 0x00010000) {
176 DEBUGF("GPT: Invalid version\n");
177 break;
178 }
179 part_entry_size = BYTES2INT32(ptr, 12);
180 if (part_entry_size != 0x5c) {
181 DEBUGF("GPT: Invalid header size\n");
182 break;
183 }
184 // XXX checksum header -- u32 @ offset 16
185 part_entry_size = BYTES2INT32(ptr, 24);
186 if (part_entry_size != 1) {
187 DEBUGF("GPT: Invalid header LBA\n");
188 break;
189 }
190
191 part_lba = BYTES2INT64(ptr, 72);
192 part_entries = BYTES2INT32(ptr, 80);
193 part_entry_size = BYTES2INT32(ptr, 84);
194
195 int part = 0;
196reload:
197 storage_read_sectors(IF_MD(drive,) part_lba, 1, sector);
198 uint8_t *pptr = ptr;
199 while (part < MAX_PARTITIONS_PER_DRIVE && part_entries) {
200 if (pptr - ptr >= SECTOR_SIZE) {
201 part_lba++;
202 goto reload;
203 }
204
205 /* Parse GPT entry. We only care about the "General Data" type, ie:
206 EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
207 LE32 LE16 LE16 BE16 BE16
208 */
209 uint64_t tmp;
210 tmp = BYTES2INT32(pptr, 0);
211 if (tmp != 0xEBD0A0A2)
212 goto skip;
213 tmp = BYTES2INT16(pptr, 4);
214 if (tmp != 0xB9E5)
215 goto skip;
216 tmp = BYTES2INT16(pptr, 6);
217 if (tmp != 0x4433)
218 goto skip;
219 if (pptr[8] != 0x87 || pptr[9] != 0xc0)
220 goto skip;
221 if (pptr[10] != 0x68 || pptr[11] != 0xb6 || pptr[12] != 0xb7 ||
222 pptr[13] != 0x26 || pptr[14] != 0x99 || pptr[15] != 0xc7)
223 goto skip;
224
225 tmp = BYTES2INT64(pptr, 48); /* Flags */
226 if (tmp) {
227 DEBUGF("GPT: Skip parition with flags\n");
228 goto skip; /* Any flag makes us ignore this */
229 }
230 tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
231 if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
232 DEBUGF("GPT: partition starts after 2GiB mark\n");
233 goto skip;
234 }
235 if (tmp < 34) {
236 DEBUGF("GPT: Invalid start LBA\n");
237 goto skip;
238 }
239 pinfo[part].start = tmp;
240 tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
241 if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
242 DEBUGF("GPT: partition ends after 2GiB mark\n");
243 goto skip;
244 }
245 if (tmp <= pinfo[part].start) {
246 DEBUGF("GPT: Invalid end LBA\n");
247 goto skip;
248 }
249 pinfo[part].size = tmp - pinfo[part].start + 1;
250 pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
251
252 DEBUGF("GPart%d: start: %08lx size: %08lx\n",
253 part,pinfo[part].start,pinfo[part].size);
254 part++;
255
256 skip:
257 pptr += part_entry_size;
258 part_entries--;
259 }
260
261 is_gpt = 0; /* To break out of the loop */
262 }
145 disk_writer_unlock(); 263 disk_writer_unlock();
146 264
147 init = true; 265 init = true;
@@ -192,8 +310,7 @@ int disk_mount(int drive)
192 disk_sector_multiplier[IF_MD_DRV(drive)] = 1; 310 disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
193#endif 311#endif
194 312
195 313 /* try "superfloppy" mode */
196 /* try "superfloppy" mode */
197 DEBUGF("Trying to mount sector 0.\n"); 314 DEBUGF("Trying to mount sector 0.\n");
198 315
199 if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0)) 316 if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
@@ -210,12 +327,14 @@ int disk_mount(int drive)
210 if (mounted == 0 && volume != -1) /* not a "superfloppy"? */ 327 if (mounted == 0 && volume != -1) /* not a "superfloppy"? */
211 { 328 {
212 for (int i = CONFIG_DEFAULT_PARTNUM; 329 for (int i = CONFIG_DEFAULT_PARTNUM;
213 volume != -1 && i < 4 && mounted < NUM_VOLUMES_PER_DRIVE; 330 volume != -1 && i < MAX_PARTITIONS_PER_DRIVE && mounted < NUM_VOLUMES_PER_DRIVE;
214 i++) 331 i++)
215 { 332 {
216 if (pinfo[i].type == 0 || pinfo[i].type == 5) 333 if (pinfo[i].type == 0 || pinfo[i].type == 5)
217 continue; /* skip free/extended partitions */ 334 continue; /* skip free/extended partitions */
218 335
336 DEBUGF("Trying to mount partition %d.\n", i);
337
219 #ifdef MAX_LOG_SECTOR_SIZE 338 #ifdef MAX_LOG_SECTOR_SIZE
220 for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1) 339 for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
221 { 340 {
diff --git a/firmware/export/disk.h b/firmware/export/disk.h
index c66028fe45..e465552fdc 100644
--- a/firmware/export/disk.h
+++ b/firmware/export/disk.h
@@ -35,6 +35,9 @@ struct partinfo
35#define PARTITION_TYPE_FAT32_LBA 0x0c 35#define PARTITION_TYPE_FAT32_LBA 0x0c
36#define PARTITION_TYPE_FAT16 0x06 36#define PARTITION_TYPE_FAT16 0x06
37#define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84 37#define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84
38#define PARTITION_TYPE_GPT_GUARD 0xee
39
40#define MAX_PARTITIONS_PER_DRIVE 4 /* Needs to be at least 4 */
38 41
39bool disk_init(IF_MD_NONVOID(int drive)); 42bool disk_init(IF_MD_NONVOID(int drive));
40bool disk_partinfo(int partition, struct partinfo *info); 43bool disk_partinfo(int partition, struct partinfo *info);