~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to grub-core/disk/i386/pc/biosdisk.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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/>.
 
17
 */
 
18
 
 
19
#include <grub/machine/biosdisk.h>
 
20
#include <grub/machine/kernel.h>
 
21
#include <grub/machine/memory.h>
 
22
#include <grub/machine/int.h>
 
23
#include <grub/disk.h>
 
24
#include <grub/dl.h>
 
25
#include <grub/mm.h>
 
26
#include <grub/types.h>
 
27
#include <grub/misc.h>
 
28
#include <grub/err.h>
 
29
#include <grub/term.h>
 
30
 
 
31
static int cd_drive = 0;
 
32
static int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap);
 
33
 
 
34
static int grub_biosdisk_get_num_floppies (void)
 
35
{
 
36
  struct grub_bios_int_registers regs;
 
37
  int drive;
 
38
 
 
39
  /* reset the disk system first */
 
40
  regs.eax = 0;
 
41
  regs.edx = 0;
 
42
  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
43
 
 
44
  grub_bios_interrupt (0x13, &regs);
 
45
 
 
46
  for (drive = 0; drive < 2; drive++)
 
47
    {
 
48
      regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT | GRUB_CPU_INT_FLAGS_CARRY;
 
49
      regs.edx = drive;
 
50
 
 
51
      /* call GET DISK TYPE */
 
52
      regs.eax = 0x1500;
 
53
      grub_bios_interrupt (0x13, &regs);
 
54
      if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
 
55
        break;
 
56
 
 
57
      /* check if this drive exists */
 
58
      if (!(regs.eax & 0x300))
 
59
        break;
 
60
    }
 
61
 
 
62
  return drive;
 
63
}
 
64
 
 
65
/*
 
66
 *   Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
 
67
 *   is passed for disk address packet. If an error occurs, return
 
68
 *   non-zero, otherwise zero.
 
69
 */
 
70
 
 
71
static int 
 
72
grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
 
73
{
 
74
  struct grub_bios_int_registers regs;
 
75
  regs.eax = ah << 8;
 
76
  /* compute the address of disk_address_packet */
 
77
  regs.ds = (((grub_addr_t) dap) & 0xffff0000) >> 4;
 
78
  regs.esi = (((grub_addr_t) dap) & 0xffff);
 
79
  regs.edx = drive;
 
80
  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
81
 
 
82
  grub_bios_interrupt (0x13, &regs);
 
83
  return (regs.eax >> 8) & 0xff;
 
84
}
 
85
 
 
86
/*
 
87
 *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
 
88
 *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
 
89
 *   return non-zero, otherwise zero.
 
90
 */
 
91
static int 
 
92
grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
 
93
                           int soff, int nsec, int segment)
 
94
{
 
95
  int ret, i;
 
96
 
 
97
  /* Try 3 times.  */
 
98
  for (i = 0; i < 3; i++)
 
99
    {
 
100
      struct grub_bios_int_registers regs;
 
101
 
 
102
      /* set up CHS information */
 
103
      /* set %ch to low eight bits of cylinder */
 
104
      regs.ecx = (coff << 8) & 0xff00;
 
105
      /* set bits 6-7 of %cl to high two bits of cylinder */
 
106
      regs.ecx |= (coff >> 2) & 0xc0;
 
107
      /* set bits 0-5 of %cl to sector */
 
108
      regs.ecx |= soff & 0x3f;
 
109
 
 
110
      /* set %dh to head and %dl to drive */  
 
111
      regs.edx = (drive & 0xff) | ((hoff << 8) & 0xff00);
 
112
      /* set %ah to AH */
 
113
      regs.eax = (ah << 8) & 0xff00;
 
114
      /* set %al to NSEC */
 
115
      regs.eax |= nsec & 0xff;
 
116
 
 
117
      regs.ebx = 0;
 
118
      regs.es = segment;
 
119
 
 
120
      regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
121
 
 
122
      grub_bios_interrupt (0x13, &regs);
 
123
      /* check if successful */
 
124
      if (!(regs.flags & GRUB_CPU_INT_FLAGS_CARRY))
 
125
        return 0;
 
126
 
 
127
      /* save return value */
 
128
      ret = regs.eax >> 8;
 
129
 
 
130
      /* if fail, reset the disk system */
 
131
      regs.eax = 0;
 
132
      regs.edx = (drive & 0xff);
 
133
      regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
134
      grub_bios_interrupt (0x13, &regs);
 
135
    }
 
136
  return ret;
 
137
}
 
