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