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