grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Drivemap module


From: Marco Gerards
Subject: Re: [PATCH] Drivemap module
Date: Wed, 13 Aug 2008 12:13:38 +0200
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

Javier Martín <address@hidden> writes:

> In this reply-to-myself hoping to keep the thread continuity, I put
> forth the new "version 7" of the patch with the following changes:
>
>   - A new switch -s/--swap has been implemented, so that running
> "drivemap -s hd0 hd1" is equivalent to issuing the command twice with
> the arguments normal, then reversed. There is one exception: if the
> first mapping fails or the second drive does not exist, no mapping is
> performed, whereas hand-performing the swap would have successfully
> assigned BIOS disk #1 to hd0, then failed to assign bd#0 to non-existent
> hd1.
>   - Raw BIOS disk number parsing has been removed: the syntax "drivemap
> hd1 0x80" is no longer legal. However, one can still map non-existent
> BIOS disk numbers with "drivemap hd0 hd42" for example and (more
> controversially, maybe I'll add a check eventually) assign a floppy to
> an HD and back.

Ah good :-)

> The only file changed from the last patch ("version 6") is drivemap.c:
> the rest of it should be the same, so if anyone was reviewing it you can
> seamlessly "jump" to version 7. In particular, the functional changes
> are localized in the drivemap_cmd function proper, and there are
> cosmetic changes elsewhere (spurious tabs removed, etc.).

You only forgot the changelog entry ;-)

> -Habbit
>
> Index: commands/i386/pc/drivemap.c
> ===================================================================
> --- commands/i386/pc/drivemap.c       (revisión: 0)
> +++ commands/i386/pc/drivemap.c       (revisión: 0)
> @@ -0,0 +1,433 @@
> +/* drivemap.c - command to manage the BIOS drive mappings.  */
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2008  Free Software Foundation, Inc.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/machine/drivemap.h>
> +#include <grub/normal.h>
> +#include <grub/dl.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include <grub/disk.h>
> +#include <grub/loader.h>
> +#include <grub/machine/loader.h>
> +#include <grub/machine/biosdisk.h>
> +
> +#define MODNAME "drivemap"
> +
> +static const struct grub_arg_option options[] = {
> +  {"list", 'l', 0, "show the current mappings", 0, 0},
> +  {"reset", 'r', 0, "reset all mappings to the default values", 0, 0},
> +  {"swap", 's', 0, "perform both direct and reverse mappings", 0, 0},
> +  {0, 0, 0, 0, 0, 0}
> +};
> +
> +enum opt_idxs {
> +  OPTIDX_LIST = 0,
> +  OPTIDX_RESET,
> +  OPTIDX_SWAP,
> +};
> +
> +typedef struct drivemap_node
> +{
> +  grub_uint8_t newdrive;
> +  grub_uint8_t redirto;
> +  struct drivemap_node *next;
> +} drivemap_node_t;
> +
> +static drivemap_node_t *drivemap;
> +static grub_preboot_hookid insthandler_hook;
> +static grub_err_t install_int13_handler (void);
> +
> +/* Puts the specified mapping into the table, replacing an existing mapping
> +   for newdrive or adding a new one if required.  */
> +static grub_err_t
> +drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
> +{
> +  drivemap_node_t *mapping = 0;
> +  drivemap_node_t *search = drivemap;
> +  while (search)
> +    {
> +      if (search->newdrive == newdrive)
> +        {
> +          mapping = search;
> +          break;
> +        }
> +      search = search->next;
> +    }
> +
> +  
> +  /* Check for pre-existing mappings to modify before creating a new one.  */
> +  if (mapping)
> +    mapping->redirto = redirto;
> +  else 
> +    {
> +      mapping = grub_malloc (sizeof (drivemap_node_t));
> +      if (!mapping)
> +        return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +                           "cannot allocate map entry, not enough memory");
> +      mapping->newdrive = newdrive;
> +      mapping->redirto = redirto;
> +      mapping->next = drivemap;
> +      drivemap = mapping;
> +    }
> +  return GRUB_ERR_NONE;
> +}
> +
> +/* Removes the mapping for newdrive from the table.  If there is no mapping,
> +   then this function behaves like a no-op on the map.  */
> +static void
> +drivemap_remove (grub_uint8_t newdrive)
> +{
> +  drivemap_node_t *mapping = 0;
> +  drivemap_node_t *search = drivemap;
> +  drivemap_node_t *previous = 0;
> +
> +  while (search)
> +    {
> +      if (search->newdrive == newdrive)
> +        {
> +          mapping = search;
> +          break;
> +        }
> +      previous = search;
> +      search = search->next;
> +    }
> +
> +  if (mapping)
> +    {
> +      if (previous)
> +        previous->next = mapping->next;
> +      else /* Entry was head of list.  */
> +        drivemap = mapping->next;
> +      grub_free (mapping);
> +    }
> +}
> +
> +/* Given a device name, resolves its BIOS disk number and stores it in the
> +   passed location, which should only be trusted if ERR_NONE is returned.  */
> +static grub_err_t
> +parse_biosdisk (const char *name, grub_uint8_t *disknum)
> +{
> +  grub_disk_t disk;
> +  if (!name || *name == 0)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name empty");
> +  /* Skip the first ( in (hd0) - disk_open wants just the name.  */
> +  if (*name == '(')
> +    name++;
> +  
> +  disk = grub_disk_open (name);
> +  if (!disk)
> +    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown device \"%s\"", 
> name);
> +  else
> +    {
> +      const enum grub_disk_dev_id id = disk->dev->id;
> +      /* The following assignment is only sound if the device is indeed a
> +         biosdisk.  The caller must check the return value.  */
> +      if (disknum)
> +        *disknum = disk->id;
> +      grub_disk_close (disk);
> +      if (id == GRUB_DISK_DEVICE_BIOSDISK_ID)
> +        return GRUB_ERR_NONE;
> +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", 
> name);

