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

« back to all changes in this revision

Viewing changes to commands/i386/pc/drivemap.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
 
/* drivemap.c - command to manage the BIOS drive mappings.  */
2
 
/*
3
 
 *  GRUB  --  GRand Unified Bootloader
4
 
 *  Copyright (C) 2008, 2009  Free Software Foundation, Inc.
5
 
 *
6
 
 *  GRUB 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 3 of the License, or
9
 
 *  (at your option) any later version.
10
 
 *
11
 
 *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
18
 
 */
19
 
 
20
 
#include <grub/extcmd.h>
21
 
#include <grub/dl.h>
22
 
#include <grub/mm.h>
23
 
#include <grub/misc.h>
24
 
#include <grub/disk.h>
25
 
#include <grub/loader.h>
26
 
#include <grub/env.h>
27
 
#include <grub/machine/memory.h>
28
 
#include <grub/machine/biosnum.h>
29
 
#include <grub/i18n.h>
30
 
 
31
 
 
32
 
/* Real mode IVT slot (seg:off far pointer) for interrupt 0x13.  */
33
 
static grub_uint32_t *const int13slot = UINT_TO_PTR (4 * 0x13);
34
 
 
35
 
/* Remember to update enum opt_idxs accordingly.  */
36
 
static const struct grub_arg_option options[] = {
37
 
  {"list", 'l', 0, N_("Show the current mappings."), 0, 0},
38
 
  {"reset", 'r', 0, N_("Reset all mappings to the default values."), 0, 0},
39
 
  {"swap", 's', 0, N_("Perform both direct and reverse mappings."), 0, 0},
40
 
  {0, 0, 0, 0, 0, 0}
41
 
};
42
 
 
43
 
/* Remember to update options[] accordingly.  */
44
 
enum opt_idxs
45
 
{
46
 
  OPTIDX_LIST = 0,
47
 
  OPTIDX_RESET,
48
 
  OPTIDX_SWAP,
49
 
};
50
 
 
51
 
/* Realmode far ptr (2 * 16b) to the previous INT13h handler.  */
52
 
extern grub_uint32_t grub_drivemap_oldhandler;
53
 
 
54
 
/* The type "void" is used for imported assembly labels, takes no storage and
55
 
   serves just to take the address with &label.  */
56
 
 
57
 
/* The assembly function to replace the old INT13h handler. It does not follow
58
 
   any C callspecs and returns with IRET.  */
59
 
extern const void grub_drivemap_handler;
60
 
 
61
 
/* Start of the drive mappings area (space reserved at runtime).  */
62
 
extern const void grub_drivemap_mapstart;
63
 
 
64
 
typedef struct drivemap_node
65
 
{
66
 
  struct drivemap_node *next;
67
 
  grub_uint8_t newdrive;
68
 
  grub_uint8_t redirto;
69
 
} drivemap_node_t;
70
 
 
71
 
typedef struct __attribute__ ((packed)) int13map_node
72
 
{
73
 
  grub_uint8_t disknum;
74
 
  grub_uint8_t mapto;
75
 
} int13map_node_t;
76
 
 
77
 
#define INT13H_OFFSET(x) \
78
 
        (((grub_uint8_t *)(x)) - ((grub_uint8_t *)&grub_drivemap_handler))
79
 
 
80
 
static drivemap_node_t *map_head;
81
 
static void *drivemap_hook;
82
 
static int drivemap_mmap;
83
 
 
84
 
/* Puts the specified mapping into the table, replacing an existing mapping
85
 
   for newdrive or adding a new one if required.  */
86
 
static grub_err_t
87
 
drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
88
 
{
89
 
  drivemap_node_t *mapping = 0;
90
 
  drivemap_node_t *search = map_head;
91
 
  while (search)
92
 
    {
93
 
      if (search->newdrive == newdrive)
94
 
        {
95
 
          mapping = search;
96
 
          break;
97
 
        }
98
 
      search = search->next;
99
 
    }
100
 
 
101
 
  /* Check for pre-existing mappings to modify before creating a new one.  */
102
 
  if (mapping)
103
 
    mapping->redirto = redirto;
104
 
  else
105
 
    {
106
 
      mapping = grub_malloc (sizeof (drivemap_node_t));
107
 
      if (! mapping)
108
 
        return grub_error (GRUB_ERR_OUT_OF_MEMORY,
109
 
                           "cannot allocate map entry, not enough memory");
110
 
      mapping->newdrive = newdrive;
111
 
      mapping->redirto = redirto;
112
 
      mapping->next = map_head;
113
 
      map_head = mapping;
114
 
    }
115
 
  return GRUB_ERR_NONE;
116
 
}
117
 
 
118
 
