Home | History | Annotate | Download | only in disk
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2009 Pierre-Alexandre Meyer
      4  *
      5  *   Some parts borrowed from chain.c32:
      6  *
      7  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
      8  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
      9  *
     10  *   This file is part of Syslinux, and is made available under
     11  *   the terms of the GNU General Public License version 2.
     12  *
     13  * ----------------------------------------------------------------------- */
     14 
     15 #include <com32.h>
     16 #include <string.h>
     17 #include <stdio.h>
     18 #include <disk/geom.h>
     19 
     20 #include <stdio.h>
     21 
     22 /**
     23  * lba_to_chs - split given lba into cylinders/heads/sectors
     24  **/
     25 void lba_to_chs(const struct driveinfo *drive_info, const int lba,
     26 		unsigned int *cylinder, unsigned int *head,
     27 		unsigned int *sector)
     28 {
     29     unsigned int track;
     30 
     31     /* Use EDD, if valid */
     32     if (drive_info->edd_params.sectors_per_track > 0 &&
     33 	drive_info->edd_params.heads > 0) {
     34 	*cylinder = (lba % drive_info->edd_params.sectors_per_track) + 1;
     35 	track = lba / drive_info->edd_params.sectors_per_track;
     36 	*head = track % drive_info->edd_params.heads;
     37 	*sector = track / drive_info->edd_params.heads;
     38     } else if (drive_info->cbios) {
     39 	*cylinder = (lba % drive_info->legacy_sectors_per_track) + 1;
     40 	track = lba / drive_info->legacy_sectors_per_track;
     41 	*head = track % (drive_info->legacy_max_head + 1);
     42 	*sector = track / (drive_info->legacy_max_head + 1);
     43     }
     44 }
     45 
     46 /**
     47  * detect_extensions - detect if we can use extensions
     48  *
     49  * INT 13 - IBM/MS INT 13 Extensions - INSTALLATION CHECK
     50  *    AH = 41h
     51  *    BX = 55AAh
     52  *    DL = drive (80h-FFh)
     53  *
     54  * Return: CF set on error (extensions not supported)
     55  *    AH = 01h (invalid function)
     56  *    CF clear if successful
     57  *    BX = AA55h if installed
     58  *    AH = major version of extensions
     59  *        01h = 1.x
     60  *        20h = 2.0 / EDD-1.0
     61  *        21h = 2.1 / EDD-1.1
     62  *        30h = EDD-3.0
     63  *    AL = internal use
     64  *    CX = API subset support bitmap (see #00271)
     65  *    DH = extension version (v2.0+ ??? -- not present in 1.x)
     66  *
     67  * Note: the Phoenix Enhanced Disk Drive Specification v1.0 uses version 2.0 of
     68  *       the INT 13 Extensions API
     69  *
     70  * Bitfields for IBM/MS INT 13 Extensions API support bitmap:
     71  * Bit(s)    Description    (Table 00271)
     72  *     0 extended disk access functions (AH=42h-44h,47h,48h) supported
     73  *     1 removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h)
     74  *       supported
     75  *     2 enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
     76  *       extended drive parameter table is valid (see #00273,#00278)
     77  *     3-15    reserved (0)
     78  **/
     79 static int detect_extensions(struct driveinfo *drive_info)
     80 {
     81     com32sys_t getebios, ebios;
     82 
     83     memset(&getebios, 0, sizeof getebios);
     84     memset(&ebios, 0, sizeof ebios);
     85 
     86     getebios.eflags.b[0] = 0x3;	/* CF set */
     87     getebios.ebx.w[0] = 0x55aa;
     88     getebios.edx.b[0] = drive_info->disk;
     89     getebios.eax.b[1] = 0x41;
     90 
     91     __intcall(0x13, &getebios, &ebios);
     92 
     93     if (!(ebios.eflags.l & EFLAGS_CF) && ebios.ebx.w[0] == 0xaa55) {
     94 	drive_info->ebios = 1;
     95 	drive_info->edd_version = ebios.eax.b[1];
     96 	drive_info->edd_functionality_subset = ebios.ecx.w[0];
     97 	return 0;
     98     } else
     99 	return -1;		/* Drive does not exist? */
    100 }
    101 
    102 /**
    103  * get_drive_parameters_with_extensions - retrieve disk parameters via AH=48h
    104  *
    105  * INT 13 - IBM/MS INT 13 Extensions - GET DRIVE PARAMETERS
    106  *     AH = 48h
    107  *     DL = drive (80h-FFh)
    108  *     DS:SI -> buffer for drive parameters
    109  * Return: CF clear if successful
    110  *     AH = 00h
    111  *     DS:SI buffer filled
    112  *     CF set on error
    113  *     AH = error code (see #00234)
    114  * BUG: several different Compaq BIOSes incorrectly report high-numbered
    115  *     drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
    116  *     same geometry as drive 80h; as a workaround, scan through disk
    117  *     numbers, stopping as soon as the number of valid drives encountered
    118  *     equals the value in 0040h:0075h
    119  **/
    120 static int get_drive_parameters_with_extensions(struct driveinfo *drive_info)
    121 {
    122     com32sys_t inreg, outreg;
    123     struct edd_device_parameters *dp;
    124 
    125     memset(&inreg, 0, sizeof inreg);
    126 
    127     /*
    128      * The caller shall set this value to the maximum Result Buffer
    129      * length, in bytes. If the length of this buffer is less than 30
    130      * bytes, this function shall not return the pointer to Drive Parameter
    131      * Table (DPT) extension. If the buffer length is 30 or greater on
    132      * entry, it shall be set to 30 on exit. If the buffer length is
    133      * between 26 and 29, it shall be set to 26 on exit.
    134      * If the buffer length is less than 26 on entry an error shall be
    135      * returned.
    136      */
    137     dp = lmalloc(sizeof *dp);
    138     if (!dp)
    139 	return -1;
    140 
    141     dp->len = sizeof(struct edd_device_parameters);
    142 
    143     inreg.esi.w[0] = OFFS(dp);
    144     inreg.ds = SEG(dp);
    145     inreg.edx.b[0] = drive_info->disk;
    146     inreg.eax.b[1] = 0x48;
    147 
    148     __intcall(0x13, &inreg, &outreg);
    149 
    150     /* CF set on error */
    151     if (outreg.eflags.l & EFLAGS_CF) {
    152 	lfree(dp);
    153 	return outreg.eax.b[1];
    154     }
    155 
    156     memcpy(&drive_info->edd_params, dp, sizeof drive_info->edd_params);
    157     lfree(dp);
    158 
    159     return 0;
    160 }
    161 
    162 /**
    163  * get_drive_parameters_without_extensions - retrieve drive parameters via AH=08h
    164  *
    165  * INT 13 - DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI)
    166  *     AH = 08h
    167  *     DL = drive (bit 7 set for hard disk)
    168  *
    169  * Return: CF set on error
    170  *     AH = status (07h) (see #00234)
    171  *     CF clear if successful
    172  *     AH = 00h
    173  *     AL = 00h on at least some BIOSes
    174  *     BL = drive type (AT/PS2 floppies only) (see #00242)
    175  *     CH = low eight bits of maximum cylinder number
    176  *     CL = maximum sector number (bits 5-0)
    177  *          high two bits of maximum cylinder number (bits 7-6)
    178  *     DH = maximum head number
    179  *     DL = number of drives
    180  *     ES:DI -> drive parameter table (floppies only)
    181  *
    182  * Notes:
    183  *   - may return successful even though specified drive is greater than the
    184  *     number of attached drives of that type (floppy/hard); check DL to
    185  *     ensure validity
    186  *   - for systems predating the IBM AT, this call is only valid for hard
    187  *     disks, as it is implemented by the hard disk BIOS rather than the
    188  *     ROM BIOS
    189  *   - Toshiba laptops with HardRAM return DL=02h when called with DL=80h,
    190  *     but fail on DL=81h. The BIOS data at 40h:75h correctly reports 01h.
    191  *     may indicate only two drives present even if more are attached; to
    192  *     ensure a correct count, one can use AH=15h to scan through possible
    193  *     drives
    194  *   - for BIOSes which reserve the last cylinder for testing purposes, the
    195  *     cylinder count is automatically decremented
    196  *     on PS/1s with IBM ROM DOS 4, nonexistent drives return CF clear,
    197  *     BX=CX=0000h, and ES:DI = 0000h:0000h
    198  *   - the PC-Tools PCFORMAT program requires that AL=00h before it will
    199  *     proceed with the formatting
    200  *
    201  * BUG: several different Compaq BIOSes incorrectly report high-numbered
    202  *      drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
    203  *      same geometry as drive 80h; as a workaround, scan through disk
    204  *      numbers, stopping as soon as the number of valid drives encountered
    205  *      equals the value in 0040h:0075h
    206  *
    207  * SeeAlso: AH=06h"Adaptec",AH=13h"SyQuest",AH=48h,AH=15h,INT 1E
    208  * SeeAlso: INT 41"HARD DISK 0"
    209  **/
    210 static int get_drive_parameters_without_extensions(struct driveinfo *drive_info)
    211 {
    212     com32sys_t getparm, parm;
    213 
    214     memset(&getparm, 0, sizeof getparm);
    215     memset(&parm, 0, sizeof parm);
    216 
    217     /* Ralf Brown recommends setting ES:DI to 0:0 */
    218     getparm.esi.w[0] = 0;
    219     getparm.ds = 0;
    220     getparm.edx.b[0] = drive_info->disk;
    221     getparm.eax.b[1] = 0x08;
    222 
    223     __intcall(0x13, &getparm, &parm);
    224 
    225     /* CF set on error */
    226     if (parm.eflags.l & EFLAGS_CF)
    227 	return parm.eax.b[1];
    228 
    229     /* DL contains the maximum drive number (it starts at 0) */
    230     drive_info->legacy_max_drive = parm.edx.b[0];
    231 
    232     // XXX broken
    233     /* Drive specified greater than the bumber of attached drives */
    234     //if (drive_info->disk > drive_info->drives)
    235     //      return -1;
    236 
    237     drive_info->legacy_type = parm.ebx.b[0];
    238 
    239     /* DH contains the maximum head number (it starts at 0) */
    240     drive_info->legacy_max_head = parm.edx.b[1];
    241 
    242     /* Maximum sector number (bits 5-0) per track */
    243     drive_info->legacy_sectors_per_track = parm.ecx.b[0] & 0x3f;
    244 
    245     /*
    246      * Maximum cylinder number:
    247      *     CH = low eight bits of maximum cylinder number
    248      *     CL = high two bits of maximum cylinder number (bits 7-6)
    249      */
    250     drive_info->legacy_max_cylinder = parm.ecx.b[1] +
    251 	((parm.ecx.b[0] & 0xc0) << 2);
    252 
    253     if (drive_info->legacy_sectors_per_track > 0)
    254 	drive_info->cbios = 1;	/* Valid geometry */
    255 
    256     return 0;
    257 }
    258 
    259 /**
    260  * get_drive_parameters - retrieve drive parameters
    261  * @drive_info:		driveinfo structure to fill
    262  **/
    263 int get_drive_parameters(struct driveinfo *drive_info)
    264 {
    265     int return_code;
    266 
    267     if (detect_extensions(drive_info))
    268 	return -1;
    269 
    270     return_code = get_drive_parameters_without_extensions(drive_info);
    271 
    272     /* If geometry isn't valid, no need to try to get more info about the drive */
    273     /* Looks like in can confuse some optical drives */
    274     if (drive_info->ebios && drive_info->cbios)
    275 	get_drive_parameters_with_extensions(drive_info);
    276 
    277     return return_code;
    278 }
    279