138
 
 
139
/*
 
140
 *   Check if LBA is supported for DRIVE. If it is supported, then return
 
141
 *   the major version of extensions, otherwise zero.
 
142
 */
 
143
static int
 
144
grub_biosdisk_check_int13_extensions (int drive)
 
145
{
 
146
  struct grub_bios_int_registers regs;
 
147
 
 
148
  regs.edx = drive & 0xff;
 
149
  regs.eax = 0x4100;
 
150
  regs.ebx = 0x55aa;
 
151
  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
152
  grub_bios_interrupt (0x13, &regs);
 
153
  
 
154
  if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
 
155
    return 0;
 
156
 
 
157
  if ((regs.ebx & 0xffff) != 0xaa55)
 
158
    return 0;
 
159
 
 
160
  /* check if AH=0x42 is supported */
 
161
  if (!(regs.ecx & 1))
 
162
    return 0;
 
163
 
 
164
  return (regs.eax >> 8) & 0xff;
 
165
}
 
166
 
 
167
/*
 
168
 *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
 
169
 *   error occurs, then return non-zero, otherwise zero.
 
170
 */
 
171
static int 
 
172
grub_biosdisk_get_diskinfo_standard (int drive,
 
173
                                     unsigned long *cylinders,
 
174
                                     unsigned long *heads,
 
175
                                     unsigned long *sectors)
 
176
{
 
177
  struct grub_bios_int_registers regs;
 
178
 
 
179
  regs.eax = 0x0800;
 
180
  regs.edx = drive & 0xff;
 
181
 
 
182
  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
183
  grub_bios_interrupt (0x13, &regs);
 
184
 
 
185
  /* Check if unsuccessful. Ignore return value if carry isn't set to 
 
186
     workaround some buggy BIOSes. */
 
187
  if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
 
188
    return (regs.eax & 0xff00) >> 8;
 
189
 
 
190
  /* bogus BIOSes may not return an error number */  
 
191
  /* 0 sectors means no disk */
 
192
  if (!(regs.ecx & 0x3f))
 
193
    /* XXX 0x60 is one of the unused error numbers */
 
194
    return 0x60;
 
195
 
 
196
  /* the number of heads is counted from zero */
 
197
  *heads = ((regs.edx >> 8) & 0xff) + 1;
 
198
  *cylinders = (((regs.ecx >> 8) & 0xff) | ((regs.ecx << 2) & 0x0300)) + 1;
 
199
  *sectors = regs.ecx & 0x3f;
 
200
  return 0;
 
201
}
 
202
 
 
203
static int
 
204
grub_biosdisk_get_diskinfo_real (int drive, void *drp, grub_uint16_t ax)
 
205
{
 
206
  struct grub_bios_int_registers regs;
 
207
 
 
208
  regs.eax = ax;
 
209
 
 
210
  /* compute the address of drive parameters */
 
211
  regs.esi = ((grub_addr_t) drp) & 0xf;
 
212
  regs.ds = ((grub_addr_t) drp) >> 4;
 
213
  regs.edx = drive & 0xff;
 
214
 
 
215
  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
 
216
  grub_bios_interrupt (0x13, &regs);
 
217
 
 
218
  /* Check if unsuccessful. Ignore return value if carry isn't set to 
 
219
     workaround some buggy BIOSes. */
 
220
  if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
 
221
    return (regs.eax & 0xff00) >> 8;
 
222
 
 
223
  return 0;
 
224
}
 
225
 
 
226
/*
 
227
 *   Return the cdrom information of DRIVE in CDRP. If an error occurs,
 
228
 *   then return non-zero, otherwise zero.
 
229
 */
 
230
static int
 
231
grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
 
232
{
 
233
  return grub_biosdisk_get_diskinfo_real (drive, cdrp, 0x4b01);
 
234
}
 
235
 
 
236
/*
 
237
 *   Return the geometry of DRIVE in a drive parameters, DRP. If an error
 
238
 *   occurs, then return non-zero, otherwise zero.
 
239
 */
 
240
static int
 
241
grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
 
242
{
 
243
  return grub_biosdisk_get_diskinfo_real (drive, drp, 0x4800);
 
244
}
 
245
 
 
246
static int
 
247
grub_biosdisk_get_drive (const char *name)
 
