All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Drivemap module
@ 2008-06-10 20:09 Javier Martín
  2008-06-10 20:23 ` Vesa Jääskeläinen
  2008-06-11 14:44 ` Marco Gerards
  0 siblings, 2 replies; 47+ messages in thread
From: Javier Martín @ 2008-06-10 20:09 UTC (permalink / raw)
  To: grub-devel


[-- Attachment #1.1: Type: text/plain, Size: 1956 bytes --]

Since the last posts in the "reimplementing the legacy map command"
thread seemingly died silently, I'm sending the improved version of the
patch here. If the death of the thread was intentional, please just tell
me (either nicely or rudely) and I'll be off.

Recapitulating the other thread, this is a new module for x86-pc that
provides functionality similar to the legacy "map" command, i.e. allows
BIOS drives to be remapped and have the second HD appear as 0x80 (the
first), which is necessary because some OSes refuse to boot otherwise,
ignoring the "boot drive" passed to their loaders in DL (Windows XP and
other NTs); or can only boot with limited functionality (FreeDOS). The
syntax of this new command is similar to the old "map", though the
second argument must be the target BIOS drive number instead of a GRUB
device, though a hack-patch parsing them would be simple.

Regarding the objections raised by Vesa, I followed most of the advices,
removing chatter in the source and completely separating drivemap from
the kernel. However, I've left the "abort_on_error" part of the new
preboot-hook interface intact, mostly because I don't know how to
replace it. As drivemap is the only module using it, the problem should
be harmless for now, but a solution should be devised - not by me,
though, finals are here (literally, I had one today).

I hope I didn't leave anything out this time. This code has been tested
on the following situations:
 * Emulators: Bochs & QEMU, booting FreeDOS in (hd1) with another fat
partition in (hd0) - works wonderfully, and even allows access to both
drives if they are crossmapped and a faux root=(hd0) is set.
 * Real hardware: an Athlon64 (939) CPU on a Gigabyte motherboard,
booting Windows XP Pro x64 in (hd1) with Ubuntu in (hd0) - works too,
with no hacks required at all (by the way, booting XP was my reason to
do this).

Well, here it is. Have a nice day!

Habbit

[-- Attachment #1.2: drivemap.patch --]
[-- Type: text/x-patch, Size: 23117 bytes --]

Index: commands/i386/pc/drivemap.c
===================================================================
RCS file: commands/i386/pc/drivemap.c
diff -N commands/i386/pc/drivemap.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/i386/pc/drivemap.c	10 Jun 2008 20:02:19 -0000
@@ -0,0 +1,348 @@
+/* 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/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>
+
+/* Uncomment the following line to enable debugging output */
+/* #define DRIVEMAP_DEBUG */
+
+#ifdef DRIVEMAP_DEBUG
+# define DBG_PRINTF(...) grub_printf(__VA_ARGS__)
+#else
+# define DBG_PRINTF(...)
+#endif
+
+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},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+/* ASSEMBLY SYMBOLS/VARS/FUNCS - start */
+
+/* realmode far ptr = 2 * 16b */
+extern grub_uint32_t EXPORT_VAR(grub_drivemap_int13_oldhandler);
+/* Size of the section to be copied */
+extern grub_uint16_t EXPORT_VAR(grub_drivemap_int13_size);
+
+/* NOT a typo - just need the symbol's address with &symbol */
+typedef void grub_symbol_t;
+extern grub_symbol_t EXPORT_VAR(grub_drivemap_int13_handler_base);
+extern grub_symbol_t EXPORT_VAR(grub_drivemap_int13_mapstart);
+
+void EXPORT_FUNC(grub_drivemap_int13_handler)(void);
+
+/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */
+
+static grub_preboot_hookid insthandler_hook = 0;
+
+typedef struct drivemap_node
+{
+  grub_uint8_t newdrive;
+  grub_uint8_t redirto;
+  struct drivemap_node *next;
+} drivemap_node_t;
+
+static drivemap_node_t *drivemap = 0;
+static grub_err_t drivemap_install_int13_handler(void);
+
+static grub_err_t
+drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
+  /* Puts the specified mapping into the table, replacing an existing mapping
+   * for newdrive or adding a new one if required. */
+{
+  drivemap_node_t *mapping = 0, *search = drivemap;
+  while (search)
+    {
+      if (search->newdrive == newdrive)
+        {
+          mapping = search;
+          break;
+        }
+      search = search->next;
+    }
+
+  if (mapping)  /* There was a mapping already in place, modify it */
+    mapping->redirto = redirto;
+  else  /* Create a new mapping and add it to the head of the list */
+    {
+      mapping = grub_malloc (sizeof (drivemap_node_t));
+      if (!mapping)
+        return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate map entry");
+      mapping->newdrive = newdrive;
+      mapping->redirto = redirto;
+      mapping->next = drivemap;
+      drivemap = mapping;
+    }
+  return GRUB_ERR_NONE;
+}
+
+static void
+drivemap_remove (grub_uint8_t newdrive)
+  /* Removes the mapping for newdrive from the table. If there is no mapping,
+   * then this function behaves like a no-op on the map. */
+{
+  drivemap_node_t *mapping = 0, *search = drivemap, *previous = 0;
+  while (search)
+    {
+      if (search->newdrive == newdrive)
+        {
+          mapping = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (mapping) /* Found */
+    {
+      if (previous)
+        previous->next = mapping->next;
+      else drivemap = mapping->next; /* Entry was head of list */
+      grub_free (mapping);
+    }
+}
+
+static grub_err_t parse_biosdisk (const char *name, grub_uint8_t *disknum)
+{
+  if (!name) return GRUB_ERR_BAD_ARGUMENT;
+  if (name[0] == '(')
+    name++; /* Skip the first ( in (hd0) - disk_open wants just the name! */
+  grub_disk_t disk = grub_disk_open (name);
+  if (!disk)
+    return GRUB_ERR_UNKNOWN_DEVICE;
+  else
+    {
+      enum grub_disk_dev_id id = disk->dev->id;
+      if (disknum)
+        *disknum = disk->id;   /* Only valid, of course if it's a biosdisk */
+      grub_disk_close (disk);
+      return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ?
+          GRUB_ERR_BAD_DEVICE : GRUB_ERR_NONE;
+    }
+}
+
+static grub_err_t revparse_biosdisk(const grub_uint8_t dnum, const char **output)
+{
+  grub_err_t retval = GRUB_ERR_UNKNOWN_DEVICE;
+  auto int find (const char *name);
+  int find (const char *name)
+  {
+    grub_disk_t disk = grub_disk_open (name);
+    if (!disk)
+      return 0;
+    else
+      {
+        int found = 0;
+        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)
+          {
+            found = 1;
+            *output = name;
+            retval = GRUB_ERR_NONE;
+          }
+        grub_disk_close (disk);
+        return found;
+      }
+  }
+
+  grub_disk_dev_iterate (&find);
+  return retval;
+}
+
+static grub_err_t
+grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
+{
+  if (state[0].set) /* Show: list mappings */
+    {
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives. Drives that have had "
+                       "their slot assigned to another one and have not been "
+                       "themselves remapped will become inaccessible through "
+                       "the BIOS routines to the booted OS.\n\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].set) /* Reset: just delete all mappings */
+    {
+      if (drivemap)
+        {
+          drivemap_node_t *curnode = drivemap, *prevnode = 0;
+          while (curnode)
+            {
+              prevnode = curnode;
+              curnode = curnode->next;
+              grub_free (prevnode);
+            }
+          drivemap = 0;
+        }
+    }
+  else
+    {
+      if (argc != 2)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
+      grub_uint8_t mapfrom = 0;
+      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
+      if (err != GRUB_ERR_NONE)
+        return grub_error (err, "invalid disk or not managed by the BIOS");
+
+      grub_errno = GRUB_ERR_NONE;
+      unsigned long mapto = grub_strtoul (args[1], 0, 0);
+      if (grub_errno != GRUB_ERR_NONE)
+        return grub_error (grub_errno,
+                          "BIOS disk number must be between 0 and 255");
+      else if (mapto == mapfrom)  /* Reset to default */      
+        {
+          DBG_PRINTF ("Removing the mapping for %s (%02x)", args[0], mapfrom);
+          drivemap_remove (mapfrom);
+        }
+      else  /* Map */
+        {
+          DBG_PRINTF ("Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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, since it accesses realmode structures by their absolute
+ * addresses, like the IVT at 0 or the BDA at 0x400 */
+static grub_err_t drivemap_install_int13_handler(void)
+{
+  grub_size_t entries = 0;
+  drivemap_node_t *curentry = drivemap;
+  while (curentry)  /* Count entries to prepare a contiguous map block */
+    {
+      entries++;
+      curentry = curentry->next;
+    }
+  if (0 == entries)
+    {
+      DBG_PRINTF ("drivemap: No drives remapped, int13h handler not installed");
+      return GRUB_ERR_NONE;  /* No need to install the int13h handler */
+    }
+  else
+    {
+      DBG_PRINTF ("drivemap: Installing int13h handler...\n");
+      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
+      
+      /* Save the pointer to the old int13h handler */    
+      grub_drivemap_int13_oldhandler = *ivtslot;
+      DBG_PRINTF ("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:
+       * BDA offset 0x13 contains the top of such memory */
+      grub_uint16_t *bpaMemInKb = (grub_uint16_t*)0x00000413;
+      DBG_PRINTF ("Top of conventional memory: %u KiB\n", *bpaMemInKb);
+      grub_size_t totalSize = grub_drivemap_int13_size
+                            + (entries + 1) * sizeof(int13map_node_t);
+      grub_uint16_t payloadSizeKb = (totalSize >> 10) +
+                                    (((totalSize % 1024) == 0) ? 0 : 1);
+      if ((*bpaMemInKb - payloadSizeKb) < MIN_FREE_MEM_KB)
+        return grub_error (GRUB_ERR_OUT_OF_MEMORY, "refusing to install int13 handler, not enough free memory after");
+      DBG_PRINTF ("Payload is %u b long, reserving %u Kb\n", totalSize, payloadSizeKb);
+      *bpaMemInKb -= payloadSizeKb;
+
+      /* Copy int13h handler chunk to reserved area */
+      grub_uint8_t *handler_base = (grub_uint8_t*)(*bpaMemInKb << 10);
+      DBG_PRINTF ("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;
+      int13map_node_t *handler_map = (int13map_node_t*) INT13H_TONEWADDR(&grub_drivemap_int13_mapstart);
+      DBG_PRINTF ("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;
+          DBG_PRINTF ("\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;
+      DBG_PRINTF ("\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 */
+      grub_uint32_t ivtentry = ((grub_uint32_t)handler_base) << 12; /* Segment address */
+      ivtentry |= (grub_uint16_t) INT13H_OFFSET(grub_drivemap_int13_handler);
+      DBG_PRINTF ("New int13 handler IVT pointer: %04x:%04x\n",
+                  (ivtentry >> 16) & 0x0ffff, ivtentry & 0x0ffff);
+      *ivtslot = ivtentry;
+      
+      return GRUB_ERR_NONE;
+    }
+}
+#undef INT13H_TONEWADDR
+#undef INT13H_REBASE
+#undef INT13H_OFFSET
+
+GRUB_MOD_INIT(drivemap)
+{
+  (void)mod;			/* To stop warning. */
+  grub_register_command ("drivemap", grub_cmd_drivemap, GRUB_COMMAND_FLAG_BOTH,
+			 "drivemap -s | -r | (hdX) newdrivenum", "Manage the BIOS drive mappings", options);
+  insthandler_hook = grub_loader_register_preboot (&drivemap_install_int13_handler, 1);
+}
+
+GRUB_MOD_FINI(drivemap)
+{
+  grub_loader_unregister_preboot (insthandler_hook);
+  insthandler_hook = 0;
+  grub_unregister_command ("drivemap");
+}
Index: commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file: commands/i386/pc/drivemap_int13h.S
diff -N commands/i386/pc/drivemap_int13h.S
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/i386/pc/drivemap_int13h.S	10 Jun 2008 20:02:19 -0000
@@ -0,0 +1,128 @@
+/*
+ *  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.
+ */
+
+/*
+ *  This is the area for all of the special variables.
+ */
+
+#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)  
+
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - 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. Pity we're not on
+ * amd64: rIP-relative addressing would make life 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
+#  push %es
+#  pushal
+#  mov $0, %di
+#  mov $0xb800, %bx
+#  mov %bx, %es
+#  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)
+  .space 0
+.code32 /* Copy stops here */
+VARIABLE(grub_drivemap_int13_size)
+  .word GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_size)
+#undef GRUB_DRIVEMAP_INT13H_OFFSET
Index: conf/i386-pc.rmk
===================================================================
RCS file: /sources/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.119
diff -u -p -r1.119 i386-pc.rmk
--- conf/i386-pc.rmk	30 May 2008 04:20:47 -0000	1.119
+++ conf/i386-pc.rmk	10 Jun 2008 20:02:26 -0000
@@ -152,7 +152,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod
 	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
+	aout.mod _bsd.mod bsd.mod drivemap.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -319,4 +319,11 @@ bsd_mod_SOURCES = loader/i386/bsd_normal
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+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
===================================================================
RCS file: /sources/grub/grub2/include/grub/loader.h,v
retrieving revision 1.9
diff -u -p -r1.9 loader.h
--- include/grub/loader.h	21 Jul 2007 23:32:22 -0000	1.9
+++ include/grub/loader.h	10 Jun 2008 20:02:26 -0000
@@ -37,6 +37,19 @@ void EXPORT_FUNC(grub_loader_set) (grub_
 /* Unset current loader, if any.  */
 void EXPORT_FUNC(grub_loader_unset) (void);
 
+typedef struct hooklist_node *grub_preboot_hookid;
+
+/* Register a function to be called before the boot hook. Returns an id that
+   can be later used to unregister the preboot (i.e. if module unloaded). If
+   abort_on_error is set, the boot sequence will abort if any of the registered
+   functions return anything else than GRUB_ERR_NONE */
+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: kern/loader.c
===================================================================
RCS file: /sources/grub/grub2/kern/loader.c,v
retrieving revision 1.9
diff -u -p -r1.9 loader.c
--- kern/loader.c	21 Jul 2007 23:32:26 -0000	1.9
+++ kern/loader.c	10 Jun 2008 20:02:27 -0000
@@ -61,11 +61,78 @@ grub_loader_unset(void)
   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)
+{
+  if (0 == hook)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hoook must not be null");
+      return 0;
+    }
+  grub_preboot_hookid 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)
+{
+  if (0 == id)
+    return;
+  grub_preboot_hookid entry = 0, search = preboot_hooks, previous = 0;
+  while (search)
+    {
+      if (search == id)
+        {
+          entry = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (entry) /* Found */
+    {
+      if (previous)
+        previous->next = entry->next;
+      else preboot_hooks = entry->next; /* Entry was head of list */
+      grub_free (entry);
+    }
+}
+
 grub_err_t
 grub_loader_boot (void)
 {
   if (! grub_loader_loaded)
     return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
+  
+  grub_preboot_hookid entry = preboot_hooks;
+  while (entry)
+    {
+      grub_err_t retVal = entry->hook();
+      if (retVal != GRUB_ERR_NONE && entry->abort_on_error)
+        return retVal;
+      entry = entry->next;
+    }
 
   if (grub_loader_noreturn)
     grub_machine_fini ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-10 20:09 [PATCH] Drivemap module Javier Martín
@ 2008-06-10 20:23 ` Vesa Jääskeläinen
  2008-06-10 21:31   ` Javier Martín
  2008-06-11 14:44 ` Marco Gerards
  1 sibling, 1 reply; 47+ messages in thread
From: Vesa Jääskeläinen @ 2008-06-10 20:23 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín wrote:
> Since the last posts in the "reimplementing the legacy map command"
> thread seemingly died silently, I'm sending the improved version of the
> patch here. If the death of the thread was intentional, please just tell
> me (either nicely or rudely) and I'll be off.

I'll answer this now and look at patch when I have better time.

Just keep in mind that while we appreciate new feature and bug fixes, we 
do not have all the time on earth to review them. Please be patient 
especially if you touch something that can be critical for stability and 
functionality of the software. We do not want to have feature on the 
system that no-one knows how it works, so we prefer to have 
understanding of the feature at hand and then that is gained then it is 
much easier to comment the patch and then diagnose if it has problems.

So please wait a bit, and lets see what kind of comments it gets.



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-10 20:23 ` Vesa Jääskeläinen
@ 2008-06-10 21:31   ` Javier Martín
  2008-06-12 20:31     ` Pavel Roskin
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-06-10 21:31 UTC (permalink / raw)
  To: The development of GRUB 2

El mar, 10-06-2008 a las 23:23 +0300, Vesa Jääskeläinen escribió:
> Javier Martín wrote:
> > Since the last posts in the "reimplementing the legacy map command"
> > thread seemingly died silently, I'm sending the improved version of the
> > patch here. If the death of the thread was intentional, please just tell
> > me (either nicely or rudely) and I'll be off.
> 
> I'll answer this now and look at patch when I have better time.
> 
> Just keep in mind that while we appreciate new feature and bug fixes, we 
> do not have all the time on earth to review them. Please be patient 
> especially if you touch something that can be critical for stability and 
> functionality of the software. We do not want to have feature on the 
> system that no-one knows how it works, so we prefer to have 
> understanding of the feature at hand and then that is gained then it is 
> much easier to comment the patch and then diagnose if it has problems.
> 
> So please wait a bit, and lets see what kind of comments it gets.
> 
Ok, I'm sorry and don't mean to be intrusive, I just thought the last
messages might have got lost between mail filters - it's happened to me.




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-10 20:09 [PATCH] Drivemap module Javier Martín
  2008-06-10 20:23 ` Vesa Jääskeläinen
@ 2008-06-11 14:44 ` Marco Gerards
  1 sibling, 0 replies; 47+ messages in thread
From: Marco Gerards @ 2008-06-11 14:44 UTC (permalink / raw)
  To: The development of GRUB 2

Hi Javier,

Javier Martín <lordhabbit@gmail.com> writes:

> Since the last posts in the "reimplementing the legacy map command"
> thread seemingly died silently, I'm sending the improved version of the
> patch here. If the death of the thread was intentional, please just tell
> me (either nicely or rudely) and I'll be off.

It's not intentional.  This feature is appreciated!  Please do this
scare you away...

The problem is that many developers are really busy at the moment
(unfortunately).  I expect to have more time to review patches in
about 2-3 weeks.

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-10 21:31   ` Javier Martín
@ 2008-06-12 20:31     ` Pavel Roskin
  2008-06-12 22:43       ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Pavel Roskin @ 2008-06-12 20:31 UTC (permalink / raw)
  To: The development of GRUB 2

On Tue, 2008-06-10 at 23:31 +0200, Javier Martín wrote:

> Ok, I'm sorry and don't mean to be intrusive, I just thought the last
> messages might have got lost between mail filters - it's happened to me.

Here's my very superficial review.

Please don't add any trailing whitespace.  Line in the patch that start
with a plus should not end with a space or a tab.

Please avoid camelCase names, such as bpaMemInKb and retVal.  Local
variables should generally be short, like "ret" and "bpa_mem".

Some strings are excessively long.  While there may be exception of the
80 column limit, I see a 118 character long line that can be trivially
wrapped.

The patch add a new warning:

commands/i386/pc/drivemap_int13h.S: Assembler messages:
commands/i386/pc/drivemap_int13h.S:124: Warning: .space repeat count is
zero, ignored

I'm not sure what you meant there.

I don't think using #undef is a good idea.  It's better to use macro
names that would never be reused accidentally and thus never need to be
undefined.

-- 
Regards,
Pavel Roskin



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-12 20:31     ` Pavel Roskin
@ 2008-06-12 22:43       ` Javier Martín
  2008-06-12 22:58         ` Colin D Bennett
  2008-06-13  1:37         ` Pavel Roskin
  0 siblings, 2 replies; 47+ messages in thread
From: Javier Martín @ 2008-06-12 22:43 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 2500 bytes --]

Hi there, I checked your comment, reread the GNU coding guidelines and
fixed some things (stated below the relevant parts of your message).
Here is the new version of the patch.

El jue, 12-06-2008 a las 16:31 -0400, Pavel Roskin escribió:
> Please don't add any trailing whitespace.  Line in the patch that start
> with a plus should not end with a space or a tab.
I could not find the line you were referring to. I could only find one
line starting with a plus (in install_int13_handler), but it does not
end with a space/tab. However, I did find one trailing tab after a
comment, and removed it.
> 
> Please avoid camelCase names, such as bpaMemInKb and retVal.  Local
> variables should generally be short, like "ret" and "bpa_mem".
> 
> Some strings are excessively long.  While there may be exception of the
> 80 column limit, I see a 118 character long line that can be trivially
> wrapped.
Both corrected. Sorry, I work on a wide screen and, tough I tried to
uphold the 80-char line rule, I'm not used to it. I think there was a
switch in gedit to graphically display the 80-char limit, but I can't
find it now...

OOC: do you know if there is a syntax highlighting mode for gas/x86
assembly code in gedit? Right now the program thinks the code is C, and
the only opcode it highlights is "int". Even a gas-only highlighting
(i.e. only the directives, not the opcodes) would be useful.
> 
> The patch add a new warning:
> 
> commands/i386/pc/drivemap_int13h.S: Assembler messages:
> commands/i386/pc/drivemap_int13h.S:124: Warning: .space repeat count is
> zero, ignored
> 
> I'm not sure what you meant there.
Removed. That was a leftover from the early stages when the "mappings"
area of the int13h handler had a maximum size of 8 entries. Then, I
decided that the space for the map was to be dynamically allocated, so I
moved it to the end of the block and set the .space directive to zero.
> 
> I don't think using #undef is a good idea.  It's better to use macro
> names that would never be reused accidentally and thus never need to be
> undefined.
Removed. Those are leftovers too, from the time the patch modified the
loader.h and loader.S files, which are public and #included, so I didn't
want to contaminate the namespace. Since Vesa's review, I moved the
relevant code to its own files inside the module, and thus it's no
longer public.

I also removed four lines from some old debug code that would have made
the int13h handler crash if the "unmap" section had been uncommented.

[-- Attachment #2: drivemap.patch --]
[-- Type: text/x-patch, Size: 22265 bytes --]

Index: commands/i386/pc/drivemap.c
===================================================================
RCS file: commands/i386/pc/drivemap.c
diff -N commands/i386/pc/drivemap.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/i386/pc/drivemap.c	12 Jun 2008 22:25:01 -0000
@@ -0,0 +1,351 @@
+/* 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/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>
+
+/* Uncomment the following line to enable debugging output */
+/* #define DRIVEMAP_DEBUG */
+
+#ifdef DRIVEMAP_DEBUG
+# define DBG_PRINTF(...) grub_printf(__VA_ARGS__)
+#else
+# define DBG_PRINTF(...)
+#endif
+
+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},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+/* ASSEMBLY SYMBOLS/VARS/FUNCS - start */
+
+/* realmode far ptr = 2 * 16b */
+extern grub_uint32_t EXPORT_VAR(grub_drivemap_int13_oldhandler);
+/* Size of the section to be copied */
+extern grub_uint16_t EXPORT_VAR(grub_drivemap_int13_size);
+
+/* NOT a typo - just need the symbol's address with &symbol */
+typedef void grub_symbol_t;
+extern grub_symbol_t EXPORT_VAR(grub_drivemap_int13_handler_base);
+extern grub_symbol_t EXPORT_VAR(grub_drivemap_int13_mapstart);
+
+void EXPORT_FUNC(grub_drivemap_int13_handler)(void);
+
+/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */
+
+static grub_preboot_hookid insthandler_hook = 0;
+
+typedef struct drivemap_node
+{
+  grub_uint8_t newdrive;
+  grub_uint8_t redirto;
+  struct drivemap_node *next;
+} drivemap_node_t;
+
+static drivemap_node_t *drivemap = 0;
+static grub_err_t install_int13_handler(void);
+
+static grub_err_t
+drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
+  /* Puts the specified mapping into the table, replacing an existing mapping
+   * for newdrive or adding a new one if required. */
+{
+  drivemap_node_t *mapping = 0, *search = drivemap;
+  while (search)
+    {
+      if (search->newdrive == newdrive)
+        {
+          mapping = search;
+          break;
+        }
+      search = search->next;
+    }
+
+  if (mapping)  /* There was a mapping already in place, modify it */
+    mapping->redirto = redirto;
+  else  /* Create a new mapping and add it to the head of the list */
+    {
+      mapping = grub_malloc (sizeof (drivemap_node_t));
+      if (!mapping)
+        return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate map entry");
+      mapping->newdrive = newdrive;
+      mapping->redirto = redirto;
+      mapping->next = drivemap;
+      drivemap = mapping;
+    }
+  return GRUB_ERR_NONE;
+}
+
+static void
+drivemap_remove (grub_uint8_t newdrive)
+  /* Removes the mapping for newdrive from the table. If there is no mapping,
+   * then this function behaves like a no-op on the map. */
+{
+  drivemap_node_t *mapping = 0, *search = drivemap, *previous = 0;
+  while (search)
+    {
+      if (search->newdrive == newdrive)
+        {
+          mapping = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (mapping) /* Found */
+    {
+      if (previous)
+        previous->next = mapping->next;
+      else drivemap = mapping->next; /* Entry was head of list */
+      grub_free (mapping);
+    }
+}
+
+static grub_err_t parse_biosdisk (const char *name, grub_uint8_t *disknum)
+{
+  if (!name) return GRUB_ERR_BAD_ARGUMENT;
+  if (name[0] == '(')
+    name++; /* Skip the first ( in (hd0) - disk_open wants just the name! */
+  grub_disk_t disk = grub_disk_open (name);
+  if (!disk)
+    return GRUB_ERR_UNKNOWN_DEVICE;
+  else
+    {
+      enum grub_disk_dev_id id = disk->dev->id;
+      if (disknum)
+        *disknum = disk->id;   /* Only valid, of course if it's a biosdisk */
+      grub_disk_close (disk);
+      return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ?
+          GRUB_ERR_BAD_DEVICE : GRUB_ERR_NONE;
+    }
+}
+
+static grub_err_t revparse_biosdisk(const grub_uint8_t dnum, const char **output)
+{
+  grub_err_t retval = GRUB_ERR_UNKNOWN_DEVICE;
+  auto int find (const char *name);
+  int find (const char *name)
+  {
+    grub_disk_t disk = grub_disk_open (name);
+    if (!disk)
+      return 0;
+    else
+      {
+        int found = 0;
+        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)
+          {
+            found = 1;
+            *output = name;
+            retval = GRUB_ERR_NONE;
+          }
+        grub_disk_close (disk);
+        return found;
+      }
+  }
+
+  grub_disk_dev_iterate (&find);
+  return retval;
+}
+
+static grub_err_t
+grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
+{
+  if (state[0].set) /* Show: list mappings */
+    {
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives. Drives that have had "
+                       "their slot assigned to another one and have not been "
+                       "themselves remapped will become inaccessible through "
+                       "the BIOS routines to the booted OS.\n\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].set) /* Reset: just delete all mappings */
+    {
+      if (drivemap)
+        {
+          drivemap_node_t *curnode = drivemap, *prevnode = 0;
+          while (curnode)
+            {
+              prevnode = curnode;
+              curnode = curnode->next;
+              grub_free (prevnode);
+            }
+          drivemap = 0;
+        }
+    }
+  else
+    {
+      if (argc != 2)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
+      grub_uint8_t mapfrom = 0;
+      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
+      if (err != GRUB_ERR_NONE)
+        return grub_error (err, "invalid disk or not managed by the BIOS");
+
+      grub_errno = GRUB_ERR_NONE;
+      unsigned long mapto = grub_strtoul (args[1], 0, 0);
+      if (grub_errno != GRUB_ERR_NONE)
+        return grub_error (grub_errno,
+                          "BIOS disk number must be between 0 and 255");
+      else if (mapto == mapfrom)  /* Reset to default */
+        {
+          DBG_PRINTF ("Removing the mapping for %s (%02x)", args[0], mapfrom);
+          drivemap_remove (mapfrom);
+        }
+      else  /* Map */
+        {
+          DBG_PRINTF ("Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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, since it accesses realmode structures by their absolute
+ * addresses, like the IVT at 0 or the BDA at 0x400 */
+static grub_err_t install_int13_handler(void)
+{
+  grub_size_t entries = 0;
+  drivemap_node_t *curentry = drivemap;
+  while (curentry)  /* Count entries to prepare a contiguous map block */
+    {
+      entries++;
+      curentry = curentry->next;
+    }
+  if (0 == entries)
+    {
+      DBG_PRINTF ("drivemap: No drives remapped, int13h handler not installed");
+      return GRUB_ERR_NONE;  /* No need to install the int13h handler */
+    }
+  else
+    {
+      DBG_PRINTF ("drivemap: Installing int13h handler...\n");
+      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
+      
+      /* Save the pointer to the old int13h handler */    
+      grub_drivemap_int13_oldhandler = *ivtslot;
+      DBG_PRINTF ("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:
+       * BDA offset 0x13 contains the top of such memory */
+      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
+      DBG_PRINTF ("Top of conventional memory: %u KiB\n", *bpa_freekb);
+      grub_size_t total_size = grub_drivemap_int13_size
+                            + (entries + 1) * sizeof(int13map_node_t);
+      grub_uint16_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");
+      DBG_PRINTF ("Payload is %u b long, reserving %u Kb\n", total_size,
+                  payload_sizekb);
+      *bpa_freekb -= payload_sizekb;
+
+      /* Copy int13h handler chunk to reserved area */
+      grub_uint8_t *handler_base = (grub_uint8_t*)(*bpa_freekb << 10);
+      DBG_PRINTF ("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;
+      int13map_node_t *handler_map = (int13map_node_t*)INT13H_TONEWADDR(&grub_drivemap_int13_mapstart);
+      DBG_PRINTF ("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;
+          DBG_PRINTF ("\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;
+      DBG_PRINTF ("\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 */
+      grub_uint32_t ivtentry = ((grub_uint32_t)handler_base) << 12; /* Segment address */
+      ivtentry |= (grub_uint16_t) INT13H_OFFSET(grub_drivemap_int13_handler);
+      DBG_PRINTF ("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;			/* To stop warning. */
+  grub_register_command ("drivemap", grub_cmd_drivemap, GRUB_COMMAND_FLAG_BOTH,
+			                   "drivemap -s | -r | (hdX) newdrivenum",
+                         "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 ("drivemap");
+}
Index: commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file: commands/i386/pc/drivemap_int13h.S
diff -N commands/i386/pc/drivemap_int13h.S
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/i386/pc/drivemap_int13h.S	12 Jun 2008 22:25:01 -0000
@@ -0,0 +1,122 @@
+/*
+ *  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.
+ */
+
+/*
+ *  This is the area for all of the special variables.
+ */
+
+#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)  
+
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - 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. Pity we're not on
+ * amd64: rIP-relative addressing would make life 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
===================================================================
RCS file: /sources/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.119
diff -u -r1.119 i386-pc.rmk
--- conf/i386-pc.rmk	30 May 2008 04:20:47 -0000	1.119
+++ conf/i386-pc.rmk	12 Jun 2008 22:25:11 -0000
@@ -152,7 +152,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
+	aout.mod _bsd.mod bsd.mod drivemap.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -319,4 +319,11 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+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
===================================================================
RCS file: /sources/grub/grub2/include/grub/loader.h,v
retrieving revision 1.9
diff -u -r1.9 loader.h
--- include/grub/loader.h	21 Jul 2007 23:32:22 -0000	1.9
+++ include/grub/loader.h	12 Jun 2008 22:25:11 -0000
@@ -37,6 +37,19 @@
 /* Unset current loader, if any.  */
 void EXPORT_FUNC(grub_loader_unset) (void);
 
+typedef struct hooklist_node *grub_preboot_hookid;
+
+/* Register a function to be called before the boot hook. Returns an id that
+   can be later used to unregister the preboot (i.e. if module unloaded). If
+   abort_on_error is set, the boot sequence will abort if any of the registered
+   functions return anything else than GRUB_ERR_NONE */
+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: kern/loader.c
===================================================================
RCS file: /sources/grub/grub2/kern/loader.c,v
retrieving revision 1.9
diff -u -r1.9 loader.c
--- kern/loader.c	21 Jul 2007 23:32:26 -0000	1.9
+++ kern/loader.c	12 Jun 2008 22:25:11 -0000
@@ -61,11 +61,78 @@
   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)
+{
+  if (0 == hook)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hoook must not be null");
+      return 0;
+    }
+  grub_preboot_hookid 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)
+{
+  if (0 == id)
+    return;
+  grub_preboot_hookid entry = 0, search = preboot_hooks, previous = 0;
+  while (search)
+    {
+      if (search == id)
+        {
+          entry = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (entry) /* Found */
+    {
+      if (previous)
+        previous->next = entry->next;
+      else preboot_hooks = entry->next; /* Entry was head of list */
+      grub_free (entry);
+    }
+}
+
 grub_err_t
 grub_loader_boot (void)
 {
   if (! grub_loader_loaded)
     return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
+  
+  grub_preboot_hookid entry = preboot_hooks;
+  while (entry)
+    {
+      grub_err_t retVal = entry->hook();
+      if (retVal != GRUB_ERR_NONE && entry->abort_on_error)
+        return retVal;
+      entry = entry->next;
+    }
 
   if (grub_loader_noreturn)
     grub_machine_fini ();

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-12 22:43       ` Javier Martín
@ 2008-06-12 22:58         ` Colin D Bennett
  2008-06-13  1:00           ` Pavel Roskin
  2008-06-13  1:37         ` Pavel Roskin
  1 sibling, 1 reply; 47+ messages in thread
From: Colin D Bennett @ 2008-06-12 22:58 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 1593 bytes --]

On Fri, 13 Jun 2008 00:43:31 +0200
Javier Martín <lordhabbit@gmail.com> wrote:

> Hi there, I checked your comment, reread the GNU coding guidelines and
> fixed some things (stated below the relevant parts of your message).
> Here is the new version of the patch.
> 
> El jue, 12-06-2008 a las 16:31 -0400, Pavel Roskin escribió:
> > Please don't add any trailing whitespace.  Line in the patch that
> > start with a plus should not end with a space or a tab.
> I could not find the line you were referring to. I could only find one
> line starting with a plus (in install_int13_handler), but it does not
> end with a space/tab. However, I did find one trailing tab after a
> comment, and removed it.

I think Pavel meant the lines added by the patch, i.e., beginning with
a plus in the patch, not in the original source.  Here's an
illustration to help find and visualize the invisible trailing
whitespace:

cdb@gamma ~ $ perl -ne '/^\+.* +$/ and y/ \t/_~/ and
print' /home/cdb/drivemap.patch
+______else_if_(mapto_==_mapfrom)__/*_Reset_to_default_*/______ +______
+______/*_Save_the_pointer_to_the_old_int13h_handler_*/____
+______
+VARIABLE(grub_drivemap_int13_handler_base)__
+__
+__push_%bp__
+___________(grub_err_t_(*hook)_(void),_int_abort_on_error);_
+__

I highly recommend running 

  indent --no-tabs

on your source code, as long as it doesn't cause too many spurious
changes to pre-existing code.  This should take care of most formatting
inconsistencies and frees you a little but from concerning yourself
with trivial details.

Colin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-12 22:58         ` Colin D Bennett
@ 2008-06-13  1:00           ` Pavel Roskin
  2008-06-13  4:09             ` Colin D Bennett
  0 siblings, 1 reply; 47+ messages in thread
From: Pavel Roskin @ 2008-06-13  1:00 UTC (permalink / raw)
  To: The development of GRUB 2

On Thu, 2008-06-12 at 15:58 -0700, Colin D Bennett wrote:

> I think Pavel meant the lines added by the patch, i.e., beginning with
> a plus in the patch, not in the original source.

Yes, I meant added lines.  There is a lot of code that needs
reformatting, but the patch shouldn't add more to that.

> I highly recommend running 
> 
>   indent --no-tabs

Why no tabs?  Tabs are heavily used in the code.  GNU indent is supposed
to default to GNU Coding Standards, and it uses tabs.

> on your source code, as long as it doesn't cause too many spurious
> changes to pre-existing code.

It will.

> This should take care of most formatting
> inconsistencies and frees you a little but from concerning yourself
> with trivial details.

We can use a script to re-indent everything.  Then new patches could be
easily run through the same script.  Otherwise, it's not going to be
simple.

-- 
Regards,
Pavel Roskin



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-12 22:43       ` Javier Martín
  2008-06-12 22:58         ` Colin D Bennett
@ 2008-06-13  1:37         ` Pavel Roskin
  2008-06-13  2:29           ` Javier Martín
  1 sibling, 1 reply; 47+ messages in thread
From: Pavel Roskin @ 2008-06-13  1:37 UTC (permalink / raw)
  To: The development of GRUB 2

On Fri, 2008-06-13 at 00:43 +0200, Javier Martín wrote:

> El jue, 12-06-2008 a las 16:31 -0400, Pavel Roskin escribió:
> > Please don't add any trailing whitespace.  Line in the patch that start
> > with a plus should not end with a space or a tab.
> I could not find the line you were referring to. I could only find one
> line starting with a plus (in install_int13_handler), but it does not
> end with a space/tab. However, I did find one trailing tab after a
> comment, and removed it.

I mean the lines your patch adds.

> > Please avoid camelCase names, such as bpaMemInKb and retVal.  Local
> > variables should generally be short, like "ret" and "bpa_mem".
> > 
> > Some strings are excessively long.  While there may be exception of the
> > 80 column limit, I see a 118 character long line that can be trivially
> > wrapped.
> Both corrected.

retVal is still there.  Please call it "ret", it's the traditional name
of the variable to be returned.

>  Sorry, I work on a wide screen and, tough I tried to
> uphold the 80-char line rule, I'm not used to it. I think there was a
> switch in gedit to graphically display the 80-char limit, but I can't
> find it now...

Edit->Preferences->View->Right Margin

> OOC: do you know if there is a syntax highlighting mode for gas/x86
> assembly code in gedit? Right now the program thinks the code is C, and
> the only opcode it highlights is "int". Even a gas-only highlighting
> (i.e. only the directives, not the opcodes) would be useful.

There are some possibly useful attachments here:
http://bugzilla.gnome.org/show_bug.cgi?id=152267

I hope that others will comment on the patch contents.

-- 
Regards,
Pavel Roskin



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-13  1:37         ` Pavel Roskin
@ 2008-06-13  2:29           ` Javier Martín
  0 siblings, 0 replies; 47+ messages in thread
From: Javier Martín @ 2008-06-13  2:29 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 1645 bytes --]

El jue, 12-06-2008 a las 21:37 -0400, Pavel Roskin escribió: 
> I mean the lines your patch adds.
> (...)
> retVal is still there.  Please call it "ret", it's the traditional name
> of the variable to be returned.
Ok, both of this completely corrected now thanks to Colin's script and
the dear "find/replace" functionality that I miss so much when I'm
handwriting. Didn't realize that retVal was in the "public" file
loader.c. I also made other aesthetic changes based on the output of
`indent', though it seems to distort "continuation" lines in C and
completely borks assembly code. It is also overzealous wrt the 80-char
line rule, breaking many lines with more than 75 chars and seriously
unnerving me - particularly with its strange resetting of nested "if"
blocks to the first indenting level.
> 
> >  Sorry, I work on a wide screen and, tough I tried to
> > uphold the 80-char line rule, I'm not used to it. I think there was a
> > switch in gedit to graphically display the 80-char limit, but I can't
> > find it now...
> 
> Edit->Preferences->View->Right Margin
Thanks! I knew I had seen it before.
> 
> I hope that others will comment on the patch contents.
lol... So do I, because until now we've just been discussing its
aesthetic aspects. I think we don't really need the (cleaned) version of
the patch to discuss its functionality, so I won't waste bandwith
sending updated versions of it if the only change is two tabs and one
variable (unless you devs wanted to commit it right away ^^). Well, I'm
going to call this a night, since it's 4:26 AM and I have an exam
tomorrow... Goodbye!




[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-06-13  1:00           ` Pavel Roskin
@ 2008-06-13  4:09             ` Colin D Bennett
  0 siblings, 0 replies; 47+ messages in thread
From: Colin D Bennett @ 2008-06-13  4:09 UTC (permalink / raw)
  To: grub-devel

On Thu, 12 Jun 2008 21:00:21 -0400
Pavel Roskin <proski@gnu.org> wrote:
> On Thu, 2008-06-12 at 15:58 -0700, Colin D Bennett wrote:
> > I highly recommend running 
> > 
> >   indent --no-tabs
> 
> Why no tabs?  Tabs are heavily used in the code.  GNU indent is
> supposed to default to GNU Coding Standards, and it uses tabs.

The video module code uses only spaces for indentation.
Since I've been working mostly on the video module, so far, I was
misled to believe that spaces are the preferred GRUB style.

I checked all the C files in GRUB2 CVS and found that you are right,
tabs are heavily used, except in the code that I have been focused
on.   The following C files do not contains any tabs:

./kern/i386/efi/init.c
./commands/videotest.c
./util/powerpc/ieee1275/misc.c
./util/i386/pc/misc.c
./util/i386/get_disk_name.c
./util/ieee1275/get_disk_name.c
./util/lvm.c
./video/video.c
./video/i386/pc/vbeutil.c
./video/i386/pc/vbefill.c
./video/i386/pc/vbeblit.c
./video/i386/pc/vbe.c
./video/readers/tga.c
./video/bitmap.c
./disk/ieee1275/nand.c

> > on your source code, as long as it doesn't cause too many spurious
> > changes to pre-existing code.
> 
> It will.

This depends on the file.  Running format (with the same choice of
tabs+spaces or spaces-only for indentation) on a GRUB source file
usually results in minimal changes, in my experience.

> > This should take care of most formatting
> > inconsistencies and frees you a little but from concerning yourself
> > with trivial details.
> 
> We can use a script to re-indent everything.  Then new patches could
> be easily run through the same script.  Otherwise, it's not going to
> be simple.

Interesting idea!

Anyway, my point is not to get into the religious war of spaces vs.
tabs in indentation, since I am comfortable with both, although I think
that mixing spaces and tabs together (using TAB characters as a
substitute for 8 spaces) negates the benefits of both approaches.  But
that's all I'll say on that.

I will indent code that I work on with the same style that it already
has, so that my patches are clean and contain, in general, only changes
related to actual features that the patch adds.

Colin



^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH] Drivemap module
@ 2008-07-04  2:12 Javier Martín
  2008-07-05 11:04 ` Marco Gerards
  2008-07-20 19:40 ` Marco Gerards
  0 siblings, 2 replies; 47+ messages in thread
From: Javier Martín @ 2008-07-04  2:12 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 911 bytes --]

Just an updated version of the patch that adds support for device-like
names instead of raw BIOS disk numbers, i.e. this is now supported:
	grub> drivemap (hd0) (hd1)
In addition to the already supported:
	grub> drivemap (hd0) 0x81
The effect is the same: the second BIOS hard drive will map to (hd0)
through the installed int13h routine. The new syntax does not require
the target device name to exist (hd1 need not exist in my example), and
the parsing is very simple: it accepts names like (fdN) and (hdN) with
and without parenthesis, and with N ranging from 0 to 127, thus allowing
the full 0x00-0xFF range even though most BIOS-probing OSes don't bother
going any further than fd7 and hd7 respectively.

For newcomers, full info on the patch is available on the list archives
- it was proposed on June and its discussion deferred for "two or three
weeks" because the developers were busy.

[-- Attachment #1.2: drivemap.patch --]
[-- Type: text/x-patch, Size: 24356 bytes --]

Index: commands/i386/pc/drivemap.c
===================================================================
RCS file: commands/i386/pc/drivemap.c
diff -N commands/i386/pc/drivemap.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/i386/pc/drivemap.c	2 Jul 2008 01:12:36 -0000
@@ -0,0 +1,391 @@
+/* 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/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>
+
+/* Uncomment the following line to enable debugging output */
+/* #define DRIVEMAP_DEBUG */
+
+#ifdef DRIVEMAP_DEBUG
+# define DBG_PRINTF(...) grub_printf(__VA_ARGS__)
+#else
+# define DBG_PRINTF(...)
+#endif
+
+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},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* ASSEMBLY SYMBOLS/VARS/FUNCS - start */
+
+/* realmode far ptr = 2 * 16b */
+extern grub_uint32_t EXPORT_VAR (grub_drivemap_int13_oldhandler);
+/* Size of the section to be copied */
+extern grub_uint16_t EXPORT_VAR (grub_drivemap_int13_size);
+
+/* NOT a typo - just need the symbol's address with &symbol */
+typedef void grub_symbol_t;
+extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_handler_base);
+extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_mapstart);
+
+void EXPORT_FUNC (grub_drivemap_int13_handler) (void);
+
+/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */
+
+static grub_preboot_hookid insthandler_hook = 0;
+
+typedef struct drivemap_node
+{
+  grub_uint8_t newdrive;
+  grub_uint8_t redirto;
+  struct drivemap_node *next;
+} drivemap_node_t;
+
+static drivemap_node_t *drivemap = 0;
+static grub_err_t install_int13_handler (void);
+
+static grub_err_t
+drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
+  /* Puts the specified mapping into the table, replacing an existing mapping
+   * for newdrive or adding a new one if required. */
+{
+  drivemap_node_t *mapping = 0, *search = drivemap;
+  while (search)
+    {
+      if (search->newdrive == newdrive)
+        {
+          mapping = search;
+          break;
+        }
+      search = search->next;
+    }
+
+  if (mapping)  /* There was a mapping already in place, modify it */
+    mapping->redirto = redirto;
+  else  /* Create a new mapping and add it to the head of the list */
+    {
+      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;
+}
+
+static void
+drivemap_remove (grub_uint8_t newdrive)
+  /* Removes the mapping for newdrive from the table. If there is no mapping,
+   * then this function behaves like a no-op on the map. */
+{
+  drivemap_node_t *mapping = 0, *search = drivemap, *previous = 0;
+  while (search)
+    {
+      if (search->newdrive == newdrive)
+        {
+          mapping = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (mapping) /* Found */
+    {
+      if (previous)
+        previous->next = mapping->next;
+      else drivemap = mapping->next; /* Entry was head of list */
+      grub_free (mapping);
+    }
+}
+
+static grub_err_t
+parse_biosdisk (const char *name, grub_uint8_t *disknum)
+{
+  if (!name) return GRUB_ERR_BAD_ARGUMENT;
+  if (*name == '(')
+    name++; /* Skip the first ( in (hd0) - disk_open wants just the name! */
+  grub_disk_t disk = grub_disk_open (name);
+  if (!disk)
+    return GRUB_ERR_UNKNOWN_DEVICE;
+  else
+    {
+      enum grub_disk_dev_id id = disk->dev->id;
+      if (disknum)
+        *disknum = disk->id;   /* Only valid, of course if it's a biosdisk */
+      grub_disk_close (disk);
+      return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ?
+          GRUB_ERR_BAD_DEVICE : GRUB_ERR_NONE;
+    }
+}
+
+static grub_err_t
+revparse_biosdisk(const grub_uint8_t dnum, const char **output)
+{
+  grub_err_t ret = GRUB_ERR_UNKNOWN_DEVICE;
+  auto int find (const char *name);
+  int find (const char *name)
+  {
+    grub_disk_t disk = grub_disk_open (name);
+    if (!disk)
+      return 0;
+    else
+      {
+        int found = 0;
+        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)
+          {
+            found = 1;
+            if (output)
+              *output = name;
+            ret = GRUB_ERR_NONE;
+          }
+        grub_disk_close (disk);
+        return found;
+      }
+  }
+
+  grub_disk_dev_iterate (&find);
+  return ret;
+}
+
+static grub_err_t
+tryparse_diskstring (const char *str, grub_uint8_t *output)
+{
+  if (!str) return GRUB_ERR_BAD_ARGUMENT;
+  if (*str == '(')
+    str++;  /* Skip opening paren in order to allow both (hd0) and hd0 */
+  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, &str, 0);
+      if (grub_errno != GRUB_ERR_NONE || drivenum > 127)
+        return GRUB_ERR_BAD_ARGUMENT; /* Could not convert, or num too high for BIOS */
+      else
+       {
+          bios_num |= drivenum;
+          if (output)
+            *output = bios_num;
+          return GRUB_ERR_NONE;
+        }
+    }
+  else return GRUB_ERR_BAD_ARGUMENT;
+}
+
+static grub_err_t
+grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
+{
+  if (state[0].set) /* Show: list mappings */
+    {
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives. Drives that have had "
+                       "their slot assigned to another one and have not been "
+                       "themselves remapped will become inaccessible through "
+                       "the BIOS routines to the booted OS.\n\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].set) /* Reset: just delete all mappings */
+    {
+      if (drivemap)
+        {
+          drivemap_node_t *curnode = drivemap, *prevnode = 0;
+          while (curnode)
+            {
+              prevnode = curnode;
+              curnode = curnode->next;
+              grub_free (prevnode);
+            }
+          drivemap = 0;
+        }
+    }
+  else
+    {
+      if (argc != 2)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
+      grub_uint8_t mapfrom = 0;
+      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
+      if (err != GRUB_ERR_NONE)
+        return grub_error (err, "invalid disk or not managed by the BIOS");
+
+      grub_uint8_t mapto = 0xFF;
+      err = tryparse_diskstring (args[1], &mapto);
+      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then? */
+        {    
+          grub_errno = GRUB_ERR_NONE;
+          unsigned long num = grub_strtoul (args[1], 0, 0);
+          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high */
+            return grub_error (grub_errno,
+                              "Target specifier must be of the form (fdN) or "
+                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
+                              "number between 0 and 255");
+          else mapto = (grub_uint8_t)num;
+        }
+      
+      if (mapto == mapfrom)  /* Reset to default */
+        {
+          DBG_PRINTF ("Removing the mapping for %s (%02x)", args[0], mapfrom);
+          drivemap_remove (mapfrom);
+        }
+      else  /* Map */
+        {
+          DBG_PRINTF ("Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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, since it accesses realmode structures by their absolute
+ * addresses, like the IVT at 0 or the BDA at 0x400 */
+static grub_err_t
+install_int13_handler (void)
+{
+  grub_size_t entries = 0;
+  drivemap_node_t *curentry = drivemap;
+  while (curentry)  /* Count entries to prepare a contiguous map block */
+    {
+      entries++;
+      curentry = curentry->next;
+    }
+  if (0 == entries)
+    {
+      DBG_PRINTF ("drivemap: No drives marked as remapped, installation of"
+                  "an int13h handler is not required.");
+      return GRUB_ERR_NONE;  /* No need to install the int13h handler */
+    }
+  else
+    {
+      DBG_PRINTF ("drivemap: Installing int13h handler...\n");
+      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
+      
+      /* Save the pointer to the old int13h handler */
+      grub_drivemap_int13_oldhandler = *ivtslot;
+      DBG_PRINTF ("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:
+       * BDA offset 0x13 contains the top of such memory */
+      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
+      DBG_PRINTF ("Top of conventional memory: %u KiB\n", *bpa_freekb);
+      grub_size_t total_size = grub_drivemap_int13_size
+                            + (entries + 1) * sizeof(int13map_node_t);
+      grub_uint16_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");
+      DBG_PRINTF ("Payload is %u b long, reserving %u Kb\n", total_size,
+                  payload_sizekb);
+      *bpa_freekb -= payload_sizekb;
+
+      /* Copy int13h handler chunk to reserved area */
+      grub_uint8_t *handler_base = (grub_uint8_t*)(*bpa_freekb << 10);
+      DBG_PRINTF ("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;
+      int13map_node_t *handler_map = (int13map_node_t*)
+                      INT13H_TONEWADDR (&grub_drivemap_int13_mapstart);
+      DBG_PRINTF ("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;
+          DBG_PRINTF ("\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;
+      DBG_PRINTF ("\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 */
+      grub_uint32_t ivtentry = ((grub_uint32_t)handler_base) << 12; /* Segment address */
+      ivtentry |= (grub_uint16_t) INT13H_OFFSET(grub_drivemap_int13_handler);
+      DBG_PRINTF ("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;			/* To stop warning. */
+  grub_register_command ("drivemap", grub_cmd_drivemap,
+                         GRUB_COMMAND_FLAG_BOTH,
+			                   "drivemap -s | -r | (hdX) newdrivenum",
+                         "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 ("drivemap");
+}
Index: commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file: commands/i386/pc/drivemap_int13h.S
diff -N commands/i386/pc/drivemap_int13h.S
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/i386/pc/drivemap_int13h.S	2 Jul 2008 01:12:36 -0000
@@ -0,0 +1,122 @@
+/*
+ *  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.
+ */
+
+/*
+ *  This is the area for all of the special variables.
+ */
+
+#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)
+
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - 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. Pity we're not on
+ * amd64: rIP-relative addressing would make life 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
===================================================================
RCS file: /sources/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.120
diff -u -r1.120 i386-pc.rmk
--- conf/i386-pc.rmk	19 Jun 2008 05:14:15 -0000	1.120
+++ conf/i386-pc.rmk	2 Jul 2008 01:12:44 -0000
@@ -153,7 +153,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
+	aout.mod _bsd.mod bsd.mod drivemap.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -320,4 +320,11 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+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
===================================================================
RCS file: /sources/grub/grub2/include/grub/loader.h,v
retrieving revision 1.9
diff -u -r1.9 loader.h
--- include/grub/loader.h	21 Jul 2007 23:32:22 -0000	1.9
+++ include/grub/loader.h	2 Jul 2008 01:12:45 -0000
@@ -37,6 +37,19 @@
 /* Unset current loader, if any.  */
 void EXPORT_FUNC(grub_loader_unset) (void);
 
+typedef struct hooklist_node *grub_preboot_hookid;
+
+/* Register a function to be called before the boot hook. Returns an id that
+   can be later used to unregister the preboot (i.e. if module unloaded). If
+   abort_on_error is set, the boot sequence will abort if any of the registered
+   functions return anything else than GRUB_ERR_NONE */
+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: kern/loader.c
===================================================================
RCS file: /sources/grub/grub2/kern/loader.c,v
retrieving revision 1.9
diff -u -r1.9 loader.c
--- kern/loader.c	21 Jul 2007 23:32:26 -0000	1.9
+++ kern/loader.c	2 Jul 2008 01:12:45 -0000
@@ -61,11 +61,78 @@
   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)
+{
+  if (0 == hook)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hoook must not be null");
+      return 0;
+    }
+  grub_preboot_hookid 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)
+{
+  if (0 == id)
+    return;
+  grub_preboot_hookid entry = 0, search = preboot_hooks, previous = 0;
+  while (search)
+    {
+      if (search == id)
+        {
+          entry = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (entry) /* Found */
+    {
+      if (previous)
+        previous->next = entry->next;
+      else preboot_hooks = entry->next; /* Entry was head of list */
+      grub_free (entry);
+    }
+}
+
 grub_err_t
 grub_loader_boot (void)
 {
   if (! grub_loader_loaded)
     return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
+  
+  grub_preboot_hookid entry = preboot_hooks;
+  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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-04  2:12 Javier Martín
@ 2008-07-05 11:04 ` Marco Gerards
  2008-07-16 15:39   ` Javier Martín
  2008-07-20 19:40 ` Marco Gerards
  1 sibling, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-07-05 11:04 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín <lordhabbit@gmail.com> writes:

> Just an updated version of the patch that adds support for device-like
> names instead of raw BIOS disk numbers, i.e. this is now supported:
> 	grub> drivemap (hd0) (hd1)
> In addition to the already supported:
> 	grub> drivemap (hd0) 0x81
> The effect is the same: the second BIOS hard drive will map to (hd0)
> through the installed int13h routine. The new syntax does not require
> the target device name to exist (hd1 need not exist in my example), and
> the parsing is very simple: it accepts names like (fdN) and (hdN) with
> and without parenthesis, and with N ranging from 0 to 127, thus allowing
> the full 0x00-0xFF range even though most BIOS-probing OSes don't bother
> going any further than fd7 and hd7 respectively.

Great!  Can you please send in a changelog entry?

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-05 11:04 ` Marco Gerards
@ 2008-07-16 15:39   ` Javier Martín
  0 siblings, 0 replies; 47+ messages in thread
From: Javier Martín @ 2008-07-16 15:39 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 1975 bytes --]

El sáb, 05-07-2008 a las 13:04 +0200, Marco Gerards escribió:
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> > Just an updated version of the patch that adds support for device-like
> > names instead of raw BIOS disk numbers, i.e. this is now supported:
> > 	grub> drivemap (hd0) (hd1)
> > In addition to the already supported:
> > 	grub> drivemap (hd0) 0x81
> > The effect is the same: the second BIOS hard drive will map to (hd0)
> > through the installed int13h routine. The new syntax does not require
> > the target device name to exist (hd1 need not exist in my example), and
> > the parsing is very simple: it accepts names like (fdN) and (hdN) with
> > and without parenthesis, and with N ranging from 0 to 127, thus allowing
> > the full 0x00-0xFF range even though most BIOS-probing OSes don't bother
> > going any further than fd7 and hd7 respectively.
> 
> Great!  Can you please send in a changelog entry?
> 
> --
> Marco
What about this:
	* commands/i386/pc/drivemap.c : New file, main part of the new
	drivemap module allowing BIOS drive remapping not unlike the
	legacy "map" command. This allows to boot OSes with boot-time
	dependencies on the particular ordering of BIOS drives or
	trusting their own to be 0x80, like Windows XP, with
	non-standard boot configurations.
	* commands/i386/pc/drivemap_int13h.S : New file, INT 13h handler
	for the drivemap module. Installed as a TSR routine by
	drivemap.c, performs the actual redirection of BIOS drives.
	* conf/i386-pc.rmk : Added the new module
	* include/grub/loader.h : Added a "just-before-boot" callback
	infrastructure used by drivemap.mod to install the INT13 handler
	only when the "boot" command has been issued.
	* kern/loader.c : Implement the preboot-hook described

By the way, I sent the data to the "assign" mail as you instructed me
to, and I've been told I'll receive some GNU copyright assignment
documents to sign-and-return in a few days.

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-04  2:12 Javier Martín
  2008-07-05 11:04 ` Marco Gerards
@ 2008-07-20 19:40 ` Marco Gerards
  2008-07-21  0:55   ` Javier Martín
  1 sibling, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-07-20 19:40 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín <lordhabbit@gmail.com> writes:

> Just an updated version of the patch that adds support for device-like
> names instead of raw BIOS disk numbers, i.e. this is now supported:
> 	grub> drivemap (hd0) (hd1)
> In addition to the already supported:
> 	grub> drivemap (hd0) 0x81
> The effect is the same: the second BIOS hard drive will map to (hd0)
> through the installed int13h routine. The new syntax does not require
> the target device name to exist (hd1 need not exist in my example), and
> the parsing is very simple: it accepts names like (fdN) and (hdN) with
> and without parenthesis, and with N ranging from 0 to 127, thus allowing
> the full 0x00-0xFF range even though most BIOS-probing OSes don't bother
> going any further than fd7 and hd7 respectively.

Did you use code from other people or projects?

> For newcomers, full info on the patch is available on the list archives
> - it was proposed on June and its discussion deferred for "two or three
> weeks" because the developers were busy.


I have copied the changelog entry from your other e-mail:

	* commands/i386/pc/drivemap.c : New file, main part of the new
	drivemap module allowing BIOS drive remapping not unlike the
	legacy "map" command. This allows to boot OSes with boot-time
	dependencies on the particular ordering of BIOS drives or
	trusting their own to be 0x80, like Windows XP, with
	non-standard boot configurations.

"New file." would be sufficient

	* commands/i386/pc/drivemap_int13h.S : New file, INT 13h handler
	for the drivemap module. Installed as a TSR routine by
	drivemap.c, performs the actual redirection of BIOS drives.

Same here.

	* conf/i386-pc.rmk : Added the new module

Please say which variables you added in this file.  You can find some
examples on how to do this in the ChangeLog file.

	* include/grub/loader.h : Added a "just-before-boot" callback
	infrastructure used by drivemap.mod to install the INT13 handler
	only when the "boot" command has been issued.

Please describe changes, not effects.  So which prototypes and macros
did you add?

	* kern/loader.c : Implement the preboot-hook described

Which functions did you change and how?  Please describe actual changes.

The header is missing, please include it.  Also newlines between the
files make it easier to read.


Here follows a review.  Sorry I kept you waiting for this long, this
feature and your work is really appreciated!  Perhaps I can spot some
more problems after you fixed it and supplied an updated changelog
entry.  There are quite some comments, but please do not let this
demotivate you, it is mainly coding style related :-)

> Index: commands/i386/pc/drivemap.c
> ===================================================================
> RCS file: commands/i386/pc/drivemap.c
> diff -N commands/i386/pc/drivemap.c
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ commands/i386/pc/drivemap.c	2 Jul 2008 01:12:36 -0000
> @@ -0,0 +1,391 @@
> +/* 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/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>
> +
> +/* Uncomment the following line to enable debugging output */
> +/* #define DRIVEMAP_DEBUG */
> +
> +#ifdef DRIVEMAP_DEBUG
> +# define DBG_PRINTF(...) grub_printf(__VA_ARGS__)
> +#else
> +# define DBG_PRINTF(...)
> +#endif

Please use the grub_dprintf infrastructure.

> +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},
> +  {0, 0, 0, 0, 0, 0}
> +};
> +
> +/* ASSEMBLY SYMBOLS/VARS/FUNCS - start */

Useless comment

> +/* realmode far ptr = 2 * 16b */
> +extern grub_uint32_t EXPORT_VAR (grub_drivemap_int13_oldhandler);
> +/* Size of the section to be copied */
> +extern grub_uint16_t EXPORT_VAR (grub_drivemap_int13_size);
> +
> +/* NOT a typo - just need the symbol's address with &symbol */
> +typedef void grub_symbol_t;
> +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_handler_base);
> +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_mapstart);

Please export stuff in header files, that's the normal practise in
this file as well, right?  What's not a typo?

> +void EXPORT_FUNC (grub_drivemap_int13_handler) (void);
> +
> +/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */

Like before.

> +static grub_preboot_hookid insthandler_hook = 0;

You can leave the "= 0" away.

> +typedef struct drivemap_node
> +{
> +  grub_uint8_t newdrive;
> +  grub_uint8_t redirto;
> +  struct drivemap_node *next;
> +} drivemap_node_t;
> +
> +static drivemap_node_t *drivemap = 0;

Same here :-)

> +static grub_err_t install_int13_handler (void);
> +
> +static grub_err_t
> +drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
> +  /* Puts the specified mapping into the table, replacing an existing mapping
> +   * for newdrive or adding a new one if required. */

Please place the comments before the function.

> +{
> +  drivemap_node_t *mapping = 0, *search = drivemap;
> +  while (search)
> +    {
> +      if (search->newdrive == newdrive)
> +        {
> +          mapping = search;
> +          break;
> +        }
> +      search = search->next;
> +    }
> +  if (mapping)  /* There was a mapping already in place, modify it */
> +    mapping->redirto = redirto;

Please place the comment before the if statement.  Can you format the
comment such that it begins with a capital and ends with ".  */" (two
spaces)?  Can you do this for the other comments you added as well?

> +  else  /* Create a new mapping and add it to the head of the list */
> +    {

I would move the comment inside the braces.

> +      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;
> +}
> +
> +static void
> +drivemap_remove (grub_uint8_t newdrive)
> +  /* Removes the mapping for newdrive from the table. If there is no mapping,
> +   * then this function behaves like a no-op on the map. */

Can you move the comments to before the function?  Please do not place
*'s on each line.  Without the second * it would be fine.  After a
".", please insert two spaces.

> +{
> +  drivemap_node_t *mapping = 0, *search = drivemap, *previous = 0;

Please define one variable per line, can you break this down to 3
lines?

> +  while (search)
> +    {
> +      if (search->newdrive == newdrive)
> +        {
> +          mapping = search;
> +          break;
> +        }
> +      previous = search;
> +      search = search->next;
> +    }
> +  if (mapping) /* Found */
> +    {
> +      if (previous)
> +        previous->next = mapping->next;
> +      else drivemap = mapping->next; /* Entry was head of list */

Please place the stuff after "else" on a new line.

> +      grub_free (mapping);
> +    }
> +}
> +
> +static grub_err_t
> +parse_biosdisk (const char *name, grub_uint8_t *disknum)
> +{
> +  if (!name) return GRUB_ERR_BAD_ARGUMENT;

Same here.  Please do not just return GRUB_ERR_BAD_ARGUMENT, use
grub_error so you can also define a string for the error.

> +  if (*name == '(')
> +    name++; /* Skip the first ( in (hd0) - disk_open wants just the name! */
> +  grub_disk_t disk = grub_disk_open (name);

Although mixed declarations and code are allowed, I personally would
avoid this.  It doesn't make the code easier to read, I think.

> +  if (!disk)
> +    return GRUB_ERR_UNKNOWN_DEVICE;

Use grub_error.  Or actually return grub_errno, since if
grub_disk_open failed it would have set this.

> +  else
> +    {
> +      enum grub_disk_dev_id id = disk->dev->id;
> +      if (disknum)
> +        *disknum = disk->id;   /* Only valid, of course if it's a biosdisk */

Please place the comment before the if.

> +      grub_disk_close (disk);
> +      return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ?
> +          GRUB_ERR_BAD_DEVICE : GRUB_ERR_NONE;

Please use grub_error.

> +    }
> +}
> +
> +static grub_err_t
> +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
> +{
> +  grub_err_t ret = GRUB_ERR_UNKNOWN_DEVICE;
> +  auto int find (const char *name);
> +  int find (const char *name)
> +  {
> +    grub_disk_t disk = grub_disk_open (name);
> +    if (!disk)
> +      return 0;
> +    else
> +      {
> +        int found = 0;
> +        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)


This is not rally wrong, but doesn't feel right.  I would use:

disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID

I know, I am a pain in the ass right now :P

> +          {
> +            found = 1;
> +            if (output)
> +              *output = name;
> +            ret = GRUB_ERR_NONE;
> +          }
> +        grub_disk_close (disk);
> +        return found;
> +      }
> +  }
> +
> +  grub_disk_dev_iterate (&find);

No need for the &.  I even wonder if it would work this way...

> +  return ret;
> +}
> +
> +static grub_err_t
> +tryparse_diskstring (const char *str, grub_uint8_t *output)
> +{
> +  if (!str) return GRUB_ERR_BAD_ARGUMENT;
> +  if (*str == '(')
> +    str++;  /* Skip opening paren in order to allow both (hd0) and hd0 */
> +  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, &str, 0);
> +      if (grub_errno != GRUB_ERR_NONE || drivenum > 127)
> +        return GRUB_ERR_BAD_ARGUMENT; /* Could not convert, or num too high for BIOS */

Use grub_error

> +      else
> +       {
> +          bios_num |= drivenum;
> +          if (output)
> +            *output = bios_num;
> +          return GRUB_ERR_NONE;
> +        }
> +    }
> +  else return GRUB_ERR_BAD_ARGUMENT;
> +}
> +
> +static grub_err_t
> +grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
> +{
> +  if (state[0].set) /* Show: list mappings */

Like the comments before

> +    {
> +      if (!drivemap)
> +        grub_printf ("No drives have been remapped");
> +      else
> +        {
> +          grub_printf ("Showing only remapped drives. Drives that have had "
> +                       "their slot assigned to another one and have not been "
> +                       "themselves remapped will become inaccessible through "
> +                       "the BIOS routines to the booted OS.\n\n");

I do not think this message is immediatly clear to the user.  Perhaps
we should even leave this out and say something in the to-be-written
manual? :-)

> +          grub_printf ("Mapped\tGRUB\n");
> +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
> +              curnode = curnode->next;
> +            }
> +        }
> +    }
> +  else if (state[1].set) /* Reset: just delete all mappings */
> +    {
> +      if (drivemap)
> +        {
> +          drivemap_node_t *curnode = drivemap, *prevnode = 0;
> +          while (curnode)
> +            {
> +              prevnode = curnode;
> +              curnode = curnode->next;
> +              grub_free (prevnode);
> +            }
> +          drivemap = 0;
> +        }
> +    }
> +  else
> +    {
> +      if (argc != 2)
> +        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
> +      grub_uint8_t mapfrom = 0;

Please do not use mixed code/declarations here.

> +      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
> +      if (err != GRUB_ERR_NONE)
> +        return grub_error (err, "invalid disk or not managed by the BIOS");
> +
> +      grub_uint8_t mapto = 0xFF;
> +      err = tryparse_diskstring (args[1], &mapto);
> +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then? */
> +        {    
> +          grub_errno = GRUB_ERR_NONE;
> +          unsigned long num = grub_strtoul (args[1], 0, 0);
> +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high */
> +            return grub_error (grub_errno,
> +                              "Target specifier must be of the form (fdN) or "
> +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
> +                              "number between 0 and 255");
> +          else mapto = (grub_uint8_t)num;
> +        }

Do we really want to support BIOS disk numbers here?  I do not think
it is useful.

> +      if (mapto == mapfrom)  /* Reset to default */
> +        {
> +          DBG_PRINTF ("Removing the mapping for %s (%02x)", args[0], mapfrom);
> +          drivemap_remove (mapfrom);
> +        }
> +      else  /* Map */
> +        {
> +          DBG_PRINTF ("Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
> +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
> +        }

Please use grub_dprintf

> +    }
> +
> +  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, since it accesses realmode structures by their absolute
> + * addresses, like the IVT at 0 or the BDA at 0x400 */

Please do not use all these *'s

Can you add newlines between the #defines and the comments, this will
get quite messy like this.

> +static grub_err_t
> +install_int13_handler (void)
> +{
> +  grub_size_t entries = 0;
> +  drivemap_node_t *curentry = drivemap;
> +  while (curentry)  /* Count entries to prepare a contiguous map block */

Same like above.  Can you change the place of comments everywhere?


> +    {
> +      entries++;
> +      curentry = curentry->next;
> +    }
> +  if (0 == entries)

if (! entries)

> +    {
> +      DBG_PRINTF ("drivemap: No drives marked as remapped, installation of"
> +                  "an int13h handler is not required.");
> +      return GRUB_ERR_NONE;  /* No need to install the int13h handler */
> +    }
> +  else
> +    {
> +      DBG_PRINTF ("drivemap: Installing int13h handler...\n");
> +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
> +      
> +      /* Save the pointer to the old int13h handler */
> +      grub_drivemap_int13_oldhandler = *ivtslot;
> +      DBG_PRINTF ("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:
> +       * BDA offset 0x13 contains the top of such memory */
> +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
> +      DBG_PRINTF ("Top of conventional memory: %u KiB\n", *bpa_freekb);
> +      grub_size_t total_size = grub_drivemap_int13_size
> +                            + (entries + 1) * sizeof(int13map_node_t);
> +      grub_uint16_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");
> +      DBG_PRINTF ("Payload is %u b long, reserving %u Kb\n", total_size,
> +                  payload_sizekb);
> +      *bpa_freekb -= payload_sizekb;
> +
> +      /* Copy int13h handler chunk to reserved area */
> +      grub_uint8_t *handler_base = (grub_uint8_t*)(*bpa_freekb << 10);
> +      DBG_PRINTF ("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;
> +      int13map_node_t *handler_map = (int13map_node_t*)
> +                      INT13H_TONEWADDR (&grub_drivemap_int13_mapstart);
> +      DBG_PRINTF ("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;
> +          DBG_PRINTF ("\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;
> +      DBG_PRINTF ("\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 */
> +      grub_uint32_t ivtentry = ((grub_uint32_t)handler_base) << 12; /* Segment address */
> +      ivtentry |= (grub_uint16_t) INT13H_OFFSET(grub_drivemap_int13_handler);
> +      DBG_PRINTF ("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;			/* To stop warning. */
> +  grub_register_command ("drivemap", grub_cmd_drivemap,
> +                         GRUB_COMMAND_FLAG_BOTH,
> +			                   "drivemap -s | -r | (hdX) newdrivenum",
> +                         "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 ("drivemap");
> +}

I wonder if this is not called, just before GRUB boots a kernel.  That
would cause a problem :-)

> Index: commands/i386/pc/drivemap_int13h.S
> ===================================================================
> RCS file: commands/i386/pc/drivemap_int13h.S
> diff -N commands/i386/pc/drivemap_int13h.S
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ commands/i386/pc/drivemap_int13h.S	2 Jul 2008 01:12:36 -0000
> @@ -0,0 +1,122 @@
> +/*
> + *  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.
> + */
>

I do not think this is required.  I mean, we know how GRUB is compiled
and how it deals with registers.

> +/*
> + *  This is the area for all of the special variables.
> + */

I don't think this comment is useful.

> +#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)
> +
> +VARIABLE(grub_drivemap_int13_oldhandler)
> +  .word 0xdead, 0xbeef
> +/* Drivemap module - 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. Pity we're not on
> + * amd64: rIP-relative addressing would make life 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
> ===================================================================
> RCS file: /sources/grub/grub2/conf/i386-pc.rmk,v
> retrieving revision 1.120
> diff -u -r1.120 i386-pc.rmk
> --- conf/i386-pc.rmk	19 Jun 2008 05:14:15 -0000	1.120
> +++ conf/i386-pc.rmk	2 Jul 2008 01:12:44 -0000
> @@ -153,7 +153,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
> +	aout.mod _bsd.mod bsd.mod drivemap.mod
>  
>  # For biosdisk.mod.
>  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
> @@ -320,4 +320,11 @@
>  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
>  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
>  
> +# For drivemap.mod.
> +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
> ===================================================================
> RCS file: /sources/grub/grub2/include/grub/loader.h,v
> retrieving revision 1.9
> diff -u -r1.9 loader.h
> --- include/grub/loader.h	21 Jul 2007 23:32:22 -0000	1.9
> +++ include/grub/loader.h	2 Jul 2008 01:12:45 -0000
> @@ -37,6 +37,19 @@
>  /* Unset current loader, if any.  */
>  void EXPORT_FUNC(grub_loader_unset) (void);
>  
> +typedef struct hooklist_node *grub_preboot_hookid;
> +
> +/* Register a function to be called before the boot hook. Returns an id that
> +   can be later used to unregister the preboot (i.e. if module unloaded). If
> +   abort_on_error is set, the boot sequence will abort if any of the registered
> +   functions return anything else than GRUB_ERR_NONE */
> +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: kern/loader.c
> ===================================================================
> RCS file: /sources/grub/grub2/kern/loader.c,v
> retrieving revision 1.9
> diff -u -r1.9 loader.c
> --- kern/loader.c	21 Jul 2007 23:32:26 -0000	1.9
> +++ kern/loader.c	2 Jul 2008 01:12:45 -0000
> @@ -61,11 +61,78 @@
>    grub_loader_loaded = 0;
>  }
>  
> +struct hooklist_node
> +{
> +  grub_err_t (*hook) (void);
> +  int abort_on_error;
> +  struct hooklist_node *next;
> +};

Isn't there something 

> +static struct hooklist_node *preboot_hooks = 0;
> +
> +grub_preboot_hookid
> +grub_loader_register_preboot(grub_err_t (*hook) (void), int abort_on_error)
> +{
> +  if (0 == hook)

if (! hook)

> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hoook must not be null");

hoook

null -> NULL

> +      return 0;
> +    }
> +  grub_preboot_hookid 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)
> +{
> +  if (0 == id)
> +    return;
> +  grub_preboot_hookid entry = 0, search = preboot_hooks, previous = 0;
>
Can you move this up so this is not mixed.  Can you split this into 3 lines?

 +  while (search)
> +    {
> +      if (search == id)
> +        {
> +          entry = search;
> +          break;
> +        }
> +      previous = search;
> +      search = search->next;
> +    }
> +  if (entry) /* Found */
> +    {
> +      if (previous)
> +        previous->next = entry->next;
> +      else preboot_hooks = entry->next; /* Entry was head of list */
> +      grub_free (entry);
> +    }
> +}
> +
>  grub_err_t
>  grub_loader_boot (void)
>  {
>    if (! grub_loader_loaded)
>      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
> +  
> +  grub_preboot_hookid entry = preboot_hooks;
> +  while (entry)
> +    {
> +      grub_err_t possible_error = entry->hook();

Just use "err"

> +      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
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-20 19:40 ` Marco Gerards
@ 2008-07-21  0:55   ` Javier Martín
  2008-07-21 11:07     ` Javier Martín
                       ` (2 more replies)
  0 siblings, 3 replies; 47+ messages in thread
From: Javier Martín @ 2008-07-21  0:55 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 25025 bytes --]

El dom, 20-07-2008 a las 21:40 +0200, Marco Gerards escribió:
> Did you use code from other people or projects?
No, as far as I can control my own mind: the assembly int13h handler is
loosely based on that of GRUB Legacy, but heavily rewritten. All other
code was written from scratch, even the crappy linked lists all over the
place.

> 
> > For newcomers, full info on the patch is available on the list archives
> > - it was proposed on June and its discussion deferred for "two or three
> > weeks" because the developers were busy.
> 
> 
> I have copied the changelog entry from your other e-mail:
> 
> 	* commands/i386/pc/drivemap.c : New file, main part of the new
> 	drivemap module allowing BIOS drive remapping not unlike the
> 	legacy "map" command. This allows to boot OSes with boot-time
> 	dependencies on the particular ordering of BIOS drives or
> 	trusting their own to be 0x80, like Windows XP, with
> 	non-standard boot configurations.
> 
> "New file." would be sufficient
> 
> 	* commands/i386/pc/drivemap_int13h.S : New file, INT 13h handler
> 	for the drivemap module. Installed as a TSR routine by
> 	drivemap.c, performs the actual redirection of BIOS drives.
> 
> Same here.
Hmm... isn't the ChangeLog too spartan? I thought it should be a bit
informative - what about a single sentence per file?
	* commands/i386/pc/drivemap.c : New file - drivemap command and
	int13h installer
	* commands/i386/pc/drivemap_int13h.S : New file, resident real
	mode BIOS int13 handler

>	* conf/i386-pc.rmk : Added the new module
> Please say which variables you added in this file.  You can find some
> examples on how to do this in the ChangeLog file.
	* conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
	(drivemap_mod_SOURCES) : New variable
	(drivemap_mod_ASFLAGS) : Likewise
	(drivemap_mod_CFLAGS) : Likewise
	(drivemap_mod_LDFLAGS) : Likewise
And now we're being uselessly verbose. IMHO, ChangeLog should be more
about semantic changes in the code and less about literal changes - we
have `svn diff' for those.

> 
> 	* include/grub/loader.h : Added a "just-before-boot" callback
> 	infrastructure used by drivemap.mod to install the INT13 handler
> 	only when the "boot" command has been issued.
> 
> Please describe changes, not effects.  So which prototypes and macros
> did you add?
> 
	* include/grub/loader.h (grub_loader_register_preboot) : New
	function (proto). Register a new pre-boot handler
	(grub_loader_unregister_preboot) : Likewise. Unregister handler
	(grub_preboot_hookid) : New typedef. Registered hook "handle"
> 	* kern/loader.c : Implement the preboot-hook described
> 
> Which functions did you change and how?  Please describe actual changes.
	* kern/loader.c (grub_loader_register_preboot) : New function.
	(grub_loader_unregister_preboot) : Likewise.
	(preboot_hooks) : New variable. Linked list of preboot hooks
	(grub_loader_boot) : Call the list of preboot-hooks before the
	actual loader
> 
> The header is missing, please include it.  Also newlines between the
> files make it easier to read.
What header? The drivemap module itself has no .h files. The only header
I touch is loader.h, and is both in the ChangeLog entry and the patch.

> 
> 
> Here follows a review.  Sorry I kept you waiting for this long, this
> feature and your work is really appreciated!  Perhaps I can spot some
> more problems after you fixed it and supplied an updated changelog
> entry.  There are quite some comments, but please do not let this
> demotivate you, it is mainly coding style related :-)
Well, thanks to all the time I had free, I have nearly finished Final
Fantasy XII, so the wait was not soo bad ^^

> (...)
> > +
> > +/* Uncomment the following line to enable debugging output */
> > +/* #define DRIVEMAP_DEBUG */
> > +
> > +#ifdef DRIVEMAP_DEBUG
> > +# define DBG_PRINTF(...) grub_printf(__VA_ARGS__)
> > +#else
> > +# define DBG_PRINTF(...)
> > +#endif
> 
> Please use the grub_dprintf infrastructure.
Done. I didn't even know it existed :S

> 
> > +/* realmode far ptr = 2 * 16b */
> > +extern grub_uint32_t EXPORT_VAR (grub_drivemap_int13_oldhandler);
> > +/* Size of the section to be copied */
> > +extern grub_uint16_t EXPORT_VAR (grub_drivemap_int13_size);
> > +
> > +/* NOT a typo - just need the symbol's address with &symbol */
> > +typedef void grub_symbol_t;
> > +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_handler_base);
> > +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_mapstart);
> 
> Please export stuff in header files, that's the normal practise in
> this file as well, right?  What's not a typo?
EXPORT_* macros removed; seemingly they are no longer needed because all
code is in the same module (initially the assembly was in kernel). 

What's not a typo is the definition of grub_symbol_t as "void" instead
of something more sound to a C programmer, like "void *". I don't really
know how to explain it in the source, but it's just a way to make the
names known to the C compiler, so that I then take their addresses with
the & operator. Such names are _not_ variables in the C file and they
take no space or storage.

> 
> > +static grub_preboot_hookid insthandler_hook = 0;
> 
> You can leave the "= 0" away.
> (...)
> > +static drivemap_node_t *drivemap = 0;
> 
> Same here :-)
Does GRUB zero-initialize the memory of modules when loading them? The
first variable would be OK without the = 0, but not the second, since
there are some checks of the form if(var) scattered all over the code -
"drivemap" is a linked list, so 0 means EOL.

> (...)
> > +static grub_err_t
> > +drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
> > +  /* Puts the specified mapping into the table, replacing an existing mapping
> > +   * for newdrive or adding a new one if required. */
> 
> Please place the comments before the function.
> (...)
> > +  if (mapping)  /* There was a mapping already in place, modify it */
> > +    mapping->redirto = redirto;
> 
> Please place the comment before the if statement.  Can you format the
> comment such that it begins with a capital and ends with ".  */" (two
> spaces)?  Can you do this for the other comments you added as well?
Hmm... Is the formatting requirement due to some script-based code
parsing or is it just OCD? ;)

Besides, saying "there was a mapping" before the "if" that checks it
would be strange. I'd prefer to leave this line as is, or find an
alternate wording for the comment (and I'm sleepy right now)

> > +  else  /* Create a new mapping and add it to the head of the list */
> > +    {
> 
> I would move the comment inside the braces.
But I wouldn't - the "else" line is almost empty, and placing the
comment two lines below does not help readability

> (...)
> > +static void
> > +drivemap_remove (grub_uint8_t newdrive)
> > +  /* Removes the mapping for newdrive from the table. If there is no mapping,
> > +   * then this function behaves like a no-op on the map. */
> 
> Can you move the comments to before the function?  Please do not place
> *'s on each line.  Without the second * it would be fine.  After a
> ".", please insert two spaces.
After _every_ "." or just after the final one? The "no * on each line"
rule conflicts with the GPL notice comment, but OK.

> > +{
> > +  drivemap_node_t *mapping = 0, *search = drivemap, *previous = 0;
> 
> Please define one variable per line, can you break this down to 3
> lines?
I think the current version is more readable, but OK.

> (...)
> > +      else drivemap = mapping->next; /* Entry was head of list */
> 
> Please place the stuff after "else" on a new line.
Done, I think.

> 
> > +      grub_free (mapping);
> > +    }
> > +}
> > +
> > +static grub_err_t
> > +parse_biosdisk (const char *name, grub_uint8_t *disknum)
> > +{
> > +  if (!name) return GRUB_ERR_BAD_ARGUMENT;
> 
> Same here.  Please do not just return GRUB_ERR_BAD_ARGUMENT, use
> grub_error so you can also define a string for the error.
This is a function internal to drivemap, called from a single point. I
don't think the additional information gained by the changes you suggest
would be worth the space lost by two more error strings (of your
additional suggestions for this function).

> > +  if (*name == '(')
> > +    name++; /* Skip the first ( in (hd0) - disk_open wants just the name! */
> > +  grub_disk_t disk = grub_disk_open (name);
> 
> Although mixed declarations and code are allowed, I personally would
> avoid this.  It doesn't make the code easier to read, I think.
What do you advocate, then? Declaring "disk" at the function start
(three lines above) and then assigning to it? Hmm... maybe, but I find
this version more sound. The function is small enough to avoid any
confusion.

> 
> > +  else
> > +    {
> > +      enum grub_disk_dev_id id = disk->dev->id;
> > +      if (disknum)
> > +        *disknum = disk->id;   /* Only valid, of course if it's a biosdisk */
> 
> Please place the comment before the if.
The comment does not refer to the "if", but to the assignment: the
"BIOS" drive num we get is only meaningful if the disk is actually
managed by biosdisk (which is checked later on return). All other
solutions (i.e. checking before) expand that section of the code by a
few lines.

> (...)
> > +        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)
> 
> 
> This is not rally wrong, but doesn't feel right.  I would use:
> 
> disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID
I find it more sound to place the constant before in the comparison,
since if the code is modified later and the == is erroneously turned
into =, my code would throw a compiler error (assignment to constant)
while yours would happily proceed as a normal assignment inside the if,
which is perfectly allowed in C, and fail at runtime.

> 
> I know, I am a pain in the ass right now :P
I won't deny that, but criticism (particularly constructive criticism as
yours) is a Good Thing (tm).

> (...)
> > +  grub_disk_dev_iterate (&find);
> 
> No need for the &.  I even wonder if it would work this way...
Corrected.

> > +  return ret;
> > +}
> > +
> > +static grub_err_t
> > +tryparse_diskstring (const char *str, grub_uint8_t *output)
> > +{
> > +  if (!str) return GRUB_ERR_BAD_ARGUMENT;
> > +  if (*str == '(')
> > +    str++;  /* Skip opening paren in order to allow both (hd0) and hd0 */
> > +  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, &str, 0);
> > +      if (grub_errno != GRUB_ERR_NONE || drivenum > 127)
> > +        return GRUB_ERR_BAD_ARGUMENT; /* Could not convert, or num too high for BIOS */
> 
> Use grub_error
Same response as before: internal helper function, used at a single
point with known error conditions which should be obvious. Moreover, the
drivemap command uses this func on a "try-this-first" basis on its
second argument, then resorts to raw number parsing. Thus, we do not
want grub_error printing anything to screen (does it, by the way?).

> > +      else
> > +       {
> > +          bios_num |= drivenum;
> > +          if (output)
> > +            *output = bios_num;
> > +          return GRUB_ERR_NONE;
> > +        }
> > +    }
> > +  else return GRUB_ERR_BAD_ARGUMENT;
> > +}
> > +
> > +static grub_err_t
> > +grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
> > +{
> > +  if (state[0].set) /* Show: list mappings */
> 
> Like the comments before
And like the answers before. Since this has been raised a lot of times,
and in order to compare our thoughts on comments, I'll post what I think
comments should tell in each pos:

	/* Preconditions and check explanation */
	if (blah && (foo || bar)) /* Cond-met: action to take (short) */
	{
		/* If too long to fit elsewhere: preconditions inside the IF block,
procedure to follow */
		(...)
	}

The same for the "else if" / "else" blocks, and after all of it maybe an
additional comment explaining the postconditions, if required.

> 
> > +    {
> > +      if (!drivemap)
> > +        grub_printf ("No drives have been remapped");
> > +      else
> > +        {
> > +          grub_printf ("Showing only remapped drives. Drives that have had "
> > +                       "their slot assigned to another one and have not been "
> > +                       "themselves remapped will become inaccessible through "
> > +                       "the BIOS routines to the booted OS.\n\n");
> 
> I do not think this message is immediatly clear to the user.  Perhaps
> we should even leave this out and say something in the to-be-written
> manual? :-)
Absolutely true. This message is pathetic and crappy, but as long as
there's no manual... Can anyone suggest an alternative, clearer wording
for it?

> > +          grub_printf ("Mapped\tGRUB\n");
> > +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
> > +              curnode = curnode->next;
> > +            }
> > +        }
> > +    }
> > +  else if (state[1].set) /* Reset: just delete all mappings */
> > +    {
> > +      if (drivemap)
> > +        {
> > +          drivemap_node_t *curnode = drivemap, *prevnode = 0;
> > +          while (curnode)
> > +            {
> > +              prevnode = curnode;
> > +              curnode = curnode->next;
> > +              grub_free (prevnode);
> > +            }
> > +          drivemap = 0;
> > +        }
> > +    }
> > +  else
> > +    {
> > +      if (argc != 2)
> > +        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
> > +      grub_uint8_t mapfrom = 0;
> 
> Please do not use mixed code/declarations here.
The only mixed code/decl I find is "grub_err_t err = revparse_biosdisk
(curnode->redirto, &dname);" and it's a call to an internal function
whose conditions are well-known - safe. All others are declarations with
initializations, which I find pretty normal.

However, if you mean literally "mixing" them... do you really advocate
the K&R approach of "declare everything at the function start"? Time
proved that scheme suboptimal. As you can see, most declarations are
either at the very start of a _block_ (the scope unit) or near it, right
after precondition checks.

> > +      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
> > +      if (err != GRUB_ERR_NONE)
> > +        return grub_error (err, "invalid disk or not managed by the BIOS");
> > +
> > +      grub_uint8_t mapto = 0xFF;
> > +      err = tryparse_diskstring (args[1], &mapto);
> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then? */
> > +        {    
> > +          grub_errno = GRUB_ERR_NONE;
> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high */
> > +            return grub_error (grub_errno,
> > +                              "Target specifier must be of the form (fdN) or "
> > +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
> > +                              "number between 0 and 255");
> > +          else mapto = (grub_uint8_t)num;
> > +        }
> 
> Do we really want to support BIOS disk numbers here?  I do not think
> it is useful.
I asked the same when I was starting to write this and I was told, more
or less, that this was the way to go.

> Can you add newlines between the #defines and the comments, this will
> get quite messy like this.
Done.

> 
> > +    {
> > +      entries++;
> > +      curentry = curentry->next;
> > +    }
> > +  if (0 == entries)
> 
> if (! entries)
Nope. You C infidels might make no distinction between int and bool, but
I was raised in the Holy Truth of languages with a boolean type. ^^ 
Semantically, "entries" represents an integer, and I want to check
whether it is zero. I only use your form when checking for
a)boolean-like conditions or b)non-null pointers.

Even this last one would be "wrong" under my guidelines, since ANSI
specifies that a null pointer is guaranteed to compare equal to zero,
but in that case alone I let my laziness win and use the Unholy
int->"boolean" interpretation of C's "if" statement.

Most probably, the compiler will optimize the additional assembly space
cost away if there's any, so no worries. I find the semantic
distinctions important to keep, particularly if the cost is zero.

> (...)
> > +GRUB_MOD_FINI (drivemap)
> > +{
> > +  grub_loader_unregister_preboot (insthandler_hook);
> > +  insthandler_hook = 0;
> > +  grub_unregister_command ("drivemap");
> > +}
> 
> I wonder if this is not called, just before GRUB boots a kernel.  That
> would cause a problem :-)
AFAIK the "fini" functions are called by grub_machine_fini, which is
called _after_ the preboot hook.

> > Index: commands/i386/pc/drivemap_int13h.S
> > ===================================================================
> > RCS file: commands/i386/pc/drivemap_int13h.S
> > diff -N commands/i386/pc/drivemap_int13h.S
> > --- /dev/null	1 Jan 1970 00:00:00 -0000
> > +++ commands/i386/pc/drivemap_int13h.S	2 Jul 2008 01:12:36 -0000
And here we go for the second round xD

> (...)
> > +
> > +
> > +/*
> > + * 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.
> > + */
> >
> 
> I do not think this is required.  I mean, we know how GRUB is compiled
> and how it deals with registers.
But nevertheless each and every assembly file I looked at states the
same. It's supposed to be useful when developing; although I did not use
it, since my only assembly function is not called from C.

> > +/*
> > + *  This is the area for all of the special variables.
> > + */
> 
> I don't think this comment is useful.
I dont remember writing that... Maybe I c-ped it from somewhere?
Deleted.

> > +#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)
> > +
> > +VARIABLE(grub_drivemap_int13_oldhandler)
> > +  .word 0xdead, 0xbeef
> > +/* Drivemap module - 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. Pity we're not on
> > + * amd64: rIP-relative addressing would make life 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)
I'm disappointed... Not a single comment on this file?? ;)

> (...)
> > Index: kern/loader.c
> > ===================================================================
> > RCS file: /sources/grub/grub2/kern/loader.c,v
> > retrieving revision 1.9
> > diff -u -r1.9 loader.c
> > --- kern/loader.c	21 Jul 2007 23:32:26 -0000	1.9
> > +++ kern/loader.c	2 Jul 2008 01:12:45 -0000
> > @@ -61,11 +61,78 @@
> >    grub_loader_loaded = 0;
> >  }
> >  
> > +struct hooklist_node
> > +{
> > +  grub_err_t (*hook) (void);
> > +  int abort_on_error;
> > +  struct hooklist_node *next;
> > +};
> 
> Isn't there something 
Woot? What do you mean with that? o_O

> > +static struct hooklist_node *preboot_hooks = 0;
> > +
> > +grub_preboot_hookid
> > +grub_loader_register_preboot(grub_err_t (*hook) (void), int abort_on_error)
> > +{
> > +  if (0 == hook)
> 
> if (! hook)
This time you're right, per my "how to write C comparisons" rationale
stated above. Changed. 

> > +    {
> > +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hoook must not be null");
> 
> hoook
> 
> null -> NULL
Changed too.

> (...)
> > +
> > +void
> > +grub_loader_unregister_preboot(grub_preboot_hookid id)
> > +{
> > +  if (0 == id)
> > +    return;
> > +  grub_preboot_hookid entry = 0, search = preboot_hooks, previous = 0;
> >
> Can you move this up so this is not mixed.  Can you split this into 3 lines?
As I understand coding style in C-like languages, precondition checks
(if short enough) and declarations may be mixed as long as they don't
get confusing; and I don't think this one is too confusing.

On the other hand, variables splitted, but why the obsession with that
particular rule?

> (...)
> > +      grub_err_t possible_error = entry->hook();
> 
> Just use "err"
I think I did so in a previous version of this patch, and I was told to
be a bit more explicit, but... changed.

Phew! That was long, even after removing some parts. Well, this is the
new version of the patch. Cheers!


PS: I already signed the copyright assignment papers and sent them to
the FSF. They should arrive within this week.


[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-21  0:55   ` Javier Martín
@ 2008-07-21 11:07     ` Javier Martín
  2008-07-22 21:32     ` Robert Millan
  2008-07-31 19:01     ` Marco Gerards
  2 siblings, 0 replies; 47+ messages in thread
From: Javier Martín @ 2008-07-21 11:07 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 268 bytes --]

El lun, 21-07-2008 a las 02:55 +0200, Javier Martín escribió:
> (...)
> Phew! That was long, even after removing some parts. Well, this is the
> new version of the patch. Cheers!
As always, I'm so stupid that I left the patch out... Sigh. Here it is.

Habbit

[-- Attachment #1.2: drivemap.patch.3 --]
[-- Type: text/x-patch, Size: 23860 bytes --]

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,393 @@
+/* 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/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},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
+
+/* realmode far ptr = 2 * 16b */
+extern grub_uint32_t grub_drivemap_int13_oldhandler;
+/* Size of the section to be copied */
+extern grub_uint16_t grub_drivemap_int13_size;
+
+/* NOT a typo - just need the symbol's address with &symbol.  */
+typedef void grub_symbol_t;
+extern grub_symbol_t grub_drivemap_int13_handler_base;
+extern grub_symbol_t grub_drivemap_int13_mapstart;
+
+void grub_drivemap_int13_handler (void);
+
+/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
+
+
+static grub_preboot_hookid insthandler_hook = 0;
+
+typedef struct drivemap_node
+{
+  grub_uint8_t newdrive;
+  grub_uint8_t redirto;
+  struct drivemap_node *next;
+} drivemap_node_t;
+
+static drivemap_node_t *drivemap = 0;
+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;
+    }
+
+  
+  if (mapping) /* There was a mapping already in place, modify it.  */
+    mapping->redirto = redirto;
+  else /* Create a new mapping and add it to the head of the list.  */
+    {
+      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) /* Found.  */
+    {
+      if (previous)
+        previous->next = mapping->next;
+      else /* Entry was head of list.  */
+        drivemap = mapping->next;
+      grub_free (mapping);
+    }
+}
+
+static grub_err_t
+parse_biosdisk (const char *name, grub_uint8_t *disknum)
+{
+  if (!name) return GRUB_ERR_BAD_ARGUMENT;
+  if (*name == '(')
+    name++; /* Skip the first ( in (hd0) - disk_open wants just the name.  */
+  grub_disk_t disk = grub_disk_open (name);
+  if (!disk)
+    return GRUB_ERR_UNKNOWN_DEVICE;
+  else
+    {
+      enum grub_disk_dev_id id = disk->dev->id;
+      if (disknum)
+        *disknum = disk->id;   /* Only sound if it's a biosdisk - check later.  */
+      grub_disk_close (disk);
+      return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ?
+          GRUB_ERR_BAD_DEVICE : GRUB_ERR_NONE;
+    }
+}
+
+static grub_err_t
+revparse_biosdisk(const grub_uint8_t dnum, const char **output)
+{
+  grub_err_t ret = GRUB_ERR_UNKNOWN_DEVICE;
+  auto int find (const char *name);
+  int find (const char *name)
+  {
+    grub_disk_t disk = grub_disk_open (name);
+    if (!disk)
+      return 0;
+    else
+      {
+        int found = 0;
+        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)
+          {
+            found = 1;
+            if (output)
+              *output = name;
+            ret = GRUB_ERR_NONE;
+          }
+        grub_disk_close (disk);
+        return found;
+      }
+  }
+
+  grub_disk_dev_iterate (find);
+  return ret;
+}
+
+static grub_err_t
+tryparse_diskstring (const char *str, grub_uint8_t *output)
+{
+  if (!str) return GRUB_ERR_BAD_ARGUMENT;
+  if (*str == '(')
+    str++;  /* Skip opening paren in order to allow both (hd0) and hd0.  */
+  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)
+        return GRUB_ERR_BAD_ARGUMENT; /* Could not convert, or num too high for BIOS.  */
+      else
+       {
+          bios_num |= drivenum;
+          if (output)
+            *output = bios_num;
+          return GRUB_ERR_NONE;
+        }
+    }
+  else return GRUB_ERR_BAD_ARGUMENT;
+}
+
+static grub_err_t
+grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
+{
+  if (state[0].set) /* Show: list mappings.  */
+    {
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives. Drives that have had "
+                       "their slot assigned to another one and have not been "
+                       "themselves remapped will become inaccessible through "
+                       "the BIOS routines to the booted OS.\n\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].set) /* Reset: just delete all mappings.  */
+    {
+      if (drivemap)
+        {
+          drivemap_node_t *curnode = drivemap, *prevnode = 0;
+          while (curnode)
+            {
+              prevnode = curnode;
+              curnode = curnode->next;
+              grub_free (prevnode);
+            }
+          drivemap = 0;
+        }
+    }
+  else
+    {
+      if (argc != 2)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
+      grub_uint8_t mapfrom = 0;
+      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
+      if (err != GRUB_ERR_NONE)
+        return grub_error (err, "invalid disk or not managed by the BIOS");
+
+      grub_uint8_t mapto = 0xFF;
+      err = tryparse_diskstring (args[1], &mapto);
+      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */
+        {    
+          grub_errno = GRUB_ERR_NONE;
+          unsigned long num = grub_strtoul (args[1], 0, 0);
+          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
+            return grub_error (grub_errno,
+                              "Target specifier must be of the form (fdN) or "
+                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
+                              "number between 0 and 255");
+          else mapto = (grub_uint8_t)num;
+        }
+      
+      if (mapto == mapfrom)  /* Reset to default.  */
+        {
+          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
+          drivemap_remove (mapfrom);
+        }
+      else  /* Map.  */
+        {
+          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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;
+  while (curentry)  /* Count entries to prepare a contiguous map block.  */
+    {
+      entries++;
+      curentry = curentry->next;
+    }
+  if (0 == entries)
+    {
+      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
+                  "an int13h handler is not required.");
+      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
+    }
+  else
+    {
+      grub_dprintf (MODNAME, "Installing int13h handler...\n");
+      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
+      
+      /* Save the pointer to the old int13h 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:
+       * BDA offset 0x13 contains the top of such memory.  */
+      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
+      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
+      grub_size_t total_size = grub_drivemap_int13_size
+                            + (entries + 1) * sizeof(int13map_node_t);
+      grub_uint16_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 chunk to reserved area.  */
+      grub_uint8_t *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;
+      int13map_node_t *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.  */
+      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
+                         "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,118 @@
+/*
+ *  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)
+
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - 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. Pity we're not on
+ * amd64: rIP-relative addressing would make life 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: 1723)
+++ conf/i386-pc.rmk	(copia de trabajo)
@@ -158,7 +158,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
+	aout.mod _bsd.mod bsd.mod drivemap.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -325,4 +325,11 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+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: 1723)
+++ include/grub/loader.h	(copia de trabajo)
@@ -37,6 +37,19 @@
 /* Unset current loader, if any.  */
 void EXPORT_FUNC(grub_loader_unset) (void);
 
+typedef struct hooklist_node *grub_preboot_hookid;
+
+/* Register a function to be called before the boot hook. Returns an id that
+   can be later used to unregister the preboot (i.e. if module unloaded). If
+   abort_on_error is set, the boot sequence will abort if any of the registered
+   functions return anything else than GRUB_ERR_NONE */
+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: kern/loader.c
===================================================================
--- kern/loader.c	(revisión: 1723)
+++ kern/loader.c	(copia de trabajo)
@@ -61,11 +61,80 @@
   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)
+{
+  if (!hook)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
+      return 0;
+    }
+  grub_preboot_hookid 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)
+{
+  if (0 == id)
+    return;
+  grub_preboot_hookid entry = 0;
+  grub_preboot_hookid search = preboot_hooks;
+  grub_preboot_hookid previous = 0;
+  while (search)
+    {
+      if (search == id)
+        {
+          entry = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (entry) /* Found */
+    {
+      if (previous)
+        previous->next = entry->next;
+      else preboot_hooks = entry->next; /* Entry was head of list */
+      grub_free (entry);
+    }
+}
+
 grub_err_t
 grub_loader_boot (void)
 {
   if (! grub_loader_loaded)
     return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
+  
+  grub_preboot_hookid entry = preboot_hooks;
+  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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-21  0:55   ` Javier Martín
  2008-07-21 11:07     ` Javier Martín
@ 2008-07-22 21:32     ` Robert Millan
  2008-07-31 19:01     ` Marco Gerards
  2 siblings, 0 replies; 47+ messages in thread
From: Robert Millan @ 2008-07-22 21:32 UTC (permalink / raw)
  To: The development of GRUB 2

On Mon, Jul 21, 2008 at 02:55:41AM +0200, Javier Martín wrote:
> Hmm... isn't the ChangeLog too spartan?

I tend to agree with that.  I don't mind as much, as I get used myself, but
it's really a PITA sometimes.

-- 
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-21  0:55   ` Javier Martín
  2008-07-21 11:07     ` Javier Martín
  2008-07-22 21:32     ` Robert Millan
@ 2008-07-31 19:01     ` Marco Gerards
  2008-08-03 23:29       ` Javier Martín
  2 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-07-31 19:01 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín <lordhabbit@gmail.com> writes:

> El dom, 20-07-2008 a las 21:40 +0200, Marco Gerards escribió:
>> Did you use code from other people or projects?
> No, as far as I can control my own mind: the assembly int13h handler is
> loosely based on that of GRUB Legacy, but heavily rewritten. All other
> code was written from scratch, even the crappy linked lists all over the
> place.

Okay :-)

Sorry I kept you waiting... again...

>> > For newcomers, full info on the patch is available on the list archives
>> > - it was proposed on June and its discussion deferred for "two or three
>> > weeks" because the developers were busy.
>> 
>> 
>> I have copied the changelog entry from your other e-mail:
>> 
>> 	* commands/i386/pc/drivemap.c : New file, main part of the new
>> 	drivemap module allowing BIOS drive remapping not unlike the
>> 	legacy "map" command. This allows to boot OSes with boot-time
>> 	dependencies on the particular ordering of BIOS drives or
>> 	trusting their own to be 0x80, like Windows XP, with
>> 	non-standard boot configurations.
>> 
>> "New file." would be sufficient
>> 
>> 	* commands/i386/pc/drivemap_int13h.S : New file, INT 13h handler
>> 	for the drivemap module. Installed as a TSR routine by
>> 	drivemap.c, performs the actual redirection of BIOS drives.
>> 
>> Same here.
> Hmm... isn't the ChangeLog too spartan? I thought it should be a bit
> informative - what about a single sentence per file?
> 	* commands/i386/pc/drivemap.c : New file - drivemap command and
> 	int13h installer
> 	* commands/i386/pc/drivemap_int13h.S : New file, resident real
> 	mode BIOS int13 handler

No, please just stick to "New file.".  The Changelog is used for
changes only.  If you add more information, it will be noise and it
will make my job harder.

>>	* conf/i386-pc.rmk : Added the new module
>> Please say which variables you added in this file.  You can find some
>> examples on how to do this in the ChangeLog file.
> 	* conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
> 	(drivemap_mod_SOURCES) : New variable
> 	(drivemap_mod_ASFLAGS) : Likewise
> 	(drivemap_mod_CFLAGS) : Likewise
> 	(drivemap_mod_LDFLAGS) : Likewise
> And now we're being uselessly verbose. IMHO, ChangeLog should be more
> about semantic changes in the code and less about literal changes - we
> have `svn diff' for those.

You are wrong.  It is your opinion that this should go into a
changelog entry and I can understand that.  However, this is simply
how we do it ;)

>> 	* include/grub/loader.h : Added a "just-before-boot" callback
>> 	infrastructure used by drivemap.mod to install the INT13 handler
>> 	only when the "boot" command has been issued.
>> 
>> Please describe changes, not effects.  So which prototypes and macros
>> did you add?
>> 
> 	* include/grub/loader.h (grub_loader_register_preboot) : New
> 	function (proto). Register a new pre-boot handler
> 	(grub_loader_unregister_preboot) : Likewise. Unregister handler
> 	(grub_preboot_hookid) : New typedef. Registered hook "handle"
>> 	* kern/loader.c : Implement the preboot-hook described
>> 
>> Which functions did you change and how?  Please describe actual changes.
> 	* kern/loader.c (grub_loader_register_preboot) : New function.
> 	(grub_loader_unregister_preboot) : Likewise.
> 	(preboot_hooks) : New variable. Linked list of preboot hooks
> 	(grub_loader_boot) : Call the list of preboot-hooks before the
> 	actual loader
>> 
>> The header is missing, please include it.  Also newlines between the
>> files make it easier to read.
> What header? The drivemap module itself has no .h files. The only header
> I touch is loader.h, and is both in the ChangeLog entry and the patch.

With header I meant the first line of the changelog entry that
includes your name and e-mail address.
 
>> Here follows a review.  Sorry I kept you waiting for this long, this
>> feature and your work is really appreciated!  Perhaps I can spot some
>> more problems after you fixed it and supplied an updated changelog
>> entry.  There are quite some comments, but please do not let this
>> demotivate you, it is mainly coding style related :-)
> Well, thanks to all the time I had free, I have nearly finished Final
> Fantasy XII, so the wait was not soo bad ^^

:-)

>> (...)
>> > +
>> > +/* Uncomment the following line to enable debugging output */
>> > +/* #define DRIVEMAP_DEBUG */
>> > +
>> > +#ifdef DRIVEMAP_DEBUG
>> > +# define DBG_PRINTF(...) grub_printf(__VA_ARGS__)
>> > +#else
>> > +# define DBG_PRINTF(...)
>> > +#endif
>> 
>> Please use the grub_dprintf infrastructure.
> Done. I didn't even know it existed :S

:-)
 
>> > +/* realmode far ptr = 2 * 16b */
>> > +extern grub_uint32_t EXPORT_VAR (grub_drivemap_int13_oldhandler);
>> > +/* Size of the section to be copied */
>> > +extern grub_uint16_t EXPORT_VAR (grub_drivemap_int13_size);
>> > +
>> > +/* NOT a typo - just need the symbol's address with &symbol */
>> > +typedef void grub_symbol_t;
>> > +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_handler_base);
>> > +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_mapstart);
>> 
>> Please export stuff in header files, that's the normal practise in
>> this file as well, right?  What's not a typo?
> EXPORT_* macros removed; seemingly they are no longer needed because all
> code is in the same module (initially the assembly was in kernel). 
>
> What's not a typo is the definition of grub_symbol_t as "void" instead
> of something more sound to a C programmer, like "void *". I don't really
> know how to explain it in the source, but it's just a way to make the
> names known to the C compiler, so that I then take their addresses with
> the & operator. Such names are _not_ variables in the C file and they
> take no space or storage.

I see.  But I do not think a comment is needed here to say that this
is not a typo.
 
>> > +static grub_preboot_hookid insthandler_hook = 0;
>> 
>> You can leave the "= 0" away.
>> (...)
>> > +static drivemap_node_t *drivemap = 0;
>> 
>> Same here :-)
> Does GRUB zero-initialize the memory of modules when loading them? The
> first variable would be OK without the = 0, but not the second, since
> there are some checks of the form if(var) scattered all over the code -
> "drivemap" is a linked list, so 0 means EOL.

Yes, all globals are set to 0.

>> (...)
>> > +static grub_err_t
>> > +drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
>> > +  /* Puts the specified mapping into the table, replacing an existing mapping
>> > +   * for newdrive or adding a new one if required. */
>> 
>> Please place the comments before the function.
>> (...)
>> > +  if (mapping)  /* There was a mapping already in place, modify it */
>> > +    mapping->redirto = redirto;
>> 
>> Please place the comment before the if statement.  Can you format the
>> comment such that it begins with a capital and ends with ".  */" (two
>> spaces)?  Can you do this for the other comments you added as well?
> Hmm... Is the formatting requirement due to some script-based code
> parsing or is it just OCD? ;)

OCD?

The reason is that is makes it easier for editors to automatically
break lines and stuff.  So this way an editor can parse the sentence,
we use that.

> Besides, saying "there was a mapping" before the "if" that checks it
> would be strange. I'd prefer to leave this line as is, or find an
> alternate wording for the comment (and I'm sleepy right now)

I think it would be clear if you moved the comment.  But rephrasing it
would be fine to me.

>> > +  else  /* Create a new mapping and add it to the head of the list */
>> > +    {
>> 
>> I would move the comment inside the braces.
> But I wouldn't - the "else" line is almost empty, and placing the
> comment two lines below does not help readability

Please stick to our coding style anyways.

>> (...)
>> > +static void
>> > +drivemap_remove (grub_uint8_t newdrive)
>> > +  /* Removes the mapping for newdrive from the table. If there is no mapping,
>> > +   * then this function behaves like a no-op on the map. */
>> 
>> Can you move the comments to before the function?  Please do not place
>> *'s on each line.  Without the second * it would be fine.  After a
>> ".", please insert two spaces.
> After _every_ "." or just after the final one? The "no * on each line"
> rule conflicts with the GPL notice comment, but OK.

The GPL notice is an exception.  Yes, after every "." that ends a
sentence.

>> > +{
>> > +  drivemap_node_t *mapping = 0, *search = drivemap, *previous = 0;
>> 
>> Please define one variable per line, can you break this down to 3
>> lines?
> I think the current version is more readable, but OK.
>
>> (...)
>> > +      else drivemap = mapping->next; /* Entry was head of list */
>> 
>> Please place the stuff after "else" on a new line.
> Done, I think.
>
>> 
>> > +      grub_free (mapping);
>> > +    }
>> > +}
>> > +
>> > +static grub_err_t
>> > +parse_biosdisk (const char *name, grub_uint8_t *disknum)
>> > +{
>> > +  if (!name) return GRUB_ERR_BAD_ARGUMENT;
>> 
>> Same here.  Please do not just return GRUB_ERR_BAD_ARGUMENT, use
>> grub_error so you can also define a string for the error.
> This is a function internal to drivemap, called from a single point. I
> don't think the additional information gained by the changes you suggest
> would be worth the space lost by two more error strings (of your
> additional suggestions for this function).

It means that you introduce different error handling.  Someone who is
familiar with GRUB but not with your code will misunderstand it.
Please change it to improve the maintainability of the code.

>> > +  if (*name == '(')
>> > +    name++; /* Skip the first ( in (hd0) - disk_open wants just the name! */
>> > +  grub_disk_t disk = grub_disk_open (name);
>> 
>> Although mixed declarations and code are allowed, I personally would
>> avoid this.  It doesn't make the code easier to read, I think.
> What do you advocate, then? Declaring "disk" at the function start
> (three lines above) and then assigning to it? Hmm... maybe, but I find
> this version more sound. The function is small enough to avoid any
> confusion.

Yes, that is what I meant.

>> 
>> > +  else
>> > +    {
>> > +      enum grub_disk_dev_id id = disk->dev->id;
>> > +      if (disknum)
>> > +        *disknum = disk->id;   /* Only valid, of course if it's a biosdisk */
>> 
>> Please place the comment before the if.
> The comment does not refer to the "if", but to the assignment: the
> "BIOS" drive num we get is only meaningful if the disk is actually
> managed by biosdisk (which is checked later on return). All other
> solutions (i.e. checking before) expand that section of the code by a
> few lines.

Can you rephrase the comment so you can move it?

>> (...)
>> > +        if (dnum == disk->id && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->id)
>> 
>> 
>> This is not rally wrong, but doesn't feel right.  I would use:
>> 
>> disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID
> I find it more sound to place the constant before in the comparison,
> since if the code is modified later and the == is erroneously turned
> into =, my code would throw a compiler error (assignment to constant)
> while yours would happily proceed as a normal assignment inside the if,
> which is perfectly allowed in C, and fail at runtime.

Can you change it anyways?
 
>> I know, I am a pain in the ass right now :P
> I won't deny that, but criticism (particularly constructive criticism as
> yours) is a Good Thing (tm).

Great :-)

>> (...)
>> > +  grub_disk_dev_iterate (&find);
>> 
>> No need for the &.  I even wonder if it would work this way...
> Corrected.
>
>> > +  return ret;
>> > +}
>> > +
>> > +static grub_err_t
>> > +tryparse_diskstring (const char *str, grub_uint8_t *output)
>> > +{
>> > +  if (!str) return GRUB_ERR_BAD_ARGUMENT;
>> > +  if (*str == '(')
>> > +    str++;  /* Skip opening paren in order to allow both (hd0) and hd0 */
>> > +  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, &str, 0);
>> > +      if (grub_errno != GRUB_ERR_NONE || drivenum > 127)
>> > +        return GRUB_ERR_BAD_ARGUMENT; /* Could not convert, or num too high for BIOS */
>> 
>> Use grub_error
> Same response as before: internal helper function, used at a single
> point with known error conditions which should be obvious. Moreover, the
> drivemap command uses this func on a "try-this-first" basis on its
> second argument, then resorts to raw number parsing. Thus, we do not
> want grub_error printing anything to screen (does it, by the way?).

It's how the grub_err_t interface is used.  Thus can you change it for
maintainability?

>> > +      else
>> > +       {
>> > +          bios_num |= drivenum;
>> > +          if (output)
>> > +            *output = bios_num;
>> > +          return GRUB_ERR_NONE;
>> > +        }
>> > +    }
>> > +  else return GRUB_ERR_BAD_ARGUMENT;
>> > +}
>> > +
>> > +static grub_err_t
>> > +grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
>> > +{
>> > +  if (state[0].set) /* Show: list mappings */
>> 
>> Like the comments before
> And like the answers before. Since this has been raised a lot of times,
> and in order to compare our thoughts on comments, I'll post what I think
> comments should tell in each pos:
>
> 	/* Preconditions and check explanation */
> 	if (blah && (foo || bar)) /* Cond-met: action to take (short) */
> 	{
> 		/* If too long to fit elsewhere: preconditions inside the IF block,
> procedure to follow */
> 		(...)
> 	}
>
> The same for the "else if" / "else" blocks, and after all of it maybe an
> additional comment explaining the postconditions, if required.

Well, everyone has a different coding style.  I simply try to keep
GRUB consistent.  I hope you can appreciate that.
 
>> > +    {
>> > +      if (!drivemap)
>> > +        grub_printf ("No drives have been remapped");
>> > +      else
>> > +        {
>> > +          grub_printf ("Showing only remapped drives. Drives that have had "
>> > +                       "their slot assigned to another one and have not been "
>> > +                       "themselves remapped will become inaccessible through "
>> > +                       "the BIOS routines to the booted OS.\n\n");
>> 
>> I do not think this message is immediatly clear to the user.  Perhaps
>> we should even leave this out and say something in the to-be-written
>> manual? :-)
> Absolutely true. This message is pathetic and crappy, but as long as
> there's no manual... Can anyone suggest an alternative, clearer wording
> for it?

By writing a manual? :-)  I think I would simply remove the entire
grub_printf.  Can you do that?  We should not compensate for a lacking
manual in the sourcecode.

>> > +          grub_printf ("Mapped\tGRUB\n");
>> > +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
>> > +              curnode = curnode->next;
>> > +            }
>> > +        }
>> > +    }
>> > +  else if (state[1].set) /* Reset: just delete all mappings */
>> > +    {
>> > +      if (drivemap)
>> > +        {
>> > +          drivemap_node_t *curnode = drivemap, *prevnode = 0;
>> > +          while (curnode)
>> > +            {
>> > +              prevnode = curnode;
>> > +              curnode = curnode->next;
>> > +              grub_free (prevnode);
>> > +            }
>> > +          drivemap = 0;
>> > +        }
>> > +    }
>> > +  else
>> > +    {
>> > +      if (argc != 2)
>> > +        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required");
>> > +      grub_uint8_t mapfrom = 0;
>> 
>> Please do not use mixed code/declarations here.
> The only mixed code/decl I find is "grub_err_t err = revparse_biosdisk
> (curnode->redirto, &dname);" and it's a call to an internal function
> whose conditions are well-known - safe. All others are declarations with
> initializations, which I find pretty normal.
>
> However, if you mean literally "mixing" them... do you really advocate
> the K&R approach of "declare everything at the function start"? Time
> proved that scheme suboptimal. As you can see, most declarations are
> either at the very start of a _block_ (the scope unit) or near it, right
> after precondition checks.

Yes, or *directly* at the beginning of a block.  But before the
precondition checks.

>> > +      grub_err_t err = parse_biosdisk (args[0], &mapfrom);
>> > +      if (err != GRUB_ERR_NONE)
>> > +        return grub_error (err, "invalid disk or not managed by the BIOS");
>> > +
>> > +      grub_uint8_t mapto = 0xFF;
>> > +      err = tryparse_diskstring (args[1], &mapto);
>> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then? */
>> > +        {    
>> > +          grub_errno = GRUB_ERR_NONE;
>> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
>> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high */
>> > +            return grub_error (grub_errno,
>> > +                              "Target specifier must be of the form (fdN) or "
>> > +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
>> > +                              "number between 0 and 255");
>> > +          else mapto = (grub_uint8_t)num;
>> > +        }
>> 
>> Do we really want to support BIOS disk numbers here?  I do not think
>> it is useful.
> I asked the same when I was starting to write this and I was told, more
> or less, that this was the way to go.

Who told you?  I think we should not do this.

>> Can you add newlines between the #defines and the comments, this will
>> get quite messy like this.
> Done.
>
>> 
>> > +    {
>> > +      entries++;
>> > +      curentry = curentry->next;
>> > +    }
>> > +  if (0 == entries)
>> 
>> if (! entries)
> Nope. You C infidels might make no distinction between int and bool, but
> I was raised in the Holy Truth of languages with a boolean type. ^^ 
> Semantically, "entries" represents an integer, and I want to check
> whether it is zero. I only use your form when checking for
> a)boolean-like conditions or b)non-null pointers.
>
> Even this last one would be "wrong" under my guidelines, since ANSI
> specifies that a null pointer is guaranteed to compare equal to zero,
> but in that case alone I let my laziness win and use the Unholy
> int->"boolean" interpretation of C's "if" statement.
>
> Most probably, the compiler will optimize the additional assembly space
> cost away if there's any, so no worries. I find the semantic
> distinctions important to keep, particularly if the cost is zero.

Can you at least change the comparison order? :)

>> (...)
>> > +GRUB_MOD_FINI (drivemap)
>> > +{
>> > +  grub_loader_unregister_preboot (insthandler_hook);
>> > +  insthandler_hook = 0;
>> > +  grub_unregister_command ("drivemap");
>> > +}
>> 
>> I wonder if this is not called, just before GRUB boots a kernel.  That
>> would cause a problem :-)
> AFAIK the "fini" functions are called by grub_machine_fini, which is
> called _after_ the preboot hook.

ok

>> > Index: commands/i386/pc/drivemap_int13h.S
>> > ===================================================================
>> > RCS file: commands/i386/pc/drivemap_int13h.S
>> > diff -N commands/i386/pc/drivemap_int13h.S
>> > --- /dev/null	1 Jan 1970 00:00:00 -0000
>> > +++ commands/i386/pc/drivemap_int13h.S	2 Jul 2008 01:12:36 -0000
> And here we go for the second round xD
>
>> (...)
>> > +
>> > +
>> > +/*
>> > + * 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.
>> > + */
>> >
>> 
>> I do not think this is required.  I mean, we know how GRUB is compiled
>> and how it deals with registers.
> But nevertheless each and every assembly file I looked at states the
> same. It's supposed to be useful when developing; although I did not use
> it, since my only assembly function is not called from C.

ok

>> > +/*
>> > + *  This is the area for all of the special variables.
>> > + */
>> 
>> I don't think this comment is useful.
> I dont remember writing that... Maybe I c-ped it from somewhere?
> Deleted.
>
>> > +#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)
>> > +
>> > +VARIABLE(grub_drivemap_int13_oldhandler)
>> > +  .word 0xdead, 0xbeef
>> > +/* Drivemap module - 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. Pity we're not on
>> > + * amd64: rIP-relative addressing would make life 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)
> I'm disappointed... Not a single comment on this file?? ;)

I am not an assembler/x86 guru ;-)

>> (...)
>> > Index: kern/loader.c
>> > ===================================================================
>> > RCS file: /sources/grub/grub2/kern/loader.c,v
>> > retrieving revision 1.9
>> > diff -u -r1.9 loader.c
>> > --- kern/loader.c	21 Jul 2007 23:32:26 -0000	1.9
>> > +++ kern/loader.c	2 Jul 2008 01:12:45 -0000
>> > @@ -61,11 +61,78 @@
>> >    grub_loader_loaded = 0;
>> >  }
>> >  
>> > +struct hooklist_node
>> > +{
>> > +  grub_err_t (*hook) (void);
>> > +  int abort_on_error;
>> > +  struct hooklist_node *next;
>> > +};
>> 
>> Isn't there something 
> Woot? What do you mean with that? o_O

I think I was distracted when I typed this, so I did not finish the

>> > +static struct hooklist_node *preboot_hooks = 0;
>> > +
>> > +grub_preboot_hookid
>> > +grub_loader_register_preboot(grub_err_t (*hook) (void), int abort_on_error)
>> > +{
>> > +  if (0 == hook)
>> 
>> if (! hook)
> This time you're right, per my "how to write C comparisons" rationale
> stated above. Changed. 
>
>> > +    {
>> > +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hoook must not be null");
>> 
>> hoook
>> 
>> null -> NULL
> Changed too.
>
>> (...)
>> > +
>> > +void
>> > +grub_loader_unregister_preboot(grub_preboot_hookid id)
>> > +{
>> > +  if (0 == id)
>> > +    return;
>> > +  grub_preboot_hookid entry = 0, search = preboot_hooks, previous = 0;
>> >
>> Can you move this up so this is not mixed.  Can you split this into 3 lines?
> As I understand coding style in C-like languages, precondition checks
> (if short enough) and declarations may be mixed as long as they don't
> get confusing; and I don't think this one is too confusing.
>
> On the other hand, variables splitted, but why the obsession with that
> particular rule?

For both issues, it is for consistency.  There is some rationale for
splitting variable declarations, although it does not apply in your
case.  Some people write:

int* pointer1, pointer2;

And sometimes it just gets messy.  The main reason is consistency
though.

>> (...)
>> > +      grub_err_t possible_error = entry->hook();
>> 
>> Just use "err"
> I think I did so in a previous version of this patch, and I was told to
> be a bit more explicit, but... changed.
>
> Phew! That was long, even after removing some parts. Well, this is the
> new version of the patch. Cheers!

I'll review the new patch...

> PS: I already signed the copyright assignment papers and sent them to
> the FSF. They should arrive within this week.

Cool :-)

--
Marco





^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-07-31 19:01     ` Marco Gerards
@ 2008-08-03 23:29       ` Javier Martín
  2008-08-04 20:51         ` Marco Gerards
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-08-03 23:29 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 3109 bytes --]

After your latest replay, I "reevaluated" my stubbornness WRT some of
your advices, and I've changed a few things:

- Variables are now declared (and, if possible, initialized) before
precondition checks, even simple ones. The install_int13_handler
function has not been modified, however, since I find it a bit
nonsensical to put bunch of declarations without an obvious meaning just
after the "else" line:
      grub_uint32_t *ivtslot;
      grub_uint16_t *bpa_freekb;
      grub_size_t total_size;
      grub_uint16_t payload_sizekb;
      grub_uint8_t *handler_base;
      int13map_node_t *handler_map;
      grub_uint32_t ivtentry;
- Only one declaration per line: even though C is a bit absurd in not
recognizing T* as a first class type and instead thinking of * as a
qualifier to the variable name; and even though my code does not incur
into such ambiguities.
- Comments moved as you required, reworded as needed
- Extensive printf showing quasi-help in the "show" mode trimmed down to
just the first sentence.
- Internal helper functions now use the standard error handling, i.e.
return grub_error (err, fmt, ...)
- Comment about the strange "void" type instead of "void*" rephrased to
be clearer

There is, however, one point in which I keep my objection: comparisons
between a variable and a constant should be of the form CONSTANT ==
variable and not in the reverse order, since an erroneous but quite
possible change of == for = results in a compile-time error instead of a
_extremely_ difficult to trace runtime bug. Such kind of bugs are quite
excruciating to find in userspace applications within an IDE, so I can't
even consider the pain to debug them in a bootloader.

WRT accepting raw BIOS disk numbers, I agree with you in principle, but
I'm keeping the functionality for now, since I don't quite like the
"drivemap (hd0) (hd1)" syntax - which device is which?. I'd rather have
something akin to "drivemap (hd0) (bios:hd1)", but I want to hear the
opinions of people in this list.

The new version of the patch is attached, and here is my suggested CLog:

2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
        * commands/i386/pc/drivemap.c : New file.
        * commands/i386/pc/drivemap_int13h.S : New file.
        * conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
        (drivemap_mod_SOURCES) : New variable
        (drivemap_mod_ASFLAGS) : Likewise
        (drivemap_mod_CFLAGS) : Likewise
        (drivemap_mod_LDFLAGS) : Likewise
        * include/grub/loader.h (grub_loader_register_preboot) : New
        function prototype. Register a new pre-boot handler
        (grub_loader_unregister_preboot) : Likewise. Unregister handler
        (grub_preboot_hookid) : New typedef. Registered hook "handle"
        * kern/loader.c (grub_loader_register_preboot) : New function.
        (grub_loader_unregister_preboot) : Likewise.
        (preboot_hooks) : New variable. Linked list of preboot hooks
        (grub_loader_boot) : Call the list of preboot-hooks before the
        actual loader

[-- Attachment #1.2: drivemap.patch.4 --]
[-- Type: text/x-patch, Size: 24761 bytes --]

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,417 @@
+/* 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/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},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
+
+/* Realmode far ptr = 2 * 16b */
+extern grub_uint32_t grub_drivemap_int13_oldhandler;
+/* Size of the section to be copied */
+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;
+extern grub_symbol_t grub_drivemap_int13_handler_base;
+extern grub_symbol_t grub_drivemap_int13_mapstart;
+
+void grub_drivemap_int13_handler (void);
+
+/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
+
+
+static grub_preboot_hookid insthandler_hook;
+
+typedef struct drivemap_node
+{
+  grub_uint8_t newdrive;
+  grub_uint8_t redirto;
+  struct drivemap_node *next;
+} drivemap_node_t;
+
+static drivemap_node_t *drivemap = 0;
+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) /* Found.  */
+    {
+      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 || 0 == *name)
+    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
+        return GRUB_ERR_NONE;
+      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
+    }
+}
+
+/* Given a BIOS disk number, returns its GRUB device name if it exists.
+   For nonexisting BIOS dnums, this function returns 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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 %d 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 || 0 == *str)
+    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[0].set)
+    {
+      /* Show: list mappings.  */
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives.\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].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;
+
+      err = tryparse_diskstring (args[1], &mapto);
+      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */
+        {    
+          grub_errno = GRUB_ERR_NONE;
+          unsigned long num = grub_strtoul (args[1], 0, 0);
+          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
+            return grub_error (grub_errno,
+                              "Target specifier must be of the form (fdN) or "
+                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
+                              "number between 0 and 255");
+          else mapto = (grub_uint8_t)num;
+        }
+      
+      if (mapto == mapfrom)  /* Reset to default.  */
+        {
+          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
+          drivemap_remove (mapfrom);
+        }
+      else  /* Map.  */
+        {
+          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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;
+  while (curentry)  /* Count entries to prepare a contiguous map block.  */
+    {
+      entries++;
+      curentry = curentry->next;
+    }
+  if (0 == entries)
+    {
+      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
+                  "an int13h handler is not required.");
+      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
+    }
+  else
+    {
+      grub_dprintf (MODNAME, "Installing int13h handler...\n");
+      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
+      
+      /* Save the pointer to the old int13h 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:
+         BDA offset 0x13 contains the top of such memory.  */
+      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
+      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
+      grub_size_t total_size = grub_drivemap_int13_size
+                            + (entries + 1) * sizeof(int13map_node_t);
+      grub_uint16_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 chunk to reserved area.  */
+      grub_uint8_t *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;
+      int13map_node_t *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.  */
+      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
+                         "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,118 @@
+/*
+ *  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)
+
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - 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: 1766)
+++ conf/i386-pc.rmk	(copia de trabajo)
@@ -158,7 +158,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
+	aout.mod _bsd.mod bsd.mod drivemap.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -325,4 +325,11 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+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: 1766)
+++ include/grub/loader.h	(copia de trabajo)
@@ -37,6 +37,22 @@
 /* Unset current loader, if any.  */
 void EXPORT_FUNC(grub_loader_unset) (void);
 
+typedef struct hooklist_node *grub_preboot_hookid;
+
+/* Register a function to be called before the boot hook.  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 errno and errmsg.  However, if the call is successful
+   those variables are _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: kern/loader.c
===================================================================
--- kern/loader.c	(revisión: 1766)
+++ kern/loader.c	(copia de trabajo)
@@ -61,11 +61,82 @@
   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)
+{
+  if (!hook)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
+      return 0;
+    }
+  grub_preboot_hookid 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 (0 == id)
+    return;
+
+  while (search)
+    {
+      if (search == id)
+        {
+          entry = search;
+          break;
+        }
+      previous = search;
+      search = search->next;
+    }
+  if (entry) /* Found */
+    {
+      if (previous)
+        previous->next = entry->next;
+      else preboot_hooks = entry->next; /* Entry was head of list */
+      grub_free (entry);
+    }
+}
+
 grub_err_t
 grub_loader_boot (void)
 {
   if (! grub_loader_loaded)
     return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
+  
+  grub_preboot_hookid entry = preboot_hooks;
+  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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-03 23:29       ` Javier Martín
@ 2008-08-04 20:51         ` Marco Gerards
  2008-08-04 23:10           ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-08-04 20:51 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín <lordhabbit@gmail.com> writes:

> After your latest replay, I "reevaluated" my stubbornness WRT some of
> your advices, and I've changed a few things:
>
> - Variables are now declared (and, if possible, initialized) before
> precondition checks, even simple ones. The install_int13_handler
> function has not been modified, however, since I find it a bit
> nonsensical to put bunch of declarations without an obvious meaning just
> after the "else" line:
>       grub_uint32_t *ivtslot;
>       grub_uint16_t *bpa_freekb;
>       grub_size_t total_size;
>       grub_uint16_t payload_sizekb;
>       grub_uint8_t *handler_base;
>       int13map_node_t *handler_map;
>       grub_uint32_t ivtentry;

Please understand me correctly.  Code just has to be written according
to our coding standards before it can and will be included.  We can
discuss things endlessly but we will simply stick to the GNU Coding
Standards as you might expect.

I hope you can appreciate that all code of GRUB has the same coding
style, if you like this style or not.

> - Only one declaration per line: even though C is a bit absurd in not
> recognizing T* as a first class type and instead thinking of * as a
> qualifier to the variable name; and even though my code does not incur
> into such ambiguities.
> - Comments moved as you required, reworded as needed
> - Extensive printf showing quasi-help in the "show" mode trimmed down to
> just the first sentence.
> - Internal helper functions now use the standard error handling, i.e.
> return grub_error (err, fmt, ...)
> - Comment about the strange "void" type instead of "void*" rephrased to
> be clearer

Thanks a lot!

> There is, however, one point in which I keep my objection: comparisons
> between a variable and a constant should be of the form CONSTANT ==
> variable and not in the reverse order, since an erroneous but quite
> possible change of == for = results in a compile-time error instead of a
> _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
> excruciating to find in userspace applications within an IDE, so I can't
> even consider the pain to debug them in a bootloader.

I understand your concern, nevertheless, can you please change it?

> WRT accepting raw BIOS disk numbers, I agree with you in principle, but
> I'm keeping the functionality for now, since I don't quite like the
> "drivemap (hd0) (hd1)" syntax - which device is which?. I'd rather have
> something akin to "drivemap (hd0) (bios:hd1)", but I want to hear the
> opinions of people in this list.

I personally do not care a lot if BIOS disk numbers are used.
Although I do not see why it is useful.

As for the syntax, I would prefer something more GRUB2ish, like:

map --bios=(hd0) --os=(hd1)

Or so, perhaps the long argument names can be chosen in a more clever
way :-)


> The new version of the patch is attached, and here is my suggested CLog:
>
> 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>

(newline)

>         * commands/i386/pc/drivemap.c : New file.
>         * commands/i386/pc/drivemap_int13h.S : New file.
>         * conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
>         (drivemap_mod_SOURCES) : New variable
>         (drivemap_mod_ASFLAGS) : Likewise
>         (drivemap_mod_CFLAGS) : Likewise
>         (drivemap_mod_LDFLAGS) : Likewise
>         * include/grub/loader.h (grub_loader_register_preboot) : New
>         function prototype. Register a new pre-boot handler

No need how the change is used or why it was added.

>         (grub_loader_unregister_preboot) : Likewise. Unregister handler

Same here.

>         (grub_preboot_hookid) : New typedef. Registered hook "handle"

Same here.

>         * kern/loader.c (grub_loader_register_preboot) : New function.
>         (grub_loader_unregister_preboot) : Likewise.
>         (preboot_hooks) : New variable. Linked list of preboot hooks

Same here.

>         (grub_loader_boot) : Call the list of preboot-hooks before the
>         actual loader

What's the `'?

Please do not add a space before the ":" 


Some comments can be found below.  Can you please fix the code mention
in the review and similar code?  I really want the code to be just
like any other GRUB code.  Please understand that we have to maintain
this in the future.  If everyone would use his own codingstyle, GRUB
would become unmaintainable.

> 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,417 @@
> +/* 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/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},
> +  {0, 0, 0, 0, 0, 0}
> +};
> +
> +/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
> +
> +/* Realmode far ptr = 2 * 16b */
> +extern grub_uint32_t grub_drivemap_int13_oldhandler;
> +/* Size of the section to be copied */
> +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;
> +extern grub_symbol_t grub_drivemap_int13_handler_base;
> +extern grub_symbol_t grub_drivemap_int13_mapstart;
> +
> +void grub_drivemap_int13_handler (void);

The lines above belong in a header file.

> +/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
> +
> +
> +static grub_preboot_hookid insthandler_hook;
> +
> +typedef struct drivemap_node
> +{
> +  grub_uint8_t newdrive;
> +  grub_uint8_t redirto;
> +  struct drivemap_node *next;
> +} drivemap_node_t;
> +
> +static drivemap_node_t *drivemap = 0;

No need to set this to zero.

> +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)
> +

Please do not add a newline here.

> +{
> +  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) /* Found.  */

You forgot one.

> +    {
> +      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 || 0 == *name)
> +    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
> +        return GRUB_ERR_NONE;
> +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
> +    }
> +}
> +
> +/* Given a BIOS disk number, returns its GRUB device name if it exists.
> +   For nonexisting BIOS dnums, this function returns ERR_UNKNOWN_DEVICE.  */

This is 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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 %d not found", dnum);

Please change this.

> +}
> +
> +/* 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 || 0 == *str)
> +    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;

Can you put this between braces, now comment was added.

> +      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[0].set)
> +    {
> +      /* Show: list mappings.  */
> +      if (!drivemap)
> +        grub_printf ("No drives have been remapped");
> +      else
> +        {
> +          grub_printf ("Showing only remapped drives.\n");
> +          grub_printf ("Mapped\tGRUB\n");
> +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
> +              curnode = curnode->next;
> +            }
> +        }
> +    }
> +  else if (state[1].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;
> +
> +      err = tryparse_diskstring (args[1], &mapto);
> +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */

Please move this up.

> +        {    
> +          grub_errno = GRUB_ERR_NONE;
> +          unsigned long num = grub_strtoul (args[1], 0, 0);
> +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
> +            return grub_error (grub_errno,
> +                              "Target specifier must be of the form (fdN) or "
> +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
> +                              "number between 0 and 255");
> +          else mapto = (grub_uint8_t)num;
> +        }
> +      
> +      if (mapto == mapfrom)  /* Reset to default.  */

Same here.

> +        {
> +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
> +          drivemap_remove (mapfrom);
> +        }
> +      else  /* Map.  */

Please move the comment inside the braces below.

> +        {
> +          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
> +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
> +        }
> +    }
> +
> +  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;
> +  while (curentry)  /* Count entries to prepare a contiguous map block.  */

...

> +    {
> +      entries++;
> +      curentry = curentry->next;
> +    }
> +  if (0 == entries)

I know this is what you prefer, but can you change this nevertheless?

> +    {
> +      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
> +                  "an int13h handler is not required.");
> +      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
> +    }
> +  else
> +    {
> +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
> +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
> +      
> +      /* Save the pointer to the old int13h 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:
> +         BDA offset 0x13 contains the top of such memory.  */
> +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
> +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
> +      grub_size_t total_size = grub_drivemap_int13_size
> +                            + (entries + 1) * sizeof(int13map_node_t);
> +      grub_uint16_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 chunk to reserved area.  */
> +      grub_uint8_t *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;
> +      int13map_node_t *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.  */
> +      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
> +                         "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,118 @@
> +/*
> + *  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)
> +
> +VARIABLE(grub_drivemap_int13_oldhandler)
> +  .word 0xdead, 0xbeef
> +/* Drivemap module - 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: 1766)
> +++ conf/i386-pc.rmk	(copia de trabajo)
> @@ -158,7 +158,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
> +	aout.mod _bsd.mod bsd.mod drivemap.mod
>  
>  # For biosdisk.mod.
>  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
> @@ -325,4 +325,11 @@
>  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
>  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
>  
> +# For drivemap.mod.
> +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: 1766)
> +++ include/grub/loader.h	(copia de trabajo)
> @@ -37,6 +37,22 @@
>  /* Unset current loader, if any.  */
>  void EXPORT_FUNC(grub_loader_unset) (void);
>  
> +typedef struct hooklist_node *grub_preboot_hookid;
> +
> +/* Register a function to be called before the boot hook.  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 errno and errmsg.  However, if the call is successful
> +   those variables are _not_ modified. */

No need to mention errmsg, it's internal to GRUB.  As for errno (which
is grub_errno, actually) it does not need to be mentioned, otherwise
we would have to do so everywhere.  Please capitalize HOOK and
ABORT_ON_ERROR in the comments above.

> +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.  */

Nitpick: "loader.  This..."

>  grub_err_t EXPORT_FUNC(grub_loader_boot) (void);
> Index: kern/loader.c
> ===================================================================
> --- kern/loader.c	(revisión: 1766)
> +++ kern/loader.c	(copia de trabajo)
> @@ -61,11 +61,82 @@
>    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)
> +{
> +  if (!hook)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
> +      return 0;
> +    }
> +  grub_preboot_hookid newentry = grub_malloc (sizeof (struct hooklist_node));

Mixed declarations/code.

> +  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)

"preboot (grub"

> +{
> +  grub_preboot_hookid entry = 0;
> +  grub_preboot_hookid search = preboot_hooks;
> +  grub_preboot_hookid previous = 0;
> +
> +  if (0 == id)
> +    return;

...

> +  while (search)
> +    {
> +      if (search == id)
> +        {
> +          entry = search;
> +          break;
> +        }
> +      previous = search;
> +      search = search->next;
> +    }
> +  if (entry) /* Found */

...

> +    {
> +      if (previous)
> +        previous->next = entry->next;
> +      else preboot_hooks = entry->next; /* Entry was head of list */
> +      grub_free (entry);
> +    }
> +}
> +
>  grub_err_t
>  grub_loader_boot (void)
>  {
>    if (! grub_loader_loaded)
>      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
> +  
> +  grub_preboot_hookid entry = preboot_hooks;

Mixed declarations/code.

> +  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
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-04 20:51         ` Marco Gerards
@ 2008-08-04 23:10           ` Javier Martín
  2008-08-05  0:50             ` Isaac Dupree
                               ` (3 more replies)
  0 siblings, 4 replies; 47+ messages in thread
From: Javier Martín @ 2008-08-04 23:10 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 38250 bytes --]

Hi there,

El lun, 04-08-2008 a las 22:51 +0200, Marco Gerards escribió:
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> > After your latest replay, I "reevaluated" my stubbornness WRT some of
> > your advices, and I've changed a few things:
> >
> > - Variables are now declared (and, if possible, initialized) before
> > precondition checks, even simple ones. The install_int13_handler
> > function has not been modified, however, since I find it a bit
> > nonsensical to put bunch of declarations without an obvious meaning just
> > after the "else" line:
> >       grub_uint32_t *ivtslot;
> >       grub_uint16_t *bpa_freekb;
> >       grub_size_t total_size;
> >       grub_uint16_t payload_sizekb;
> >       grub_uint8_t *handler_base;
> >       int13map_node_t *handler_map;
> >       grub_uint32_t ivtentry;
> 
> Please understand me correctly.  Code just has to be written according
> to our coding standards before it can and will be included.  We can
> discuss things endlessly but we will simply stick to the GNU Coding
> Standards as you might expect.
> 
> I hope you can appreciate that all code of GRUB has the same coding
> style, if you like this style or not.
Big sigh. Function modified to conform to your preciousss coding style.
I might look a bit childish or arrogant saying this, but what is the
point upholding a coding style that, no matter how consistent it is,
hinders readability. With so much local variables, they are hard to keep
track of no matter the coding style, but with the old ("mixed, heretic")
style there was not need for a comment before each declaration: they
were declared and used when needed instead of at the start of a block,
because that function is inherently linear, not block-structured.
> 
> > - Only one declaration per line: even though C is a bit absurd in not
> > recognizing T* as a first class type and instead thinking of * as a
> > qualifier to the variable name; and even though my code does not incur
> > into such ambiguities.
> > - Comments moved as you required, reworded as needed
> > - Extensive printf showing quasi-help in the "show" mode trimmed down to
> > just the first sentence.
> > - Internal helper functions now use the standard error handling, i.e.
> > return grub_error (err, fmt, ...)
> > - Comment about the strange "void" type instead of "void*" rephrased to
> > be clearer
> 
> Thanks a lot!
> 
> > There is, however, one point in which I keep my objection: comparisons
> > between a variable and a constant should be of the form CONSTANT ==
> > variable and not in the reverse order, since an erroneous but quite
> > possible change of == for = results in a compile-time error instead of a
> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
> > excruciating to find in userspace applications within an IDE, so I can't
> > even consider the pain to debug them in a bootloader.
> 
> I understand your concern, nevertheless, can you please change it?
You understand my concern, but seemingly do not understand that in order
to conform to the Holy Coding Style you are asking me to write code that
can become buggy (and with a very hard to trace bug) with a simple
deltion! (point: did you notice that last word _without_ a spelling
checker? Now try to do so in a multithousand-line program).

While tools like `svn diff' can help in these kind of problems, their
utility is greatly diminished if the offending change is part of a
multi-line change. Besides, sometimes checks like "if (a=b)", or more
frequently "if (a=f())" are intentionally used in C, so the error might
become even more difficult to spot and correct. I ask for a deep
reflection on this issue: maybe I'm dead wrong and an arrogant brat in
my attempt to tackle the Holy GNU Coding Standards, but I'd like to ask
input from more people on this.
> 
> > WRT accepting raw BIOS disk numbers, I agree with you in principle, but
> > I'm keeping the functionality for now, since I don't quite like the
> > "drivemap (hd0) (hd1)" syntax - which device is which?. I'd rather have
> > something akin to "drivemap (hd0) (bios:hd1)", but I want to hear the
> > opinions of people in this list.
> 
> I personally do not care a lot if BIOS disk numbers are used.
> Although I do not see why it is useful.
> 
> As for the syntax, I would prefer something more GRUB2ish, like:
> 
> map --bios=(hd0) --os=(hd1)
I agree. What about "grub" for the source drive instead of "os", with
"bios" being the target drive? I mean:
	drivemap --grub=(hd1) --bios=(hd0)
Would map 0x80 in the BIOS to grub's (hd1) drive which will most likely
be BIOS drive 0x81.
However, I prefer not to change it right now. Maybe when there are no
other issues WRT to this patch we can set on to tackle this one.
> 
> Or so, perhaps the long argument names can be chosen in a more clever
> way :-)
> > The new version of the patch is attached, and here is my suggested CLog:
> >
> > 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
> 
> (newline)
> 
> >         * commands/i386/pc/drivemap.c : New file.
> >         * commands/i386/pc/drivemap_int13h.S : New file.
> >         * conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
> >         (drivemap_mod_SOURCES) : New variable
> >         (drivemap_mod_ASFLAGS) : Likewise
> >         (drivemap_mod_CFLAGS) : Likewise
> >         (drivemap_mod_LDFLAGS) : Likewise
> >         * include/grub/loader.h (grub_loader_register_preboot) : New
> >         function prototype. Register a new pre-boot handler
> 
> No need how the change is used or why it was added.
> 
> >         (grub_loader_unregister_preboot) : Likewise. Unregister handler
> 
> Same here.
> 
> >         (grub_preboot_hookid) : New typedef. Registered hook "handle"
> 
> Same here.
> 
> >         * kern/loader.c (grub_loader_register_preboot) : New function.
> >         (grub_loader_unregister_preboot) : Likewise.
> >         (preboot_hooks) : New variable. Linked list of preboot hooks
> 
> Same here.
> 
> >         (grub_loader_boot) : Call the list of preboot-hooks before the
> >         actual loader
> 
> What's the `'?
The what? o_O
> 
> Please do not add a space before the ":" 
Ok, everything corrected. New CL entry:

2008-08-XX  Javier Martin  <lordhabbit@gmail.com>

        * commands/i386/pc/drivemap.c: New file.
        * commands/i386/pc/drivemap_int13h.S: New file.
        * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod
        (drivemap_mod_SOURCES): New variable
        (drivemap_mod_ASFLAGS): Likewise
        (drivemap_mod_CFLAGS): Likewise
        (drivemap_mod_LDFLAGS): Likewise
        * include/grub/loader.h (grub_loader_register_preboot): New
        function prototype.
        (grub_loader_unregister_preboot): Likewise.
        (grub_preboot_hookid): New typedef.
        * kern/loader.c (grub_loader_register_preboot): New function.
        (grub_loader_unregister_preboot): Likewise.
        (preboot_hooks): New variable.
        (grub_loader_boot): Call the list of preboot-hooks before the
        actual loader
> 
> 
> Some comments can be found below.  Can you please fix the code mention
> in the review and similar code?  I really want the code to be just
> like any other GRUB code.  Please understand that we have to maintain
> this in the future.  If everyone would use his own codingstyle, GRUB
> would become unmaintainable.
> 
> > 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,417 @@
> > +/* 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/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},
> > +  {0, 0, 0, 0, 0, 0}
> > +};
> > +
> > +/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
> > +
> > +/* Realmode far ptr = 2 * 16b */
> > +extern grub_uint32_t grub_drivemap_int13_oldhandler;
> > +/* Size of the section to be copied */
> > +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;
> > +extern grub_symbol_t grub_drivemap_int13_handler_base;
> > +extern grub_symbol_t grub_drivemap_int13_mapstart;
> > +
> > +void grub_drivemap_int13_handler (void);
> 
> The lines above belong in a header file.
True, but they are used in a single file in the whole project and thus I
see it pointless to extract an unneeded header, which would become one
more SVN object to track. However, if you insist I will split the header
file at once. In particular, I think the grub_symbol_t typedef should go
into the "standard" GRUB headers somehow (but not in symbol.h, which is
included from assembly files).
> 
> > +/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
> > +
> > +
> > +static grub_preboot_hookid insthandler_hook;
> > +
> > +typedef struct drivemap_node
> > +{
> > +  grub_uint8_t newdrive;
> > +  grub_uint8_t redirto;
> > +  struct drivemap_node *next;
> > +} drivemap_node_t;
> > +
> > +static drivemap_node_t *drivemap = 0;
> 
> No need to set this to zero.
Yes, you said so already, but I wanted to make the initial state very
explicit to a future developer, since that variable is checked against
zero in several points. Given that the added source size is four bytes
and the added binary size is _zero_, is all the fuss really necessary?
Notice that I changed the other variable in which you pointed out this
issue, because it is not checked against zero anywhere.
> 
> > +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)
> > +
> 
> Please do not add a newline here.
Oops, sorry. I forgot to remove it when moving the comment
> 
> > +{
> > +  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) /* Found.  */
> 
> You forgot one.
Corrected. Sorry.
> 
> > +    {
> > +      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 || 0 == *name)
> > +    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
> > +        return GRUB_ERR_NONE;
> > +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
> > +    }
> > +}
> > +
> > +/* Given a BIOS disk number, returns its GRUB device name if it exists.
> > +   For nonexisting BIOS dnums, this function returns ERR_UNKNOWN_DEVICE.  */
> 
> This is GRUB_ERR_UNKNOWN_DEVICE
I know, I consciously left the GRUB_ part out because 1) it would
require the line to be split and 2) that prefix is all over the place.
Corrected, however.
> 
> > +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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 %d not found", dnum);
> 
> Please change this.
Em... to what? What is the problem? Do you want me to reverse the
comparison? i.e. if (!found) { return error; } else { return ok; }
> 
> > +}
> > +
> > +/* 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 || 0 == *str)
> > +    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;
> 
> Can you put this between braces, now comment was added.
Done.
> 
> > +      else
> > +        {
> > +          bios_num |= drivenum;
> > +          if (output)
> > +            *output = bios_num;
> > +          return GRUB_ERR_NONE;
> > +        }
> > +    }
> > +  else goto fail;
> 
> ...
What's the problem here? The lack of braces? The goto (as used in the
ext2 code)?
> 
> > +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[0].set)
> > +    {
> > +      /* Show: list mappings.  */
> > +      if (!drivemap)
> > +        grub_printf ("No drives have been remapped");
> > +      else
> > +        {
> > +          grub_printf ("Showing only remapped drives.\n");
> > +          grub_printf ("Mapped\tGRUB\n");
> > +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
> > +              curnode = curnode->next;
> > +            }
> > +        }
> > +    }
> > +  else if (state[1].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 */
> 
> ".  */
Done
> 
> > +      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;
> > +
> > +      err = tryparse_diskstring (args[1], &mapto);
> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */
> 
> Please move this up.
Done.
> 
> > +        {    
> > +          grub_errno = GRUB_ERR_NONE;
> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
> > +            return grub_error (grub_errno,
> > +                              "Target specifier must be of the form (fdN) or "
> > +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
> > +                              "number between 0 and 255");
> > +          else mapto = (grub_uint8_t)num;
> > +        }
> > +      
> > +      if (mapto == mapfrom)  /* Reset to default.  */
> 
> Same here.
Done.
> 
> > +        {
> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
> > +          drivemap_remove (mapfrom);
> > +        }
> > +      else  /* Map.  */
> 
> Please move the comment inside the braces below.
Done, and reworded.
> 
> > +        {
> > +          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
> > +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
> > +        }
> > +    }
> > +
> > +  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;
> > +  while (curentry)  /* Count entries to prepare a contiguous map block.  */
> 
> ...
Comment moved up.
> 
> > +    {
> > +      entries++;
> > +      curentry = curentry->next;
> > +    }
> > +  if (0 == entries)
> 
> I know this is what you prefer, but can you change this nevertheless?
I refer to my objection near the top of the post.
> 
> > +    {
> > +      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
> > +                  "an int13h handler is not required.");
> > +      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
> > +    }
> > +  else
> > +    {
> > +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
> > +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
> > +      
> > +      /* Save the pointer to the old int13h 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:
> > +         BDA offset 0x13 contains the top of such memory.  */
> > +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
> > +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
> > +      grub_size_t total_size = grub_drivemap_int13_size
> > +                            + (entries + 1) * sizeof(int13map_node_t);
> > +      grub_uint16_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 chunk to reserved area.  */
> > +      grub_uint8_t *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;
> > +      int13map_node_t *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.  */
> > +      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
> > +                         "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,118 @@
> > +/*
> > + *  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)
> > +
> > +VARIABLE(grub_drivemap_int13_oldhandler)
> > +  .word 0xdead, 0xbeef
> > +/* Drivemap module - 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: 1766)
> > +++ conf/i386-pc.rmk	(copia de trabajo)
> > @@ -158,7 +158,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
> > +	aout.mod _bsd.mod bsd.mod drivemap.mod
> >  
> >  # For biosdisk.mod.
> >  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
> > @@ -325,4 +325,11 @@
> >  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
> >  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
> >  
> > +# For drivemap.mod.
> > +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: 1766)
> > +++ include/grub/loader.h	(copia de trabajo)
> > @@ -37,6 +37,22 @@
> >  /* Unset current loader, if any.  */
> >  void EXPORT_FUNC(grub_loader_unset) (void);
> >  
> > +typedef struct hooklist_node *grub_preboot_hookid;
> > +
> > +/* Register a function to be called before the boot hook.  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 errno and errmsg.  However, if the call is successful
> > +   those variables are _not_ modified. */
> 
> No need to mention errmsg, it's internal to GRUB.  As for errno (which
> is grub_errno, actually) it does not need to be mentioned, otherwise
> we would have to do so everywhere.  Please capitalize HOOK and
> ABORT_ON_ERROR in the comments above.
Done. "hook" removed because it referred to the loader module boot
function.
> 
> > +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.  */
> 
> Nitpick: "loader.  This..."
Are you a bot? ¬¬ Corrected
> 
> >  grub_err_t EXPORT_FUNC(grub_loader_boot) (void);
> > Index: kern/loader.c
> > ===================================================================
> > --- kern/loader.c	(revisión: 1766)
> > +++ kern/loader.c	(copia de trabajo)
> > @@ -61,11 +61,82 @@
> >    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)
> > +{
> > +  if (!hook)
> > +    {
> > +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
> > +      return 0;
> > +    }
> > +  grub_preboot_hookid newentry = grub_malloc (sizeof (struct hooklist_node));
> 
> Mixed declarations/code.
Oops, sorry. I put most of my attention on drivemap.c (and even then
many comments slipped through). Corrected.
> 
> > +  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)
> 
> "preboot (grub"
Corrected on both functions ;)
> 
> > +{
> > +  grub_preboot_hookid entry = 0;
> > +  grub_preboot_hookid search = preboot_hooks;
> > +  grub_preboot_hookid previous = 0;
> > +
> > +  if (0 == id)
> > +    return;
> 
> ...
... xD
> 
> > +  while (search)
> > +    {
> > +      if (search == id)
> > +        {
> > +          entry = search;
> > +          break;
> > +        }
> > +      previous = search;
> > +      search = search->next;
> > +    }
> > +  if (entry) /* Found */
> 
> ...
Comment removed, was unnecessary.
> 
> > +    {
> > +      if (previous)
> > +        previous->next = entry->next;
> > +      else preboot_hooks = entry->next; /* Entry was head of list */
> > +      grub_free (entry);
> > +    }
> > +}
> > +
> >  grub_err_t
> >  grub_loader_boot (void)
> >  {
> >    if (! grub_loader_loaded)
> >      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
> > +  
> > +  grub_preboot_hookid entry = preboot_hooks;
> 
> Mixed declarations/code.
Moved the whole line up.
> 
> > +  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
> > Grub-devel@gnu.org
> > http://lists.gnu.org/mailman/listinfo/grub-devel
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

New version of the patch follows. I must say that the discussion here
has become pretty interesting without degrading into flamewarring - who
whats to call Mr. Torvalds and Prof. Tanembaum? ^^

[-- Attachment #1.2: drivemap.patch.5 --]
[-- Type: text/x-patch, Size: 25562 bytes --]

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,439 @@
+/* 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/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},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
+
+/* Realmode far ptr = 2 * 16b */
+extern grub_uint32_t grub_drivemap_int13_oldhandler;
+/* Size of the section to be copied */
+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;
+extern grub_symbol_t grub_drivemap_int13_handler_base;
+extern grub_symbol_t grub_drivemap_int13_mapstart;
+
+void grub_drivemap_int13_handler (void);
+
+/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
+
+
+static grub_preboot_hookid insthandler_hook;
+
+typedef struct drivemap_node
+{
+  grub_uint8_t newdrive;
+  grub_uint8_t redirto;
+  struct drivemap_node *next;
+} drivemap_node_t;
+
+static drivemap_node_t *drivemap = 0;
+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 || 0 == *name)
+    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
+        return GRUB_ERR_NONE;
+      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
+    }
+}
+
+/* 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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 || 0 == *str)
+    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[0].set)
+    {
+      /* Show: list mappings.  */
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives.\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].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;
+
+      err = tryparse_diskstring (args[1], &mapto);
+      /* Was that a disk string? If not, maybe a raw num then?  */
+      if (err != GRUB_ERR_NONE)
+        {    
+          grub_errno = GRUB_ERR_NONE;
+          unsigned long num = grub_strtoul (args[1], 0, 0);
+          /* The input could be outside range, or not a number at all.  */
+          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)
+            return grub_error (grub_errno,
+                              "Target specifier must be of the form (fdN) or "
+                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
+                              "number between 0 and 255");
+          else mapto = (grub_uint8_t)num;
+        }
+      
+      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, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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 (0 == entries)
+    {
+      /* 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 " -s | -r | (hdX) newdrivenum",
+                         "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,118 @@
+/*
+ *  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)
+
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - 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: 1771)
+++ conf/i386-pc.rmk	(copia de trabajo)
@@ -158,7 +158,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
+	aout.mod _bsd.mod bsd.mod drivemap.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -325,4 +325,11 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+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: 1771)
+++ 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: kern/loader.c
===================================================================
--- kern/loader.c	(revisión: 1771)
+++ 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 (0 == id)
+    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 */
+      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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-04 23:10           ` Javier Martín
@ 2008-08-05  0:50             ` Isaac Dupree
  2008-08-05  2:38               ` Javier Martín
  2008-08-05 11:23             ` Marco Gerards
                               ` (2 subsequent siblings)
  3 siblings, 1 reply; 47+ messages in thread
From: Isaac Dupree @ 2008-08-05  0:50 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín wrote:
> You understand my concern, but seemingly do not understand that in order
> to conform to the Holy Coding Style you are asking me to write code that
> can become buggy (and with a very hard to trace bug) with a simple
> deltion! (point: did you notice that last word _without_ a spelling
> checker? Now try to do so in a multithousand-line program).

well, maybe a bit off topic.
But I can't imagine how, after code is written, I could 
accidentally delete an "=" character even when editing it. 
I prefer the (to me) intuitive meaning of (variable == 
value) in my own code.  That particular problem has never 
bitten me.  Although, in C++ coding style, a lot more local 
variables are "const" and therefore the error could be 
caught by the compiler anyway.  It seems like an odd 
paranoia to choose.  Say, take "uint32_t".  It's only a 
one-character deletion to become int32_t and then there is 
subtle breakage.  "htons" and many other functions with 
similar names and suffixes.  Etc.?  It's half C language and 
culture, and half inevitable in programming, IMHO.

point[2]: I half did notice the typo (only "half" because 
I've trained myself not to be too distracted by people's 
spelling), and I'm generally more precise when looking at 
code (maybe). ;-)

Anyway, since "they" are more likely to maintain the code in 
the long run than you, in general, the question is whether 
the code is more likely to become buggy by their hacking on 
it, if it follows project coding style or someone else's 
(your) "safer" coding style.  Likely it's safer if using a 
consistent programming style.  Although I personally don't 
see that it's very helpful to have a style that makes things 
down to the order of "==" arguments be consistent within the 
project; haphazard only slows reading the tiniest bit, and I 
don't think it makes a different what order the arguments are...

-Isaac



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-05  0:50             ` Isaac Dupree
@ 2008-08-05  2:38               ` Javier Martín
  2008-08-05 11:31                 ` Marco Gerards
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-08-05  2:38 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 3325 bytes --]

Hi there and thanks for playing The Game!

El lun, 04-08-2008 a las 20:50 -0400, Isaac Dupree escribió:
> Javier Martín wrote:
> > You understand my concern, but seemingly do not understand that in order
> > to conform to the Holy Coding Style you are asking me to write code that
> > can become buggy (and with a very hard to trace bug) with a simple
> > deltion! (point: did you notice that last word _without_ a spelling
> > checker? Now try to do so in a multithousand-line program).
> 
> well, maybe a bit off topic.
> But I can't imagine how, after code is written, I could 
> accidentally delete an "=" character even when editing it. 
> I prefer the (to me) intuitive meaning of (variable == 
> value) in my own code.  That particular problem has never 
> bitten me.  Although, in C++ coding style, a lot more local 
> variables are "const" and therefore the error could be 
> caught by the compiler anyway.  It seems like an odd 
> paranoia to choose.  Say, take "uint32_t".  It's only a 
> one-character deletion to become int32_t and then there is 
> subtle breakage.  "htons" and many other functions with 
> similar names and suffixes.  Etc.?  It's half C language and 
> culture, and half inevitable in programming, IMHO.
Sigh... Maybe I'm just a bit paranoid because I have been bitten by
similar problems (not in "if"s, but mainly in loop condition checks) and
they were painstakingly difficult to hunt down even in a good IDE like
Netbeans: I sometimes considered the possibility of my computer having
lost all sense of logic because I saw it singlestep into the "wrong"
branch until I noticed the = instead of ==. True, the possibilities of
causing such a change are very low.
> 
> point[2]: I half did notice the typo (only "half" because 
> I've trained myself not to be too distracted by people's 
> spelling), and I'm generally more precise when looking at 
> code (maybe). ;-)
So I thought, but even my own code dodges me at times, not to speak of
others' code...
> 
> Anyway, since "they" are more likely to maintain the code in 
> the long run than you, in general, the question is whether 
> the code is more likely to become buggy by their hacking on 
> it, if it follows project coding style or someone else's 
> (your) "safer" coding style.  Likely it's safer if using a 
> consistent programming style.  Although I personally don't 
> see that it's very helpful to have a style that makes things 
> down to the order of "==" arguments be consistent within the 
> project; haphazard only slows reading the tiniest bit, and I 
> don't think it makes a different what order the arguments are...

Hmm... I was partially expecting a flamefest to start. Pity ^^
Well, let's spill a little napalm: the GNU style bracing is extremely
silly!! Why the hell are the "if" and "else" blocks indented differently
in this example?
  if (condition)
    return 0;
  else
    {
      return -1;
    }
Nah, I'm not really bringing that issue, I was just joking, and in fact
I'm reconsidering my objections to the operator== arguments order rule,
even though I still consider my style safer and more sensible. If
someone else wants to express their opinion on that issue, do it fast
before I completely submit to Master Marco's will :D

Habbit

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-04 23:10           ` Javier Martín
  2008-08-05  0:50             ` Isaac Dupree
@ 2008-08-05 11:23             ` Marco Gerards
  2008-08-05 17:18               ` Colin D Bennett
  2008-08-05 11:28             ` Marco Gerards
  2008-08-05 17:15             ` Colin D Bennett
  3 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-08-05 11:23 UTC (permalink / raw)
  To: The development of GRUB 2

Hi Javier,

Javier Martín <lordhabbit@gmail.com> writes:

> El lun, 04-08-2008 a las 22:51 +0200, Marco Gerards escribió:
>> Javier Martín <lordhabbit@gmail.com> writes:
>> 
>> > After your latest replay, I "reevaluated" my stubbornness WRT some of
>> > your advices, and I've changed a few things:
>> >
>> > - Variables are now declared (and, if possible, initialized) before
>> > precondition checks, even simple ones. The install_int13_handler
>> > function has not been modified, however, since I find it a bit
>> > nonsensical to put bunch of declarations without an obvious meaning just
>> > after the "else" line:
>> >       grub_uint32_t *ivtslot;
>> >       grub_uint16_t *bpa_freekb;
>> >       grub_size_t total_size;
>> >       grub_uint16_t payload_sizekb;
>> >       grub_uint8_t *handler_base;
>> >       int13map_node_t *handler_map;
>> >       grub_uint32_t ivtentry;
>> 
>> Please understand me correctly.  Code just has to be written according
>> to our coding standards before it can and will be included.  We can
>> discuss things endlessly but we will simply stick to the GNU Coding
>> Standards as you might expect.
>> 
>> I hope you can appreciate that all code of GRUB has the same coding
>> style, if you like this style or not.
> Big sigh. Function modified to conform to your preciousss coding style.
> I might look a bit childish or arrogant saying this, but what is the
> point upholding a coding style that, no matter how consistent it is,
> hinders readability. With so much local variables, they are hard to keep
> track of no matter the coding style, but with the old ("mixed, heretic")
> style there was not need for a comment before each declaration: they
> were declared and used when needed instead of at the start of a block,
> because that function is inherently linear, not block-structured.

In your opinion it is not a good coding style.  I just avoid
discussions about it because such discussions just waste my time.
Even if you are able to convince me, GRUB is a GNU project and will
remain to use the GCS.


>> > - Only one declaration per line: even though C is a bit absurd in not
>> > recognizing T* as a first class type and instead thinking of * as a
>> > qualifier to the variable name; and even though my code does not incur
>> > into such ambiguities.
>> > - Comments moved as you required, reworded as needed
>> > - Extensive printf showing quasi-help in the "show" mode trimmed down to
>> > just the first sentence.
>> > - Internal helper functions now use the standard error handling, i.e.
>> > return grub_error (err, fmt, ...)
>> > - Comment about the strange "void" type instead of "void*" rephrased to
>> > be clearer
>> 
>> Thanks a lot!
>> 
>> > There is, however, one point in which I keep my objection: comparisons
>> > between a variable and a constant should be of the form CONSTANT ==
>> > variable and not in the reverse order, since an erroneous but quite
>> > possible change of == for = results in a compile-time error instead of a
>> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
>> > excruciating to find in userspace applications within an IDE, so I can't
>> > even consider the pain to debug them in a bootloader.
>> 
>> I understand your concern, nevertheless, can you please change it?
> You understand my concern, but seemingly do not understand that in order
> to conform to the Holy Coding Style you are asking me to write code that
> can become buggy (and with a very hard to trace bug) with a simple
> deltion! (point: did you notice that last word _without_ a spelling
> checker? Now try to do so in a multithousand-line program).

Yes, I did notice it immediately.

The coding style is not holy in any way.  Everyone has its own coding
style.  You must understand I do not want to fully discuss it every
time someone sends in a patch, especially since it will not change
anyways.

> While tools like `svn diff' can help in these kind of problems, their
> utility is greatly diminished if the offending change is part of a
> multi-line change. Besides, sometimes checks like "if (a=b)", or more
> frequently "if (a=f())" are intentionally used in C, so the error might
> become even more difficult to spot and correct. I ask for a deep
> reflection on this issue: maybe I'm dead wrong and an arrogant brat in
> my attempt to tackle the Holy GNU Coding Standards, but I'd like to ask
> input from more people on this.

I will refuse patches with "if (a = f())", if that makes you sleep
better ;-)
 
>> > WRT accepting raw BIOS disk numbers, I agree with you in principle, but
>> > I'm keeping the functionality for now, since I don't quite like the
>> > "drivemap (hd0) (hd1)" syntax - which device is which?. I'd rather have
>> > something akin to "drivemap (hd0) (bios:hd1)", but I want to hear the
>> > opinions of people in this list.
>> 
>> I personally do not care a lot if BIOS disk numbers are used.
>> Although I do not see why it is useful.
>> 
>> As for the syntax, I would prefer something more GRUB2ish, like:
>> 
>> map --bios=(hd0) --os=(hd1)
> I agree. What about "grub" for the source drive instead of "os", with
> "bios" being the target drive? I mean:
> 	drivemap --grub=(hd1) --bios=(hd0)
> Would map 0x80 in the BIOS to grub's (hd1) drive which will most likely
> be BIOS drive 0x81.

It will not change the functionality which GRUB is active.  But it
will change it for the OS that is loaded.  So I do not think --grub=
is a good idea because of this.  As for GRUB Legacy, it confused a lot
of people, so making people explicitly say that it is changed for the
OS, this confusion might go away :-)

> However, I prefer not to change it right now. Maybe when there are no
> other issues WRT to this patch we can set on to tackle this one.

So first I'll review this patch, then you will send in a new one?
 
>> Or so, perhaps the long argument names can be chosen in a more clever
>> way :-)
>> > The new version of the patch is attached, and here is my suggested CLog:
>> >
>> > 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
>> 
>> (newline)
>> 
>> >         * commands/i386/pc/drivemap.c : New file.
>> >         * commands/i386/pc/drivemap_int13h.S : New file.
>> >         * conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
>> >         (drivemap_mod_SOURCES) : New variable
>> >         (drivemap_mod_ASFLAGS) : Likewise
>> >         (drivemap_mod_CFLAGS) : Likewise
>> >         (drivemap_mod_LDFLAGS) : Likewise
>> >         * include/grub/loader.h (grub_loader_register_preboot) : New
>> >         function prototype. Register a new pre-boot handler
>> 
>> No need how the change is used or why it was added.
>> 
>> >         (grub_loader_unregister_preboot) : Likewise. Unregister handler
>> 
>> Same here.
>> 
>> >         (grub_preboot_hookid) : New typedef. Registered hook "handle"
>> 
>> Same here.
>> 
>> >         * kern/loader.c (grub_loader_register_preboot) : New function.
>> >         (grub_loader_unregister_preboot) : Likewise.
>> >         (preboot_hooks) : New variable. Linked list of preboot hooks
>> 
>> Same here.
>> 
>> >         (grub_loader_boot) : Call the list of preboot-hooks before the
>> >         actual loader
>> 
>> What's the `'?
> The what? o_O

I see some weird character in your text.  My font shows it as a block
before every `*'.

>> Please do not add a space before the ":" 
> Ok, everything corrected. New CL entry:
>
> 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
>
>         * commands/i386/pc/drivemap.c: New file.
>         * commands/i386/pc/drivemap_int13h.S: New file.
>         * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod
>         (drivemap_mod_SOURCES): New variable
>         (drivemap_mod_ASFLAGS): Likewise
>         (drivemap_mod_CFLAGS): Likewise
>         (drivemap_mod_LDFLAGS): Likewise
>         * include/grub/loader.h (grub_loader_register_preboot): New
>         function prototype.
>         (grub_loader_unregister_preboot): Likewise.
>         (grub_preboot_hookid): New typedef.
>         * kern/loader.c (grub_loader_register_preboot): New function.
>         (grub_loader_unregister_preboot): Likewise.
>         (preboot_hooks): New variable.
>         (grub_loader_boot): Call the list of preboot-hooks before the
>         actual loader

Please add a `.' after "New variable" and "Likewise", same for the
third and the last sentence.  Sometimes you did it right :-).

>> Some comments can be found below.  Can you please fix the code mention
>> in the review and similar code?  I really want the code to be just
>> like any other GRUB code.  Please understand that we have to maintain
>> this in the future.  If everyone would use his own codingstyle, GRUB
>> would become unmaintainable.
>> 
>> > 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,417 @@
>> > +/* 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/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},
>> > +  {0, 0, 0, 0, 0, 0}
>> > +};
>> > +
>> > +/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
>> > +
>> > +/* Realmode far ptr = 2 * 16b */
>> > +extern grub_uint32_t grub_drivemap_int13_oldhandler;
>> > +/* Size of the section to be copied */
>> > +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;
>> > +extern grub_symbol_t grub_drivemap_int13_handler_base;
>> > +extern grub_symbol_t grub_drivemap_int13_mapstart;
>> > +
>> > +void grub_drivemap_int13_handler (void);
>> 
>> The lines above belong in a header file.
> True, but they are used in a single file in the whole project and thus I
> see it pointless to extract an unneeded header, which would become one
> more SVN object to track. However, if you insist I will split the header
> file at once. In particular, I think the grub_symbol_t typedef should go
> into the "standard" GRUB headers somehow (but not in symbol.h, which is
> included from assembly files).

Please do so.

Other people might want to comment on the symbol change.  They will
most likely miss it if we keep discussing it here ;-).  Can you please
send that in as a separate change to give other the opportunity to
react on it?

>> > +/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
>> > +
>> > +
>> > +static grub_preboot_hookid insthandler_hook;
>> > +
>> > +typedef struct drivemap_node
>> > +{
>> > +  grub_uint8_t newdrive;
>> > +  grub_uint8_t redirto;
>> > +  struct drivemap_node *next;
>> > +} drivemap_node_t;
>> > +
>> > +static drivemap_node_t *drivemap = 0;
>> 
>> No need to set this to zero.
> Yes, you said so already, but I wanted to make the initial state very
> explicit to a future developer, since that variable is checked against
> zero in several points. Given that the added source size is four bytes
> and the added binary size is _zero_, is all the fuss really necessary?
> Notice that I changed the other variable in which you pointed out this
> issue, because it is not checked against zero anywhere.

Please do so anyways.

>> > +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)
>> > +
>> 
>> Please do not add a newline here.
> Oops, sorry. I forgot to remove it when moving the comment

:-)

>> > +  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) /* Found.  */
>> 
>> You forgot one.
> Corrected. Sorry.
>> 
>> > +    {
>> > +      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 || 0 == *name)
>> > +    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
>> > +        return GRUB_ERR_NONE;
>> > +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
>> > +    }
>> > +}
>> > +
>> > +/* Given a BIOS disk number, returns its GRUB device name if it exists.
>> > +   For nonexisting BIOS dnums, this function returns ERR_UNKNOWN_DEVICE.  */
>> 
>> This is GRUB_ERR_UNKNOWN_DEVICE
> I know, I consciously left the GRUB_ part out because 1) it would
> require the line to be split and 2) that prefix is all over the place.
> Corrected, however.
>> 
>> > +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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 %d not found", dnum);
>> 
>> Please change this.
> Em... to what? What is the problem? Do you want me to reverse the
> comparison? i.e. if (!found) { return error; } else { return ok; }

