2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/machine/biosdisk.h>
20 #include <grub/machine/memory.h>
21 #include <grub/machine/kernel.h>
22 #include <grub/disk.h>
25 #include <grub/types.h>
26 #include <grub/misc.h>
28 #include <grub/term.h>
30 static int cd_drive
= 0;
33 grub_biosdisk_get_drive (const char *name
)
37 if ((name
[0] != 'f' && name
[0] != 'h') || name
[1] != 'd')
40 drive
= grub_strtoul (name
+ 2, 0, 10);
41 if (grub_errno
!= GRUB_ERR_NONE
)
50 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a biosdisk");
55 grub_biosdisk_call_hook (int (*hook
) (const char *name
), int drive
)
59 grub_sprintf (name
, (drive
& 0x80) ? "hd%d" : "fd%d", drive
& (~0x80));
64 grub_biosdisk_iterate (int (*hook
) (const char *name
))
69 /* For hard disks, attempt to read the MBR. */
70 for (drive
= 0x80; drive
< 0x90; drive
++)
72 if (grub_biosdisk_rw_standard (0x02, drive
, 0, 0, 1, 1,
73 GRUB_MEMORY_MACHINE_SCRATCH_SEG
) != 0)
75 grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive
);
79 if (grub_biosdisk_call_hook (hook
, drive
))
85 if (grub_biosdisk_call_hook (hook
, cd_drive
))
89 /* For floppy disks, we can get the number safely. */
90 num_floppies
= grub_biosdisk_get_num_floppies ();
91 for (drive
= 0; drive
< num_floppies
; drive
++)
92 if (grub_biosdisk_call_hook (hook
, drive
))
99 grub_biosdisk_open (const char *name
, grub_disk_t disk
)
101 grub_uint64_t total_sectors
= 0;
103 struct grub_biosdisk_data
*data
;
105 drive
= grub_biosdisk_get_drive (name
);
109 disk
->has_partitions
= ((drive
& 0x80) && (drive
!= cd_drive
));
112 data
= (struct grub_biosdisk_data
*) grub_malloc (sizeof (*data
));
119 if ((cd_drive
) && (drive
== cd_drive
))
121 data
->flags
= GRUB_BIOSDISK_FLAG_LBA
| GRUB_BIOSDISK_FLAG_CDROM
;
123 total_sectors
= GRUB_ULONG_MAX
; /* TODO: get the correct size. */
125 else if (drive
& 0x80)
130 version
= grub_biosdisk_check_int13_extensions (drive
);
133 struct grub_biosdisk_drp
*drp
134 = (struct grub_biosdisk_drp
*) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
136 /* Clear out the DRP. */
137 grub_memset (drp
, 0, sizeof (*drp
));
138 drp
->size
= sizeof (*drp
);
139 if (! grub_biosdisk_get_diskinfo_int13_extensions (drive
, drp
))
141 data
->flags
= GRUB_BIOSDISK_FLAG_LBA
;
143 if (drp
->total_sectors
)
144 total_sectors
= drp
->total_sectors
;
146 /* Some buggy BIOSes doesn't return the total sectors
147 correctly but returns zero. So if it is zero, compute
148 it by C/H/S returned by the LBA BIOS call. */
149 total_sectors
= drp
->cylinders
* drp
->heads
* drp
->sectors
;
154 if (! (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
))
156 if (grub_biosdisk_get_diskinfo_standard (drive
,
159 &data
->sectors
) != 0)
161 if (total_sectors
&& (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
))
166 = grub_divmod64 (total_sectors
167 + data
->heads
* data
->sectors
- 1,
168 data
->heads
* data
->sectors
, 0);
173 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot get C/H/S values");
178 total_sectors
= data
->cylinders
* data
->heads
* data
->sectors
;
181 disk
->total_sectors
= total_sectors
;
184 return GRUB_ERR_NONE
;
188 grub_biosdisk_close (grub_disk_t disk
)
190 grub_free (disk
->data
);
193 /* For readability. */
194 #define GRUB_BIOSDISK_READ 0
195 #define GRUB_BIOSDISK_WRITE 1
197 #define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
200 grub_biosdisk_rw (int cmd
, grub_disk_t disk
,
201 grub_disk_addr_t sector
, grub_size_t size
,
204 struct grub_biosdisk_data
*data
= disk
->data
;
206 if (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
)
208 struct grub_biosdisk_dap
*dap
;
210 dap
= (struct grub_biosdisk_dap
*) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
212 << GRUB_DISK_SECTOR_BITS
));
213 dap
->length
= sizeof (*dap
);
216 dap
->buffer
= segment
<< 16; /* The format SEGMENT:ADDRESS. */
219 if (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
)
224 return grub_error (GRUB_ERR_WRITE_ERROR
, "can\'t write to cdrom");
226 dap
->blocks
= (dap
->blocks
+ 3) >> 2;
229 for (i
= 0; i
< GRUB_BIOSDISK_CDROM_RETRY_COUNT
; i
++)
230 if (! grub_biosdisk_rw_int13_extensions (0x42, data
->drive
, dap
))
233 if (i
== GRUB_BIOSDISK_CDROM_RETRY_COUNT
)
234 return grub_error (GRUB_ERR_READ_ERROR
, "cdrom read error");
237 if (grub_biosdisk_rw_int13_extensions (cmd
+ 0x42, data
->drive
, dap
))
239 /* Fall back to the CHS mode. */
240 data
->flags
&= ~GRUB_BIOSDISK_FLAG_LBA
;
241 disk
->total_sectors
= data
->cylinders
* data
->heads
* data
->sectors
;
242 return grub_biosdisk_rw (cmd
, disk
, sector
, size
, segment
);
247 unsigned coff
, hoff
, soff
;
250 /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
251 the traditional CHS access. */
253 1024 /* cylinders */ *
256 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "out of disk");
258 soff
= ((grub_uint32_t
) sector
) % data
->sectors
+ 1;
259 head
= ((grub_uint32_t
) sector
) / data
->sectors
;
260 hoff
= head
% data
->heads
;
261 coff
= head
/ data
->heads
;
263 if (coff
>= data
->cylinders
)
264 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "out of disk");
266 if (grub_biosdisk_rw_standard (cmd
+ 0x02, data
->drive
,
267 coff
, hoff
, soff
, size
, segment
))
271 case GRUB_BIOSDISK_READ
:
272 return grub_error (GRUB_ERR_READ_ERROR
, "biosdisk read error");
273 case GRUB_BIOSDISK_WRITE
:
274 return grub_error (GRUB_ERR_WRITE_ERROR
, "biosdisk write error");
279 return GRUB_ERR_NONE
;
282 /* Return the number of sectors which can be read safely at a time. */
284 get_safe_sectors (grub_disk_addr_t sector
, grub_uint32_t sectors
)
287 grub_uint32_t offset
;
289 /* OFFSET = SECTOR % SECTORS */
290 grub_divmod64 (sector
, sectors
, &offset
);
292 size
= sectors
- offset
;
294 /* Limit the max to 0x7f because of Phoenix EDD. */
302 grub_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
303 grub_size_t size
, char *buf
)
305 struct grub_biosdisk_data
*data
= disk
->data
;
311 len
= get_safe_sectors (sector
, data
->sectors
);
315 if (grub_biosdisk_rw (GRUB_BIOSDISK_READ
, disk
, sector
, len
,
316 GRUB_MEMORY_MACHINE_SCRATCH_SEG
))
319 grub_memcpy (buf
, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
320 len
<< GRUB_DISK_SECTOR_BITS
);
321 buf
+= len
<< GRUB_DISK_SECTOR_BITS
;
330 grub_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
331 grub_size_t size
, const char *buf
)
333 struct grub_biosdisk_data
*data
= disk
->data
;
339 len
= get_safe_sectors (sector
, data
->sectors
);
343 grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
, buf
,
344 len
<< GRUB_DISK_SECTOR_BITS
);
346 if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE
, disk
, sector
, len
,
347 GRUB_MEMORY_MACHINE_SCRATCH_SEG
))
350 buf
+= len
<< GRUB_DISK_SECTOR_BITS
;
358 static struct grub_disk_dev grub_biosdisk_dev
=
361 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
362 .iterate
= grub_biosdisk_iterate
,
363 .open
= grub_biosdisk_open
,
364 .close
= grub_biosdisk_close
,
365 .read
= grub_biosdisk_read
,
366 .write
= grub_biosdisk_write
,
371 grub_disk_biosdisk_fini (void)
373 grub_disk_dev_unregister (&grub_biosdisk_dev
);
376 GRUB_MOD_INIT(biosdisk
)
378 struct grub_biosdisk_cdrp
*cdrp
379 = (struct grub_biosdisk_cdrp
*) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
381 if (grub_disk_firmware_is_tainted
)
383 grub_printf ("Firmware is marked as tainted, refusing to initialize.\n");
386 grub_disk_firmware_fini
= grub_disk_biosdisk_fini
;
388 grub_memset (cdrp
, 0, sizeof (*cdrp
));
389 cdrp
->size
= sizeof (*cdrp
);
390 cdrp
->media_type
= 0xFF;
391 if ((! grub_biosdisk_get_cdinfo_int13_extensions (grub_boot_drive
, cdrp
)) &&
392 ((cdrp
->media_type
& GRUB_BIOSDISK_CDTYPE_MASK
)
393 == GRUB_BIOSDISK_CDTYPE_NO_EMUL
))
394 cd_drive
= cdrp
->drive_no
;
396 grub_disk_dev_register (&grub_biosdisk_dev
);
399 GRUB_MOD_FINI(biosdisk
)
401 grub_disk_biosdisk_fini ();