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