The return is on the same line as the else.

>> > +/* 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 || 0 == *str)
>> > +    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;
>> 
>> Can you put this between braces, now comment was added.
> Done.
>> 
>> > +      else
>> > +        {
>> > +          bios_num |= drivenum;
>> > +          if (output)
>> > +            *output = bios_num;
>> > +          return GRUB_ERR_NONE;
>> > +        }
>> > +    }
>> > +  else goto fail;
>> 
>> ...
> What's the problem here? The lack of braces? The goto (as used in the
> ext2 code)?

goto is on the same line as the else.

>> > +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[0].set)
>> > +    {
>> > +      /* Show: list mappings.  */
>> > +      if (!drivemap)
>> > +        grub_printf ("No drives have been remapped");
>> > +      else
>> > +        {
>> > +          grub_printf ("Showing only remapped drives.\n");
>> > +          grub_printf ("Mapped\tGRUB\n");
>> > +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
>> > +              curnode = curnode->next;
>> > +            }
>> > +        }
>> > +    }
>> > +  else if (state[1].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 */
>> 
>> ".  */
> Done
>> 
>> > +      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;
>> > +
>> > +      err = tryparse_diskstring (args[1], &mapto);
>> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */
>> 
>> Please move this up.
> Done.
>> 
>> > +        {    
>> > +          grub_errno = GRUB_ERR_NONE;
>> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
>> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
>> > +            return grub_error (grub_errno,
>> > +                              "Target specifier must be of the form (fdN) or "
>> > +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
>> > +                              "number between 0 and 255");
>> > +          else mapto = (grub_uint8_t)num;
>> > +        }
>> > +      
>> > +      if (mapto == mapfrom)  /* Reset to default.  */
>> 
>> Same here.
> Done.
>> 
>> > +        {
>> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
>> > +          drivemap_remove (mapfrom);
>> > +        }
>> > +      else  /* Map.  */
>> 
>> Please move the comment inside the braces below.
> Done, and reworded.
>> 
>> > +        {
>> > +          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
>> > +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
>> > +        }
>> > +    }
>> > +
>> > +  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;
>> > +  while (curentry)  /* Count entries to prepare a contiguous map block.  */
>> 
>> ...
> Comment moved up.
>> 
>> > +    {
>> > +      entries++;
>> > +      curentry = curentry->next;
>> > +    }
>> > +  if (0 == entries)
>> 
>> I know this is what you prefer, but can you change this nevertheless?
> I refer to my objection near the top of the post.

