1 /* ----------------------------------------------------------------------- *
2 *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 * Copyright (C) 2010 Shao Miller
6 *
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use,
11 * copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom
13 * the Software is furnished to do so, subject to the following
14 * conditions:
15 *
16 * The above copyright notice and this permission notice shall
17 * be included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 * OTHER DEALINGS IN THE SOFTWARE.
27 *
28 * ----------------------------------------------------------------------- */
29
30 /**
31 * @file disk.c
32 *
33 * Deal with disks and partitions
34 */
35
36 #include <core.h>
37 #include <dprintf.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslinux/disk.h>
42
43 /**
44 * Call int 13h, but with retry on failure. Especially floppies need this.
45 *
46 * @v inreg CPU register settings upon INT call
47 * @v outreg CPU register settings returned by INT call
48 * @ret (int) 0 upon success, -1 upon failure
49 */
disk_int13_retry(const com32sys_t * inreg,com32sys_t * outreg)50 int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
51 {
52 int retry = 6; /* Number of retries */
53 com32sys_t tmpregs;
54
55 if (!outreg)
56 outreg = &tmpregs;
57
58 while (retry--) {
59 __intcall(0x13, inreg, outreg);
60 if (!(outreg->eflags.l & EFLAGS_CF))
61 return 0; /* CF=0, OK */
62 }
63
64 return -1; /* Error */
65 }
66
67 /**
68 * Query disk parameters and EBIOS availability for a particular disk.
69 *
70 * @v disk The INT 0x13 disk drive number to process
71 * @v diskinfo The structure to save the queried params to
72 * @ret (int) 0 upon success, -1 upon failure
73 */
disk_get_params(int disk,struct disk_info * const diskinfo)74 int disk_get_params(int disk, struct disk_info *const diskinfo)
75 {
76 static com32sys_t inreg, outreg;
77 struct disk_ebios_eparam *eparam;
78 int rv = 0;
79
80 memset(diskinfo, 0, sizeof *diskinfo);
81 diskinfo->disk = disk;
82 diskinfo->bps = SECTOR;
83
84 /* Get EBIOS support */
85 memset(&inreg, 0, sizeof inreg);
86 inreg.eax.b[1] = 0x41;
87 inreg.ebx.w[0] = 0x55aa;
88 inreg.edx.b[0] = disk;
89 inreg.eflags.b[0] = 0x3; /* CF set */
90
91 __intcall(0x13, &inreg, &outreg);
92
93 if (!(outreg.eflags.l & EFLAGS_CF) &&
94 outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) {
95 diskinfo->ebios = 1;
96 }
97
98 eparam = lmalloc(sizeof *eparam);
99 if (!eparam)
100 return -1;
101
102 /* Get extended disk parameters if ebios == 1 */
103 if (diskinfo->ebios) {
104 memset(&inreg, 0, sizeof inreg);
105 inreg.eax.b[1] = 0x48;
106 inreg.edx.b[0] = disk;
107 inreg.esi.w[0] = OFFS(eparam);
108 inreg.ds = SEG(eparam);
109
110 memset(eparam, 0, sizeof *eparam);
111 eparam->len = sizeof *eparam;
112
113 __intcall(0x13, &inreg, &outreg);
114
115 if (!(outreg.eflags.l & EFLAGS_CF)) {
116 diskinfo->lbacnt = eparam->lbacnt;
117 if (eparam->bps)
118 diskinfo->bps = eparam->bps;
119 /*
120 * don't think about using geometry data returned by
121 * 48h, as it can differ from 08h a lot ...
122 */
123 }
124 }
125 /*
126 * Get disk parameters the old way - really only useful for hard
127 * disks, but if we have a partitioned floppy it's actually our best
128 * chance...
129 */
130 memset(&inreg, 0, sizeof inreg);
131 inreg.eax.b[1] = 0x08;
132 inreg.edx.b[0] = disk;
133
134 __intcall(0x13, &inreg, &outreg);
135
136 if (outreg.eflags.l & EFLAGS_CF) {
137 rv = diskinfo->ebios ? 0 : -1;
138 goto out;
139 }
140
141 diskinfo->spt = 0x3f & outreg.ecx.b[0];
142 diskinfo->head = 1 + outreg.edx.b[1];
143 diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u) << 2));
144
145 if (diskinfo->spt)
146 diskinfo->cbios = 1; /* Valid geometry */
147 else {
148 diskinfo->head = 1;
149 diskinfo->spt = 1;
150 diskinfo->cyl = 1;
151 }
152
153 if (!diskinfo->lbacnt)
154 diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt;
155
156 out:
157 lfree(eparam);
158 return rv;
159 }
160
161 /**
162 * Fill inreg based on EBIOS addressing properties.
163 *
164 * @v diskinfo The disk drive to read from
165 * @v inreg Register data structure to be filled.
166 * @v lba The logical block address to begin reading at
167 * @v count The number of sectors to read
168 * @v op_code Code to write/read operation
169 * @ret lmalloc'd buf upon success, NULL upon failure
170 */
ebios_setup(const struct disk_info * const diskinfo,com32sys_t * inreg,uint64_t lba,uint8_t count,uint8_t op_code)171 static void *ebios_setup(const struct disk_info *const diskinfo, com32sys_t *inreg,
172 uint64_t lba, uint8_t count, uint8_t op_code)
173 {
174 static struct disk_ebios_dapa *dapa = NULL;
175 void *buf;
176
177 if (!dapa) {
178 dapa = lmalloc(sizeof *dapa);
179 if (!dapa)
180 return NULL;
181 }
182
183 buf = lmalloc(count * diskinfo->bps);
184 if (!buf)
185 return NULL;
186
187 dapa->len = sizeof(*dapa);
188 dapa->count = count;
189 dapa->off = OFFS(buf);
190 dapa->seg = SEG(buf);
191 dapa->lba = lba;
192
193 inreg->eax.b[1] = op_code;
194 inreg->esi.w[0] = OFFS(dapa);
195 inreg->ds = SEG(dapa);
196 inreg->edx.b[0] = diskinfo->disk;
197
198 return buf;
199 }
200
201 /**
202 * Fill inreg based on CHS addressing properties.
203 *
204 * @v diskinfo The disk drive to read from
205 * @v inreg Register data structure to be filled.
206 * @v lba The logical block address to begin reading at
207 * @v count The number of sectors to read
208 * @v op_code Code to write/read operation
209 * @ret lmalloc'd buf upon success, NULL upon failure
210 */
chs_setup(const struct disk_info * const diskinfo,com32sys_t * inreg,uint64_t lba,uint8_t count,uint8_t op_code)211 static void *chs_setup(const struct disk_info *const diskinfo, com32sys_t *inreg,
212 uint64_t lba, uint8_t count, uint8_t op_code)
213 {
214 unsigned int c, h, s, t;
215 void *buf;
216
217 buf = lmalloc(count * diskinfo->bps);
218 if (!buf)
219 return NULL;
220
221 /*
222 * if we passed lba + count check and we get here, that means that
223 * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
224 * 32bits are perfectly enough and lbacnt corresponds to cylinder
225 * boundary
226 */
227 s = lba % diskinfo->spt;
228 t = lba / diskinfo->spt;
229 h = t % diskinfo->head;
230 c = t / diskinfo->head;
231
232 memset(inreg, 0, sizeof *inreg);
233 inreg->eax.b[0] = count;
234 inreg->eax.b[1] = op_code;
235 inreg->ecx.b[1] = c;
236 inreg->ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
237 inreg->edx.b[1] = h;
238 inreg->edx.b[0] = diskinfo->disk;
239 inreg->ebx.w[0] = OFFS(buf);
240 inreg->es = SEG(buf);
241
242 return buf;
243 }
244
245 /**
246 * Get disk block(s) and return a malloc'd buffer.
247 *
248 * @v diskinfo The disk drive to read from
249 * @v lba The logical block address to begin reading at
250 * @v count The number of sectors to read
251 * @ret data An allocated buffer with the read data
252 *
253 * Uses the disk number and information from diskinfo. Read count sectors
254 * from drive, starting at lba. Return a new buffer, or NULL upon failure.
255 */
disk_read_sectors(const struct disk_info * const diskinfo,uint64_t lba,uint8_t count)256 void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba,
257 uint8_t count)
258 {
259 com32sys_t inreg;
260 void *buf;
261 void *data = NULL;
262 uint32_t maxcnt;
263 uint32_t size = 65536;
264
265 maxcnt = (size - diskinfo->bps) / diskinfo->bps;
266 if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
267 return NULL;
268
269 memset(&inreg, 0, sizeof inreg);
270
271 if (diskinfo->ebios)
272 buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_READ_CODE);
273 else
274 buf = chs_setup(diskinfo, &inreg, lba, count, CHS_READ_CODE);
275
276 if (!buf)
277 return NULL;
278
279 if (disk_int13_retry(&inreg, NULL))
280 goto out;
281
282 data = malloc(count * diskinfo->bps);
283 if (data)
284 memcpy(data, buf, count * diskinfo->bps);
285 out:
286 lfree(buf);
287 return data;
288 }
289
290 /**
291 * Write disk block(s).
292 *
293 * @v diskinfo The disk drive to write to
294 * @v lba The logical block address to begin writing at
295 * @v data The data to write
296 * @v count The number of sectors to write
297 * @ret (int) 0 upon success, -1 upon failure
298 *
299 * Uses the disk number and information from diskinfo.
300 * Write sector(s) to a disk drive, starting at lba.
301 */
disk_write_sectors(const struct disk_info * const diskinfo,uint64_t lba,const void * data,uint8_t count)302 int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba,
303 const void *data, uint8_t count)
304 {
305 com32sys_t inreg;
306 void *buf;
307 uint32_t maxcnt;
308 uint32_t size = 65536;
309 int rv = -1;
310
311 maxcnt = (size - diskinfo->bps) / diskinfo->bps;
312 if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
313 return -1;
314
315 memset(&inreg, 0, sizeof inreg);
316
317 if (diskinfo->ebios)
318 buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_WRITE_CODE);
319 else
320 buf = chs_setup(diskinfo, &inreg, lba, count, CHS_WRITE_CODE);
321
322 if (!buf)
323 return -1;
324
325 memcpy(buf, data, count * diskinfo->bps);
326
327 if (disk_int13_retry(&inreg, NULL))
328 goto out;
329
330 rv = 0; /* ok */
331 out:
332 lfree(buf);
333 return rv;
334 }
335
336 /**
337 * Write disk blocks and verify they were written.
338 *
339 * @v diskinfo The disk drive to write to
340 * @v lba The logical block address to begin writing at
341 * @v buf The data to write
342 * @v count The number of sectors to write
343 * @ret rv 0 upon success, -1 upon failure
344 *
345 * Uses the disk number and information from diskinfo.
346 * Writes sectors to a disk drive starting at lba, then reads them back
347 * to verify they were written correctly.
348 */
disk_write_verify_sectors(const struct disk_info * const diskinfo,uint64_t lba,const void * buf,uint8_t count)349 int disk_write_verify_sectors(const struct disk_info *const diskinfo,
350 uint64_t lba, const void *buf, uint8_t count)
351 {
352 char *rb;
353 int rv;
354
355 rv = disk_write_sectors(diskinfo, lba, buf, count);
356 if (rv)
357 return rv; /* Write failure */
358 rb = disk_read_sectors(diskinfo, lba, count);
359 if (!rb)
360 return -1; /* Readback failure */
361 rv = memcmp(buf, rb, count * diskinfo->bps);
362 free(rb);
363 return rv ? -1 : 0;
364 }
365
366 /**
367 * Dump info about a DOS partition entry
368 *
369 * @v part The 16-byte partition entry to examine
370 */
disk_dos_part_dump(const struct disk_dos_part_entry * const part)371 void disk_dos_part_dump(const struct disk_dos_part_entry *const part)
372 {
373 (void)part;
374 dprintf("Partition status _____ : 0x%.2x\n"
375 "Partition CHS start\n"
376 " Cylinder ___________ : 0x%.4x (%u)\n"
377 " Head _______________ : 0x%.2x (%u)\n"
378 " Sector _____________ : 0x%.2x (%u)\n"
379 "Partition type _______ : 0x%.2x\n"
380 "Partition CHS end\n"
381 " Cylinder ___________ : 0x%.4x (%u)\n"
382 " Head _______________ : 0x%.2x (%u)\n"
383 " Sector _____________ : 0x%.2x (%u)\n"
384 "Partition LBA start __ : 0x%.8x (%u)\n"
385 "Partition LBA count __ : 0x%.8x (%u)\n"
386 "-------------------------------\n",
387 part->active_flag,
388 chs_cylinder(part->start),
389 chs_cylinder(part->start),
390 chs_head(part->start),
391 chs_head(part->start),
392 chs_sector(part->start),
393 chs_sector(part->start),
394 part->ostype,
395 chs_cylinder(part->end),
396 chs_cylinder(part->end),
397 chs_head(part->end),
398 chs_head(part->end),
399 chs_sector(part->end),
400 chs_sector(part->end),
401 part->start_lba, part->start_lba, part->length, part->length);
402 }
403
404 /* Trivial error message output */
error(const char * msg)405 static inline void error(const char *msg)
406 {
407 fputs(msg, stderr);
408 }
409
410 /**
411 * This walk-map effectively reverses the little-endian
412 * portions of a GPT disk/partition GUID for a string representation.
413 * There might be a better header for this...
414 */
415 static const char guid_le_walk_map[] = {
416 3, -1, -1, -1, 0,
417 5, -1, 0,
418 3, -1, 0,
419 2, 1, 0,
420 1, 1, 1, 1, 1, 1
421 };
422
423 /**
424 * Fill a buffer with a textual GUID representation.
425 *
426 * @v buf Points to a minimum array of 37 chars
427 * @v id The GUID to represent as text
428 *
429 * The buffer must be >= char[37] and will be populated
430 * with an ASCII NUL C string terminator.
431 * Example: 11111111-2222-3333-4444-444444444444
432 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
433 */
guid_to_str(char * buf,const struct guid * const id)434 void guid_to_str(char *buf, const struct guid *const id)
435 {
436 unsigned int i = 0;
437 const char *walker = (const char *)id;
438
439 while (i < sizeof(guid_le_walk_map)) {
440 walker += guid_le_walk_map[i];
441 if (!guid_le_walk_map[i])
442 *buf = '-';
443 else {
444 *buf = ((*walker & 0xF0) >> 4) + '0';
445 if (*buf > '9')
446 *buf += 'A' - '9' - 1;
447 buf++;
448 *buf = (*walker & 0x0F) + '0';
449 if (*buf > '9')
450 *buf += 'A' - '9' - 1;
451 }
452 buf++;
453 i++;
454 }
455 *buf = 0;
456 }
457
458 /**
459 * Create a GUID structure from a textual GUID representation.
460 *
461 * @v buf Points to a GUID string to parse
462 * @v id Points to a GUID to be populated
463 * @ret (int) Returns 0 upon success, -1 upon failure
464 *
465 * The input buffer must be >= 32 hexadecimal chars and be
466 * terminated with an ASCII NUL. Returns non-zero on failure.
467 * Example: 11111111-2222-3333-4444-444444444444
468 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
469 */
str_to_guid(const char * buf,struct guid * const id)470 int str_to_guid(const char *buf, struct guid *const id)
471 {
472 char guid_seq[sizeof(struct guid) * 2];
473 unsigned int i = 0;
474 char *walker = (char *)id;
475
476 while (*buf && i < sizeof(guid_seq)) {
477 switch (*buf) {
478 /* Skip these three characters */
479 case '{':
480 case '}':
481 case '-':
482 break;
483 default:
484 /* Copy something useful to the temp. sequence */
485 if ((*buf >= '0') && (*buf <= '9'))
486 guid_seq[i] = *buf - '0';
487 else if ((*buf >= 'A') && (*buf <= 'F'))
488 guid_seq[i] = *buf - 'A' + 10;
489 else if ((*buf >= 'a') && (*buf <= 'f'))
490 guid_seq[i] = *buf - 'a' + 10;
491 else {
492 /* Or not */
493 error("Illegal character in GUID!\n");
494 return -1;
495 }
496 i++;
497 }
498 buf++;
499 }
500 /* Check for insufficient valid characters */
501 if (i < sizeof(guid_seq)) {
502 error("Too few GUID characters!\n");
503 return -1;
504 }
505 buf = guid_seq;
506 i = 0;
507 while (i < sizeof(guid_le_walk_map)) {
508 if (!guid_le_walk_map[i])
509 i++;
510 walker += guid_le_walk_map[i];
511 *walker = *buf << 4;
512 buf++;
513 *walker |= *buf;
514 buf++;
515 i++;
516 }
517 return 0;
518 }
519
520 /**
521 * Display GPT partition details.
522 *
523 * @v gpt_part The GPT partition entry to display
524 */
disk_gpt_part_dump(const struct disk_gpt_part_entry * const gpt_part)525 void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part)
526 {
527 unsigned int i;
528 char guid_text[37];
529
530 dprintf("----------------------------------\n"
531 "GPT part. LBA first __ : 0x%.16llx\n"
532 "GPT part. LBA last ___ : 0x%.16llx\n"
533 "GPT part. attribs ____ : 0x%.16llx\n"
534 "GPT part. name _______ : '",
535 gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
536 for (i = 0; i < sizeof(gpt_part->name); i++) {
537 if (gpt_part->name[i])
538 dprintf("%c", gpt_part->name[i]);
539 }
540 dprintf("'");
541 guid_to_str(guid_text, &gpt_part->type);
542 dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
543 guid_to_str(guid_text, &gpt_part->uid);
544 dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
545 }
546
547 /**
548 * Display GPT header details.
549 *
550 * @v gpt The GPT header to display
551 */
disk_gpt_header_dump(const struct disk_gpt_header * const gpt)552 void disk_gpt_header_dump(const struct disk_gpt_header *const gpt)
553 {
554 char guid_text[37];
555
556 printf("GPT sig ______________ : '%8.8s'\n"
557 "GPT major revision ___ : 0x%.4x\n"
558 "GPT minor revision ___ : 0x%.4x\n"
559 "GPT header size ______ : 0x%.8x\n"
560 "GPT header checksum __ : 0x%.8x\n"
561 "GPT reserved _________ : '%4.4s'\n"
562 "GPT LBA current ______ : 0x%.16llx\n"
563 "GPT LBA alternative __ : 0x%.16llx\n"
564 "GPT LBA first usable _ : 0x%.16llx\n"
565 "GPT LBA last usable __ : 0x%.16llx\n"
566 "GPT LBA part. table __ : 0x%.16llx\n"
567 "GPT partition count __ : 0x%.8x\n"
568 "GPT partition size ___ : 0x%.8x\n"
569 "GPT part. table chksum : 0x%.8x\n",
570 gpt->sig,
571 gpt->rev.fields.major,
572 gpt->rev.fields.minor,
573 gpt->hdr_size,
574 gpt->chksum,
575 gpt->reserved1,
576 gpt->lba_cur,
577 gpt->lba_alt,
578 gpt->lba_first_usable,
579 gpt->lba_last_usable,
580 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
581 guid_to_str(guid_text, &gpt->disk_guid);
582 printf("GPT disk GUID ________ : {%s}\n", guid_text);
583 }
584