GRUB-1.98 changes
[grub2/jjazz.git] / disk / i386 / pc / biosdisk.c
blob94d0e3708ce9dd22ec01b90ead7d0c310aa4cbd1
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010 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>
23 #include <grub/dl.h>
24 #include <grub/mm.h>
25 #include <grub/types.h>
26 #include <grub/misc.h>
27 #include <grub/err.h>
28 #include <grub/term.h>
30 static int cd_drive = 0;
32 static int
33 grub_biosdisk_get_drive (const char *name)
35 unsigned long drive;
37 if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
38 goto fail;
40 drive = grub_strtoul (name + 2, 0, 10);
41 if (grub_errno != GRUB_ERR_NONE)
42 goto fail;
44 if (name[0] == 'h')
45 drive += 0x80;
47 return (int) drive ;
49 fail:
50 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
51 return -1;
54 static int
55 grub_biosdisk_call_hook (int (*hook) (const char *name), int drive)
57 char name[10];
59 grub_snprintf (name, sizeof (name),
60 (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
61 return hook (name);
64 static int
65 grub_biosdisk_iterate (int (*hook) (const char *name))
67 int drive;
68 int num_floppies;
70 /* For hard disks, attempt to read the MBR. */
71 for (drive = 0x80; drive < 0x90; drive++)
73 if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
74 GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
76 grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
77 break;
80 if (grub_biosdisk_call_hook (hook, drive))
81 return 1;
84 if (cd_drive)
86 if (grub_biosdisk_call_hook (hook, cd_drive))
87 return 1;
90 /* For floppy disks, we can get the number safely. */
91 num_floppies = grub_biosdisk_get_num_floppies ();
92 for (drive = 0; drive < num_floppies; drive++)
93 if (grub_biosdisk_call_hook (hook, drive))
94 return 1;
96 return 0;
99 static grub_err_t
100 grub_biosdisk_open (const char *name, grub_disk_t disk)
102 grub_uint64_t total_sectors = 0;
103 int drive;
104 struct grub_biosdisk_data *data;
106 drive = grub_biosdisk_get_drive (name);
107 if (drive < 0)
108 return grub_errno;
110 disk->has_partitions = ((drive & 0x80) && (drive != cd_drive));
111 disk->id = drive;
113 data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data));
114 if (! data)
115 return grub_errno;
117 data->drive = drive;
119 if ((cd_drive) && (drive == cd_drive))
121 data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
122 data->sectors = 32;
123 total_sectors = GRUB_ULONG_MAX; /* TODO: get the correct size. */
125 else if (drive & 0x80)
127 /* HDD */
128 int version;
130 version = grub_biosdisk_check_int13_extensions (drive);
131 if (version)
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;
145 else
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,
157 &data->cylinders,
158 &data->heads,
159 &data->sectors) != 0)
161 if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA))
163 data->sectors = 63;
164 data->heads = 255;
165 data->cylinders
166 = grub_divmod64 (total_sectors
167 + data->heads * data->sectors - 1,
168 data->heads * data->sectors, 0);
170 else
172 grub_free (data);
173 return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name);
177 if (! total_sectors)
178 total_sectors = data->cylinders * data->heads * data->sectors;
181 disk->total_sectors = total_sectors;
182 disk->data = data;
184 return GRUB_ERR_NONE;
187 static void
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
199 static grub_err_t
200 grub_biosdisk_rw (int cmd, grub_disk_t disk,
201 grub_disk_addr_t sector, grub_size_t size,
202 unsigned segment)
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
211 + (data->sectors
212 << GRUB_DISK_SECTOR_BITS));
213 dap->length = sizeof (*dap);
214 dap->reserved = 0;
215 dap->blocks = size;
216 dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */
217 dap->block = sector;
219 if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
221 int i;
223 if (cmd)
224 return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom");
226 dap->blocks = ALIGN_UP (dap->blocks, 4) >> 2;
227 dap->block >>= 2;
229 for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
230 if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
231 break;
233 if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT)
234 return grub_error (GRUB_ERR_READ_ERROR, "cdrom read error");
236 else
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);
245 else
247 unsigned coff, hoff, soff;
248 unsigned head;
250 /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
251 the traditional CHS access. */
252 if (sector >
253 1024 /* cylinders */ *
254 256 /* heads */ *
255 63 /* spt */)
256 return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name);
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, "%s out of disk", disk->name);
266 if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive,
267 coff, hoff, soff, size, segment))
269 switch (cmd)
271 case GRUB_BIOSDISK_READ:
272 return grub_error (GRUB_ERR_READ_ERROR, "%s read error", disk->name);
273 case GRUB_BIOSDISK_WRITE:
274 return grub_error (GRUB_ERR_WRITE_ERROR, "%s write error", disk->name);
279 return GRUB_ERR_NONE;
282 /* Return the number of sectors which can be read safely at a time. */
283 static grub_size_t
284 get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors)
286 grub_size_t size;
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. */
295 if (size > 0x7f)
296 size = 0x7f;
298 return size;
301 static grub_err_t
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;
307 while (size)
309 grub_size_t len;
310 grub_size_t cdoff = 0;
312 len = get_safe_sectors (sector, data->sectors);
314 if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
316 cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS;
317 len = ALIGN_UP (sector + len, 4) - (sector & ~3);
318 sector &= ~3;
321 if (len > size)
322 len = size;
324 if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len,
325 GRUB_MEMORY_MACHINE_SCRATCH_SEG))
326 return grub_errno;
328 grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff),
329 len << GRUB_DISK_SECTOR_BITS);
330 buf += len << GRUB_DISK_SECTOR_BITS;
331 sector += len;
332 size -= len;
335 return grub_errno;
338 static grub_err_t
339 grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
340 grub_size_t size, const char *buf)
342 struct grub_biosdisk_data *data = disk->data;
344 if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
345 return grub_error (GRUB_ERR_IO, "can't write to CDROM");
347 while (size)
349 grub_size_t len;
351 len = get_safe_sectors (sector, data->sectors);
352 if (len > size)
353 len = size;
355 grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
356 len << GRUB_DISK_SECTOR_BITS);
358 if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
359 GRUB_MEMORY_MACHINE_SCRATCH_SEG))
360 return grub_errno;
362 buf += len << GRUB_DISK_SECTOR_BITS;
363 sector += len;
364 size -= len;
367 return grub_errno;
370 static struct grub_disk_dev grub_biosdisk_dev =
372 .name = "biosdisk",
373 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
374 .iterate = grub_biosdisk_iterate,
375 .open = grub_biosdisk_open,
376 .close = grub_biosdisk_close,
377 .read = grub_biosdisk_read,
378 .write = grub_biosdisk_write,
379 .next = 0
382 static void
383 grub_disk_biosdisk_fini (void)
385 grub_disk_dev_unregister (&grub_biosdisk_dev);
388 GRUB_MOD_INIT(biosdisk)
390 struct grub_biosdisk_cdrp *cdrp
391 = (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
393 if (grub_disk_firmware_is_tainted)
395 grub_printf ("Firmware is marked as tainted, refusing to initialize.\n");
396 return;
398 grub_disk_firmware_fini = grub_disk_biosdisk_fini;
400 grub_memset (cdrp, 0, sizeof (*cdrp));
401 cdrp->size = sizeof (*cdrp);
402 cdrp->media_type = 0xFF;
403 if ((! grub_biosdisk_get_cdinfo_int13_extensions (grub_boot_drive, cdrp)) &&
404 ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
405 == GRUB_BIOSDISK_CDTYPE_NO_EMUL))
406 cd_drive = cdrp->drive_no;
408 grub_disk_dev_register (&grub_biosdisk_dev);
411 GRUB_MOD_FINI(biosdisk)
413 grub_disk_biosdisk_fini ();