I know you object, but did you change it?

>> > +      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
>> > +                  "an int13h handler is not required.");
>> > +      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
>> > +    }
>> > +  else
>> > +    {
>> > +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
>> > +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
>> > +      
>> > +      /* Save the pointer to the old int13h 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:
>> > +         BDA offset 0x13 contains the top of such memory.  */
>> > +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
>> > +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
>> > +      grub_size_t total_size = grub_drivemap_int13_size
>> > +                            + (entries + 1) * sizeof(int13map_node_t);
>> > +      grub_uint16_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 chunk to reserved area.  */
>> > +      grub_uint8_t *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;
>> > +      int13map_node_t *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.  */
>> > +      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
>> > +                         "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,118 @@
>> > +/*
>> > + *  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)
>> > +
>> > +VARIABLE(grub_drivemap_int13_oldhandler)
>> > +  .word 0xdead, 0xbeef
>> > +/* Drivemap module - 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: 1766)
>> > +++ conf/i386-pc.rmk	(copia de trabajo)
>> > @@ -158,7 +158,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
>> > +	aout.mod _bsd.mod bsd.mod drivemap.mod
>> >  
>> >  # For biosdisk.mod.
>> >  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
>> > @@ -325,4 +325,11 @@
>> >  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
>> >  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
>> >  
>> > +# For drivemap.mod.
>> > +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: 1766)
>> > +++ include/grub/loader.h	(copia de trabajo)
>> > @@ -37,6 +37,22 @@
>> >  /* Unset current loader, if any.  */
>> >  void EXPORT_FUNC(grub_loader_unset) (void);
>> >  
>> > +typedef struct hooklist_node *grub_preboot_hookid;
>> > +
>> > +/* Register a function to be called before the boot hook.  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 errno and errmsg.  However, if the call is successful
>> > +   those variables are _not_ modified. */
>> 
>> No need to mention errmsg, it's internal to GRUB.  As for errno (which
>> is grub_errno, actually) it does not need to be mentioned, otherwise
>> we would have to do so everywhere.  Please capitalize HOOK and
>> ABORT_ON_ERROR in the comments above.
> Done. "hook" removed because it referred to the loader module boot
> function.
>> 
>> > +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.  */
>> 
>> Nitpick: "loader.  This..."
> Are you a bot? ¬¬ Corrected

