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 unsigned int sector, int nsec, int segment) 51 { 52 53 int err; 54 55 if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) 56 { 57 struct disk_address_packet 58 { 59 unsigned char length; 60 unsigned char reserved; 61 unsigned short blocks; 62 unsigned long buffer; 63 unsigned long long block; 64 } __attribute__ ((packed)) dap; 65 66 /* XXX: Don't check the geometry by default, because some buggy 67 BIOSes don't return the number of total sectors correctly, 68 even if they have working LBA support. Hell. */ 69 #ifdef NO_BUGGY_BIOS_IN_THE_WORLD 70 if (sector >= geometry->total_sectors) 71 return BIOSDISK_ERROR_GEOMETRY; 72 #endif /* NO_BUGGY_BIOS_IN_THE_WORLD */ 73 74 /* FIXME: sizeof (DAP) must be 0x10. Should assert that the compiler 75 can't add any padding. */ 76 dap.length = sizeof (dap); 77 dap.block = sector; 78 dap.blocks = nsec; 79 dap.reserved = 0; 80 /* This is undocumented part. The address is formated in 81 SEGMENT:ADDRESS. */ 82 dap.buffer = segment << 16; 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 = ((unsigned long long)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 /* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and 112 CYLINDER_OFFSET are counted from zero. */ 113 sector_offset = sector % geometry->sectors + 1; 114 head = sector / geometry->sectors; 115 head_offset = head % geometry->heads; 116 cylinder_offset = head / geometry->heads; 117 118 if (cylinder_offset >= geometry->cylinders) 119 return BIOSDISK_ERROR_GEOMETRY; 120 121 err = biosdisk_standard (read + 0x02, drive, 122 cylinder_offset, head_offset, sector_offset, 123 nsec, segment); 124 } 125 126 return err; 127 } 128 129 /* Check bootable CD-ROM emulation status. */ 130 static int 131 get_cdinfo (int drive, struct geometry *geometry) 132 { 133 int err; 134 struct iso_spec_packet 135 { 136 unsigned char size; 137 unsigned char media_type; 138 unsigned char drive_no; 139 unsigned char controller_no; 140 unsigned long image_lba; 141 unsigned short device_spec; 142 unsigned short cache_seg; 143 unsigned short load_seg; 144 unsigned short length_sec512; 145 unsigned char cylinders; 146 unsigned char sectors; 147 unsigned char heads; 148 149 unsigned char dummy[16]; 150 } __attribute__ ((packed)) cdrp; 151 152 grub_memset (&cdrp, 0, sizeof (cdrp)); 153 cdrp.size = sizeof (cdrp) - sizeof (cdrp.dummy); 154 err = biosdisk_int13_extensions (0x4B01, drive, &cdrp); 155 if (! err && cdrp.drive_no == drive) 156 { 157 if ((cdrp.media_type & 0x0F) == 0) 158 { 159 /* No emulation bootable CD-ROM */ 160 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM; 161 geometry->cylinders = 0; 162 geometry->heads = 1; 163 geometry->sectors = 15; 164 geometry->sector_size = 2048; 165 geometry->total_sectors = MAXUINT; 166 return 1; 167 } 168 else 169 { 170 /* Floppy or hard-disk emulation */ 171 geometry->cylinders 172 = ((unsigned int) cdrp.cylinders 173 + (((unsigned int) (cdrp.sectors & 0xC0)) << 2)); 174 geometry->heads = cdrp.heads; 175 geometry->sectors = cdrp.sectors & 0x3F; 176 geometry->sector_size = SECTOR_SIZE; 177 geometry->total_sectors = ((unsigned long long)geometry->cylinders 178 * geometry->heads 179 * geometry->sectors); 180 return -1; 181 } 182 } 183 184 /* 185 * If this is the boot_drive, default to non-emulation bootable CD-ROM. 186 * 187 * Some BIOS (Tecra S1) fails the int13 call above. If we return 188 * failure here, GRUB will run, but cannot see the boot drive, 189 * not a very good situation. Defaulting to non-emulation mode 190 * is a last-ditch effort. 191 */ 192 if (drive >= 0x88 && drive == boot_drive) 193 { 194 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM; 195 geometry->cylinders = 0; 196 geometry->heads = 1; 197 geometry->sectors = 15; 198 geometry->sector_size = 2048; 199 geometry->total_sectors = MAXUINT; 200 return 1; 201 } 202 return 0; 203 } 204 205 /* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return 206 non-zero, otherwise zero. */ 207 int 208 get_diskinfo (int drive, struct geometry *geometry) 209 { 210 int err; 211 212 /* Clear the flags. */ 213 geometry->flags = 0; 214 215 if (drive & 0x80) 216 { 217 /* hard disk or CD-ROM */ 218 int version; 219 unsigned long long total_sectors = 0; 220 221 version = check_int13_extensions (drive); 222 223 if (drive >= 0x88 || version) 224 { 225 /* Possible CD-ROM - check the status. */ 226 if (get_cdinfo (drive, geometry)) 227 return 0; 228 } 229 230 if (version) 231 { 232 struct drive_parameters 233 { 234 unsigned short size; 235 unsigned short flags; 236 unsigned long cylinders; 237 unsigned long heads; 238 unsigned long sectors; 239 unsigned long long total_sectors; 240 unsigned short bytes_per_sector; 241 /* ver 2.0 or higher */ 242 unsigned long EDD_configuration_parameters; 243 /* ver 3.0 or higher */ 244 unsigned short signature_dpi; 245 unsigned char length_dpi; 246 unsigned char reserved[3]; 247 unsigned char name_of_host_bus[4]; 248 unsigned char name_of_interface_type[8]; 249 unsigned char interface_path[8]; 250 unsigned char device_path[8]; 251 unsigned char reserved2; 252 unsigned char checksum; 253 254 /* XXX: This is necessary, because the BIOS of Thinkpad X20 255 writes a garbage to the tail of drive parameters, 256 regardless of a size specified in a caller. */ 257 unsigned char dummy[16]; 258 } __attribute__ ((packed)) drp; 259 260 /* It is safe to clear out DRP. */ 261 grub_memset (&drp, 0, sizeof (drp)); 262 263 /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand 264 the greater buffer size for the "get drive parameters" int 265 0x13 call in its own way. Supposedly the BIOS assumes even 266 bigger space is available and thus corrupts the stack. 267 This is why we specify the exactly necessary size of 0x42 268 bytes. */ 269 drp.size = sizeof (drp) - sizeof (drp.dummy); 270 271 err = biosdisk_int13_extensions (0x4800, drive, &drp); 272 if (! err) 273 { 274 /* Set the LBA flag. */ 275 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION; 276 277 /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS, 278 so I omit the check for now. - okuji */ 279 /* if (drp.flags & (1 << 1)) */ 280 281 if (drp.total_sectors) 282 total_sectors = drp.total_sectors; 283 else 284 /* Some buggy BIOSes doesn't return the total sectors 285 correctly but returns zero. So if it is zero, compute 286 it by C/H/S returned by the LBA BIOS call. */ 287 total_sectors = (unsigned long long)drp.cylinders * 288 drp.heads * drp.sectors; 289 } 290 } 291 292 /* Don't pass GEOMETRY directly, but pass each element instead, 293 so that we can change the structure easily. */ 294 err = get_diskinfo_standard (drive, 295 &geometry->cylinders, 296 &geometry->heads, 297 &geometry->sectors); 298 if (err) 299 return err; 300 301 if (! total_sectors) 302 { 303 total_sectors = ((unsigned long long)geometry->cylinders 304 * geometry->heads 305 * geometry->sectors); 306 } 307 geometry->total_sectors = total_sectors; 308 geometry->sector_size = SECTOR_SIZE; 309 } 310 else 311 { 312 /* floppy disk */ 313 314 /* First, try INT 13 AH=8h call. */ 315 err = get_diskinfo_standard (drive, 316 &geometry->cylinders, 317 &geometry->heads, 318 &geometry->sectors); 319 320 #if 0 321 /* If fails, then try floppy-specific probe routine. */ 322 if (err) 323 err = get_diskinfo_floppy (drive, 324 &geometry->cylinders, 325 &geometry->heads, 326 &geometry->sectors); 327 #endif 328 329 if (err) 330 return err; 331 332 geometry->total_sectors = ((unsigned long long)geometry->cylinders 333 * geometry->heads 334 * geometry->sectors); 335 geometry->sector_size = SECTOR_SIZE; 336 } 337 338 return 0; 339 } 340