1 /* bios.c - implement C part of low-level BIOS disk input and output */ 2 /* 3 * GRUB -- GRand Unified Bootloader 4 * Copyright (C) 1999,2000,2003,2004 Free Software Foundation, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include "shared.h" 22 23 24 /* These are defined in asm.S, and never be used elsewhere, so declare the 25 prototypes here. */ 26 extern int biosdisk_int13_extensions (int ax, int drive, void *dap); 27 extern int biosdisk_standard (int ah, int drive, 28 int coff, int hoff, int soff, 29 int nsec, int segment); 30 extern int check_int13_extensions (int drive); 31 extern int get_diskinfo_standard (int drive, 32 unsigned long *cylinders, 33 unsigned long *heads, 34 unsigned long *sectors); 35 #if 0 36 extern int get_diskinfo_floppy (int drive, 37 unsigned long *cylinders, 38 unsigned long *heads, 39 unsigned long *sectors); 40 #endif 41 42 43 /* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY 44 from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it, 45 else if READ is BIOSDISK_WRITE, then write it. If an geometry error 46 occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then 47 return the error number. Otherwise, return 0. */ 48 int 49 biosdisk (int read, int drive, struct geometry *geometry, 50 int sector, int nsec, int segment) 51 { 52 int err; 53 54 if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) 55 { 56 struct disk_address_packet 57 { 58 unsigned char length; 59 unsigned char reserved; 60 unsigned short blocks; 61 unsigned long buffer; 62 unsigned long long block; 63 } __attribute__ ((packed)) dap; 64 65 /* XXX: Don't check the geometry by default, because some buggy 66 BIOSes don't return the number of total sectors correctly, 67 even if they have working LBA support. Hell. */ 68 #ifdef NO_BUGGY_BIOS_IN_THE_WORLD 69 if (sector >= geometry->total_sectors) 70 return BIOSDISK_ERROR_GEOMETRY; 71 #endif /* NO_BUGGY_BIOS_IN_THE_WORLD */ 72 73 /* FIXME: sizeof (DAP) must be 0x10. Should assert that the compiler 74 can't add any padding. */ 75 dap.length = sizeof (dap); 76 dap.block = sector; 77 dap.blocks = nsec; 78 dap.reserved = 0; 79 /* This is undocumented part. The address is formated in 80 SEGMENT:ADDRESS. */ 81 dap.buffer = segment << 16; 82 83 err = biosdisk_int13_extensions ((read + 0x42) << 8, drive, &dap); 84 /* 85 * Try to report errors upwards when the bios has read only part of 86 * the requested buffer, but didn't return an error code. 87 */ 88 if (err == 0 && dap.blocks != nsec) 89 err = BIOSDISK_ERROR_SHORT_IO; 90 91 /* #undef NO_INT13_FALLBACK */ 92 #ifndef NO_INT13_FALLBACK 93 if (err) 94 { 95 if (geometry->flags & BIOSDISK_FLAG_CDROM) 96 return err; 97 98 geometry->flags &= ~BIOSDISK_FLAG_LBA_EXTENSION; 99 geometry->total_sectors = (geometry->cylinders 100 * geometry->heads 101 * geometry->sectors); 102 return biosdisk (read, drive, geometry, sector, nsec, segment); 103 } 104 #endif /* ! NO_INT13_FALLBACK */ 105 106 } 107 else 108 { 109 int cylinder_offset, head_offset, sector_offset; 110 int head; 111 112 /* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and 113 CYLINDER_OFFSET are counted from zero. */ 114 sector_offset = sector % geometry->sectors + 1; 115 head = sector / geometry->sectors; 116 head_offset = head % geometry->heads; 117 cylinder_offset = head / geometry->heads; 118 119 if (cylinder_offset >= geometry->cylinders) 120 return BIOSDISK_ERROR_GEOMETRY; 121 122 err = biosdisk_standard (read + 0x02, drive, 123 cylinder_offset, head_offset, sector_offset, 124 nsec, segment); 125 } 126 127 return err; 128 } 129 130 /* Check bootable CD-ROM emulation status. */ 131 static int 132 get_cdinfo (int drive, struct geometry *geometry) 133 { 134 int err; 135 struct iso_spec_packet 136 { 137 unsigned char size; 138 unsigned char media_type; 139 unsigned char drive_no; 140 unsigned char controller_no; 141 unsigned long image_lba; 142 unsigned short device_spec; 143 unsigned short cache_seg; 144 unsigned short load_seg; 145 unsigned short length_sec512; 146 unsigned char cylinders; 147 unsigned char sectors; 148 unsigned char heads; 149 150 unsigned char dummy[16]; 151 } __attribute__ ((packed)) cdrp; 152 153 grub_memset (&cdrp, 0, sizeof (cdrp)); 154 cdrp.size = sizeof (cdrp) - sizeof (cdrp.dummy); 155 err = biosdisk_int13_extensions (0x4B01, drive, &cdrp); 156 if (! err && cdrp.drive_no == drive) 157 { 158 if ((cdrp.media_type & 0x0F) == 0) 159 { 160 /* No emulation bootable CD-ROM */ 161 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM; 162 geometry->cylinders = 0; 163 geometry->heads = 1; 164 geometry->sectors = 15; 165 geometry->sector_size = 2048; 166 geometry->total_sectors = MAXINT; 167 return 1; 168 } 169 else 170 { 171 /* Floppy or hard-disk emulation */ 172 geometry->cylinders 173 = ((unsigned int) cdrp.cylinders 174 + (((unsigned int) (cdrp.sectors & 0xC0)) << 2)); 175 geometry->heads = cdrp.heads; 176 geometry->sectors = cdrp.sectors & 0x3F; 177 geometry->sector_size = SECTOR_SIZE; 178 geometry->total_sectors = (geometry->cylinders 179 * geometry->heads 180 * geometry->sectors); 181 return -1; 182 } 183 } 184 185 /* 186 * If this is the boot_drive, default to non-emulation bootable CD-ROM. 187 * 188 * Some BIOS (Tecra S1) fails the int13 call above. If we return 189 * failure here, GRUB will run, but cannot see the boot drive, 190 * not a very good situation. Defaulting to non-emulation mode 191 * is a last-ditch effort. 192 */ 193 if (drive >= 0x88 && drive == boot_drive) 194 { 195 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM; 196 geometry->cylinders = 0; 197 geometry->heads = 1; 198 geometry->sectors = 15; 199 geometry->sector_size = 2048; 200 geometry->total_sectors = MAXINT; 201 return 1; 202 } 203 return 0; 204 } 205 206 /* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return 207 non-zero, otherwise zero. */ 208 int 209 get_diskinfo (int drive, struct geometry *geometry) 210 { 211 int err; 212 213 /* Clear the flags. */ 214 geometry->flags = 0; 215 216 if (drive & 0x80) 217 { 218 /* hard disk or CD-ROM */ 219 int version; 220 unsigned long total_sectors = 0; 221 222 version = check_int13_extensions (drive); 223 224 if (drive >= 0x88 || version) 225 { 226 /* Possible CD-ROM - check the status. */ 227 if (get_cdinfo (drive, geometry)) 228 return 0; 229 } 230 231 if (version) 232 { 233 struct drive_parameters 234 { 235 unsigned short size; 236 unsigned short flags; 237 unsigned long cylinders; 238 unsigned long heads; 239 unsigned long sectors; 240 unsigned long long total_sectors; 241 unsigned short bytes_per_sector; 242 /* ver 2.0 or higher */ 243 unsigned long EDD_configuration_parameters; 244 /* ver 3.0 or higher */ 245 unsigned short signature_dpi; 246 unsigned char length_dpi; 247 unsigned char reserved[3]; 248 unsigned char name_of_host_bus[4]; 249 unsigned char name_of_interface_type[8]; 250 unsigned char interface_path[8]; 251 unsigned char device_path[8]; 252 unsigned char reserved2; 253 unsigned char checksum; 254 255 /* XXX: This is necessary, because the BIOS of Thinkpad X20 256 writes a garbage to the tail of drive parameters, 257 regardless of a size specified in a caller. */ 258 unsigned char dummy[16]; 259 } __attribute__ ((packed)) drp; 260 261 /* It is safe to clear out DRP. */ 262 grub_memset (&drp, 0, sizeof (drp)); 263 264 /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand 265 the greater buffer size for the "get drive parameters" int 266 0x13 call in its own way. Supposedly the BIOS assumes even 267 bigger space is available and thus corrupts the stack. 268 This is why we specify the exactly necessary size of 0x42 269 bytes. */ 270 drp.size = sizeof (drp) - sizeof (drp.dummy); 271 272 err = biosdisk_int13_extensions (0x4800, drive, &drp); 273 if (! err) 274 { 275 /* Set the LBA flag. */ 276 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION; 277 278 /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS, 279 so I omit the check for now. - okuji */ 280 /* if (drp.flags & (1 << 1)) */ 281 282 /* FIXME: when the 2TB limit becomes critical, we must 283 change the type of TOTAL_SECTORS to unsigned long 284 long. */ 285 if (drp.total_sectors) 286 total_sectors = drp.total_sectors & ~0L; 287 else 288 /* Some buggy BIOSes doesn't return the total sectors 289 correctly but returns zero. So if it is zero, compute 290 it by C/H/S returned by the LBA BIOS call. */ 291 total_sectors = drp.cylinders * drp.heads * drp.sectors; 292 } 293 } 294 295 /* Don't pass GEOMETRY directly, but pass each element instead, 296 so that we can change the structure easily. */ 297 err = get_diskinfo_standard (drive, 298 &geometry->cylinders, 299 &geometry->heads, 300 &geometry->sectors); 301 if (err) 302 return err; 303 304 if (! total_sectors) 305 { 306 total_sectors = (geometry->cylinders 307 * geometry->heads 308 * geometry->sectors); 309 } 310 geometry->total_sectors = total_sectors; 311 geometry->sector_size = SECTOR_SIZE; 312 } 313 else 314 { 315 /* floppy disk */ 316 317 /* First, try INT 13 AH=8h call. */ 318 err = get_diskinfo_standard (drive, 319 &geometry->cylinders, 320 &geometry->heads, 321 &geometry->sectors); 322 323 #if 0 324 /* If fails, then try floppy-specific probe routine. */ 325 if (err) 326 err = get_diskinfo_floppy (drive, 327 &geometry->cylinders, 328 &geometry->heads, 329 &geometry->sectors); 330 #endif 331 332 if (err) 333 return err; 334 335 geometry->total_sectors = (geometry->cylinders 336 * geometry->heads 337 * geometry->sectors); 338 geometry->sector_size = SECTOR_SIZE; 339 } 340 341 return 0; 342 } 343