It would make like much simpler if I were ;-).  What makes you think
so?

>> >  grub_err_t EXPORT_FUNC(grub_loader_boot) (void);
>> > Index: kern/loader.c
>> > ===================================================================
>> > --- kern/loader.c	(revisión: 1766)
>> > +++ kern/loader.c	(copia de trabajo)
>> > @@ -61,11 +61,82 @@
>> >    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)
>> > +{
>> > +  if (!hook)
>> > +    {
>> > +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
>> > +      return 0;
>> > +    }
>> > +  grub_preboot_hookid newentry = grub_malloc (sizeof (struct hooklist_node));
>> 
>> Mixed declarations/code.
> Oops, sorry. I put most of my attention on drivemap.c (and even then
> many comments slipped through). Corrected.

Please re-check them, I might have missed things this time...

>> > +  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)
>> 
>> "preboot (grub"
> Corrected on both functions ;)
>> 
>> > +{
>> > +  grub_preboot_hookid entry = 0;
>> > +  grub_preboot_hookid search = preboot_hooks;
>> > +  grub_preboot_hookid previous = 0;
>> > +
>> > +  if (0 == id)
>> > +    return;
>> 
>> ...
> ... xD
>> 
>> > +  while (search)
>> > +    {
>> > +      if (search == id)
>> > +        {
>> > +          entry = search;
>> > +          break;
>> > +        }
>> > +      previous = search;
>> > +      search = search->next;
>> > +    }
>> > +  if (entry) /* Found */
>> 
>> ...
> Comment removed, was unnecessary.
>> 
>> > +    {
>> > +      if (previous)
>> > +        previous->next = entry->next;
>> > +      else preboot_hooks = entry->next; /* Entry was head of list */
>> > +      grub_free (entry);
>> > +    }
>> > +}
>> > +
>> >  grub_err_t
>> >  grub_loader_boot (void)
>> >  {
>> >    if (! grub_loader_loaded)
>> >      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
>> > +  
>> > +  grub_preboot_hookid entry = preboot_hooks;
>> 
>> Mixed declarations/code.
> Moved the whole line up.
>> 
>> > +  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 ();




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-04 23:10           ` Javier Martín
  2008-08-05  0:50             ` Isaac Dupree
  2008-08-05 11:23             ` Marco Gerards
@ 2008-08-05 11:28             ` Marco Gerards
  2008-08-05 16:39               ` Javier Martín
  2008-08-05 17:15             ` Colin D Bennett
  3 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-08-05 11:28 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Javier Martín <lordhabbit@gmail.com> writes:

>> > There is, however, one point in which I keep my objection: comparisons
>> > between a variable and a constant should be of the form CONSTANT ==
>> > variable and not in the reverse order, since an erroneous but quite
>> > possible change of == for = results in a compile-time error instead of a
>> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
>> > excruciating to find in userspace applications within an IDE, so I can't
>> > even consider the pain to debug them in a bootloader.
>> 
>> I understand your concern, nevertheless, can you please change it?
> You understand my concern, but seemingly do not understand that in order
> to conform to the Holy Coding Style you are asking me to write code that
> can become buggy (and with a very hard to trace bug) with a simple
> deltion! (point: did you notice that last word _without_ a spelling
> checker? Now try to do so in a multithousand-line program).

BTW, your patch still contains this, can you please change it before I
go over it again?

I know people who claim that this code will become buggy because we
write it in C.  Should we start accepting patches to rewrite GRUB in
Haskell or whatever? :-)