/* Removes the mapping for newdrive from the table.  If there is no mapping,
119
 
   then this function behaves like a no-op on the map.  */
120
 
static void
121
 
drivemap_remove (grub_uint8_t newdrive)
122
 
{
123
 
  drivemap_node_t *mapping = 0;
124
 
  drivemap_node_t *search = map_head;
125
 
  drivemap_node_t *previous = 0;
126
 
 
127
 
  while (search)
128
 
    {
129
 
      if (search->newdrive == newdrive)
130
 
        {
131
 
          mapping = search;
132
 
          break;
133
 
        }
134
 
      previous = search;
135
 
      search = search->next;
136
 
    }
137
 
 
138
 
  if (mapping)
139
 
    {
140
 
      if (previous)
141
 
        previous->next = mapping->next;
142
 
      else
143
 
        map_head = mapping->next;
144
 
      grub_free (mapping);
145
 
    }
146
 
}
147
 
 
148
 
/* Given a GRUB-like device name and a convenient location, stores the
149
 
   related BIOS disk number.  Accepts devices like \((f|h)dN\), with
150
 
   0 <= N < 128.  */
151
 
static grub_err_t
152
 
tryparse_diskstring (const char *str, grub_uint8_t *output)
153
 
{
154
 
  /* Skip opening paren in order to allow both (hd0) and hd0.  */
155
 
  if (*str == '(')
156
 
    str++;
157
 
  if ((str[0] == 'f' || str[0] == 'h') && str[1] == 'd')
158
 
    {
159
 
      grub_uint8_t bios_num = (str[0] == 'h') ? 0x80 : 0x00;
160
 
      unsigned long drivenum = grub_strtoul (str + 2, 0, 0);
161
 
      if (grub_errno == GRUB_ERR_NONE && drivenum < 128)
162
 
        {
163
 
          bios_num |= drivenum;
164
 
          if (output)
165
 
            *output = bios_num;
166
 
          return GRUB_ERR_NONE;
167
 
        }
168
 
    }
169
 
  return grub_error (GRUB_ERR_BAD_ARGUMENT, "device format \"%s\" "
170
 
                     "invalid: must be (f|h)dN, with 0 <= N < 128", str);
171
 
}
172
 
 
173
 
static grub_err_t
174
 
list_mappings (void)
175
 
{
176
 
  /* Show: list mappings.  */
177
 
  if (! map_head)
178
 
    {
179
 
      grub_printf ("No drives have been remapped\n");
180
 
      return GRUB_ERR_NONE;
181
 
    }
182
 
 
183
 
  grub_printf ("OS disk #num ------> GRUB/BIOS device\n");
184
 
  drivemap_node_t *curnode = map_head;
185
 
  while (curnode)
186
 
    {
187
 
      grub_printf ("%cD #%-3u (0x%02x)       %cd%d\n",
188
 
                   (curnode->newdrive & 0x80) ? 'H' : 'F',
189
 
                   curnode->newdrive & 0x7F, curnode->newdrive,
190
 
                   (curnode->redirto & 0x80) ? 'h' : 'f',
191
 
                   curnode->redirto & 0x7F
192
 
                   );
193
 
      curnode = curnode->next;
194
 
    }
195
 
  return GRUB_ERR_NONE;
196
 
}
197
 
 
198
 
static grub_err_t
199
 
grub_cmd_drivemap (struct grub_extcmd *cmd, int argc, char **args)
200
 
