source: bootcd/isolinux/syslinux-6.03/com32/lib/syslinux/disk.c @ 7b6b7ba

Last change on this file since 7b6b7ba was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 15.7 KB
Line 
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 */
50int 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 */
74int 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
156out:
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 */
171static 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 */
211static 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 */
256void *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);
285out:
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 */
302int 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 */
331out:
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 */
349int 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 */
371void 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 */
405static 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 */
415static 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 */
434void 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 */
470int 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 */
525void 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 */
552void 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}
Note: See TracBrowser for help on using the repository browser.