248
{
 
249
  unsigned long drive;
 
250
 
 
251
  if (name[0] == 'c' && name[1] == 'd' && name[2] == 0 && cd_drive)
 
252
    return cd_drive;
 
253
 
 
254
  if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
 
255
    goto fail;
 
256
 
 
257
  drive = grub_strtoul (name + 2, 0, 10);
 
258
  if (grub_errno != GRUB_ERR_NONE)
 
259
    goto fail;
 
260
 
 
261
  if (name[0] == 'h')
 
262
    drive += 0x80;
 
263
 
 
264
  return (int) drive ;
 
265
 
 
266
 fail:
 
267
  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
 
268
  return -1;
 
269
}
 
270
 
 
271
static int
 
272
grub_biosdisk_call_hook (int (*hook) (const char *name), int drive)
 
273
{
 
274
  char name[10];
 
275
 
 
276
  if (cd_drive && drive == cd_drive)
 
277
    return hook ("cd");
 
278
 
 
279
  grub_snprintf (name, sizeof (name),
 
280
                 (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
 
281
  return hook (name);
 
282
}
 
283
 
 
284
static int
 
285
grub_biosdisk_iterate (int (*hook) (const char *name))
 
286
{
 
287
  int drive;
 
288
  int num_floppies;
 
289
 
 
290
  /* For hard disks, attempt to read the MBR.  */
 
291
  for (drive = 0x80; drive < 0x90; drive++)
 
292
    {
 
293
      if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
 
294
                                     GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
 
295
        {
 
296
          grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
 
297
          break;
 
298
        }
 
299
 
 
300
      if (grub_biosdisk_call_hook (hook, drive))
 
301
        return 1;
 
302
    }
 
303
 
 
304
  if (cd_drive)
 
305
    {
 
306
      if (grub_biosdisk_call_hook (hook, cd_drive))
 
307
      return 1;
 
308
    }
 
309
 
 
310
  /* For floppy disks, we can get the number safely.  */
 
311
  num_floppies = grub_biosdisk_get_num_floppies ();
 
312
  for (drive = 0; drive < num_floppies; drive++)
 
313
    if (grub_biosdisk_call_hook (hook, drive))
 
314
      return 1;
 
315
 
 
316
  return 0;
 
317
}
 
318
 
 
319
static grub_err_t
 
320
grub_biosdisk_open (const char *name, grub_disk_t disk)
 
321
{
 
322
  grub_uint64_t total_sectors = 0;
 
323
  int drive;
 
324
  struct grub_biosdisk_data *data;
 
325
 
 
326
  drive = grub_biosdisk_get_drive (name);
 
327
  if (drive < 0)
 
328
    return grub_errno;
 
329
 
 
330
  disk->id = drive;
 
331
 
 
332
  data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data));
 
333
  if (! data)
 
334
    return grub_errno;
 
335
 
 
336
  data->drive = drive;
 
337
 
 
338
  if ((cd_drive) && (drive == cd_drive))
 
339
    {
 
340
      data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
 
341
      data->sectors = 32;
 
342
      /* TODO: get the correct size.  */
 
343
      total_sectors = GRUB_DISK_SIZE_UNKNOWN;
 
344
    }
 
345
  else
 
346
    {
 
347
      /* HDD */
 
348
      int version;
 
349
 
 
350
      version = grub_biosdisk_check_int13_extensions (drive);
 
351
      if (version)
 
352
        {
 
353
          struct grub_biosdisk_drp *drp
 
354
            = (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
 
355
 
 
356
          /* Clear out the DRP.  */
 
357
          grub_memset (drp, 0, sizeof (*drp));
 
358
          drp->size = sizeof (*drp);
 
359
          if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp))
 
360
            {
 
361
              data->flags = GRUB_BIOSDISK_FLAG_LBA;
 
362
 
 
363
              if (drp->total_sectors)
 
364
                total_sectors = drp->total_sectors;
 
365
              else
 
366
                /* Some buggy BIOSes doesn't return the total sectors
 
367
                   correctly but returns zero. So if it is zero, compute
 
368
                   it by C/H/S returned by the LBA BIOS call.  */
 
369
                total_sectors = drp->cylinders * drp->heads * drp->sectors;
 
370
            }
 
371
        }
 
372
    }
 
373
 
 
374
  if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM))
 
375
    {
 
376
      if (grub_biosdisk_get_diskinfo_standard (drive,
 
377
                                               &data->cylinders,
 
378
                                               &data->heads,
 
379
                                               &data->sectors) != 0)
 
380
        {
 
381
          if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA))
 
