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