{
201
 
  if (cmd->state[OPTIDX_LIST].set)
202
 
    {
203
 
      return list_mappings ();
204
 
    }
205
 
  else if (cmd->state[OPTIDX_RESET].set)
206
 
    {
207
 
      /* Reset: just delete all mappings, freeing their memory.  */
208
 
      drivemap_node_t *curnode = map_head;
209
 
      drivemap_node_t *prevnode = 0;
210
 
      while (curnode)
211
 
        {
212
 
          prevnode = curnode;
213
 
          curnode = curnode->next;
214
 
          grub_free (prevnode);
215
 
        }
216
 
      map_head = 0;
217
 
      return GRUB_ERR_NONE;
218
 
    }
219
 
  else if (!cmd->state[OPTIDX_SWAP].set && argc == 0)
220
 
    {
221
 
      /* No arguments */
222
 
      return list_mappings ();
223
 
    }
224
 
 
225
 
  /* Neither flag: put mapping.  */
226
 
  grub_uint8_t mapfrom = 0;
227
 
  grub_uint8_t mapto = 0xFF;
228
 
  grub_err_t err;
229
 
 
230
 
  if (argc != 2)
231
 
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
232
 
 
233
 
  err = tryparse_diskstring (args[0], &mapfrom);
234
 
  if (err != GRUB_ERR_NONE)
235
 
    return err;
236
 
 
237
 
  err = tryparse_diskstring (args[1], &mapto);
238
 
  if (err != GRUB_ERR_NONE)
239
 
    return err;
240
 
 
241
 
  if (mapto == mapfrom)
242
 
    {
243
 
      /* Reset to default.  */
244
 
      grub_dprintf ("drivemap", "Removing mapping for %s (%02x)\n",
245
 
                    args[0], mapfrom);
246
 
      drivemap_remove (mapfrom);
247
 
      return GRUB_ERR_NONE;
248
 
    }
249
 
  /* Set the mapping for the disk (overwrites any existing mapping).  */
250
 
  grub_dprintf ("drivemap", "%s %s (%02x) = %s (%02x)\n",
251
 
                cmd->state[OPTIDX_SWAP].set ? "Swapping" : "Mapping",
252
 
                args[1], mapto, args[0], mapfrom);
253
 
  err = drivemap_set (mapto, mapfrom);
254
 
  /* If -s, perform the reverse mapping too (only if the first was OK).  */
255
 
  if (cmd->state[OPTIDX_SWAP].set && err == GRUB_ERR_NONE)
256
 
    err = drivemap_set (mapfrom, mapto);
257
 
  return err;
258
 
}
259
 
 
260
 
/* Int13h handler installer - reserves conventional memory for the handler,
261
 
   copies it over and sets the IVT entry for int13h.
262
 
   This code rests on the assumption that GRUB does not activate any kind
263
 
   of memory mapping apart from identity paging, since it accesses
264
 
   realmode structures by their absolute addresses, like the IVT at 0;
265
 
   and transforms a pmode pointer into a rmode seg:off far ptr.  */
266
 
static grub_err_t
267
 
install_int13_handler (int noret __attribute__ ((unused)))
268
 
{
269
 
  /* Size of the full int13 handler "bundle", including code and map.  */
270
 
  grub_uint32_t total_size;
271
 
  /* Base address of the space reserved for the handler bundle.  */
272
 
  grub_uint8_t *handler_base = 0;
273
 
  /* Address of the map within the deployed bundle.  */
274
 
  int13map_node_t *handler_map;
275
 
 
276
 
  int i;
277
 
  int entries = 0;
278
 
  drivemap_node_t *curentry = map_head;
279
 
 
280
 
  /* Count entries to prepare a contiguous map block.  */
281
 
  while (curentry)
282
 
    {
283
 
      entries++;
284
 
      curentry = curentry->next;
285
 
    }
286
 
  if (entries == 0)
287
 
    {
288
 
      /* No need to install the int13h handler.  */
289
 
      grub_dprintf ("drivemap", "No drives marked as remapped, not installing "
290
 
                    "our int13h handler.\n");
291
 
      return GRUB_ERR_NONE;
292
 
    }
293
 
 
294
 
  grub_dprintf ("drivemap", "Installing our int13h handler\n");
295
 
 
296
 
  /* Save the pointer to the old handler.  */
297
 
  grub_drivemap_oldhandler = *int13slot;
298
 
  grub_dprintf ("drivemap", "Original int13 handler: %04x:%04x\n",
299
 
                (grub_drivemap_oldhandler >> 16) & 0x0ffff,
300
 
                grub_drivemap_oldhandler & 0x0ffff);
301
 
 
302
 
  /* Find a rmode-segment-aligned zone in conventional memory big
303
 
     enough to hold the handler and its data.  */
304
 
  total_size = INT13H_OFFSET (&grub_drivemap_mapstart)
305
 
    + (entries + 1) * sizeof (int13map_node_t);
306
 
  grub_dprintf ("drivemap", "Payload is %u bytes long\n", total_size);
307
 
  handler_base = grub_mmap_malign_and_register (16, total_size,
308
 
                                                &drivemap_mmap,
309
 
                                                GRUB_MACHINE_MEMORY_RESERVED,
310
 
                                                GRUB_MMAP_MALLOC_LOW);
311
 
  if (! handler_base)
312
 
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reserve "
313
 
                       "memory for the int13h handler");
314
 
 
315
 
  /* Copy int13h handler bundle to reserved area.  */
316
 
  grub_dprintf ("drivemap", "Reserved memory at %p, copying handler\n",
317
 
                handler_base);
318
 
  grub_memcpy (handler_base, &grub_drivemap_handler,
319
 
               INT13H_OFFSET (&grub_drivemap_mapstart));
320
 
 
321
 
  /* Copy the mappings to the reserved area.  */
