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
biosdisk(int read,int drive,struct geometry * geometry,int sector,int nsec,int segment)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 /* #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. */
125 static int
get_cdinfo(int drive,struct geometry * geometry)126 get_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. */
183 int
get_diskinfo(int drive,struct geometry * geometry)184 get_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