diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2023-05-19 08:56:49 -0400 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2023-05-21 20:34:03 -0400 |
commit | 5dc0e4e0bc8011918b58baf0d8b03a3840a7b213 (patch) | |
tree | 145848015c29853e0a0437e9731cd8235f0f22c7 /firmware | |
parent | 8fbd44a3d37fddf323dea36e8f52193fd007bd75 (diff) | |
download | rockbox-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
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/common/disk.c | 139 | ||||
-rw-r--r-- | firmware/export/disk.h | 3 |
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 */ |
72 | static struct partinfo part[NUM_DRIVES*4]; | 82 | static 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) */ |
74 | static int vol_drive[NUM_VOLUMES]; | 84 | static 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; | ||
196 | reload: | ||
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 | ||
39 | bool disk_init(IF_MD_NONVOID(int drive)); | 42 | bool disk_init(IF_MD_NONVOID(int drive)); |
40 | bool disk_partinfo(int partition, struct partinfo *info); | 43 | bool disk_partinfo(int partition, struct partinfo *info); |