322
 
  curentry = map_head;
323
 
  handler_map = (int13map_node_t *) (handler_base +
324
 
                                     INT13H_OFFSET (&grub_drivemap_mapstart));
325
 
  grub_dprintf ("drivemap", "Target map at %p, copying mappings\n", handler_map);
326
 
  for (i = 0; i < entries; ++i, curentry = curentry->next)
327
 
    {
328
 
      handler_map[i].disknum = curentry->newdrive;
329
 
      handler_map[i].mapto = curentry->redirto;
330
 
      grub_dprintf ("drivemap", "\t#%d: 0x%02x <- 0x%02x\n", i,
331
 
                    handler_map[i].disknum, handler_map[i].mapto);
332
 
    }
333
 
  /* Signal end-of-map.  */
334
 
  handler_map[i].disknum = 0;
335
 
  handler_map[i].mapto = 0;
336
 
  grub_dprintf ("drivemap", "\t#%d: 0x00 <- 0x00 (end)\n", i);
337
 
 
338
 
  /* Install our function as the int13h handler in the IVT.  */
339
 
  *int13slot = ((grub_uint32_t) handler_base) << 12;    /* Segment address.  */
340
 
  grub_dprintf ("drivemap", "New int13 handler: %04x:%04x\n",
341
 
                (*int13slot >> 16) & 0x0ffff, *int13slot & 0x0ffff);
342
 
 
343
 
  return GRUB_ERR_NONE;
344
 
}
345
 
 
346
 
static grub_err_t
347
 
uninstall_int13_handler (void)
348
 
{
349
 
  if (! grub_drivemap_oldhandler)
350
 
    return GRUB_ERR_NONE;
351
 
 
352
 
  *int13slot = grub_drivemap_oldhandler;
353
 
  grub_mmap_free_and_unregister (drivemap_mmap);
354
 
  grub_drivemap_oldhandler = 0;
355
 
  grub_dprintf ("drivemap", "Restored int13 handler: %04x:%04x\n",
356
 
                (*int13slot >> 16) & 0x0ffff, *int13slot & 0x0ffff);
357
 
 
358
 
  return GRUB_ERR_NONE;
359
 
}
360
 
 
361
 
static int
362
 
grub_get_root_biosnumber_drivemap (void)
363
 
{
364
 
  char *biosnum;
365
 
  int ret = -1;
366
 
  grub_device_t dev;
367
 
 
368
 
  biosnum = grub_env_get ("biosnum");
369
 
 
370
 
  if (biosnum)
371
 
    return grub_strtoul (biosnum, 0, 0);
372
 
 
373
 
  dev = grub_device_open (0);
374
 
  if (dev && dev->disk && dev->disk->dev
375
 
      && dev->disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID)
376
 
    {
377
 
      drivemap_node_t *curnode = map_head;
378
 
      ret = (int) dev->disk->id;
379
 
      while (curnode)
380
 
        {
381
 
          if (curnode->redirto == ret)
382
 
            {
383
 
              ret = curnode->newdrive;
384
 
              break;
385
 
            }
386
 
          curnode = curnode->next;
387
 
        }
388
 
 
389
 
    }
390
 
 
391
 
  if (dev)
392
 
    grub_device_close (dev);
393
 
 
394
 
  return ret;
395
 
}
396
 
 
397
 
static grub_extcmd_t cmd;
398
 
static int (*grub_get_root_biosnumber_saved) (void);
399
 
 
400
 
GRUB_MOD_INIT (drivemap)
401
 
{
402
 
  grub_get_root_biosnumber_saved = grub_get_root_biosnumber;
403
 
  grub_get_root_biosnumber = grub_get_root_biosnumber_drivemap;
404
 
  cmd = grub_register_extcmd ("drivemap", grub_cmd_drivemap,
405
 
                              GRUB_COMMAND_FLAG_BOTH,
406
 
                              N_("-l | -r | [-s] grubdev osdisk."),
407
 
                              N_("Manage the BIOS drive mappings."),
408
 
                              options);
409
 
  drivemap_hook =
410
 
    grub_loader_register_preboot_hook (&install_int13_handler,
411
 
                                       &uninstall_int13_handler,
412
 
                                       GRUB_LOADER_PREBOOT_HOOK_PRIO_NORMAL);
413
 
}
414
 
 
415
 
GRUB_MOD_FINI (drivemap)
416
 
{
417
 
  grub_get_root_biosnumber = grub_get_root_biosnumber_saved;
418
 
  grub_loader_unregister_preboot_hook (drivemap_hook);
419
 
  drivemap_hook = 0;
420
 
  grub_unregister_extcmd (cmd);
421
 
}