382
            {
 
383
              data->sectors = 63;
 
384
              data->heads = 255;
 
385
              data->cylinders
 
386
                = grub_divmod64 (total_sectors
 
387
                                 + data->heads * data->sectors - 1,
 
388
                                 data->heads * data->sectors, 0);
 
389
            }
 
390
          else
 
391
            {
 
392
              grub_free (data);
 
393
              return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name);
 
394
            }
 
395
        }
 
396
 
 
397
      if (! total_sectors)
 
398
        total_sectors = data->cylinders * data->heads * data->sectors;
 
399
    }
 
400
 
 
401
  disk->total_sectors = total_sectors;
 
402
  disk->data = data;
 
403
 
 
404
  return GRUB_ERR_NONE;
 
405
}
 
406
 
 
407
static void
 
408
grub_biosdisk_close (grub_disk_t disk)
 
409
{
 
410
  grub_free (disk->data);
 
411
}
 
412
 
 
413
/* For readability.  */
 
414
#define GRUB_BIOSDISK_READ      0
 
415
#define GRUB_BIOSDISK_WRITE     1
 
416
 
 
417
#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
 
418
 
 
419
static grub_err_t
 
420
grub_biosdisk_rw (int cmd, grub_disk_t disk,
 
421
                  grub_disk_addr_t sector, grub_size_t size,
 
422
                  unsigned segment)
 
423
{
 
424
  struct grub_biosdisk_data *data = disk->data;
 
425
 
 
426
  if (data->flags & GRUB_BIOSDISK_FLAG_LBA)
 
427
    {
 
428
      struct grub_biosdisk_dap *dap;
 
429
 
 
430
      dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
 
431
                                          + (data->sectors
 
432
                                             << GRUB_DISK_SECTOR_BITS));
 
433
      dap->length = sizeof (*dap);
 
434
      dap->reserved = 0;
 
435
      dap->blocks = size;
 
436
      dap->buffer = segment << 16;      /* The format SEGMENT:ADDRESS.  */
 
437
      dap->block = sector;
 
438
 
 
439
      if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
 
440
        {
 
441
          int i;
 
442
 
 
443
          if (cmd)
 
444
            return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom");
 
445
 
 
446
          dap->blocks = ALIGN_UP (dap->blocks, 4) >> 2;
 
447
          dap->block >>= 2;
 
448
 
 
449
          for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
 
450
            if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
 
451
              break;
 
452
 
 
453
          if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT)
 
454
            return grub_error (GRUB_ERR_READ_ERROR, "cdrom read error");
 
455
        }
 
456
      else
 
457
        if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap))
 
458
          {
 
459
            /* Fall back to the CHS mode.  */
 
460
            data->flags &= ~GRUB_BIOSDISK_FLAG_LBA;
 
461
            disk->total_sectors = data->cylinders * data->heads * data->sectors;
 
462
            return grub_biosdisk_rw (cmd, disk, sector, size, segment);
 
463
          }
 
464
    }
 
465
  else
 
466
    {
 
467
      unsigned coff, hoff, soff;
 
468
      unsigned head;
 
469
 
 
470
      /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
 
471
         the traditional CHS access.  */
 
472
      if (sector >
 
473
          1024 /* cylinders */ *
 
474
          256 /* heads */ *
 
475
          63 /* spt */)
 
476
        return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name);
 
477
 
 
478
      soff = ((grub_uint32_t) sector) % data->sectors + 1;
 
479
      head = ((grub_uint32_t) sector) / data->sectors;
 
480
      hoff = head % data->heads;
 
481
      coff = head / data->heads;
 
482
 
 
483
      if (coff >= data->cylinders)
 
484
        return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name);
 
485
 
 
486
      if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive,
 
487
                                     coff, hoff, soff, size, segment))
 
488
        {
 
489
          switch (cmd)
 
490
            {
 
491
            case GRUB_BIOSDISK_READ:
 
492
              return grub_error (GRUB_ERR_READ_ERROR, "%s read error", disk->name);
 
493
            case GRUB_BIOSDISK_WRITE:
 
494
              return grub_error (GRUB_ERR_WRITE_ERROR, "%s write error", disk->name);
 
495
            }
 
496
        }
 
497
    }
 
498
 
 
499
  return GRUB_ERR_NONE;
 
500
}
 
501
 
 
502
/* Return the number of sectors which can be read safely at a time.  */
 
503
static grub_size_t
 
504
get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors)
 