Really, as a maintainer I should set some standards and stick to it.
Of course not everyone will like me and sometimes I have to act like a
jerk.  But I rather be a jerk, than committing code that do not meet
my expectations.  But please understand, this contribution is highly
appreciated.  However, we want to have something maintainable for the
far future as well :-)

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-05  2:38               ` Javier Martín
@ 2008-08-05 11:31                 ` Marco Gerards
  0 siblings, 0 replies; 47+ messages in thread
From: Marco Gerards @ 2008-08-05 11:31 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Javier Martín <lordhabbit@gmail.com> writes:

>> Anyway, since "they" are more likely to maintain the code in 
>> the long run than you, in general, the question is whether 
>> the code is more likely to become buggy by their hacking on 
>> it, if it follows project coding style or someone else's 
>> (your) "safer" coding style.  Likely it's safer if using a 
>> consistent programming style.  Although I personally don't 
>> see that it's very helpful to have a style that makes things 
>> down to the order of "==" arguments be consistent within the 
>> project; haphazard only slows reading the tiniest bit, and I 
>> don't think it makes a different what order the arguments are...
> 
> Hmm... I was partially expecting a flamefest to start. Pity ^^
> Well, let's spill a little napalm: the GNU style bracing is extremely
> silly!! Why the hell are the "if" and "else" blocks indented differently
> in this example?
>   if (condition)
>     return 0;
>   else
>     {
>       return -1;
>     }
> Nah, I'm not really bringing that issue, I was just joking, and in fact
> I'm reconsidering my objections to the operator== arguments order rule,
> even though I still consider my style safer and more sensible. If
> someone else wants to express their opinion on that issue, do it fast
> before I completely submit to Master Marco's will :D

Please don't be sarcastic, start flame wars or call names.  It will not
help anyone.

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-05 11:28             ` Marco Gerards
@ 2008-08-05 16:39               ` Javier Martín
  2008-08-09 15:33                 ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-08-05 16:39 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 50250 bytes --]

Ok, I will reply to your last three messages on a single post:
Hi there!

