• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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