505
{
 
506
  grub_size_t size;
 
507
  grub_uint32_t offset;
 
508
 
 
509
  /* OFFSET = SECTOR % SECTORS */
 
510
  grub_divmod64 (sector, sectors, &offset);
 
511
 
 
512
  size = sectors - offset;
 
513
 
 
514
  /* Limit the max to 0x7f because of Phoenix EDD.  */
 
515
  if (size > 0x7f)
 
516
    size = 0x7f;
 
517
 
 
518
  return size;
 
519
}
 
520
 
 
521
static grub_err_t
 
522
grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
 
523
                    grub_size_t size, char *buf)
 
524
{
 
525
  struct grub_biosdisk_data *data = disk->data;
 
526
 
 
527
  while (size)
 
528
    {
 
529
      grub_size_t len;
 
530
      grub_size_t cdoff = 0;
 
531
 
 
532
      len = get_safe_sectors (sector, data->sectors);
 
533
 
 
534
      if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
 
535
        {
 
536
          cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS;
 
537
          len = ALIGN_UP (sector + len, 4) - (sector & ~3);
 
538
          sector &= ~3;
 
539
        }
 
540
 
 
541
      if (len > size)
 
542
        len = size;
 
543
 
 
544
      if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len,
 
545
                            GRUB_MEMORY_MACHINE_SCRATCH_SEG))
 
546
        return grub_errno;
 
547
 
 
548
      grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff),
 
549
                   len << GRUB_DISK_SECTOR_BITS);
 
550
      buf += len << GRUB_DISK_SECTOR_BITS;
 
551
      sector += len;
 
552
      size -= len;
 
553
    }
 
554
 
 
555
  return grub_errno;
 
556
}
 
557
 
 
558
static grub_err_t
 
559
grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
 
560
                     grub_size_t size, const char *buf)
 
561
{
 
562
  struct grub_biosdisk_data *data = disk->data;
 
563
 
 
564
  if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
 
565
    return grub_error (GRUB_ERR_IO, "can't write to CDROM");
 
566
 
 
567
  while (size)
 
568
    {
 
569
      grub_size_t len;
 
570
 
 
571
      len = get_safe_sectors (sector, data->sectors);
 
572
      if (len > size)
 
573
        len = size;
 
574
 
 
575
      grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
 
576
                   len << GRUB_DISK_SECTOR_BITS);
 
577
 
 
578
      if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
 
579
                            GRUB_MEMORY_MACHINE_SCRATCH_SEG))
 
580
        return grub_errno;
 
581
 
 
582
      buf += len << GRUB_DISK_SECTOR_BITS;
 
583
      sector += len;
 
584
      size -= len;
 
585
    }
 
586
 
 
587
  return grub_errno;
 
588
}
 
589
 
 
590
static struct grub_disk_dev grub_biosdisk_dev =
 
591
  {
 
592
    .name = "biosdisk",
 
593
    .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
 
594
    .iterate = grub_biosdisk_iterate,
 
595
    .open = grub_biosdisk_open,
 
596
    .close = grub_biosdisk_close,
 
597
    .read = grub_biosdisk_read,
 
598
    .write = grub_biosdisk_write,
 
599
    .next = 0
 
600
  };
 
601
 
 
602
static void
 
603
grub_disk_biosdisk_fini (void)
 
604
{
 
605
  grub_disk_dev_unregister (&grub_biosdisk_dev);
 
606
}
 
607
 
 
608
GRUB_MOD_INIT(biosdisk)
 
609
{
 
610
  struct grub_biosdisk_cdrp *cdrp
 
611
        = (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
 
612
 
 
613
  if (grub_disk_firmware_is_tainted)
 
614
    {
 
615
      grub_printf ("Firmware is marked as tainted, refusing to initialize.\n");
 
616
      return;
 
617
    }
 
618
  grub_disk_firmware_fini = grub_disk_biosdisk_fini;
 
619
 
 
620
  grub_memset (cdrp, 0, sizeof (*cdrp));
 
621
  cdrp->size = sizeof (*cdrp);
 
622
  cdrp->media_type = 0xFF;
 
623
  if ((! grub_biosdisk_get_cdinfo_int13_extensions (grub_boot_drive, cdrp)) &&
 
624
      ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
 
625
       == GRUB_BIOSDISK_CDTYPE_NO_EMUL))
 
626
    cd_drive = cdrp->drive_no;
 
627
 
 
628
  grub_disk_dev_register (&grub_biosdisk_dev);
 
629
}
 
630
 
 
631
GRUB_MOD_FINI(biosdisk)
 
632
{
 
633
  grub_disk_biosdisk_fini ();
 
634
}