El mar, 05-08-2008 a las 13:31 +0200, Marco Gerards escribió: 
> Hi,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> >> Anyway, since "they" are more likely to maintain the code in 
> >> the long run than you, in general, the question is whether 
> >> the code is more likely to become buggy by their hacking on 
> >> it, if it follows project coding style or someone else's 
> >> (your) "safer" coding style.  Likely it's safer if using a 
> >> consistent programming style.  Although I personally don't 
> >> see that it's very helpful to have a style that makes things 
> >> down to the order of "==" arguments be consistent within the 
> >> project; haphazard only slows reading the tiniest bit, and I 
> >> don't think it makes a different what order the arguments are...
> > 
> > Hmm... I was partially expecting a flamefest to start. Pity ^^
> > Well, let's spill a little napalm: the GNU style bracing is extremely
> > silly!! Why the hell are the "if" and "else" blocks indented differently
> > in this example?
> >   if (condition)
> >     return 0;
> >   else
> >     {
> >       return -1;
> >     }
> > Nah, I'm not really bringing that issue, I was just joking, and in fact
> > I'm reconsidering my objections to the operator== arguments order rule,
> > even though I still consider my style safer and more sensible. If
> > someone else wants to express their opinion on that issue, do it fast
> > before I completely submit to Master Marco's will :D
> 
> Please don't be sarcastic, start flame wars or call names.  It will not
> help anyone.

Ok, sorry, picturing you as a whip-cracking dominatrix was really not proper ^^. I was just joking about how fast can flamewars be started.

> 
> --
> Marco
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

El mar, 05-08-2008 a las 13:23 +0200, Marco Gerards escribió:
> Hi Javier,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> > El lun, 04-08-2008 a las 22:51 +0200, Marco Gerards escribió:
> >> Javier Martín <lordhabbit@gmail.com> writes:
> >> 
> >> > After your latest replay, I "reevaluated" my stubbornness WRT some of
> >> > your advices, and I've changed a few things:
> >> >
> >> > - Variables are now declared (and, if possible, initialized) before
> >> > precondition checks, even simple ones. The install_int13_handler
> >> > function has not been modified, however, since I find it a bit
> >> > nonsensical to put bunch of declarations without an obvious meaning just
> >> > after the "else" line:
> >> >       grub_uint32_t *ivtslot;
> >> >       grub_uint16_t *bpa_freekb;
> >> >       grub_size_t total_size;
> >> >       grub_uint16_t payload_sizekb;
> >> >       grub_uint8_t *handler_base;
> >> >       int13map_node_t *handler_map;
> >> >       grub_uint32_t ivtentry;
> >> 
> >> Please understand me correctly.  Code just has to be written according
> >> to our coding standards before it can and will be included.  We can
> >> discuss things endlessly but we will simply stick to the GNU Coding
> >> Standards as you might expect.
> >> 
> >> I hope you can appreciate that all code of GRUB has the same coding
> >> style, if you like this style or not.
> > Big sigh. Function modified to conform to your preciousss coding style.
> > I might look a bit childish or arrogant saying this, but what is the
> > point upholding a coding style that, no matter how consistent it is,
> > hinders readability. With so much local variables, they are hard to keep
> > track of no matter the coding style, but with the old ("mixed, heretic")
> > style there was not need for a comment before each declaration: they
> > were declared and used when needed instead of at the start of a block,
> > because that function is inherently linear, not block-structured.
> 
> In your opinion it is not a good coding style.  I just avoid
> discussions about it because such discussions just waste my time.
> Even if you are able to convince me, GRUB is a GNU project and will
> remain to use the GCS.

Which is fine, but the order of the arguments to == is specified nowhere in the GCS: the order you defend only appears in examples less than five times. Thus, your insistence is a matter of internal consistence within GRUB and is only based on examples in the GCS. Additionally, your previous suggestion to change checks line "entries == 0 " to "!entries" are _against_ the examples of the GCS, even for pointers.

Then, given that the _examples_ in the GCS are non-authoritative, I was advocating a simple change for better resilience against future changes. You want to keep consistency with the current sources, and that I understand, so I will perform the required trivial changes in my code to use the style used in other GRUB code, but I still think it is worse and more error-prone.

> 
> 
> >> > - Only one declaration per line: even though C is a bit absurd in not
> >> > recognizing T* as a first class type and instead thinking of * as a
> >> > qualifier to the variable name; and even though my code does not incur
> >> > into such ambiguities.
> >> > - Comments moved as you required, reworded as needed
> >> > - Extensive printf showing quasi-help in the "show" mode trimmed down to
> >> > just the first sentence.
> >> > - Internal helper functions now use the standard error handling, i.e.
> >> > return grub_error (err, fmt, ...)
> >> > - Comment about the strange "void" type instead of "void*" rephrased to
> >> > be clearer
> >> 
> >> Thanks a lot!
> >> 
> >> > There is, however, one point in which I keep my objection: comparisons
> >> > between a variable and a constant should be of the form CONSTANT ==
> >> > variable and not in the reverse order, since an erroneous but quite
> >> > possible change of == for = results in a compile-time error instead of a
> >> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
> >> > excruciating to find in userspace applications within an IDE, so I can't
> >> > even consider the pain to debug them in a bootloader.
> >> 
> >> I understand your concern, nevertheless, can you please change it?
> > You understand my concern, but seemingly do not understand that in order
> > to conform to the Holy Coding Style you are asking me to write code that
> > can become buggy (and with a very hard to trace bug) with a simple
> > deltion! (point: did you notice that last word _without_ a spelling
> > checker? Now try to do so in a multithousand-line program).
> 
> Yes, I did notice it immediately.
> 
> The coding style is not holy in any way.  Everyone has its own coding
> style.  You must understand I do not want to fully discuss it every
> time someone sends in a patch, especially since it will not change
> anyways.

By the way, I just noticed that you write _normal_ text with two spaces after a dot, just like comments in the code! o_O
> 
> > While tools like `svn diff' can help in these kind of problems, their
> > utility is greatly diminished if the offending change is part of a
> > multi-line change. Besides, sometimes checks like "if (a=b)", or more
> > frequently "if (a=f())" are intentionally used in C, so the error might
> > become even more difficult to spot and correct. I ask for a deep
> > reflection on this issue: maybe I'm dead wrong and an arrogant brat in
> > my attempt to tackle the Holy GNU Coding Standards, but I'd like to ask
> > input from more people on this.
> 
> I will refuse patches with "if (a = f())", if that makes you sleep
> better ;-)
In fact the GCS discourages that use, but allows the same in loop checks, particularly "while" loops.
>  
> >> > WRT accepting raw BIOS disk numbers, I agree with you in principle, but
> >> > I'm keeping the functionality for now, since I don't quite like the
> >> > "drivemap (hd0) (hd1)" syntax - which device is which?. I'd rather have
> >> > something akin to "drivemap (hd0) (bios:hd1)", but I want to hear the
> >> > opinions of people in this list.
> >> 
> >> I personally do not care a lot if BIOS disk numbers are used.
> >> Although I do not see why it is useful.
> >> 
> >> As for the syntax, I would prefer something more GRUB2ish, like:
> >> 
> >> map --bios=(hd0) --os=(hd1)
> > I agree. What about "grub" for the source drive instead of "os", with
> > "bios" being the target drive? I mean:
> > 	drivemap --grub=(hd1) --bios=(hd0)
> > Would map 0x80 in the BIOS to grub's (hd1) drive which will most likely
> > be BIOS drive 0x81.
> 
> It will not change the functionality which GRUB is active.  But it
> will change it for the OS that is loaded.  So I do not think --grub=
> is a good idea because of this.  As for GRUB Legacy, it confused a lot
> of people, so making people explicitly say that it is changed for the
> OS, this confusion might go away :-)
> 
> > However, I prefer not to change it right now. Maybe when there are no
> > other issues WRT to this patch we can set on to tackle this one.
> 
> So first I'll review this patch, then you will send in a new one?

Of course. I meant that I prefer to smash all "syntactic" changes in the patch before introducing a functionality change that could lead to new syntactic issues.
>  
> >> Or so, perhaps the long argument names can be chosen in a more clever
> >> way :-)
> >> > The new version of the patch is attached, and here is my suggested CLog:
> >> >
> >> > 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
> >> 
> >> (newline)
> >> 
> >> >         * commands/i386/pc/drivemap.c : New file.
> >> >         * commands/i386/pc/drivemap_int13h.S : New file.
> >> >         * conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
> >> >         (drivemap_mod_SOURCES) : New variable
> >> >         (drivemap_mod_ASFLAGS) : Likewise
> >> >         (drivemap_mod_CFLAGS) : Likewise
> >> >         (drivemap_mod_LDFLAGS) : Likewise
> >> >         * include/grub/loader.h (grub_loader_register_preboot) : New
> >> >         function prototype. Register a new pre-boot handler
> >> 
> >> No need how the change is used or why it was added.
> >> 
> >> >         (grub_loader_unregister_preboot) : Likewise. Unregister handler
> >> 
> >> Same here.
> >> 
> >> >         (grub_preboot_hookid) : New typedef. Registered hook "handle"
> >> 
> >> Same here.
> >> 
> >> >         * kern/loader.c (grub_loader_register_preboot) : New function.
> >> >         (grub_loader_unregister_preboot) : Likewise.
> >> >         (preboot_hooks) : New variable. Linked list of preboot hooks
> >> 
> >> Same here.
> >> 
> >> >         (grub_loader_boot) : Call the list of preboot-hooks before the
> >> >         actual loader
> >> 
> >> What's the `'?
> > The what? o_O
> 
> I see some weird character in your text.  My font shows it as a block
> before every `*'.
I see nothing: "What's the `'?". Maybe it's some kind of tab?
> 
> >> Please do not add a space before the ":" 
> > Ok, everything corrected. New CL entry:
> >
> > 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
> >
> >         * commands/i386/pc/drivemap.c: New file.
> >         * commands/i386/pc/drivemap_int13h.S: New file.
> >         * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod
> >         (drivemap_mod_SOURCES): New variable
> >         (drivemap_mod_ASFLAGS): Likewise
> >         (drivemap_mod_CFLAGS): Likewise
> >         (drivemap_mod_LDFLAGS): Likewise
> >         * include/grub/loader.h (grub_loader_register_preboot): New
> >         function prototype.
> >         (grub_loader_unregister_preboot): Likewise.
> >         (grub_preboot_hookid): New typedef.
> >         * kern/loader.c (grub_loader_register_preboot): New function.
> >         (grub_loader_unregister_preboot): Likewise.
> >         (preboot_hooks): New variable.
> >         (grub_loader_boot): Call the list of preboot-hooks before the
> >         actual loader
> 
> Please add a `.' after "New variable" and "Likewise", same for the
> third and the last sentence.  Sometimes you did it right :-).
> 
Done. New CL entry will go at the end of this neverending post.

> 
> >> Some comments can be found below.  Can you please fix the code mention
> >> in the review and similar code?  I really want the code to be just
> >> like any other GRUB code.  Please understand that we have to maintain
> >> this in the future.  If everyone would use his own codingstyle, GRUB
> >> would become unmaintainable.
> >> 
> >> > 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,417 @@
> >> > +/* 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/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},
> >> > +  {0, 0, 0, 0, 0, 0}
> >> > +};
> >> > +
> >> > +/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
> >> > +
> >> > +/* Realmode far ptr = 2 * 16b */
> >> > +extern grub_uint32_t grub_drivemap_int13_oldhandler;
> >> > +/* Size of the section to be copied */
> >> > +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;
> >> > +extern grub_symbol_t grub_drivemap_int13_handler_base;
> >> > +extern grub_symbol_t grub_drivemap_int13_mapstart;
> >> > +
> >> > +void grub_drivemap_int13_handler (void);
> >> 
> >> The lines above belong in a header file.
> > True, but they are used in a single file in the whole project and thus I
> > see it pointless to extract an unneeded header, which would become one
> > more SVN object to track. However, if you insist I will split the header
> > file at once. In particular, I think the grub_symbol_t typedef should go
> > into the "standard" GRUB headers somehow (but not in symbol.h, which is
> > included from assembly files).
> 
> Please do so.
> 
> Other people might want to comment on the symbol change.  They will
> most likely miss it if we keep discussing it here ;-).  Can you please
> send that in as a separate change to give other the opportunity to
> react on it?
Ok, so in a first stage I will put the discussed block in a drivemap.h file in include/, then when that's committed start a discussion about moving grub_symbol_t from drivemap.h to another header file.
> 
> >> > +/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
> >> > +
> >> > +
> >> > +static grub_preboot_hookid insthandler_hook;
> >> > +
> >> > +typedef struct drivemap_node
> >> > +{
> >> > +  grub_uint8_t newdrive;
> >> > +  grub_uint8_t redirto;
> >> > +  struct drivemap_node *next;
> >> > +} drivemap_node_t;
> >> > +
> >> > +static drivemap_node_t *drivemap = 0;
> >> 
> >> No need to set this to zero.
> > Yes, you said so already, but I wanted to make the initial state very
> > explicit to a future developer, since that variable is checked against
> > zero in several points. Given that the added source size is four bytes
> > and the added binary size is _zero_, is all the fuss really necessary?
> > Notice that I changed the other variable in which you pointed out this
> > issue, because it is not checked against zero anywhere.
> 
> Please do so anyways.
> 
> >> > +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)
> >> > +
> >> 
> >> Please do not add a newline here.
> > Oops, sorry. I forgot to remove it when moving the comment
> 
> :-)
> 
> >> > +  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) /* Found.  */
> >> 
> >> You forgot one.
> > Corrected. Sorry.
> >> 
> >> > +    {
> >> > +      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 || 0 == *name)
> >> > +    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
> >> > +        return GRUB_ERR_NONE;
> >> > +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
> >> > +    }
> >> > +}
> >> > +
> >> > +/* Given a BIOS disk number, returns its GRUB device name if it exists.
> >> > +   For nonexisting BIOS dnums, this function returns ERR_UNKNOWN_DEVICE.  */
> >> 
> >> This is GRUB_ERR_UNKNOWN_DEVICE
> > I know, I consciously left the GRUB_ part out because 1) it would
> > require the line to be split and 2) that prefix is all over the place.
> > Corrected, however.
> >> 
> >> > +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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 %d not found", dnum);
> >> 
> >> Please change this.
> > Em... to what? What is the problem? Do you want me to reverse the
> > comparison? i.e. if (!found) { return error; } else { return ok; }
> 
> The return is on the same line as the else.
> 

Corrected.

> 
> >> > +/* 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 || 0 == *str)
> >> > +    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;
> >> 
> >> Can you put this between braces, now comment was added.
> > Done.
> >> 
> >> > +      else
> >> > +        {
> >> > +          bios_num |= drivenum;
> >> > +          if (output)
> >> > +            *output = bios_num;
> >> > +          return GRUB_ERR_NONE;
> >> > +        }
> >> > +    }
> >> > +  else goto fail;
> >> 
> >> ...
> > What's the problem here? The lack of braces? The goto (as used in the
> > ext2 code)?
> 
> goto is on the same line as the else.

Corrected, though I find this change less logic than the last one (splitting the else retun grub_error(...)) line because this generates two _extremely_ short lines;
> 
> >> > +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[0].set)
> >> > +    {
> >> > +      /* Show: list mappings.  */
> >> > +      if (!drivemap)
> >> > +        grub_printf ("No drives have been remapped");
> >> > +      else
> >> > +        {
> >> > +          grub_printf ("Showing only remapped drives.\n");
> >> > +          grub_printf ("Mapped\tGRUB\n");
> >> > +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
> >> > +              curnode = curnode->next;
> >> > +            }
> >> > +        }
> >> > +    }
> >> > +  else if (state[1].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 */
> >> 
> >> ".  */
> > Done
> >> 
> >> > +      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;
> >> > +
> >> > +      err = tryparse_diskstring (args[1], &mapto);
> >> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */
> >> 
> >> Please move this up.
> > Done.
> >> 
> >> > +        {    
> >> > +          grub_errno = GRUB_ERR_NONE;
> >> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
> >> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
> >> > +            return grub_error (grub_errno,
> >> > +                              "Target specifier must be of the form (fdN) or "
> >> > +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
> >> > +                              "number between 0 and 255");
> >> > +          else mapto = (grub_uint8_t)num;
> >> > +        }
> >> > +      
> >> > +      if (mapto == mapfrom)  /* Reset to default.  */
> >> 
> >> Same here.
> > Done.
> >> 
> >> > +        {
> >> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
> >> > +          drivemap_remove (mapfrom);
> >> > +        }
> >> > +      else  /* Map.  */
> >> 
> >> Please move the comment inside the braces below.
> > Done, and reworded.
> >> 
> >> > +        {
> >> > +          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
> >> > +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
> >> > +        }
> >> > +    }
> >> > +
> >> > +  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;
> >> > +  while (curentry)  /* Count entries to prepare a contiguous map block.  */
> >> 
> >> ...
> > Comment moved up.
> >> 
> >> > +    {
> >> > +      entries++;
> >> > +      curentry = curentry->next;
> >> > +    }
> >> > +  if (0 == entries)
> >> 
> >> I know this is what you prefer, but can you change this nevertheless?
> > I refer to my objection near the top of the post.
> 
> I know you object, but did you change it?
Done.
> 
> >> > +      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
> >> > +                  "an int13h handler is not required.");
> >> > +      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
> >> > +    }
> >> > +  else
> >> > +    {
> >> > +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
> >> > +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
> >> > +      
> >> > +      /* Save the pointer to the old int13h 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:
> >> > +         BDA offset 0x13 contains the top of such memory.  */
> >> > +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
> >> > +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
> >> > +      grub_size_t total_size = grub_drivemap_int13_size
> >> > +                            + (entries + 1) * sizeof(int13map_node_t);
> >> > +      grub_uint16_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 chunk to reserved area.  */
> >> > +      grub_uint8_t *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;
> >> > +      int13map_node_t *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.  */
> >> > +      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
> >> > +                         "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,118 @@
> >> > +/*
> >> > + *  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)
> >> > +
> >> > +VARIABLE(grub_drivemap_int13_oldhandler)
> >> > +  .word 0xdead, 0xbeef
> >> > +/* Drivemap module - 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: 1766)
> >> > +++ conf/i386-pc.rmk	(copia de trabajo)
> >> > @@ -158,7 +158,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
> >> > +	aout.mod _bsd.mod bsd.mod drivemap.mod
> >> >  
> >> >  # For biosdisk.mod.
> >> >  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
> >> > @@ -325,4 +325,11 @@
> >> >  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
> >> >  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
> >> >  
> >> > +# For drivemap.mod.
> >> > +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: 1766)
> >> > +++ include/grub/loader.h	(copia de trabajo)
> >> > @@ -37,6 +37,22 @@
> >> >  /* Unset current loader, if any.  */
> >> >  void EXPORT_FUNC(grub_loader_unset) (void);
> >> >  
> >> > +typedef struct hooklist_node *grub_preboot_hookid;
> >> > +
> >> > +/* Register a function to be called before the boot hook.  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 errno and errmsg.  However, if the call is successful
> >> > +   those variables are _not_ modified. */
> >> 
> >> No need to mention errmsg, it's internal to GRUB.  As for errno (which
> >> is grub_errno, actually) it does not need to be mentioned, otherwise
> >> we would have to do so everywhere.  Please capitalize HOOK and
> >> ABORT_ON_ERROR in the comments above.
> > Done. "hook" removed because it referred to the loader module boot
> > function.
> >> 
> >> > +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.  */
> >> 
> >> Nitpick: "loader.  This..."
> > Are you a bot? ¬¬ Corrected
> 
> It would make like much simpler if I were ;-).  What makes you think
> so?
Your ability to spot these kind of smallish things, like the "deltion" word and the lack of a double space. You even write normal text with double spaces!

> 
> >> >  grub_err_t EXPORT_FUNC(grub_loader_boot) (void);
> >> > Index: kern/loader.c
> >> > ===================================================================
> >> > --- kern/loader.c	(revisión: 1766)
> >> > +++ kern/loader.c	(copia de trabajo)
> >> > @@ -61,11 +61,82 @@
> >> >    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)
> >> > +{
> >> > +  if (!hook)
> >> > +    {
> >> > +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
> >> > +      return 0;
> >> > +    }
> >> > +  grub_preboot_hookid newentry = grub_malloc (sizeof (struct hooklist_node));
> >> 
> >> Mixed declarations/code.
> > Oops, sorry. I put most of my attention on drivemap.c (and even then
> > many comments slipped through). Corrected.
> 
> Please re-check them, I might have missed things this time...
> 
> >> > +  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)
> >> 
> >> "preboot (grub"
> > Corrected on both functions ;)
> >> 
> >> > +{
> >> > +  grub_preboot_hookid entry = 0;
> >> > +  grub_preboot_hookid search = preboot_hooks;
> >> > +  grub_preboot_hookid previous = 0;
> >> > +
> >> > +  if (0 == id)
> >> > +    return;
> >> 
> >> ...
> > ... xD
> >> 
> >> > +  while (search)
> >> > +    {
> >> > +      if (search == id)
> >> > +        {
> >> > +          entry = search;
> >> > +          break;
> >> > +        }
> >> > +      previous = search;
> >> > +      search = search->next;
> >> > +    }
> >> > +  if (entry) /* Found */
> >> 
> >> ...
> > Comment removed, was unnecessary.
> >> 
> >> > +    {
> >> > +      if (previous)
> >> > +        previous->next = entry->next;
> >> > +      else preboot_hooks = entry->next; /* Entry was head of list */
> >> > +      grub_free (entry);
> >> > +    }
> >> > +}
> >> > +
> >> >  grub_err_t
> >> >  grub_loader_boot (void)
> >> >  {
> >> >    if (! grub_loader_loaded)
> >> >      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
> >> > +  
> >> > +  grub_preboot_hookid entry = preboot_hooks;
> >> 
> >> Mixed declarations/code.
> > Moved the whole line up.
> >> 
> >> > +  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
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

El mar, 05-08-2008 a las 13:28 +0200, Marco Gerards escribió:
> Hi,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> >> > There is, however, one point in which I keep my objection: comparisons
> >> > between a variable and a constant should be of the form CONSTANT ==
> >> > variable and not in the reverse order, since an erroneous but quite
> >> > possible change of == for = results in a compile-time error instead of a
> >> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
> >> > excruciating to find in userspace applications within an IDE, so I can't
> >> > even consider the pain to debug them in a bootloader.
> >> 
> >> I understand your concern, nevertheless, can you please change it?
> > You understand my concern, but seemingly do not understand that in order
> > to conform to the Holy Coding Style you are asking me to write code that
> > can become buggy (and with a very hard to trace bug) with a simple
> > deltion! (point: did you notice that last word _without_ a spelling
> > checker? Now try to do so in a multithousand-line program).
> 
> BTW, your patch still contains this, can you please change it before I
> go over it again?
> 
> I know people who claim that this code will become buggy because we
> write it in C.  Should we start accepting patches to rewrite GRUB in
> Haskell or whatever? :-)
What about Ada? The stock GCC has Ada support ^^
> 
> Really, as a maintainer I should set some standards and stick to it.
> Of course not everyone will like me and sometimes I have to act like a
> jerk.  But I rather be a jerk, than committing code that do not meet
> my expectations.  But please understand, this contribution is highly
> appreciated.  However, we want to have something maintainable for the
> far future as well :-)
I understand these kind of concerns, particularly seeing how GRUB Legacy
ended - tangled, unscalable spaghetti code. You're not a jerk, just a
bit obsessive, but that's fine when trying to handle herds of us
all-important devs which think all we do is The Right Thing (tm) and
others are heretics to The Truth.
> 
> --
> Marco
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

Ok, so here is the new version of the patch, with a new drivemap.h
header and the == arguments put in the Holy Ordering ^^. I hope I didn't
forget anything this time. Here is the new CL entry:

2008-08-XX  Javier Martin  <lordhabbit@gmail.com>

        * commands/i386/pc/drivemap.c: New file.
        * commands/i386/pc/drivemap_int13h.S: New file.
        * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod.
        (drivemap_mod_SOURCES): New variable.
        (drivemap_mod_ASFLAGS): Likewise.
        (drivemap_mod_CFLAGS): Likewise.
        (drivemap_mod_LDFLAGS): Likewise.
        * include/grub/loader.h (grub_loader_register_preboot): New
        function prototype.
        (grub_loader_unregister_preboot): Likewise.
        (grub_preboot_hookid): New typedef.
        * kern/loader.c (grub_loader_register_preboot): New function.
        (grub_loader_unregister_preboot): Likewise.
        (preboot_hooks): New variable.
        (grub_loader_boot): Call the list of preboot-hooks before the
        actual loader.

By the way, is there anything I can do to make `svn up' updates less
traumatic? I don't want to search each "C" file for "<<<<<< mine" lines
and correct them: is there any tool to do this with a workflow not
unlike that of Gentoo's `etc-update'?

Habbit

[-- Attachment #1.2: drivemap.patch.6 --]
[-- Type: text/x-patch, Size: 27161 bytes --]

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,423 @@
+/* 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},
+  {0, 0, 0, 0, 0, 0}
+};
+
+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);
+    }
+}
+
+/* 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[0].set)
+    {
+      /* Show: list mappings.  */
+      if (!drivemap)
+        grub_printf ("No drives have been remapped");
+      else
+        {
+          grub_printf ("Showing only remapped drives.\n");
+          grub_printf ("Mapped\tGRUB\n");
+          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("0x%02x\t%4s\n", curnode->newdrive, dname);
+              curnode = curnode->next;
+            }
+        }
+    }
+  else if (state[1].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;
+
+      err = tryparse_diskstring (args[1], &mapto);
+      /* Was that a disk string? If not, maybe a raw num then?  */
+      if (err != GRUB_ERR_NONE)
+        {    
+          grub_errno = GRUB_ERR_NONE;
+          unsigned long num = grub_strtoul (args[1], 0, 0);
+          /* The input could be outside range, or not a number at all.  */
+          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)
+            return grub_error (grub_errno,
+                              "Target specifier must be of the form (fdN) or "
+                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
+                              "number between 0 and 255");
+          else mapto = (grub_uint8_t)num;
+        }
+      
+      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, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
+          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
+        }
+    }
+
+  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 " -s | -r | (hdX) newdrivenum",
+                         "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: 1779)
+++ 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: 1779)
+++ 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/>.
+ */
+
+#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: 1779)
+++ 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 */
+      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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-04 23:10           ` Javier Martín
                               ` (2 preceding siblings ...)
  2008-08-05 11:28             ` Marco Gerards
@ 2008-08-05 17:15             ` Colin D Bennett
  3 siblings, 0 replies; 47+ messages in thread
From: Colin D Bennett @ 2008-08-05 17:15 UTC (permalink / raw)
  To: grub-devel

On Tue, 05 Aug 2008 01:10:25 +0200
Javier Martín <lordhabbit@gmail.com> wrote:

> Besides, sometimes checks like "if (a=b)", or more
> frequently "if (a=f())" are intentionally used in C, so the error
> might become even more difficult to spot and correct.

Respect and heed the gcc warnings!!

Note that gcc will warn you (you *are* using -Wall, at least, right?)
if you try to do

  if (a = b)

but if you really mean it, you should surround the expression with an
extra pair of parentheses:

  if ((a = b))

Use of the value of an assignment in an expression is common in code
like

  if ((n = read (fd, buf, sz)) > 0)
    /* ... use the data in buf ... */

Here's an example showing gcc being helpful:

  $ cat a.c
  int f()
  {
      int a = 1;
      int b;
      if ((b = a))
        return 1;
      return 0;
  }
  $ cat b.c
  int f()
  {
      int a = 1;
      int b;
      if (b = a)
        return 1;
      return 0;
  }
  $ gcc -Wall -c a.c
  $ gcc -Wall -c b.c
  b.c: In function ‘f’:
  b.c:5: warning: suggest parentheses around assignment used as truth
  $ 

> New version of the patch follows. I must say that the discussion here
> has become pretty interesting without degrading into flamewarring -
> who whats to call Mr. Torvalds and Prof. Tanembaum? ^^

Well, it sounds to me like you are trying your best to set Marco ablaze,
but he is delightfully fire-retardant.

Regards,
Colin



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-05 11:23             ` Marco Gerards
@ 2008-08-05 17:18               ` Colin D Bennett
  0 siblings, 0 replies; 47+ messages in thread
From: Colin D Bennett @ 2008-08-05 17:18 UTC (permalink / raw)
  To: grub-devel

On Tue, 05 Aug 2008 13:23:11 +0200
Marco Gerards <mgerards@xs4all.nl> wrote:

> Hi Javier,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> > While tools like `svn diff' can help in these kind of problems,
> > their utility is greatly diminished if the offending change is part
> > of a multi-line change. Besides, sometimes checks like "if (a=b)",
> > or more frequently "if (a=f())" are intentionally used in C, so the
> > error might become even more difficult to spot and correct. I ask
> > for a deep reflection on this issue: maybe I'm dead wrong and an
> > arrogant brat in my attempt to tackle the Holy GNU Coding
> > Standards, but I'd like to ask input from more people on this.
> 
> I will refuse patches with "if (a = f())", if that makes you sleep
> better ;-)

Since that will generate a gcc warning, I hope that the author would at
least do

  if ((a = f ()))

to indicate to gcc that you mean what you said.

Regards,
Colin



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
@ 2008-08-06 14:43 Viswesh S
  2008-08-06 17:31 ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Viswesh S @ 2008-08-06 14:43 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 52629 bytes --]

Hi,
Could you let all know how to use your drivemap command.Is it similar to the map command in legacy grub.
drivemap (hd0) (hd1)
drivemap (hd1) (hd0)
with the grub.cfg to be 
menuentry "Linux" {
linux ....
initrd ...
}
menuentry "Windows" {
        set root=(hd1,1)
        chainloader +1
}
Viswesh



----- Original Message ----
From: Javier Martín <lordhabbit@gmail.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Sent: Tuesday, 5 August, 2008 10:09:41 PM
Subject: Re: [PATCH] Drivemap module

Ok, I will reply to your last three messages on a single post:
Hi there!

El mar, 05-08-2008 a las 13:31 +0200, Marco Gerards escribió: 
> Hi,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> >> Anyway, since "they" are more likely to maintain the code in 
> >> the long run than you, in general, the question is whether 
> >> the code is more likely to become buggy by their hacking on 
> >> it, if it follows project coding style or someone else's 
> >> (your) "safer" coding style.  Likely it's safer if using a 
> >> consistent programming style.  Although I personally don't 
> >> see that it's very helpful to have a style that makes things 
> >> down to the order of "==" arguments be consistent within the 
> >> project; haphazard only slows reading the tiniest bit, and I 
> >> don't think it makes a different what order the arguments are...
> > 
> > Hmm... I was partially expecting a flamefest to start. Pity ^^
> > Well, let's spill a little napalm: the GNU style bracing is extremely
> > silly!! Why the hell are the "if" and "else" blocks indented differently
> > in this example?
> >  if (condition)
> >    return 0;
> >  else
> >    {
> >      return -1;
> >    }
> > Nah, I'm not really bringing that issue, I was just joking, and in fact
> > I'm reconsidering my objections to the operator== arguments order rule,
> > even though I still consider my style safer and more sensible. If
> > someone else wants to express their opinion on that issue, do it fast
> > before I completely submit to Master Marco's will :D
> 
> Please don't be sarcastic, start flame wars or call names.  It will not
> help anyone.

Ok, sorry, picturing you as a whip-cracking dominatrix was really not proper ^^. I was just joking about how fast can flamewars be started.

> 
> --
> Marco
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

El mar, 05-08-2008 a las 13:23 +0200, Marco Gerards escribió:
> Hi Javier,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> > El lun, 04-08-2008 a las 22:51 +0200, Marco Gerards escribió:
> >> Javier Martín <lordhabbit@gmail.com> writes:
> >> 
> >> > After your latest replay, I "reevaluated" my stubbornness WRT some of
> >> > your advices, and I've changed a few things:
> >> >
> >> > - Variables are now declared (and, if possible, initialized) before
> >> > precondition checks, even simple ones. The install_int13_handler
> >> > function has not been modified, however, since I find it a bit
> >> > nonsensical to put bunch of declarations without an obvious meaning just
> >> > after the "else" line:
> >> >      grub_uint32_t *ivtslot;
> >> >      grub_uint16_t *bpa_freekb;
> >> >      grub_size_t total_size;
> >> >      grub_uint16_t payload_sizekb;
> >> >      grub_uint8_t *handler_base;
> >> >      int13map_node_t *handler_map;
> >> >      grub_uint32_t ivtentry;
> >> 
> >> Please understand me correctly.  Code just has to be written according
> >> to our coding standards before it can and will be included.  We can
> >> discuss things endlessly but we will simply stick to the GNU Coding
> >> Standards as you might expect.
> >> 
> >> I hope you can appreciate that all code of GRUB has the same coding
> >> style, if you like this style or not.
> > Big sigh. Function modified to conform to your preciousss coding style.
> > I might look a bit childish or arrogant saying this, but what is the
> > point upholding a coding style that, no matter how consistent it is,
> > hinders readability. With so much local variables, they are hard to keep
> > track of no matter the coding style, but with the old ("mixed, heretic")
> > style there was not need for a comment before each declaration: they
> > were declared and used when needed instead of at the start of a block,
> > because that function is inherently linear, not block-structured.
> 
> In your opinion it is not a good coding style.  I just avoid
> discussions about it because such discussions just waste my time.
> Even if you are able to convince me, GRUB is a GNU project and will
> remain to use the GCS.

Which is fine, but the order of the arguments to == is specified nowhere in the GCS: the order you defend only appears in examples less than five times. Thus, your insistence is a matter of internal consistence within GRUB and is only based on examples in the GCS. Additionally, your previous suggestion to change checks line "entries == 0 " to "!entries" are _against_ the examples of the GCS, even for pointers.

Then, given that the _examples_ in the GCS are non-authoritative, I was advocating a simple change for better resilience against future changes. You want to keep consistency with the current sources, and that I understand, so I will perform the required trivial changes in my code to use the style used in other GRUB code, but I still think it is worse and more error-prone.

> 
> 
> >> > - Only one declaration per line: even though C is a bit absurd in not
> >> > recognizing T* as a first class type and instead thinking of * as a
> >> > qualifier to the variable name; and even though my code does not incur
> >> > into such ambiguities.
> >> > - Comments moved as you required, reworded as needed
> >> > - Extensive printf showing quasi-help in the "show" mode trimmed down to
> >> > just the first sentence.
> >> > - Internal helper functions now use the standard error handling, i.e.
> >> > return grub_error (err, fmt, ...)
> >> > - Comment about the strange "void" type instead of "void*" rephrased to
> >> > be clearer
> >> 
> >> Thanks a lot!
> >> 
> >> > There is, however, one point in which I keep my objection: comparisons
> >> > between a variable and a constant should be of the form CONSTANT ==
> >> > variable and not in the reverse order, since an erroneous but quite
> >> > possible change of == for = results in a compile-time error instead of a
> >> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
> >> > excruciating to find in userspace applications within an IDE, so I can't
> >> > even consider the pain to debug them in a bootloader.
> >> 
> >> I understand your concern, nevertheless, can you please change it?
> > You understand my concern, but seemingly do not understand that in order
> > to conform to the Holy Coding Style you are asking me to write code that
> > can become buggy (and with a very hard to trace bug) with a simple
> > deltion! (point: did you notice that last word _without_ a spelling
> > checker? Now try to do so in a multithousand-line program).
> 
> Yes, I did notice it immediately.
> 
> The coding style is not holy in any way.  Everyone has its own coding
> style.  You must understand I do not want to fully discuss it every
> time someone sends in a patch, especially since it will not change
> anyways.

By the way, I just noticed that you write _normal_ text with two spaces after a dot, just like comments in the code! o_O
> 
> > While tools like `svn diff' can help in these kind of problems, their
> > utility is greatly diminished if the offending change is part of a
> > multi-line change. Besides, sometimes checks like "if (a=b)", or more
> > frequently "if (a=f())" are intentionally used in C, so the error might
> > become even more difficult to spot and correct. I ask for a deep
> > reflection on this issue: maybe I'm dead wrong and an arrogant brat in
> > my attempt to tackle the Holy GNU Coding Standards, but I'd like to ask
> > input from more people on this.
> 
> I will refuse patches with "if (a = f())", if that makes you sleep
> better ;-)
In fact the GCS discourages that use, but allows the same in loop checks, particularly "while" loops.
>  
> >> > WRT accepting raw BIOS disk numbers, I agree with you in principle, but
> >> > I'm keeping the functionality for now, since I don't quite like the
> >> > "drivemap (hd0) (hd1)" syntax - which device is which?. I'd rather have
> >> > something akin to "drivemap (hd0) (bios:hd1)", but I want to hear the
> >> > opinions of people in this list.
> >> 
> >> I personally do not care a lot if BIOS disk numbers are used.
> >> Although I do not see why it is useful.
> >> 
> >> As for the syntax, I would prefer something more GRUB2ish, like:
> >> 
> >> map --bios=(hd0) --os=(hd1)
> > I agree. What about "grub" for the source drive instead of "os", with
> > "bios" being the target drive? I mean:
> >     drivemap --grub=(hd1) --bios=(hd0)
> > Would map 0x80 in the BIOS to grub's (hd1) drive which will most likely
> > be BIOS drive 0x81.
> 
> It will not change the functionality which GRUB is active.  But it
> will change it for the OS that is loaded.  So I do not think --grub=
> is a good idea because of this.  As for GRUB Legacy, it confused a lot
> of people, so making people explicitly say that it is changed for the
> OS, this confusion might go away :-)
> 
> > However, I prefer not to change it right now. Maybe when there are no
> > other issues WRT to this patch we can set on to tackle this one.
> 
> So first I'll review this patch, then you will send in a new one?