Please put the return on a separate line.

> +    }
> +}
> +
> +/* Given a BIOS disk number, returns its GRUB device name if it exists.
> +   For nonexisting BIOS disk numbers, this function returns
> +   GRUB_ERR_UNKNOWN_DEVICE.  */
> +static grub_err_t
> +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
> +{
> +  int found = 0;
> +  auto int find (const char *name);
> +  int find (const char *name)
> +  {
> +    const grub_disk_t disk = grub_disk_open (name);
> +    if (!disk)
> +      return 0;
> +    else
> +      {
> +        if (disk->id == dnum && disk->dev->id == 
> GRUB_DISK_DEVICE_BIOSDISK_ID)
> +          {
> +            found = 1;
> +            if (output)
> +              *output = name;
> +          }
> +        grub_disk_close (disk);
> +        return found;
> +      }
> +  }
> +
> +  grub_disk_dev_iterate (find);
> +  if (found)
> +    return GRUB_ERR_NONE;
> +  else
> +    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "BIOS disk %02x not found", 
> dnum);
> +}
> +
> +/* Given a GRUB-like device name and a convenient location, stores the 
> related
> +   BIOS disk number.  Accepts devices like \((f|h)dN\), with 0 <= N < 128.  
> */
> +static grub_err_t
> +tryparse_diskstring (const char *str, grub_uint8_t *output)
> +{
> +  if (!str || *str == 0)
> +    goto fail;
> +  /* Skip opening paren in order to allow both (hd0) and hd0.  */
> +  if (*str == '(')
> +    str++;
> +  if ((str[0] == 'f' || str[0] == 'h') && str[1] == 'd')
> +    {
> +      grub_uint8_t bios_num = (str[0] == 'h')? 0x80 : 0x00;
> +      grub_errno = GRUB_ERR_NONE;
> +      unsigned long drivenum = grub_strtoul (str + 2, 0, 0);
> +      if (grub_errno != GRUB_ERR_NONE || drivenum > 127)
> +        {
> +          /* N not a number or out of range.  */
> +          goto fail;
> +        }
> +      else
> +        {
> +          bios_num |= drivenum;
> +          if (output)
> +            *output = bios_num;
> +          return GRUB_ERR_NONE;
> +        }
> +    }
> +  else
> +    goto fail;
> +
> +fail:
> +  return grub_error (GRUB_ERR_BAD_ARGUMENT, "device format \"%s\" invalid: 
> must"
> +                     "be (f|h)dN, with 0 <= N < 128", str);
> +}
> +
> +static grub_err_t
> +grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
> +{
> +  if (state[OPTIDX_LIST].set)
> +    {
> +      /* Show: list mappings.  */
> +      if (!drivemap)
> +        grub_printf ("No drives have been remapped");
> +      else
> +        {
> +          grub_printf ("Showing only remapped drives.\n");
> +          grub_printf ("BIOS disk #num ----> GRUB device\n");

BIOS disk #num?

Can you give an example?

> +          drivemap_node_t *curnode = drivemap;
> +          while (curnode)
> +            {
> +              const char *dname = 0;
> +              grub_err_t err = revparse_biosdisk (curnode->redirto, &dname);
> +              if (err != GRUB_ERR_NONE)
> +                return grub_error (err, "invalid mapping: non-existent disk"
> +                                        "or not managed by the BIOS");
> +              grub_printf("%cD #%-3u (0x%02x)       %s\n",
> +                          (curnode->newdrive & 0x80) ? 'H' : 'F',
> +                          curnode->newdrive & 0x7F, curnode->newdrive, 
> dname);
> +              curnode = curnode->next;
> +            }
> +        }
> +    }
> +  else if (state[OPTIDX_RESET].set)
> +    {
> +      /* Reset: just delete all mappings, freeing their memory.  */
> +      drivemap_node_t *curnode = drivemap;
> +      drivemap_node_t *prevnode = 0;
> +      while (curnode)
> +        {
> +          prevnode = curnode;
> +          curnode = curnode->next;
> +          grub_free (prevnode);
> +        }
> +      drivemap = 0;
> +    }
> +  else
> +    {
> +      /* Neither flag: put mapping.  */
> +      grub_uint8_t mapfrom = 0;
> +      grub_uint8_t mapto = 0xFF;
> +      grub_err_t err;
> +      
> +      if (argc != 2)
> +        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
> +
> +      err = parse_biosdisk (args[0], &mapfrom);
> +      if (err != GRUB_ERR_NONE)
> +        return err;
> +      
> +      /* When swapping we require both devices to be BIOS disks, but when
> +         performing direct mappings we only require the 2nd argument to look
> +         like a BIOS disk in order to resolve it into a BIOS disk number.  */
> +      if (state[OPTIDX_SWAP].set)
> +        err = parse_biosdisk (args[1], &mapto);
> +      else
> +        err = tryparse_diskstring (args[1], &mapto);
> +      if (err != GRUB_ERR_NONE)
> +        return err;
> +      
> +      if (mapto == mapfrom)
> +        {
> +          /* Reset to default.  */
> +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", 
> args[0], mapfrom);
> +          drivemap_remove (mapfrom);
> +        }
> +      else
> +        {
> +          /* Set the mapping for the disk (overwrites any existing mapping). 
>  */
> +          grub_dprintf (MODNAME, "%s %s (%02x) = %s (%02x)\n",
> +                        state[OPTIDX_SWAP].set ? "Swapping" : "Mapping",
> +                        args[1], mapto, args[0], mapfrom);
> +          err = drivemap_set (mapto, mapfrom);
> +          /* If -s, perform the reverse mapping too (only if the first was 
> OK).  */
> +          if (state[OPTIDX_SWAP].set && err == GRUB_ERR_NONE)
> +            err = drivemap_set (mapfrom, mapto);
> +          return err;
> +        }
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +typedef struct __attribute__ ((packed)) int13map_node
> +{
> +  grub_uint8_t disknum;
> +  grub_uint8_t mapto;
> +} int13map_node_t;
> +
> +/* The min amount of mem that must remain free after installing the handler.
> +   32 KiB is just above 0x7C00-0x7E00, where the bootsector is loaded.  */
> +#define MIN_FREE_MEM_KB 32
> +#define INT13H_OFFSET(x) ( ((grub_uint8_t*)(x)) - 
> ((grub_uint8_t*)&grub_drivemap_int13_handler_base) )
> +#define INT13H_REBASE(x) ( (void*) (((grub_uint8_t*)handler_base) + (x)) )
> +#define INT13H_TONEWADDR(x) INT13H_REBASE( INT13H_OFFSET( x ) )
> +
> +/* Int13h handler installer - reserves conventional memory for the handler,
> +   copies it over and sets the IVT entry for int13h.  
> +   This code rests on the assumption that GRUB does not activate any kind of
> +   memory mapping apart from identity paging, since it accesses realmode
> +   structures by their absolute addresses, like the IVT at 0 or the BDA at
> +   0x400; and transforms a pmode pointer into a rmode seg:off far ptr.  */
> +static grub_err_t
> +install_int13_handler (void)
> +{
> +  grub_size_t entries = 0;
> +  drivemap_node_t *curentry = drivemap;
> +  /* Count entries to prepare a contiguous map block.  */
> +  while (curentry)
> +    {
> +      entries++;
> +      curentry = curentry->next;
> +    }
> +  if (entries == 0)
> +    {
> +      /* No need to install the int13h handler.  */
> +      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
> +                    "an int13h handler is not required.");
> +      return GRUB_ERR_NONE;
> +    }
> +  else
> +    {
> +      /* Real mode IVT slot (seg:off far pointer) for interrupt 0x13.  */
> +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
> +      /* BDA offset 0x13 contains the top of conventional memory, in kiB.  */
> +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
> +      /* Size of the full int13 handler "bundle", including code and map.  */
> +      grub_size_t total_size;
> +      /* The former, ceil-rounded to KiB.  */
> +      grub_uint16_t payload_sizekb;
> +      /* Base address of the space reserved for the handler bundle.  */
> +      grub_uint8_t *handler_base;
> +      /* Address of the map within the deployed bundle.  */
> +      int13map_node_t *handler_map;
> +      /* Real mode IVT entry (seg:off far pointer) for the new handler.  */
> +      grub_uint32_t ivtentry;
> +
> +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
> +      
> +      /* Save the pointer to the old handler.  */
> +      grub_drivemap_int13_oldhandler = *ivtslot;
> +      grub_dprintf (MODNAME, "Old int13 handler at %04x:%04x\n",
> +                    (grub_drivemap_int13_oldhandler >> 16) & 0x0ffff,
> +                    grub_drivemap_int13_oldhandler & 0x0ffff);
> +
> +      /* Reserve a section of conventional memory as "BIOS memory" for 
> handler.  */
> +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", 
> *bpa_freekb);
> +      total_size = grub_drivemap_int13_size
> +                   + (entries + 1) * sizeof(int13map_node_t);
> +      payload_sizekb = (total_size >> 10) +
> +                       (((total_size % 1024) == 0) ? 0 : 1);
> +      if ((*bpa_freekb - payload_sizekb) < MIN_FREE_MEM_KB)
> +        return grub_error (GRUB_ERR_OUT_OF_MEMORY, "refusing to install"
> +                           "int13 handler, not enough free memory after");
> +      grub_dprintf (MODNAME, "Payload is %u b long, reserving %u Kb\n",
> +                    total_size, payload_sizekb);
> +      *bpa_freekb -= payload_sizekb;
> +
> +      /* Copy int13h handler bundle to reserved area.  */
> +      handler_base = (grub_uint8_t*)(*bpa_freekb << 10);
> +      grub_dprintf (MODNAME, "Copying int13 handler to: %p\n", handler_base);
> +      grub_memcpy (handler_base, &grub_drivemap_int13_handler_base,
> +                   grub_drivemap_int13_size);
> +
> +      /* Copy the mappings to the reserved area.  */
> +      curentry = drivemap;
> +      grub_size_t i;
> +      handler_map = (int13map_node_t*)
> +                    INT13H_TONEWADDR (&grub_drivemap_int13_mapstart);
> +      grub_dprintf (MODNAME, "Target map at %p, copying mappings...\n", 
> handler_map);
> +      for (i = 0; i < entries && curentry; i++, curentry = curentry->next)
> +        {
> +          handler_map[i].disknum = curentry->newdrive;
> +          handler_map[i].mapto = curentry->redirto;
> +          grub_dprintf (MODNAME, "\t#%d: 0x%02x <- 0x%02x\n", i,
> +                        handler_map[i].disknum, handler_map[i].mapto);
> +        }
> +      /* Signal end-of-map.  */
> +      handler_map[i].disknum = 0;
> +      handler_map[i].mapto = 0;
> +      grub_dprintf (MODNAME, "\t#%d: 0x%02x <- 0x%02x (end)\n", i,
> +                    handler_map[i].disknum, handler_map[i].mapto);
> +
> +      /* Install our function as the int13h handler in the IVT.  */
> +      ivtentry = ((grub_uint32_t)handler_base) << 12; /* Segment address.  */
> +      ivtentry |= (grub_uint16_t) 
> INT13H_OFFSET(&grub_drivemap_int13_handler);
> +      grub_dprintf (MODNAME, "New int13 handler IVT pointer: %04x:%04x\n",
> +                    (ivtentry >> 16) & 0x0ffff, ivtentry & 0x0ffff);
> +      *ivtslot = ivtentry;
> +      
> +      return GRUB_ERR_NONE;
> +    }
> +}
> +
> +GRUB_MOD_INIT (drivemap)
> +{
> +  (void) mod;                        /* Stop warning.  */
> +  grub_register_command (MODNAME, grub_cmd_drivemap,
> +                         GRUB_COMMAND_FLAG_BOTH,
> +                         MODNAME " -l | -r | [-s] grubdev biosdisk",
> +                         "Manage the BIOS drive mappings", options);
> +  insthandler_hook = grub_loader_register_preboot (&install_int13_handler, 
> 1);
> +}
> +
> +GRUB_MOD_FINI (drivemap)
> +{
> +  grub_loader_unregister_preboot (insthandler_hook);
> +  insthandler_hook = 0;
> +  grub_unregister_command (MODNAME);
> +}
> +
> Index: commands/i386/pc/drivemap_int13h.S
> ===================================================================
> --- commands/i386/pc/drivemap_int13h.S        (revisión: 0)
> +++ commands/i386/pc/drivemap_int13h.S        (revisión: 0)
> @@ -0,0 +1,121 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 Free Software 
> Foundation, Inc.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +
> +/*
> + * Note: These functions defined in this file may be called from C.
> + *       Be careful of that you must not modify some registers. Quote
> + *       from gcc-2.95.2/gcc/config/i386/i386.h:
> +
> +   1 for registers not available across function calls.
> +   These must include the FIXED_REGISTERS and also any
> +   registers that can be used without being saved.
> +   The latter must include the registers where values are returned
> +   and the register where structure-value addresses are passed.
> +   Aside from that, you can include as many other registers as you like.
> +
> +  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
> +{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
> + */
> +
> +/*
> + * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
> + *       So the first three arguments are passed in %eax, %edx, and %ecx,
> + *       respectively, and if a function has a fixed number of arguments
> + *       and the number if greater than three, the function must return
> + *       with "ret $N" where N is ((the number of arguments) - 3) * 4.
> + */
> +
> +#include <grub/symbol.h>
> +
> +#define GRUB_DRIVEMAP_INT13H_OFFSET(x) ((x) - 
> grub_drivemap_int13_handler_base)
> +
> +/* Copy starts here.  When deployed, this label must be segment-aligned.  */
> +VARIABLE(grub_drivemap_int13_handler_base)
> +
> +/* Far pointer to the old handler.  Stored as a CS:IP in the style of 
> real-mode
> +   IVT entries (thus PI:SC in mem).  */
> +VARIABLE(grub_drivemap_int13_oldhandler)
> +  .word 0xdead, 0xbeef
> +
> +/* Drivemap module bundle - INT 13h handler - BIOS HD map */
> +/* We need to use relative addressing, and with CS to top it all, since we
> +   must make as few changes to the registers as possible.  IP-relative
> +   addressing like on amd64 would make life way easier here. */
> +.code16
> +FUNCTION(grub_drivemap_int13_handler)
> +  push %bp
> +  mov %sp, %bp
> +  push %ax  /* We'll need it later to determine the used BIOS function.  */
> +
> +  /* Map the drive number (always in DL?).  */
> +  push %ax
> +  push %bx
> +  push %si
> +  mov $GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_mapstart), %bx
> +  xor %si, %si
> +1:movw %cs:(%bx,%si), %ax
> +  cmp %ah, %al
> +  jz 3f /* DRV=DST => map end - drive not remapped, leave DL as-is.  */
> +  cmp %dl, %al
> +  jz 2f /* Found - drive remapped, modify DL.  */
> +  add $2, %si
> +  jmp 1b /* Not found, but more remaining, loop.  */
> +2:mov %ah, %dl
> +3:pop %si
> +  pop %bx
> +  xchgw %ax, -4(%bp) /* Recover the old AX and save the map entry for later. 
>  */
> +  
> +  push %bp
> +  /* Simulate interrupt call: push flags and do a far call in order to set
> +     the stack the way the old handler expects it so that its iret works.  */
> +  push 6(%bp)
> +  movw (%bp), %bp  /* Restore the caller BP (is this needed and/or 
> sensible?).  */
> +  lcall *%cs:GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_oldhandler)
> +  pop %bp /* The pushed flags were removed by iret.  */
> +  /* Set the saved flags to what the int13h handler returned.  */
> +  push %ax
> +  pushf
> +  pop %ax
> +  movw %ax, 6(%bp)
> +  pop %ax
> +
> +  /* Reverse map any returned drive number if the data returned includes it. 
>  
> +     The only func that does this seems to be origAH = 0x08, but many BIOS
> +     refs say retDL = # of drives connected.  However, the GRUB Legacy code
> +     treats this as the _drive number_ and "undoes" the remapping.  Thus,
> +     this section has been disabled for testing if it's required.  */
> +#  cmpb $0x08, -1(%bp) /* Caller's AH.  */
> +#  jne 4f
> +#  xchgw %ax, -4(%bp) /* Map entry used.  */
> +#  cmp %ah, %al  /* DRV=DST => drive not remapped.  */
> +#  je 4f
> +#  mov %ah, %dl  /* Undo remap.  */
> +
> +4:mov %bp, %sp
> +  pop %bp
> +  iret
> +/* This label MUST be at the end of the copied block, since the installer 
> code
> +   reserves additional space for mappings at runtime and copies them over 
> it.  */
> +.align 2
> +VARIABLE(grub_drivemap_int13_mapstart)
> +/* Copy stops here.  */
> +.code32
> +VARIABLE(grub_drivemap_int13_size)
> +  .word GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_size)
> +
> Index: conf/i386-pc.rmk
> ===================================================================
> --- conf/i386-pc.rmk  (revisión: 1798)
> +++ conf/i386-pc.rmk  (copia de trabajo)
> @@ -163,7 +163,7 @@
>       vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
>       videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod  \
>       ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \
> -     aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod
> +     aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod drivemap.mod
>  
>  # For biosdisk.mod.
>  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
> @@ -340,4 +340,12 @@
>  pxecmd_mod_CFLAGS = $(COMMON_CFLAGS)
>  pxecmd_mod_LDFLAGS = $(COMMON_LDFLAGS)
>  
> +# For drivemap.mod.
> +drivemap_mod_HEADERS = machine/drivemap.h
> +drivemap_mod_SOURCES = commands/i386/pc/drivemap.c \
> +                       commands/i386/pc/drivemap_int13h.S
> +drivemap_mod_ASFLAGS = $(COMMON_ASFLAGS)
> +drivemap_mod_CFLAGS = $(COMMON_CFLAGS)
> +drivemap_mod_LDFLAGS = $(COMMON_LDFLAGS)
> +
>  include $(srcdir)/conf/common.mk
> Index: include/grub/loader.h
> ===================================================================
> --- include/grub/loader.h     (revisión: 1798)
> +++ include/grub/loader.h     (copia de trabajo)
> @@ -37,7 +37,23 @@
>  /* Unset current loader, if any.  */
>  void EXPORT_FUNC(grub_loader_unset) (void);
>  
> -/* Call the boot hook in current loader. This may or may not return,
> +typedef struct hooklist_node *grub_preboot_hookid;
> +
> +/* Register a function to be called before the loader "boot" function.  
> Returns
> +   an id that can be later used to unregister the preboot (i.e. on module
> +   unload).  If ABORT_ON_ERROR is set, the boot sequence will abort if any of
> +   the registered functions return anything else than GRUB_ERR_NONE.
> +   On error, the return value will compare equal to 0 and the error 
> information
> +   will be available in grub_errno.  However, if the call is successful that
> +   variable is _not_ modified. */
> +grub_preboot_hookid EXPORT_FUNC(grub_loader_register_preboot)
> +           (grub_err_t (*hook) (void), int abort_on_error);
> +
> +/* Unregister a preboot hook by the id returned by loader_register_preboot.  
> +   This functions becomes a no-op if no such function is registered */
> +void EXPORT_FUNC(grub_loader_unregister_preboot) (grub_preboot_hookid id);
> +
> +/* Call the boot hook in current loader.  This may or may not return,
>     depending on the setting by grub_loader_set.  */
>  grub_err_t EXPORT_FUNC(grub_loader_boot) (void);
>  
> Index: include/grub/i386/pc/drivemap.h
> ===================================================================
> --- include/grub/i386/pc/drivemap.h   (revisión: 0)
> +++ include/grub/i386/pc/drivemap.h   (revisión: 0)
> @@ -0,0 +1,37 @@
> +/* drivemap.h - command to manage the BIOS drive mappings.  */
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2008  Free Software Foundation, Inc.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */

