Home | History | Annotate | Download | only in syslinux
      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  */
     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  */
     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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 */
    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  */
    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  */
    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  */
    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  */
    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