Of course. I meant that I prefer to smash all "syntactic" changes in the patch before introducing a functionality change that could lead to new syntactic issues.
>  
> >> Or so, perhaps the long argument names can be chosen in a more clever
> >> way :-)
> >> > The new version of the patch is attached, and here is my suggested CLog:
> >> >
> >> > 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
> >> 
> >> (newline)
> >> 
> >> >        * commands/i386/pc/drivemap.c : New file.
> >> >        * commands/i386/pc/drivemap_int13h.S : New file.
> >> >        * conf/i386-pc.rmk (pkglib_MODULES) : Added drivemap.mod
> >> >        (drivemap_mod_SOURCES) : New variable
> >> >        (drivemap_mod_ASFLAGS) : Likewise
> >> >        (drivemap_mod_CFLAGS) : Likewise
> >> >        (drivemap_mod_LDFLAGS) : Likewise
> >> >        * include/grub/loader.h (grub_loader_register_preboot) : New
> >> >        function prototype. Register a new pre-boot handler
> >> 
> >> No need how the change is used or why it was added.
> >> 
> >> >        (grub_loader_unregister_preboot) : Likewise. Unregister handler
> >> 
> >> Same here.
> >> 
> >> >        (grub_preboot_hookid) : New typedef. Registered hook "handle"
> >> 
> >> Same here.
> >> 
> >> >        * kern/loader.c (grub_loader_register_preboot) : New function.
> >> >        (grub_loader_unregister_preboot) : Likewise.
> >> >        (preboot_hooks) : New variable. Linked list of preboot hooks
> >> 
> >> Same here.
> >> 
> >> >        (grub_loader_boot) : Call the list of preboot-hooks before the
> >> >        actual loader
> >> 
> >> What's the `'?
> > The what? o_O
> 
> I see some weird character in your text.  My font shows it as a block
> before every `*'.
I see nothing: "What's the `'?". Maybe it's some kind of tab?
> 
> >> Please do not add a space before the ":" 
> > Ok, everything corrected. New CL entry:
> >
> > 2008-08-XX  Javier Martin  <lordhabbit@gmail.com>
> >
> >        * commands/i386/pc/drivemap.c: New file.
> >        * commands/i386/pc/drivemap_int13h.S: New file.
> >        * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod
> >        (drivemap_mod_SOURCES): New variable
> >        (drivemap_mod_ASFLAGS): Likewise
> >        (drivemap_mod_CFLAGS): Likewise
> >        (drivemap_mod_LDFLAGS): Likewise
> >        * include/grub/loader.h (grub_loader_register_preboot): New
> >        function prototype.
> >        (grub_loader_unregister_preboot): Likewise.
> >        (grub_preboot_hookid): New typedef.
> >        * kern/loader.c (grub_loader_register_preboot): New function.
> >        (grub_loader_unregister_preboot): Likewise.
> >        (preboot_hooks): New variable.
> >        (grub_loader_boot): Call the list of preboot-hooks before the
> >        actual loader
> 
> Please add a `.' after "New variable" and "Likewise", same for the
> third and the last sentence.  Sometimes you did it right :-).
> 
Done. New CL entry will go at the end of this neverending post.

> 
> >> Some comments can be found below.  Can you please fix the code mention
> >> in the review and similar code?  I really want the code to be just
> >> like any other GRUB code.  Please understand that we have to maintain
> >> this in the future.  If everyone would use his own codingstyle, GRUB
> >> would become unmaintainable.
> >> 
> >> > 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,417 @@
> >> > +/* 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/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},
> >> > +  {0, 0, 0, 0, 0, 0}
> >> > +};
> >> > +
> >> > +/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
> >> > +
> >> > +/* Realmode far ptr = 2 * 16b */
> >> > +extern grub_uint32_t grub_drivemap_int13_oldhandler;
> >> > +/* Size of the section to be copied */
> >> > +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;
> >> > +extern grub_symbol_t grub_drivemap_int13_handler_base;
> >> > +extern grub_symbol_t grub_drivemap_int13_mapstart;
> >> > +
> >> > +void grub_drivemap_int13_handler (void);
> >> 
> >> The lines above belong in a header file.
> > True, but they are used in a single file in the whole project and thus I
> > see it pointless to extract an unneeded header, which would become one
> > more SVN object to track. However, if you insist I will split the header
> > file at once. In particular, I think the grub_symbol_t typedef should go
> > into the "standard" GRUB headers somehow (but not in symbol.h, which is
> > included from assembly files).
> 
> Please do so.
> 
> Other people might want to comment on the symbol change.  They will
> most likely miss it if we keep discussing it here ;-).  Can you please
> send that in as a separate change to give other the opportunity to
> react on it?
Ok, so in a first stage I will put the discussed block in a drivemap.h file in include/, then when that's committed start a discussion about moving grub_symbol_t from drivemap.h to another header file.
> 
> >> > +/* Syms/vars/funcs exported from drivemap_int13h.S - end.  */
> >> > +
> >> > +
> >> > +static grub_preboot_hookid insthandler_hook;
> >> > +
> >> > +typedef struct drivemap_node
> >> > +{
> >> > +  grub_uint8_t newdrive;
> >> > +  grub_uint8_t redirto;
> >> > +  struct drivemap_node *next;
> >> > +} drivemap_node_t;
> >> > +
> >> > +static drivemap_node_t *drivemap = 0;
> >> 
> >> No need to set this to zero.
> > Yes, you said so already, but I wanted to make the initial state very
> > explicit to a future developer, since that variable is checked against
> > zero in several points. Given that the added source size is four bytes
> > and the added binary size is _zero_, is all the fuss really necessary?
> > Notice that I changed the other variable in which you pointed out this
> > issue, because it is not checked against zero anywhere.
> 
> Please do so anyways.
> 
> >> > +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)
> >> > +
> >> 
> >> Please do not add a newline here.
> > Oops, sorry. I forgot to remove it when moving the comment
> 
> :-)
> 
> >> > +  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) /* Found.  */
> >> 
> >> You forgot one.
> > Corrected. Sorry.
> >> 
> >> > +    {
> >> > +      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 || 0 == *name)
> >> > +    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 (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
> >> > +        return GRUB_ERR_NONE;
> >> > +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name);
> >> > +    }
> >> > +}
> >> > +
> >> > +/* Given a BIOS disk number, returns its GRUB device name if it exists.
> >> > +  For nonexisting BIOS dnums, this function returns ERR_UNKNOWN_DEVICE.  */
> >> 
> >> This is GRUB_ERR_UNKNOWN_DEVICE
> > I know, I consciously left the GRUB_ part out because 1) it would
> > require the line to be split and 2) that prefix is all over the place.
> > Corrected, however.
> >> 
> >> > +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 && GRUB_DISK_DEVICE_BIOSDISK_ID == disk->dev->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 %d not found", dnum);
> >> 
> >> Please change this.
> > Em... to what? What is the problem? Do you want me to reverse the
> > comparison? i.e. if (!found) { return error; } else { return ok; }
> 
> The return is on the same line as the else.
> 

Corrected.

> 
> >> > +/* 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 || 0 == *str)
> >> > +    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;
> >> 
> >> Can you put this between braces, now comment was added.
> > Done.
> >> 
> >> > +      else
> >> > +        {
> >> > +          bios_num |= drivenum;
> >> > +          if (output)
> >> > +            *output = bios_num;
> >> > +          return GRUB_ERR_NONE;
> >> > +        }
> >> > +    }
> >> > +  else goto fail;
> >> 
> >> ...
> > What's the problem here? The lack of braces? The goto (as used in the
> > ext2 code)?
> 
> goto is on the same line as the else.

Corrected, though I find this change less logic than the last one (splitting the else retun grub_error(...)) line because this generates two _extremely_ short lines;
> 
> >> > +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[0].set)
> >> > +    {
> >> > +      /* Show: list mappings.  */
> >> > +      if (!drivemap)
> >> > +        grub_printf ("No drives have been remapped");
> >> > +      else
> >> > +        {
> >> > +          grub_printf ("Showing only remapped drives.\n");
> >> > +          grub_printf ("Mapped\tGRUB\n");
> >> > +          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("0x%02x\t%4s\n", curnode->newdrive, dname);
> >> > +              curnode = curnode->next;
> >> > +            }
> >> > +        }
> >> > +    }
> >> > +  else if (state[1].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 */
> >> 
> >> ".  */
> > Done
> >> 
> >> > +      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;
> >> > +
> >> > +      err = tryparse_diskstring (args[1], &mapto);
> >> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num then?  */
> >> 
> >> Please move this up.
> > Done.
> >> 
> >> > +        {    
> >> > +          grub_errno = GRUB_ERR_NONE;
> >> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
> >> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw num or too high.  */
> >> > +            return grub_error (grub_errno,
> >> > +                              "Target specifier must be of the form (fdN) or "
> >> > +                              "(hdN), with 0 <= N < 128; or a plain dec/hex "
> >> > +                              "number between 0 and 255");
> >> > +          else mapto = (grub_uint8_t)num;
> >> > +        }
> >> > +      
> >> > +      if (mapto == mapfrom)  /* Reset to default.  */
> >> 
> >> Same here.
> > Done.
> >> 
> >> > +        {
> >> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
> >> > +          drivemap_remove (mapfrom);
> >> > +        }
> >> > +      else  /* Map.  */
> >> 
> >> Please move the comment inside the braces below.
> > Done, and reworded.
> >> 
> >> > +        {
> >> > +          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], mapfrom, mapto);
> >> > +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
> >> > +        }
> >> > +    }
> >> > +
> >> > +  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;
> >> > +  while (curentry)  /* Count entries to prepare a contiguous map block.  */
> >> 
> >> ...
> > Comment moved up.
> >> 
> >> > +    {
> >> > +      entries++;
> >> > +      curentry = curentry->next;
> >> > +    }
> >> > +  if (0 == entries)
> >> 
> >> I know this is what you prefer, but can you change this nevertheless?
> > I refer to my objection near the top of the post.
> 
> I know you object, but did you change it?
Done.
> 
> >> > +      grub_dprintf (MODNAME, "No drives marked as remapped, installation of"
> >> > +                  "an int13h handler is not required.");
> >> > +      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
> >> > +    }
> >> > +  else
> >> > +    {
> >> > +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
> >> > +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
> >> > +      
> >> > +      /* Save the pointer to the old int13h 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:
> >> > +        BDA offset 0x13 contains the top of such memory.  */
> >> > +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
> >> > +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", *bpa_freekb);
> >> > +      grub_size_t total_size = grub_drivemap_int13_size
> >> > +                            + (entries + 1) * sizeof(int13map_node_t);
> >> > +      grub_uint16_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 chunk to reserved area.  */
> >> > +      grub_uint8_t *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;
> >> > +      int13map_node_t *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.  */
> >> > +      grub_uint32_t 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 " -s | -r | (hdX) newdrivenum",
> >> > +                        "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,118 @@
> >> > +/*
> >> > + *  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)
> >> > +
> >> > +VARIABLE(grub_drivemap_int13_oldhandler)
> >> > +  .word 0xdead, 0xbeef
> >> > +/* Drivemap module - 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: 1766)
> >> > +++ conf/i386-pc.rmk    (copia de trabajo)
> >> > @@ -158,7 +158,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
> >> > +    aout.mod _bsd.mod bsd.mod drivemap.mod
> >> >  
> >> >  # For biosdisk.mod.
> >> >  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
> >> > @@ -325,4 +325,11 @@
> >> >  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
> >> >  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
> >> >  
> >> > +# For drivemap.mod.
> >> > +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: 1766)
> >> > +++ include/grub/loader.h    (copia de trabajo)
> >> > @@ -37,6 +37,22 @@
> >> >  /* Unset current loader, if any.  */
> >> >  void EXPORT_FUNC(grub_loader_unset) (void);
> >> >  
> >> > +typedef struct hooklist_node *grub_preboot_hookid;
> >> > +
> >> > +/* Register a function to be called before the boot hook.  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 errno and errmsg.  However, if the call is successful
> >> > +  those variables are _not_ modified. */
> >> 
> >> No need to mention errmsg, it's internal to GRUB.  As for errno (which
> >> is grub_errno, actually) it does not need to be mentioned, otherwise
> >> we would have to do so everywhere.  Please capitalize HOOK and
> >> ABORT_ON_ERROR in the comments above.
> > Done. "hook" removed because it referred to the loader module boot
> > function.
> >> 
> >> > +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.  */
> >> 
> >> Nitpick: "loader.  This..."
> > Are you a bot? ¬¬ Corrected
> 
> It would make like much simpler if I were ;-).  What makes you think
> so?
Your ability to spot these kind of smallish things, like the "deltion" word and the lack of a double space. You even write normal text with double spaces!

> 
> >> >  grub_err_t EXPORT_FUNC(grub_loader_boot) (void);
> >> > Index: kern/loader.c
> >> > ===================================================================
> >> > --- kern/loader.c    (revisión: 1766)
> >> > +++ kern/loader.c    (copia de trabajo)
> >> > @@ -61,11 +61,82 @@
> >> >    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)
> >> > +{
> >> > +  if (!hook)
> >> > +    {
> >> > +      grub_error (GRUB_ERR_BAD_ARGUMENT, "preboot hook must not be NULL");
> >> > +      return 0;
> >> > +    }
> >> > +  grub_preboot_hookid newentry = grub_malloc (sizeof (struct hooklist_node));
> >> 
> >> Mixed declarations/code.
> > Oops, sorry. I put most of my attention on drivemap.c (and even then
> > many comments slipped through). Corrected.
> 
> Please re-check them, I might have missed things this time...
> 
> >> > +  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)
> >> 
> >> "preboot (grub"
> > Corrected on both functions ;)
> >> 
> >> > +{
> >> > +  grub_preboot_hookid entry = 0;
> >> > +  grub_preboot_hookid search = preboot_hooks;
> >> > +  grub_preboot_hookid previous = 0;
> >> > +
> >> > +  if (0 == id)
> >> > +    return;
> >> 
> >> ...
> > ... xD
> >> 
> >> > +  while (search)
> >> > +    {
> >> > +      if (search == id)
> >> > +        {
> >> > +          entry = search;
> >> > +          break;
> >> > +        }
> >> > +      previous = search;
> >> > +      search = search->next;
> >> > +    }
> >> > +  if (entry) /* Found */
> >> 
> >> ...
> > Comment removed, was unnecessary.
> >> 
> >> > +    {
> >> > +      if (previous)
> >> > +        previous->next = entry->next;
> >> > +      else preboot_hooks = entry->next; /* Entry was head of list */
> >> > +      grub_free (entry);
> >> > +    }
> >> > +}
> >> > +
> >> >  grub_err_t
> >> >  grub_loader_boot (void)
> >> >  {
> >> >    if (! grub_loader_loaded)
> >> >      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
> >> > +  
> >> > +  grub_preboot_hookid entry = preboot_hooks;
> >> 
> >> Mixed declarations/code.
> > Moved the whole line up.
> >> 
> >> > +  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
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

El mar, 05-08-2008 a las 13:28 +0200, Marco Gerards escribió:
> Hi,
> 
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> >> > There is, however, one point in which I keep my objection: comparisons
> >> > between a variable and a constant should be of the form CONSTANT ==
> >> > variable and not in the reverse order, since an erroneous but quite
> >> > possible change of == for = results in a compile-time error instead of a
> >> > _extremely_ difficult to trace runtime bug. Such kind of bugs are quite
> >> > excruciating to find in userspace applications within an IDE, so I can't
> >> > even consider the pain to debug them in a bootloader.
> >> 
> >> I understand your concern, nevertheless, can you please change it?
> > You understand my concern, but seemingly do not understand that in order
> > to conform to the Holy Coding Style you are asking me to write code that
> > can become buggy (and with a very hard to trace bug) with a simple
> > deltion! (point: did you notice that last word _without_ a spelling
> > checker? Now try to do so in a multithousand-line program).
> 
> BTW, your patch still contains this, can you please change it before I
> go over it again?
> 
> I know people who claim that this code will become buggy because we
> write it in C.  Should we start accepting patches to rewrite GRUB in
> Haskell or whatever? :-)
What about Ada? The stock GCC has Ada support ^^
> 
> Really, as a maintainer I should set some standards and stick to it.
> Of course not everyone will like me and sometimes I have to act like a
> jerk.  But I rather be a jerk, than committing code that do not meet
> my expectations.  But please understand, this contribution is highly
> appreciated.  However, we want to have something maintainable for the
> far future as well :-)
I understand these kind of concerns, particularly seeing how GRUB Legacy
ended - tangled, unscalable spaghetti code. You're not a jerk, just a
bit obsessive, but that's fine when trying to handle herds of us
all-important devs which think all we do is The Right Thing (tm) and
others are heretics to The Truth.
> 
> --
> Marco
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

Ok, so here is the new version of the patch, with a new drivemap.h
header and the == arguments put in the Holy Ordering ^^. I hope I didn't
forget anything this time. Here is the new CL entry:

2008-08-XX  Javier Martin  <lordhabbit@gmail.com>

        * commands/i386/pc/drivemap.c: New file.
        * commands/i386/pc/drivemap_int13h.S: New file.
        * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod.
        (drivemap_mod_SOURCES): New variable.
        (drivemap_mod_ASFLAGS): Likewise.
        (drivemap_mod_CFLAGS): Likewise.
        (drivemap_mod_LDFLAGS): Likewise.
        * include/grub/loader.h (grub_loader_register_preboot): New
        function prototype.
        (grub_loader_unregister_preboot): Likewise.
        (grub_preboot_hookid): New typedef.
        * kern/loader.c (grub_loader_register_preboot): New function.
        (grub_loader_unregister_preboot): Likewise.
        (preboot_hooks): New variable.
        (grub_loader_boot): Call the list of preboot-hooks before the
        actual loader.

By the way, is there anything I can do to make `svn up' updates less
traumatic? I don't want to search each "C" file for "<<<<<< mine" lines
and correct them: is there any tool to do this with a workflow not
unlike that of Gentoo's `etc-update'?

Habbit



      Add more friends to your messenger and enjoy! Go to http://in.messenger.yahoo.com/invite/

[-- Attachment #2: Type: text/html, Size: 75159 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-06 14:43 Viswesh S
@ 2008-08-06 17:31 ` Javier Martín
  2008-08-08 13:20   ` Felix Zielcke
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-08-06 17:31 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 4125 bytes --]

El mié, 06-08-2008 a las 07:43 -0700, Viswesh S escribió:
> Hi,
> 
>  
> 
> Could you let all know how to use your drivemap command.Is it similar
> to the map command in legacy grub.

Hi there. Information about the use of a command is better placed in the
wiki than in this list, however this command is not merged in yet, and I
reckon its "help" feature could be quite better.

Currently, the "safe" GRUB2 commands for booting from (hd1,1) is:

drivemap (hd0) (hd1)
drivemap (hd1) (hd0)
set root=(hd0)
chainloader (hd1,1)+1
boot

Maybe an explanation of what "drivemap" does would put a bit of light
here:

	* The BIOS hard disks are numbered from 0 to 128 (actually from 0x80 to
0xFF because the lower numbers are reserved for floppy disks). These
numbers are used to select the target drive when issuing I/O requests
through the BIOS routines (INT 13h)
	* When the BIOS loads a boot sector and transfers control to it, the DL
register contains the "boot disk number", i.e. the disk from which the
bootsector was loaded. This allows the bootsector to load its OS from
the same disk it was run, instead of having to probe every single disk
in the system.
	* The "chainloader" command works like the BIOS: it loads a bootsector
into memory and jumps to it. In this case, the value in DL corresponds
to the disk that was set as "root drive" to GRUB. If no root drive is
set, the OS receives 0xFF, which should be recognized as an impossible
drive. Some OSes will just trust this and fail (i.e. FreeDOS) while
others will try to boot from the first hard disk (0x80).
	* The "drivemap" command acts as the old TSRs from the DOS times: when
the "boot" command is issued, it installs its own handler for INT 13h
requests, which performs the requested mappings and then call the old
(usually BIOS) handler with the changed parameters. Thus, an OS
accessing the drive through the BIOS routines will see the drives moved,
duplicated or whatever the mappings provided. Again: drivemap does NOT
modify the "live" drive mappings within GRUB; its changes only affect
the booted OS.

The strange root=(hd0) line that appears to contradict the chainloader
line is there because drivemap has no communication with the particular
loader. If you set root to (hd1,1) and then issue chainloader and boot,
the OS will receive 0x81 in DL because hd1 was the second hard drive
when chainloader was issued (remember that drivemap doesn't act until
"boot"). Thus, the target OS will try to boot from what _it_ sees as the
second hard disk, which will now be the old hd0 - wrong.

If the target OS does not need to access the old hd0 or it only uses the
BIOS routines for the boot process (i.e. it later uses ATA drivers and
will redetect everything from scratch), you can leave the second
drivemap line out and use a more compact script like this:

drivemap (hd0) (hd1)
set root=(hd1,1)
chainloader +1
boot

This will work on Windows, because no matter where it tries to boot from
it will find the same disk: both the first and second BIOS disks point
to hd1 now. However, if you use a DOS-like system which uses the BIOS
routines exclusively (i.e. FreeDOS) then your hd0 disk would have
disappeared to it and you'd have D: to be a mirror of C:. In order to
have hd0 as D:, hd1 as C: and everything working, you need the first
script I posted, which makes the complete swap and then makes FreeDOS
load from the "first" hard drive (i.e. hd1). This is the setup I have
extensively tested in QEMU, while the Windows boot has been tested in
both QEMU (with ReactOS) and some random computers including mine (with
Windows XP Home and Windows XP Pro x64).

I'm looking forward to streamline the drivemap syntax, so that the two
drivemap lines can be fused into one like "drivemap --swap (hd0) (hd1)".
However, given that it's not a bug and that GRUB is still in heavy
development (and thus syntax changes are acceptable), I'd prefer to have
the patch integrated as it is - so we have the functionality - and then
modify what's needed - so we have it pretty.

-Habbit

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
@ 2008-08-07 12:15 Viswesh S
  0 siblings, 0 replies; 47+ messages in thread
From: Viswesh S @ 2008-08-07 12:15 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 4793 bytes --]





From: Javier Martín <lordhabbit@gmail.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Sent: Wednesday, 6 August, 2008 11:01:19 PM
Subject: Re: [PATCH] Drivemap module

El mié, 06-08-2008 a las 07:43 -0700, Viswesh S escribió:
> Hi,
> 
>  
> 
> Could you let all know how to use your drivemap command.Is it similar
> to the map command in legacy grub.

Hi there. Information about the use of a command is better placed in the
wiki than in this list, however this command is not merged in yet, and I
reckon its "help" feature could be quite better.

Currently, the "safe" GRUB2 commands for booting from (hd1,1) is:

drivemap (hd0) (hd1)
drivemap (hd1) (hd0)
set root=(hd0)
chainloader (hd1,1)+1
boot

Maybe an explanation of what "drivemap" does would put a bit of light
here:

    * The BIOS hard disks are numbered from 0 to 128 (actually from 0x80 to
0xFF because the lower numbers are reserved for floppy disks). These
numbers are used to select the target drive when issuing I/O requests
through the BIOS routines (INT 13h)
    * When the BIOS loads a boot sector and transfers control to it, the DL
register contains the "boot disk number", i.e. the disk from which the
bootsector was loaded. This allows the bootsector to load its OS from
the same disk it was run, instead of having to probe every single disk
in the system.
    * The "chainloader" command works like the BIOS: it loads a bootsector
into memory and jumps to it. In this case, the value in DL corresponds
to the disk that was set as "root drive" to GRUB. If no root drive is
set, the OS receives 0xFF, which should be recognized as an impossible
drive. Some OSes will just trust this and fail (i.e. FreeDOS) while
others will try to boot from the first hard disk (0x80).
    * The "drivemap" command acts as the old TSRs from the DOS times: when
the "boot" command is issued, it installs its own handler for INT 13h
requests, which performs the requested mappings and then call the old
(usually BIOS) handler with the changed parameters. Thus, an OS
accessing the drive through the BIOS routines will see the drives moved,
duplicated or whatever the mappings provided. Again: drivemap does NOT
modify the "live" drive mappings within GRUB; its changes only affect
the booted OS.

The strange root=(hd0) line that appears to contradict the chainloader
line is there because drivemap has no communication with the particular
loader. If you set root to (hd1,1) and then issue chainloader and boot,
the OS will receive 0x81 in DL because hd1 was the second hard drive
when chainloader was issued (remember that drivemap doesn't act until
"boot"). Thus, the target OS will try to boot from what _it_ sees as the
second hard disk, which will now be the old hd0 - wrong.

If the target OS does not need to access the old hd0 or it only uses the
BIOS routines for the boot process (i.e. it later uses ATA drivers and
will redetect everything from scratch), you can leave the second
drivemap line out and use a more compact script like this:

drivemap (hd0) (hd1)
set root=(hd1,1)
chainloader +1
boot

This will work on Windows, because no matter where it tries to boot from
it will find the same disk: both the first and second BIOS disks point
to hd1 now. However, if you use a DOS-like system which uses the BIOS
routines exclusively (i.e. FreeDOS) then your hd0 disk would have
disappeared to it and you'd have D: to be a mirror of C:. In order to
have hd0 as D:, hd1 as C: and everything working, you need the first
script I posted, which makes the complete swap and then makes FreeDOS
load from the "first" hard drive (i.e. hd1). This is the setup I have
extensively tested in QEMU, while the Windows boot has been tested in
both QEMU (with ReactOS) and some random computers including mine (with
Windows XP Home and Windows XP Pro x64).

I'm looking forward to streamline the drivemap syntax, so that the two
drivemap lines can be fused into one like "drivemap --swap (hd0) (hd1)".
However, given that it's not a bug and that GRUB is still in heavy
development (and thus syntax changes are acceptable), I'd prefer to have
the patch integrated as it is - so we have the functionality - and then
modify what's needed - so we have it pretty.

-Habbit

Hi,
I have tried the same and different combinations,but still the machine is going for reset after I execute the boot command.
As nothing gets displayed also,I am not sure where exactly this is going wrong also.
What all might be the details in which I might have to look on, if I have to boot windows using grub2 only.
Viswesh


      Connect with friends all over the world. Get Yahoo! India Messenger at http://in.messenger.yahoo.com/?wm=n/

[-- Attachment #2: Type: text/html, Size: 6319 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-06 17:31 ` Javier Martín
@ 2008-08-08 13:20   ` Felix Zielcke
  0 siblings, 0 replies; 47+ messages in thread
From: Felix Zielcke @ 2008-08-08 13:20 UTC (permalink / raw)
  To: The development of GRUB 2

Am Mittwoch, den 06.08.2008, 19:31 +0200 schrieb Javier Martín:

> This will work on Windows, because no matter where it tries to boot from
> it will find the same disk: both the first and second BIOS disks point
> to hd1 now. 
> ...
> This is the setup I have
> extensively tested in QEMU, while the Windows boot has been tested in
> both QEMU (with ReactOS) and some random computers including mine (with
> Windows XP Home and Windows XP Pro x64).
> 

Even though I don't need your drivemap at all (see one of my replies to
Marco's ATA topic) I was so kind to try this out with my Vista x64
SP1 ;)

I haven't remembered exactly the 2 menuentrys you wrote in your mail so
I used a mixture of both :)

drivemap (hd1) (hd0)
set root=(hd0)
chainloader (hd1,1)+1

This booted fine for me, GRUB loaded from IDE disk and Vista chainloaded
from my SATA RAID 0.

I really hope that you get this commited, then there's one feature less
missing from grub-legacy ;)




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
@ 2008-08-08 13:43 Viswesh S
  0 siblings, 0 replies; 47+ messages in thread
From: Viswesh S @ 2008-08-08 13:43 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 1707 bytes --]





----- Original Message ----
From: Felix Zielcke <fzielcke@z-51.de>
To: The development of GRUB 2 <grub-devel@gnu.org>
Sent: Friday, 8 August, 2008 6:50:12 PM
Subject: Re: [PATCH] Drivemap module

Am Mittwoch, den 06.08.2008, 19:31 +0200 schrieb Javier Martín:

> This will work on Windows, because no matter where it tries to boot from
> it will find the same disk: both the first and second BIOS disks point
> to hd1 now. 
> ...
> This is the setup I have
> extensively tested in QEMU, while the Windows boot has been tested in
> both QEMU (with ReactOS) and some random computers including mine (with
> Windows XP Home and Windows XP Pro x64).
> 

Even though I don't need your drivemap at all (see one of my replies to
Marco's ATA topic) I was so kind to try this out with my Vista x64
SP1 ;)

I haven't remembered exactly the 2 menuentrys you wrote in your mail so
I used a mixture of both :)

drivemap (hd1) (hd0)
set root=(hd0)
chainloader (hd1,1)+1

This booted fine for me, GRUB loaded from IDE disk and Vista chainloaded
from my SATA RAID 0.

I really hope that you get this commited, then there's one feature less
missing from grub-legacy ;)

Hi,
Nice to know that the patch is working..for others also.meanwhile I am not too much sure,why the same is not happening to me.
The only difference in this case is that , my linux is in sata and my windows is in IDE.
Viswesh

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel



      Unlimited freedom, unlimited storage. Get it now, on http://help.yahoo.com/l/in/yahoo/mail/yahoomail/tools/tools-08.html/

[-- Attachment #2: Type: text/html, Size: 2897 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-05 16:39               ` Javier Martín
@ 2008-08-09 15:33                 ` Javier Martín
  2008-08-13 10:13                   ` Marco Gerards
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-08-09 15:33 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 1219 bytes --]

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.

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.).

-Habbit

[-- Attachment #1.2: drivemap.patch.7 --]
[-- Type: text/x-patch, Size: 27594 bytes --]

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);
+    }
+}
+
+/* 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");
+          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/>.
+ */
+
+#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 */
+      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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-09 15:33                 ` Javier Martín
@ 2008-08-13 10:13                   ` Marco Gerards
  2008-08-13 12:16                     ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-08-13 10:13 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín <lordhabbit@gmail.com> 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
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 10:13                   ` Marco Gerards
@ 2008-08-13 12:16                     ` Javier Martín
  2008-08-13 13:00                       ` Robert Millan
  2008-08-13 13:01                       ` Robert Millan
  0 siblings, 2 replies; 47+ messages in thread
From: Javier Martín @ 2008-08-13 12:16 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 3700 bytes --]

El mié, 13-08-2008 a las 12:13 +0200, Marco Gerards escribió:
> Javier Martín <lordhabbit@gmail.com> 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 ;-)
Oops, sorry... the only addition would be the new file drivemap.h, so
the "final" (I hope) version would be like this:

2008-08-13  Javier Martin  <lordhabbit@gmail.com>

        * commands/i386/pc/drivemap.c: New file.
        * commands/i386/pc/drivemap_int13h.S: New file.
        * conf/i386-pc.rmk (pkglib_MODULES): Added drivemap.mod.
        (drivemap_mod_SOURCES): New variable.
        (drivemap_mod_ASFLAGS): Likewise.
        (drivemap_mod_CFLAGS): Likewise.
        (drivemap_mod_LDFLAGS): Likewise.
        * include/grub/i386/pc/drivemap.h: New file.
        * include/grub/loader.h (grub_loader_register_preboot): New
        function prototype.
        (grub_loader_unregister_preboot): Likewise.
        (grub_preboot_hookid): New typedef.
        * kern/loader.c (grub_loader_register_preboot): New function.
        (grub_loader_unregister_preboot): Likewise.
        (preboot_hooks): New variable.
        (grub_loader_boot): Call the list of preboot-hooks before the
        actual loader.

WRT the code, all your concerns were taken care of (I keep forgetting
the ".  " in comments, sorry), and about this snippet:

> +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?

The "drivemap -l" command lists all remapped drives. The format of the listing has been reworked in order to be both understandable for end-users and unambiguous for devs and power users. It is now something akin to:

BIOS disk #num ----> GRUB device
FD #0   (0x00)       (fd3)
(...)
FD #127 (0x7f)       (hd15)
HD #0   (0x80)       (hd2)
(...)
HD #127 (0xff)       (fd0)

Of course, it only shows remapped drives, not the full 255 possibilities. Well, here is "version 8" of the patch... hope you like it ;)

-Habbit

[-- Attachment #1.2: drivemap.patch.8 --]
[-- Type: text/x-patch, Size: 27859 bytes --]

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,434 @@
+/* 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);
+    }
+}
+
+/* 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");
+          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: 1802)
+++ 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: 1802)
+++ 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,42 @@
+/* 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/>.
+ */
+
+#ifndef GRUB_DRIVEMAP_MACHINE_HEADER
+#define GRUB_DRIVEMAP_MACHINE_HEADER	1
+
+#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;
+
+#endif /* ! GRUB_DRIVEMAP_MACHINE_HEADER */
Index: kern/loader.c
===================================================================
--- kern/loader.c	(revisión: 1802)
+++ kern/loader.c	(copia de trabajo)
@@ -61,11 +61,88 @@
   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)
+    {
+      /* Patch the "hole" in the list, if the entry was in the middle,
+         or reassign the list head if the entry was the first.  */
+      if (previous)
+        previous->next = entry->next;
+      else
+        preboot_hooks = entry->next;
+      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 ();

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 12:16                     ` Javier Martín
@ 2008-08-13 13:00                       ` Robert Millan
  2008-08-13 14:28                         ` Javier Martín
  2008-08-13 13:01                       ` Robert Millan
  1 sibling, 1 reply; 47+ messages in thread
From: Robert Millan @ 2008-08-13 13:00 UTC (permalink / raw)
  To: The development of GRUB 2


Hi,

Marco asked me to review this.  I haven't followed the earlier discussion,
so if I say or ask something that was discussed before, please bear with me
and just point me to that.

On Wed, Aug 13, 2008 at 02:16:25PM +0200, Javier Martín wrote:
> +
> +#define MODNAME "drivemap"
> +
> [...]
> +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);

I don't think this MODNAME approach is a bad idea per se [1][2], but if we
are to do it, IMHO this should really be done globally for consistency, and
preferably separately from this patch.

[1] But I'd use a const char[] instead of a macro to save space.  Maybe
    significant space can be saved when doing this throurough the code!

[2] In fact, I think it's a nice idea.

> +/* 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)
> +{

Can this be made generic?  Like "install_int_handler (int n)".  We're probably
going to need interrupts for other things later on.

Or is this code suitable for i8086 mode interrupts only?

Anyway, if it's made generic, remember the handler itself becomes suitable for
all i386 ports, not just i386-pc (for directory placement).

> +  drivemap_node_t *curentry = drivemap;

We use 'curr' as a handle for 'current' in lots of places throurough the code
(rgrep for curr[^e]).  I think it's better to be consistent with that.

> +/* 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

Is this a signature?  Then a macro would be preferred, so that it can be shared
with whoever checks for it.

What is it used for, anyway?  In general, I like to be careful when using
signatures because they introduce a non-deterministic factor (e.g. GRUB
might have a 1/64k possibility to missbehave).

> +FUNCTION(grub_drivemap_int13_handler)
> +  push %bp
> +  mov %sp, %bp
> +  push %ax  /* We'll need it later to determine the used BIOS function.  */

Please use size modifiers (pushw, movw, etc).

> Index: include/grub/loader.h
> ===================================================================
> --- include/grub/loader.h	(revisión: 1802)
> +++ 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);

This interface is added for all platforms.  I didn't follow the discussion;
has it been considered that it will be useful elsewhere then i386-pc?

