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
biosdisk(int read,int drive,struct geometry * geometry,unsigned long long sector,int nsec,int segment)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
get_cdinfo(int drive,struct geometry * geometry)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
get_diskinfo(int drive,struct geometry * geometry)211 get_diskinfo (int drive, struct geometry *geometry)
212 {
213 int err;
214 int gotchs = 0;
215
216 /* Clear the flags. */
217 geometry->flags = 0;
218
219 if (drive & 0x80)
220 {
221 /* hard disk or CD-ROM */
222 int version;
223 unsigned long long total_sectors = 0;
224
225 version = check_int13_extensions (drive);
226
227 if (drive >= 0x88 || version)
228 {
229 /* Possible CD-ROM - check the status. */
230 if (get_cdinfo (drive, geometry))
231 return 0;
232 }
233
234 /* Don't pass GEOMETRY directly, but pass each element instead,
235 so that we can change the structure easily. */
236 err = get_diskinfo_standard (drive,
237 &geometry->cylinders,
238 &geometry->heads,
239 &geometry->sectors);
240 if (err == 0)
241 gotchs = 1;
242 /* get_diskinfo_standard returns 0x60 if the BIOS call actually
243 succeeded but returned 0 sectors -- in this case don't
244 return yet but continue to check the LBA geom */
245 else if (err != 0x60)
246 return err;
247
248 if (version)
249 {
250 struct drive_parameters
251 {
252 unsigned short size;
253 unsigned short flags;
254 unsigned long cylinders;
255 unsigned long heads;
256 unsigned long sectors;
257 unsigned long long total_sectors;
258 unsigned short bytes_per_sector;
259 /* ver 2.0 or higher */
260 unsigned long EDD_configuration_parameters;
261 /* ver 3.0 or higher */
262 unsigned short signature_dpi;
263 unsigned char length_dpi;
264 unsigned char reserved[3];
265 unsigned char name_of_host_bus[4];
266 unsigned char name_of_interface_type[8];
267 unsigned char interface_path[8];
268 unsigned char device_path[8];
269 unsigned char reserved2;
270 unsigned char checksum;
271
272 /* XXX: This is necessary, because the BIOS of Thinkpad X20
273 writes a garbage to the tail of drive parameters,
274 regardless of a size specified in a caller. */
275 unsigned char dummy[16];
276 } __attribute__ ((packed)) drp;
277
278 /* It is safe to clear out DRP. */
279 grub_memset (&drp, 0, sizeof (drp));
280
281 /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand
282 the greater buffer size for the "get drive parameters" int
283 0x13 call in its own way. Supposedly the BIOS assumes even
284 bigger space is available and thus corrupts the stack.
285 This is why we specify the exactly necessary size of 0x42
286 bytes. */
287 drp.size = sizeof (drp) - sizeof (drp.dummy);
288
289 err = biosdisk_int13_extensions (0x4800, drive, &drp);
290 if (! err)
291 {
292 /* Set the LBA flag. */
293 geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
294
295 /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS,
296 so I omit the check for now. - okuji */
297 /* if (drp.flags & (1 << 1)) */
298
299 /* If we didn't get valid CHS info from the standard call,
300 then we should fill it out here */
301 if (! gotchs)
302 {
303 geometry->cylinders = drp.cylinders;
304
305 if (drp.sectors > 0 && drp.heads > 0)
306 {
307 geometry->heads = drp.heads;
308 geometry->sectors = drp.sectors;
309 }
310 else
311 {
312 /* Return fake geometry. This disk reports that it
313 supports LBA, so all the other routines will use LBA
314 to talk to it and not look at this geometry. However,
315 some of the partition-finding routines still need
316 non-zero values in these fields. */
317 geometry->heads = 16;
318 geometry->sectors = 63;
319 }
320 gotchs = 1;
321 }
322
323 if (drp.total_sectors)
324 total_sectors = drp.total_sectors;
325 else
326 /* Some buggy BIOSes doesn't return the total sectors
327 correctly but returns zero. So if it is zero, compute
328 it by C/H/S returned by the LBA BIOS call. */
329 total_sectors = (unsigned long long)drp.cylinders *
330 drp.heads * drp.sectors;
331 }
332 }
333
334 /* In case we got the 0x60 return code from _standard on a disk that
335 didn't support LBA (or was somehow invalid), return that error now */
336 if (! gotchs)
337 return 0x60;
338
339 if (! total_sectors)
340 {
341 total_sectors = ((unsigned long long)geometry->cylinders
342 * geometry->heads
343 * geometry->sectors);
344 }
345 geometry->total_sectors = total_sectors;
346 geometry->sector_size = SECTOR_SIZE;
347 }
348 else
349 {
350 /* floppy disk */
351
352 /* First, try INT 13 AH=8h call. */
353 err = get_diskinfo_standard (drive,
354 &geometry->cylinders,
355 &geometry->heads,
356 &geometry->sectors);
357
358 #if 0
359 /* If fails, then try floppy-specific probe routine. */
360 if (err)
361 err = get_diskinfo_floppy (drive,
362 &geometry->cylinders,
363 &geometry->heads,
364 &geometry->sectors);
365 #endif
366
367 if (err)
368 return err;
369
370 geometry->total_sectors = ((unsigned long long)geometry->cylinders
371 * geometry->heads
372 * geometry->sectors);
373 geometry->sector_size = SECTOR_SIZE;
374 }
375
376 return 0;
377 }
378