Please add an inclusion guard.

> +#include <grub/types.h>
> +
> +/* Realmode far ptr (2 * 16b) to the previous INT13h handler.  */
> +extern grub_uint32_t grub_drivemap_int13_oldhandler;
> +/* Size of the INT13h handler bundle (code+data) to be deployed.  */
> +extern grub_uint16_t grub_drivemap_int13_size;
> +
> +/* This type is used for imported assembly labels, takes no storage and is 
> only
> +   used to take the symbol address with &label.  Do NOT put void* here.  */
> +typedef void grub_symbol_t;
> +
> +/* Start of the handler bundle.  */
> +extern grub_symbol_t grub_drivemap_int13_handler_base;
> +/* Start of the drive mappings area (space reserved at runtime).  */
> +extern grub_symbol_t grub_drivemap_int13_mapstart;
> +/* The assembly function to replace the old INT13h handler. It should not be
> +   called because it does not follow any C callspecs and returns with IRET.  
> */
> +extern grub_symbol_t grub_drivemap_int13_handler;
> Index: kern/loader.c
> ===================================================================
> --- kern/loader.c     (revisión: 1798)
> +++ kern/loader.c     (copia de trabajo)
> @@ -61,11 +61,85 @@
>    grub_loader_loaded = 0;
>  }
>  
> +struct hooklist_node
> +{
> +  grub_err_t (*hook) (void);
> +  int abort_on_error;
> +  struct hooklist_node *next;
> +};
> +
> +static struct hooklist_node *preboot_hooks = 0;
> +
> +grub_preboot_hookid
> +grub_loader_register_preboot (grub_err_t (*hook) (void), int abort_on_error)
> +{
> +  grub_preboot_hookid newentry;
> +  if (!hook)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
> +      return 0;
> +    }
> +  newentry = grub_malloc (sizeof (struct hooklist_node));
> +  if (!newentry)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot alloc a hookinfo 
> structure");
> +      return 0;
> +    }
> +  else
> +    {
> +      newentry->hook = hook;
> +      newentry->abort_on_error = abort_on_error;
> +      newentry->next = preboot_hooks;
> +      preboot_hooks = newentry;
> +      return newentry;
> +    }
> +}
> +
> +void
> +grub_loader_unregister_preboot (grub_preboot_hookid id)
> +{
> +  grub_preboot_hookid entry = 0;
> +  grub_preboot_hookid search = preboot_hooks;
> +  grub_preboot_hookid previous = 0;
> +
> +  if (id == 0)
> +    return;
> +
> +  while (search)
> +    {
> +      if (search == id)
> +        {
> +          entry = search;
> +          break;
> +        }
> +      previous = search;
> +      search = search->next;
> +    }
> +
> +  if (entry)
> +    {
> +      if (previous)
> +        previous->next = entry->next;
> +      else preboot_hooks = entry->next; /* Entry was head of list */

.  */

Please put the code after the else on a separate line.

> +      grub_free (entry);
> +    }
> +}
> +
>  grub_err_t
>  grub_loader_boot (void)
>  {
> +  grub_preboot_hookid entry = preboot_hooks;
> +
>    if (! grub_loader_loaded)
>      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
> +  
> +  while (entry)
> +    {
> +      grub_err_t possible_error = entry->hook();
> +      if (possible_error != GRUB_ERR_NONE && entry->abort_on_error)
> +        return possible_error;
> +      entry = entry->next;
> +    }
>  
>    if (grub_loader_noreturn)
>      grub_machine_fini ();
> _______________________________________________
> Grub-devel mailing list
> address@hidden
> http://lists.gnu.org/mailman/listinfo/grub-devel





reply via email to

[Prev in Thread] Current Thread [Next in Thread]