> +/* 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;

I think this name is too generic for such an specific purpose.

> Index: kern/loader.c
> ===================================================================
> --- kern/loader.c	(revisión: 1802)
> +++ kern/loader.c	(copia de trabajo)
> @@ -61,11 +61,88 @@
>    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)
> +{
> [...]

This is a lot of code being added to kernel, and space in kernel is highly
valuable.

Would the same functionality work if put inside a module?

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 12:16                     ` Javier Martín
  2008-08-13 13:00                       ` Robert Millan
@ 2008-08-13 13:01                       ` Robert Millan
  1 sibling, 0 replies; 47+ messages in thread
From: Robert Millan @ 2008-08-13 13:01 UTC (permalink / raw)
  To: The development of GRUB 2

On Wed, Aug 13, 2008 at 02:16:25PM +0200, Javier Martín wrote:
> +static grub_err_t
> +revparse_biosdisk(const grub_uint8_t dnum, const char **output)

Ah, and please separate function names from parenthesis ;-)

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 13:00                       ` Robert Millan
@ 2008-08-13 14:28                         ` Javier Martín
  2008-08-13 14:51                           ` Marco Gerards
  2008-08-13 15:14                           ` Robert Millan
  0 siblings, 2 replies; 47+ messages in thread
From: Javier Martín @ 2008-08-13 14:28 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 8935 bytes --]

El mié, 13-08-2008 a las 15:00 +0200, Robert Millan escribió:
> Hi,
> 
> Marco asked me to review this.
So he finally got fed up of me... Understandable ^^

>   I haven't followed the earlier discussion,
> so if I say or ask something that was discussed before, please bear with me
> and just point me to that.
> 
> On Wed, Aug 13, 2008 at 02:16:25PM +0200, Javier Martín wrote:
> > +
> > +#define MODNAME "drivemap"
> > +
> > [...]
> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
> 
> I don't think this MODNAME approach is a bad idea per se [1][2], but if we
> are to do it, IMHO this should really be done globally for consistency, and
> preferably separately from this patch.
> 
> [1] But I'd use a const char[] instead of a macro to save space.  Maybe
>     significant space can be saved when doing this throurough the code!
> 
> [2] In fact, I think it's a nice idea.
Ok, so following your [1], what about replacing the define with... ?

static const char[] MODNAME = "drivemap";
> 
> > +/* 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)
> > +{
> 
> Can this be made generic?  Like "install_int_handler (int n)".  We're probably
> going to need interrupts for other things later on.
> 
> Or is this code suitable for i8086 mode interrupts only?
> 
> Anyway, if it's made generic, remember the handler itself becomes suitable for
> all i386 ports, not just i386-pc (for directory placement).

It _could_ be made generic, but the function as it is currently designed
installs a TSR-like assembly routine (more properly a bundle formed by a
routine and its data) in conventional memory that it has previously
reserved. Furthermore, it accesses the real-mode IVT at its "standard"
location of 0, which could be a weakness since from the 286 on even the
realmode IVT can be relocated with lidt.

Nevertheless, I don't think this functionality is so badly needed on its
own that it would be good to delay the implementation of "drivemap" to
wait for the re-engineering of this function.

> 
> > +  drivemap_node_t *curentry = drivemap;
> 
> We use 'curr' as a handle for 'current' in lots of places throurough the code
> (rgrep for curr[^e]).  I think it's better to be consistent with that.
Done.  

> 
> > +/* 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
> 
> Is this a signature?  Then a macro would be preferred, so that it can be shared
> with whoever checks for it.
> 
> What is it used for, anyway?  In general, I like to be careful when using
> signatures because they introduce a non-deterministic factor (e.g. GRUB
> might have a 1/64k possibility to missbehave).
Sorry, it was a leftover from early development, in which I had to debug
the installing code to see whether the pointer to the old int13 was
installer: a pointer of "beef:dead" was a clue that it didn't work.
Removed and replaced with 32 bits of zeros.  

> 
> > +FUNCTION(grub_drivemap_int13_handler)
> > +  push %bp
> > +  mov %sp, %bp
> > +  push %ax  /* We'll need it later to determine the used BIOS function.  */
> 
> Please use size modifiers (pushw, movw, etc).
What for? the operands are clearly unambiguous.  As you can see with the
"xchgw %ax, -4(%bp)" line, I only use them for disambiguation.  Assembly
language is cluttered enough - and AT&T syntax is twisted enough as it
is.  In fact, given that the code is specific for i386, I'd like to
rewrite this code in GAS-Intel syntax so that memory references are not
insane: -4(%bp)? please... we can have a simpler [bp - 4].  

> 
> > Index: include/grub/loader.h
> > ===================================================================
> > --- include/grub/loader.h	(revisión: 1802)
> > +++ 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);
> 
> This interface is added for all platforms.  I didn't follow the discussion;
> has it been considered that it will be useful elsewhere then i386-pc?
Most likely not, and the discussion on this particular piece of the code
died out long time (months) ago without reaching any decision.  It's a
way I've found for a fully out-kernel module like drivemap to set a
just-before-boot hook in order to install its int13 handler: installing
it earlier could cause havoc with the biosdisk driver, as drives in GRUB
would suddenly reverse, duplicate, disappear, etc.  

This "solution" is the lightest and most scalable I've found that does
not introduce drivemap-specific code in the kernel, because this
infrastructure could be used by other modules.  

> 
> > +/* 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;
> 
> I think this name is too generic for such an specific purpose.
What about "grub_asmsymbol_t"?

> 
> > Index: kern/loader.c
> > ===================================================================
> > --- kern/loader.c	(revisión: 1802)
> > +++ kern/loader.c	(copia de trabajo)
> > @@ -61,11 +61,88 @@
> >    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)
> > +{
> > [...]
> 
> This is a lot of code being added to kernel, and space in kernel is highly
> valuable.
> 
> Would the same functionality work if put inside a module?
For the reasons discussed above in the loader.h snippet, I don't think
so: the only "lighter" solution would be to just put a drivemap_hook
variable that would be called before boot, but as I mentioned before,
this solution can be employed by other modules as well.

Besides (and I realize this is not a great defense) it's not _that_ much
code: just a simple linked-list implementation with add and delete
operations, and the iteration of it on loader_boot. I did not check how
many bytes does this patch add by itself, but I can run some simulations
(I totally _had_ to say that ^^) if you want.

El mié, 13-08-2008 a las 15:01 +0200, Robert Millan escribió:
> On Wed, Aug 13, 2008 at 02:16:25PM +0200, Javier Martín wrote:
> > +static grub_err_t
> > +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
> 
> Ah, and please separate function names from parenthesis ;-)
Done.  Do you and/or Marco perform any automated search (grep & friends)
for these thingies? It's either that or the robot theory... ¬¬

Well, I'm feeling lazy enough today not to attach a new version of the
patch for just five cosmetic changes (unless you're going to tell me
that it's ripe for commit :D) so we can continue discussion on the other
issues on the table.

-Habbit

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 14:28                         ` Javier Martín
@ 2008-08-13 14:51                           ` Marco Gerards
  2008-08-13 15:14                           ` Robert Millan
  1 sibling, 0 replies; 47+ messages in thread
From: Marco Gerards @ 2008-08-13 14:51 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Javier Martín <lordhabbit@gmail.com> writes:

> El mié, 13-08-2008 a las 15:00 +0200, Robert Millan escribió:
>> Hi,
>> 
>> Marco asked me to review this.
> So he finally got fed up of me... Understandable ^^

No, but I am not as qualified regarding the BIOS as Robert is, except
for general remarks ;-)

>>   I haven't followed the earlier discussion,
>> so if I say or ask something that was discussed before, please bear with me
>> and just point me to that.
>> 
>> On Wed, Aug 13, 2008 at 02:16:25PM +0200, Javier Martín wrote:
>> > +
>> > +#define MODNAME "drivemap"
>> > +
>> > [...]
>> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", args[0], mapfrom);
>> 
>> I don't think this MODNAME approach is a bad idea per se [1][2], but if we
>> are to do it, IMHO this should really be done globally for consistency, and
>> preferably separately from this patch.
>> 
>> [1] But I'd use a const char[] instead of a macro to save space.  Maybe
>>     significant space can be saved when doing this throurough the code!
>> 
>> [2] In fact, I think it's a nice idea.
> Ok, so following your [1], what about replacing the define with... ?
>
> static const char[] MODNAME = "drivemap";

static const char[] modname = "drivemap";

We don't capatilize variables ;)
 
>> > +/* 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)
>> > +{
>> 
>> Can this be made generic?  Like "install_int_handler (int n)".  We're probably
>> going to need interrupts for other things later on.
>> 
>> Or is this code suitable for i8086 mode interrupts only?
>> 
>> Anyway, if it's made generic, remember the handler itself becomes suitable for
>> all i386 ports, not just i386-pc (for directory placement).
>
> It _could_ be made generic, but the function as it is currently designed
> installs a TSR-like assembly routine (more properly a bundle formed by a
> routine and its data) in conventional memory that it has previously
> reserved. Furthermore, it accesses the real-mode IVT at its "standard"
> location of 0, which could be a weakness since from the 286 on even the
> realmode IVT can be relocated with lidt.
>
> Nevertheless, I don't think this functionality is so badly needed on its
> own that it would be good to delay the implementation of "drivemap" to
> wait for the re-engineering of this function.

This can go in and we can change it, I think.
 
>> > +  drivemap_node_t *curentry = drivemap;
>> 
>> We use 'curr' as a handle for 'current' in lots of places throurough the code
>> (rgrep for curr[^e]).  I think it's better to be consistent with that.
> Done.  
>
>> 
>> > +/* 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
>> 
>> Is this a signature?  Then a macro would be preferred, so that it can be shared
>> with whoever checks for it.
>> 
>> What is it used for, anyway?  In general, I like to be careful when using
>> signatures because they introduce a non-deterministic factor (e.g. GRUB
>> might have a 1/64k possibility to missbehave).
> Sorry, it was a leftover from early development, in which I had to debug
> the installing code to see whether the pointer to the old int13 was
> installer: a pointer of "beef:dead" was a clue that it didn't work.
> Removed and replaced with 32 bits of zeros.  
>
>> 
>> > +FUNCTION(grub_drivemap_int13_handler)
>> > +  push %bp
>> > +  mov %sp, %bp
>> > +  push %ax  /* We'll need it later to determine the used BIOS function.  */
>> 
>> Please use size modifiers (pushw, movw, etc).
> What for? the operands are clearly unambiguous.  As you can see with the
> "xchgw %ax, -4(%bp)" line, I only use them for disambiguation.  Assembly
> language is cluttered enough - and AT&T syntax is twisted enough as it
> is.  In fact, given that the code is specific for i386, I'd like to
> rewrite this code in GAS-Intel syntax so that memory references are not
> insane: -4(%bp)? please... we can have a simpler [bp - 4].  

I'll leave that to Robert :-)

>> > Index: include/grub/loader.h
>> > ===================================================================
>> > --- include/grub/loader.h	(revisión: 1802)
>> > +++ 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);
>> 
>> This interface is added for all platforms.  I didn't follow the discussion;
>> has it been considered that it will be useful elsewhere then i386-pc?
> Most likely not, and the discussion on this particular piece of the code
> died out long time (months) ago without reaching any decision.  It's a
> way I've found for a fully out-kernel module like drivemap to set a
> just-before-boot hook in order to install its int13 handler: installing
> it earlier could cause havoc with the biosdisk driver, as drives in GRUB
> would suddenly reverse, duplicate, disappear, etc.  

Perhaps Bean should get involved in the discussion ;-).  He talked
about this in another thread.

> This "solution" is the lightest and most scalable I've found that does
> not introduce drivemap-specific code in the kernel, because this
> infrastructure could be used by other modules.  
>
>> 
>> > +/* 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;
>> 
>> I think this name is too generic for such an specific purpose.
> What about "grub_asmsymbol_t"?

How about a grub_drivemap_ prefix?  Actually, I forgot to check you
used a prefix for exported symbols and in headerfiles.  Did you? :)

>> 
>> > Index: kern/loader.c
>> > ===================================================================
>> > --- kern/loader.c	(revisión: 1802)
>> > +++ kern/loader.c	(copia de trabajo)
>> > @@ -61,11 +61,88 @@
>> >    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)
>> > +{
>> > [...]
>> 
>> This is a lot of code being added to kernel, and space in kernel is highly
>> valuable.
>> 
>> Would the same functionality work if put inside a module?
> For the reasons discussed above in the loader.h snippet, I don't think
> so: the only "lighter" solution would be to just put a drivemap_hook
> variable that would be called before boot, but as I mentioned before,
> this solution can be employed by other modules as well.
>
> Besides (and I realize this is not a great defense) it's not _that_ much
> code: just a simple linked-list implementation with add and delete
> operations, and the iteration of it on loader_boot. I did not check how
> many bytes does this patch add by itself, but I can run some simulations
> (I totally _had_ to say that ^^) if you want.
>
> El mié, 13-08-2008 a las 15:01 +0200, Robert Millan escribió:
>> On Wed, Aug 13, 2008 at 02:16:25PM +0200, Javier Martín wrote:
>> > +static grub_err_t
>> > +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
>> 
>> Ah, and please separate function names from parenthesis ;-)
> Done.  Do you and/or Marco perform any automated search (grep & friends)
> for these thingies? It's either that or the robot theory... ¬¬

It might be a good idea to make some script that tests patches
automatically.  Do you want to volunteer? :P

> Well, I'm feeling lazy enough today not to attach a new version of the
> patch for just five cosmetic changes (unless you're going to tell me
> that it's ripe for commit :D) so we can continue discussion on the other
> issues on the table.

Like the argument syntax I proposed?  map --grub (hd0) --os (hd1) and
alike?

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 14:28                         ` Javier Martín
  2008-08-13 14:51                           ` Marco Gerards
@ 2008-08-13 15:14                           ` Robert Millan
  2008-08-13 15:57                             ` Marco Gerards
  1 sibling, 1 reply; 47+ messages in thread
From: Robert Millan @ 2008-08-13 15:14 UTC (permalink / raw)
  To: The development of GRUB 2

On Wed, Aug 13, 2008 at 04:28:24PM +0200, Javier Martín wrote:
> > 
> > I don't think this MODNAME approach is a bad idea per se [1][2], but if we
> > are to do it, IMHO this should really be done globally for consistency, and
> > preferably separately from this patch.
> > 
> > [1] But I'd use a const char[] instead of a macro to save space.  Maybe
> >     significant space can be saved when doing this throurough the code!
> > 
> > [2] In fact, I think it's a nice idea.
> Ok, so following your [1], what about replacing the define with... ?
> 
> static const char[] MODNAME = "drivemap";

Yes, but I'd merge this change separately from your drivemap patch (either
before or after, as you prefer), for the whole of the codebase.  Doesn't make
sense to do it in some places but not in others, IMHO.

> It _could_ be made generic, but the function as it is currently designed
> installs a TSR-like assembly routine (more properly a bundle formed by a
> routine and its data) in conventional memory that it has previously
> reserved. Furthermore, it accesses the real-mode IVT at its "standard"
> location of 0, which could be a weakness since from the 286 on even the
> realmode IVT can be relocated with lidt.
> 
> Nevertheless, I don't think this functionality is so badly needed on its
> own that it would be good to delay the implementation of "drivemap" to
> wait for the re-engineering of this function.

Fair enough.  The addr=0 assumption sounds troubling, though.  Why not use
sidt instead?

> > > +/* 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
> > 
> > Is this a signature?  Then a macro would be preferred, so that it can be shared
> > with whoever checks for it.
> > 
> > What is it used for, anyway?  In general, I like to be careful when using
> > signatures because they introduce a non-deterministic factor (e.g. GRUB
> > might have a 1/64k possibility to missbehave).
> Sorry, it was a leftover from early development, in which I had to debug
> the installing code to see whether the pointer to the old int13 was
> installer: a pointer of "beef:dead" was a clue that it didn't work.
> Removed and replaced with 32 bits of zeros.  

Ok.

> > 
> > > +FUNCTION(grub_drivemap_int13_handler)
> > > +  push %bp
> > > +  mov %sp, %bp
> > > +  push %ax  /* We'll need it later to determine the used BIOS function.  */
> > 
> > Please use size modifiers (pushw, movw, etc).
> What for? the operands are clearly unambiguous.

For consistency (I'm so predictable).  We do the same everywhere else.  Also,
I think some versions of binutils reject instructions that don't have size
qualifiers.

And it's more readable for people used to gas (I know, it's also less readable
for people used to tasm or so).

> > This interface is added for all platforms.  I didn't follow the discussion;
> > has it been considered that it will be useful elsewhere then i386-pc?
> Most likely not, and the discussion on this particular piece of the code
> died out long time (months) ago without reaching any decision.  It's a
> way I've found for a fully out-kernel module like drivemap to set a
> just-before-boot hook in order to install its int13 handler: installing
> it earlier could cause havoc with the biosdisk driver, as drives in GRUB
> would suddenly reverse, duplicate, disappear, etc.  
> 
> This "solution" is the lightest and most scalable I've found that does
> not introduce drivemap-specific code in the kernel, because this
> infrastructure could be used by other modules.  
>
> [...]
> > This is a lot of code being added to kernel, and space in kernel is highly
> > valuable.
> > 
> > Would the same functionality work if put inside a module?
> For the reasons discussed above in the loader.h snippet, I don't think
> so: the only "lighter" solution would be to just put a drivemap_hook
> variable that would be called before boot, but as I mentioned before,
> this solution can be employed by other modules as well.
> 
> Besides (and I realize this is not a great defense) it's not _that_ much
> code: just a simple linked-list implementation with add and delete
> operations, and the iteration of it on loader_boot. I did not check how
> many bytes does this patch add by itself, but I can run some simulations
> (I totally _had_ to say that ^^) if you want.

Having a small kernel is highly desireable for most users.  If the kernel is
too big, it won't fit and then either we have to use blocklists (which are
unreliable), or we have to abort the install.

Please, try to find a way that doesn't increase kernel size significantly.

If the kernel interfaces are not extensible enough, you could try to readjust
them for your needs.  This approach works well most of the time (although I
haven't studied this particular problem in detail).

> > > +static grub_err_t
> > > +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
> > 
> > Ah, and please separate function names from parenthesis ;-)
> Done.  Do you and/or Marco perform any automated search (grep & friends)
> for these thingies? It's either that or the robot theory...

Actually, I just spotted it by chance.  But don't you give me ideas ;-)

> Well, I'm feeling lazy enough today not to attach a new version of the
> patch for just five cosmetic changes (unless you're going to tell me
> that it's ripe for commit :D) so we can continue discussion on the other
> issues on the table.

Okay.  Looking forward to a very high quality drivemap patch from you :-)

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 15:14                           ` Robert Millan
@ 2008-08-13 15:57                             ` Marco Gerards
  2008-08-13 22:38                               ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-08-13 15:57 UTC (permalink / raw)
  To: The development of GRUB 2

Robert Millan <rmh@aybabtu.com> writes:

[...]

>> > This is a lot of code being added to kernel, and space in kernel is highly
>> > valuable.
>> > 
>> > Would the same functionality work if put inside a module?
>> For the reasons discussed above in the loader.h snippet, I don't think
>> so: the only "lighter" solution would be to just put a drivemap_hook
>> variable that would be called before boot, but as I mentioned before,
>> this solution can be employed by other modules as well.
>> 
>> Besides (and I realize this is not a great defense) it's not _that_ much
>> code: just a simple linked-list implementation with add and delete
>> operations, and the iteration of it on loader_boot. I did not check how
>> many bytes does this patch add by itself, but I can run some simulations
>> (I totally _had_ to say that ^^) if you want.
>
> Having a small kernel is highly desireable for most users.  If the kernel is
> too big, it won't fit and then either we have to use blocklists (which are
> unreliable), or we have to abort the install.
>
> Please, try to find a way that doesn't increase kernel size significantly.
>
> If the kernel interfaces are not extensible enough, you could try to readjust
> them for your needs.  This approach works well most of the time (although I
> haven't studied this particular problem in detail).

Like discussed before.  Bring up such modifications like hooks up in a
*separate* thread.  I already said that not everyone reads this
discussion.  I will not accept a patch that changes the kernel if it
is part of a bigger patch that not many people read.

Please don't discuss this over with Robert and me, you know that it
was pointed out that this has to be a patch in a separate thread.
Furthermore, this is a way to get some feedback from Bean who wants
something similar, IIRC.

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 15:57                             ` Marco Gerards
@ 2008-08-13 22:38                               ` Javier Martín
  2008-08-14 17:15                                 ` Marco Gerards
  0 siblings, 1 reply; 47+ messages in thread
From: Javier Martín @ 2008-08-13 22:38 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 8451 bytes --]

Ok, making a mixup reply...

El mié, 13-08-2008 a las 17:14 +0200, Robert Millan escribió: 
> On Wed, Aug 13, 2008 at 04:28:24PM +0200, Javier Martín wrote:
> > > 
> > > I don't think this MODNAME approach is a bad idea per se [1][2], but if we
> > > are to do it, IMHO this should really be done globally for consistency, and
> > > preferably separately from this patch.
> > > 
> > > [1] But I'd use a const char[] instead of a macro to save space.  Maybe
> > >     significant space can be saved when doing this throurough the code!
> > > 
> > > [2] In fact, I think it's a nice idea.
> > Ok, so following your [1], what about replacing the define with... ?
> > 
> > static const char[] MODNAME = "drivemap";
> 
> Yes, but I'd merge this change separately from your drivemap patch (either
> before or after, as you prefer), for the whole of the codebase.  Doesn't make
> sense to do it in some places but not in others, IMHO.
Urgh... It's already been implemented in drivemap, why not put it in
with it, then change everything else? Or should I just keep the #define
in the meantime?

> 
> > It _could_ be made generic, but the function as it is currently designed
> > installs a TSR-like assembly routine (more properly a bundle formed by a
> > routine and its data) in conventional memory that it has previously
> > reserved. Furthermore, it accesses the real-mode IVT at its "standard"
> > location of 0, which could be a weakness since from the 286 on even the
> > realmode IVT can be relocated with lidt.
> > 
> > Nevertheless, I don't think this functionality is so badly needed on its
> > own that it would be good to delay the implementation of "drivemap" to
> > wait for the re-engineering of this function.
> 
> Fair enough.  The addr=0 assumption sounds troubling, though.  Why not use
> sidt instead?
Well, so is the assumption that GRUB does not enable any kind of paging
or memory remapping for that matter. WRT sidt, I think that could be
better implemented as a function in kern/i386/pc called
grub_machine_get_rmove_ivt() or SLT, because it requires dropping to
rmode, executing sidt, going back to pmode and then returning the
address, modified to be valid in pmode as required.

This approach would cost a few bytes in kernel (I can hear you screaming
already), but it would be extensible for the future "interrupt
installer" you envisioned, and it would take care of the paging/mappings
if there ever were any. Same for a function to get the address of the
BDA or other machine-specific addresses.

> 
> > > > +/* 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
> > > 
> > > Is this a signature?  Then a macro would be preferred, so that it can be shared
> > > with whoever checks for it.
> > > 
> > > What is it used for, anyway?  In general, I like to be careful when using
> > > signatures because they introduce a non-deterministic factor (e.g. GRUB
> > > might have a 1/64k possibility to missbehave).
> > Sorry, it was a leftover from early development, in which I had to debug
> > the installing code to see whether the pointer to the old int13 was
> > installer: a pointer of "beef:dead" was a clue that it didn't work.
> > Removed and replaced with 32 bits of zeros.  
> 
> Ok.
> 
> > > 
> > > > +FUNCTION(grub_drivemap_int13_handler)
> > > > +  push %bp
> > > > +  mov %sp, %bp
> > > > +  push %ax  /* We'll need it later to determine the used BIOS function.  */
> > > 
> > > Please use size modifiers (pushw, movw, etc).
> > What for? the operands are clearly unambiguous.
> 
> For consistency (I'm so predictable).  We do the same everywhere else.  Also,
> I think some versions of binutils reject instructions that don't have size
> qualifiers.
Version 0.1 (alpha 2) maybe? IIRC, it's been long since the size
qualifiers inference is available in GAS.  
> 
> And it's more readable for people used to gas (I know, it's also less readable
> for people used to tasm or so).
TASM? Don't even name the devil.  I like GAS and its directives (even
better and NASM and YASM), but many of the conventions of the AT&T
syntax are at best broken (i.e. src, dest looks good, but breaks on FP
instructions), and memory references are a royal PITA.  Given that the
code is _not_ platform-portable and that a PPC dev will not understand
it even in AT&T syntax, why bother with it...  

However, I take it that your advertised predictability means that I
should consider the "int13h in intel syntax" request denied.
Nevertheless, I've attached the two versions of the asm file, so you can
check which one is less of a mind boggler. Both assemble to _exactly_
the same machine code (checked with gcc + objdump).


El mié, 13-08-2008 a las 17:57 +0200, Marco Gerards escribió:
> Robert Millan <rmh@aybabtu.com> writes:
> 
> [...]
> 
> >> > This is a lot of code being added to kernel, and space in kernel is highly
> >> > valuable.
> >> > 
> >> > Would the same functionality work if put inside a module?
> >> For the reasons discussed above in the loader.h snippet, I don't think
> >> so: the only "lighter" solution would be to just put a drivemap_hook
> >> variable that would be called before boot, but as I mentioned before,
> >> this solution can be employed by other modules as well.
> >> 
> >> Besides (and I realize this is not a great defense) it's not _that_ much
> >> code: just a simple linked-list implementation with add and delete
> >> operations, and the iteration of it on loader_boot. I did not check how
> >> many bytes does this patch add by itself, but I can run some simulations
> >> (I totally _had_ to say that ^^) if you want.
> >
> > Having a small kernel is highly desireable for most users.  If the kernel is
> > too big, it won't fit and then either we have to use blocklists (which are
> > unreliable), or we have to abort the install.
> >
> > Please, try to find a way that doesn't increase kernel size significantly.
> >
> > If the kernel interfaces are not extensible enough, you could try to readjust
> > them for your needs.  This approach works well most of the time (although I
> > haven't studied this particular problem in detail).
> 
> Like discussed before.  Bring up such modifications like hooks up in a
> *separate* thread.  I already said that not everyone reads this
> discussion.  I will not accept a patch that changes the kernel if it
> is part of a bigger patch that not many people read.
> 
> Please don't discuss this over with Robert and me, you know that it
> was pointed out that this has to be a patch in a separate thread.
> Furthermore, this is a way to get some feedback from Bean who wants
> something similar, IIRC.

I know I will be regretting saying this, but it is _very_ rude to review
some five versions of the patch, spotting mostly coding-style errors on
each, and then, on version 8, tell me that "you won't accept a patch
that contains blah" (with "blah" being essential for the patch to work).
Quite the proverbial slap in the face to me.

There was a discussion with other people sometime near May and June,
true, and many people were mildly opposed to the current design, but I
think I had proved that while the implementation could change (e.g. to a
fixed-size array), the interface is pretty much as simple as it can get.
Besides, and given that it is (and will be for the foreseeable future)
only used by drivemap, changes to it should be pretty manageable and
havoc-less.

WRT Bean's hook proposal, even the BOOT_PRELOAD I've seen discussed
works before the _high-level_ "boot" command is issued, while my patch
acts from within grub_loader_boot, before grub_X_real_boot is called.
However, given that the only actual difference in that timing is that my
code works after loader.c has checked whether a kernel has been loaded
and that can be checked from "userland" (i.e. modules), I could
eventually use Bean's hooks for drivemap. I've searched the archives and
haven't found any code, though. Could I get a cookie, er... pointer?

-Habbit

> 
> --
> Marco
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

[-- Attachment #1.2: drivemap_int13h.S --]
[-- Type: text/x-csrc, Size: 4757 bytes --]

/*
 *  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)
  .long 0

/* 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)


[-- Attachment #1.3: drivemap_int13h_intel.S --]
[-- Type: text/x-csrc, Size: 4795 bytes --]

/*
 *  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>

.intel_syntax noprefix

#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)
  .long 0

/* 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 bp, sp
  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 bx, offset GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_mapstart)
  xor si, si
1:mov ax, word ptr cs:[bx + si]
  cmp al, ah
  jz 3f /* DRV=DST => map end - drive not remapped, leave DL as-is.  */
  cmp al, dl
  jz 2f /* Found - drive remapped, modify DL.  */
  add si, 2
  jmp 1b /* Not found, but more remaining, loop.  */
2:mov dl, ah
3:pop si
  pop bx
  xchg ax, [bp - 4] /* 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 [bp + 6]
  mov bp, word ptr [bp]  /* Restore the caller BP (is this needed and/or sensible?).  */
  call dword ptr 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
  mov word ptr [bp + 6], ax
  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.  */
#  cmp byte ptr [bp - 1], 0x08 /* Caller's AH.  */
#  jne 4f
#  xchg ax, [bp - 4] /* Map entry used.  */
#  cmp al, ah  /* DRV=DST => drive not remapped.  */
#  je 4f
#  mov dl, ah  /* Undo remap.  */

4:mov sp, bp
  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)


[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-13 22:38                               ` Javier Martín
@ 2008-08-14 17:15                                 ` Marco Gerards
  2008-08-14 22:17                                   ` Javier Martín
  0 siblings, 1 reply; 47+ messages in thread
From: Marco Gerards @ 2008-08-14 17:15 UTC (permalink / raw)
  To: The development of GRUB 2

Javier Martín <lordhabbit@gmail.com> writes:

> Ok, making a mixup reply...

Please don't.  Unless you do not like it that your mails go unread.

>> > Having a small kernel is highly desireable for most users.  If the kernel is
>> > too big, it won't fit and then either we have to use blocklists (which are
>> > unreliable), or we have to abort the install.
>> >
>> > Please, try to find a way that doesn't increase kernel size significantly.
>> >
>> > If the kernel interfaces are not extensible enough, you could try to readjust
>> > them for your needs.  This approach works well most of the time (although I
>> > haven't studied this particular problem in detail).
>> 
>> Like discussed before.  Bring up such modifications like hooks up in a
>> *separate* thread.  I already said that not everyone reads this
>> discussion.  I will not accept a patch that changes the kernel if it
>> is part of a bigger patch that not many people read.
>> 
>> Please don't discuss this over with Robert and me, you know that it
>> was pointed out that this has to be a patch in a separate thread.
>> Furthermore, this is a way to get some feedback from Bean who wants
>> something similar, IIRC.
>
> I know I will be regretting saying this, but it is _very_ rude to review
> some five versions of the patch, spotting mostly coding-style errors on
> each, and then, on version 8, tell me that "you won't accept a patch
> that contains blah" (with "blah" being essential for the patch to work).
> Quite the proverbial slap in the face to me.

I did NOT say that your patch will not be accepted.  In one of the
earlier reviews (perhaps even the first) I mentioned that certain
parts should be reviewed separately.  It is a slap in the face that
you imply I am a bad person when I repeat that you need to bring up
some issues *separately*.  I would consider it bad style from me
towards other developers to change code they might have a strong
opinion on.

And you can say what you want, but if you are stubborn enough to
ignore what I say in the reviews, you shouldn't be surprised if I
didn't change my opinion in the next review and still ask you to
change something.  In fact, it is rude just to send in a new patch
while you did not take the review seriously.  There are a lot of
projects that reject patches on beforehand if you didn't at least
bother to get familiar with their coding styles.

Furthermore, I reviewed your patch mainly because I care, not because
I am the foremost expert in BIOS disks.  It's for the best if other
people can look at specific parts of your patch, especially if it
changes the kernel.

Perhaps you do not notice how many time I spent on reviewing patches
on this list lately.  Anyways, if it is not appreciated, I will leave
the reviews to other people.  But I simply will reject all code I do
not like without actually bothering to explain why.  I rather spend my
time on coding instead of feeding trolls.

--
Marco




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Drivemap module
  2008-08-14 17:15                                 ` Marco Gerards
@ 2008-08-14 22:17                                   ` Javier Martín
  0 siblings, 0 replies; 47+ messages in thread
From: Javier Martín @ 2008-08-14 22:17 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 5600 bytes --]

El jue, 14-08-2008 a las 19:15 +0200, Marco Gerards escribió:
> Javier Martín <lordhabbit@gmail.com> writes:
> 
> > Ok, making a mixup reply...
> 
> Please don't.  Unless you do not like it that your mails go unread.
> 
> >> > Having a small kernel is highly desireable for most users.  If the kernel is
> >> > too big, it won't fit and then either we have to use blocklists (which are
> >> > unreliable), or we have to abort the install.
> >> >
> >> > Please, try to find a way that doesn't increase kernel size significantly.
> >> >
> >> > If the kernel interfaces are not extensible enough, you could try to readjust
> >> > them for your needs.  This approach works well most of the time (although I
> >> > haven't studied this particular problem in detail).
> >> 
> >> Like discussed before.  Bring up such modifications like hooks up in a
> >> *separate* thread.  I already said that not everyone reads this
> >> discussion.  I will not accept a patch that changes the kernel if it
> >> is part of a bigger patch that not many people read.
> >> 
> >> Please don't discuss this over with Robert and me, you know that it
> >> was pointed out that this has to be a patch in a separate thread.
> >> Furthermore, this is a way to get some feedback from Bean who wants
> >> something similar, IIRC.
> >
> > I know I will be regretting saying this, but it is _very_ rude to review
> > some five versions of the patch, spotting mostly coding-style errors on
> > each, and then, on version 8, tell me that "you won't accept a patch
> > that contains blah" (with "blah" being essential for the patch to work).
> > Quite the proverbial slap in the face to me.
> 
> I did NOT say that your patch will not be accepted.  In one of the
> earlier reviews (perhaps even the first) I mentioned that certain
> parts should be reviewed separately.  It is a slap in the face that
> you imply I am a bad person when I repeat that you need to bring up
> some issues *separately*.  I would consider it bad style from me
> towards other developers to change code they might have a strong
> opinion on.
I don't imply you're a bad person or anything like that, sorry if it
seemed so.  By the way, I searched the whole thread of "[PATCH] drivemap
module" (starting in july 4-20) and the only instances in which
"separate", "separately", "split" or similar words are related to
phrases like "put the return on a separate line" or "split these
declarations" - even though I _do_ remember you telling me something
like what you say.

I have actually taken the time to re-read all three threads related to
drivemap [0][1][2] and the main opposition (from Vesa) was due to what
the boot command would do if one of the hooks with abort_on_error set
failed and the abort_on_error bit itself.  Nothing _fundamental_ about
the hook system.  The only other objection has surfaced recently (from
Robert) and is about the size such system would add to kernel - which
mostly depends on the implementation of the hook system (single variable
vs. array vs. linked list, for example), not the interface.  
> 
> And you can say what you want, but if you are stubborn enough to
> ignore what I say in the reviews, you shouldn't be surprised if I
> didn't change my opinion in the next review and still ask you to
> change something.  In fact, it is rude just to send in a new patch
> while you did not take the review seriously.  There are a lot of
> projects that reject patches on beforehand if you didn't at least
> bother to get familiar with their coding styles.
(kneels on the floor japanese-teen-style) That is true and completely my
fault: I was stubbornly trying to convince you to change a big part of
the coding style without thinking that conventions are there for a
reason. I apologize for all the headaches I may have caused.
> 
> Furthermore, I reviewed your patch mainly because I care, not because
> I am the foremost expert in BIOS disks.  It's for the best if other
> people can look at specific parts of your patch, especially if it
> changes the kernel.
True, but the list is public and we can't be blamed if others are not
interested in parts of the patch. Besides, even though I've shown that
the "strong opinions" of Vesa and Robert did not affect any fundamental
part of the hook system, I _could_ split it from drivemap and put it up
for discussion on a separate thread.
> 
> Perhaps you do not notice how many time I spent on reviewing patches
> on this list lately.  Anyways, if it is not appreciated, I will leave
> the reviews to other people.  But I simply will reject all code I do
> not like without actually bothering to explain why.  I rather spend my
> time on coding instead of feeding trolls.
It _is_ appreciated: if it weren't for you, this patch would have been
lost in the blue long ago, just because no-one would bother to look at
it.  Besides, if you don't feed the trolls (me?), I'll have to go back
to Wikipedia, where my 42 sockpuppets have already been banned! ^^
Seriously, though, I don't mean to be trollish at all, nor to bludgeon
others into submission - that's just politics...

-Habbit


[0] http://lists.gnu.org/archive/html/grub-devel/2008-05/msg00153.html
[1] http://lists.gnu.org/archive/html/grub-devel/2008-06/msg00053.html
[2] http://lists.gnu.org/archive/html/grub-devel/2008-07/msg00095.html

> 
> --
> Marco
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel


[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2008-08-14 22:16 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-10 20:09 [PATCH] Drivemap module Javier Martín
2008-06-10 20:23 ` Vesa Jääskeläinen
2008-06-10 21:31   ` Javier Martín
2008-06-12 20:31     ` Pavel Roskin
2008-06-12 22:43       ` Javier Martín
2008-06-12 22:58         ` Colin D Bennett
2008-06-13  1:00           ` Pavel Roskin
2008-06-13  4:09             ` Colin D Bennett
2008-06-13  1:37         ` Pavel Roskin
2008-06-13  2:29           ` Javier Martín
2008-06-11 14:44 ` Marco Gerards
  -- strict thread matches above, loose matches on Subject: below --
2008-07-04  2:12 Javier Martín
2008-07-05 11:04 ` Marco Gerards
2008-07-16 15:39   ` Javier Martín
2008-07-20 19:40 ` Marco Gerards
2008-07-21  0:55   ` Javier Martín
2008-07-21 11:07     ` Javier Martín
2008-07-22 21:32     ` Robert Millan
2008-07-31 19:01     ` Marco Gerards
2008-08-03 23:29       ` Javier Martín
2008-08-04 20:51         ` Marco Gerards
2008-08-04 23:10           ` Javier Martín
2008-08-05  0:50             ` Isaac Dupree
2008-08-05  2:38               ` Javier Martín
2008-08-05 11:31                 ` Marco Gerards
2008-08-05 11:23             ` Marco Gerards
2008-08-05 17:18               ` Colin D Bennett
2008-08-05 11:28             ` Marco Gerards
2008-08-05 16:39               ` Javier Martín
2008-08-09 15:33                 ` Javier Martín
2008-08-13 10:13                   ` Marco Gerards
2008-08-13 12:16                     ` Javier Martín
2008-08-13 13:00                       ` Robert Millan
2008-08-13 14:28                         ` Javier Martín
2008-08-13 14:51                           ` Marco Gerards
2008-08-13 15:14                           ` Robert Millan
2008-08-13 15:57                             ` Marco Gerards
2008-08-13 22:38                               ` Javier Martín
2008-08-14 17:15                                 ` Marco Gerards
2008-08-14 22:17                                   ` Javier Martín
2008-08-13 13:01                       ` Robert Millan
2008-08-05 17:15             ` Colin D Bennett
2008-08-06 14:43 Viswesh S
2008-08-06 17:31 ` Javier Martín
2008-08-08 13:20   ` Felix Zielcke
2008-08-07 12:15 Viswesh S
2008-08-08 13:43 Viswesh S

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.