Hi again everybody. After some hacking with the GRUB internals, I've
completed a reimplementation of the legacy "map" command that allowed
the BIOS drives to have their numbers reassigned, in order to boot OSes
whose boot code requires their installation to be located in the first
HD (BIOS drive 0x80).

This implementation gets next to the status of the command in GRUB
Legacy, with the main changes being:

* Command: now called "drivemap", attending to suggestions in this list
* UI: the less complete part of the job. The syntax is now as follows:
        drivemap (hd1) 0x80 # Makes (hd1) appear as the first BIOS drive
      i.e. the second argument is now any BIOS drive number instead
      of another device. This change is in part due to my lack of time,
      but also because others here pointed that it would be useful to
      allow arbitrary mappings. If required, old syntax could be
      reimplemented in short time.
* Backend: the command is now a module, but there is also code in the
           several files like the machine-specific loader.S, the loader
           header file and the makefiles.
* Backend: a preboot hook system has been added to the loader backend,
           allowing modules to register functions to be run just before
           the grub_X_real_boot function is called. The drivemap module
           uses this new functionality to get its install_int13_handler
           function to be called before booting the target OS without
           creating chaos in GRUB itself.
* Functionality: the new module is independent of chainloader, so its
                 functions can be applied with any target loader. What
                 effects will this have, I don't know.

This patch is just a preliminary version that has been tested only in
QEMU and Bochs, not on real hardware because I'm cut off from x86
computers from the time being (I'm running on a PPC mac now). I will be
back to town (and to my olde amd64 box) this weekend, but I wanted to
submit this so others could scrutinize it for obvious mistakes, and
maybe test it.

I've tried to respect the style rules I've seen, but please note that
this is my first patch. Cheers!

Habbit
? commands/i386/pc/drivemap.c
Index: conf/i386-pc.mk
===================================================================
RCS file: /sources/grub/grub2/conf/i386-pc.mk,v
retrieving revision 1.132
diff -u -r1.132 i386-pc.mk
--- conf/i386-pc.mk	30 May 2008 04:20:47 -0000	1.132
+++ conf/i386-pc.mk	5 Jun 2008 20:08:20 -0000
@@ -842,7 +842,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
@@ -2970,4 +2970,60 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+drivemap_mod_SOURCES = commands/i386/pc/drivemap.c
+CLEANFILES += drivemap.mod mod-drivemap.o mod-drivemap.c pre-drivemap.o drivemap_mod-commands_i386_pc_drivemap.o und-drivemap.lst
+ifneq ($(drivemap_mod_EXPORTS),no)
+CLEANFILES += def-drivemap.lst
+DEFSYMFILES += def-drivemap.lst
+endif
+MOSTLYCLEANFILES += drivemap_mod-commands_i386_pc_drivemap.d
+UNDSYMFILES += und-drivemap.lst
+
+drivemap.mod: pre-drivemap.o mod-drivemap.o
+	-rm -f $@
+	$(TARGET_CC) $(drivemap_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ $^
+	$(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@
+
+pre-drivemap.o: $(drivemap_mod_DEPENDENCIES) drivemap_mod-commands_i386_pc_drivemap.o
+	-rm -f $@
+	$(TARGET_CC) $(drivemap_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ drivemap_mod-commands_i386_pc_drivemap.o
+
+mod-drivemap.o: mod-drivemap.c
+	$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(drivemap_mod_CFLAGS) -c -o $@ $<
+
+mod-drivemap.c: moddep.lst genmodsrc.sh
+	sh $(srcdir)/genmodsrc.sh 'drivemap' $< > $@ || (rm -f $@; exit 1)
+
+ifneq ($(drivemap_mod_EXPORTS),no)
+def-drivemap.lst: pre-drivemap.o
+	$(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 drivemap/' > $@
+endif
+
+und-drivemap.lst: pre-drivemap.o
+	echo 'drivemap' > $@
+	$(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+drivemap_mod-commands_i386_pc_drivemap.o: commands/i386/pc/drivemap.c $(commands/i386/pc/drivemap.c_DEPENDENCIES)
+	$(TARGET_CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(drivemap_mod_CFLAGS) -MD -c -o $@ $<
+-include drivemap_mod-commands_i386_pc_drivemap.d
+
+CLEANFILES += cmd-drivemap_mod-commands_i386_pc_drivemap.lst fs-drivemap_mod-commands_i386_pc_drivemap.lst partmap-drivemap_mod-commands_i386_pc_drivemap.lst
+COMMANDFILES += cmd-drivemap_mod-commands_i386_pc_drivemap.lst
+FSFILES += fs-drivemap_mod-commands_i386_pc_drivemap.lst
+PARTMAPFILES += partmap-drivemap_mod-commands_i386_pc_drivemap.lst
+
+cmd-drivemap_mod-commands_i386_pc_drivemap.lst: commands/i386/pc/drivemap.c $(commands/i386/pc/drivemap.c_DEPENDENCIES) gencmdlist.sh
+	set -e; 	  $(TARGET_CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(drivemap_mod_CFLAGS) -E $< 	  | sh $(srcdir)/gencmdlist.sh drivemap > $@ || (rm -f $@; exit 1)
+
+fs-drivemap_mod-commands_i386_pc_drivemap.lst: commands/i386/pc/drivemap.c $(commands/i386/pc/drivemap.c_DEPENDENCIES) genfslist.sh
+	set -e; 	  $(TARGET_CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(drivemap_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genfslist.sh drivemap > $@ || (rm -f $@; exit 1)
+
+partmap-drivemap_mod-commands_i386_pc_drivemap.lst: commands/i386/pc/drivemap.c $(commands/i386/pc/drivemap.c_DEPENDENCIES) genpartmaplist.sh
+	set -e; 	  $(TARGET_CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(drivemap_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genpartmaplist.sh drivemap > $@ || (rm -f $@; exit 1)
+
+
+drivemap_mod_CFLAGS = $(COMMON_CFLAGS)
+drivemap_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 include $(srcdir)/conf/common.mk
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	5 Jun 2008 20:08:21 -0000
@@ -319,4 +319,9 @@
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For drivemap.mod.
+drivemap_mod_SOURCES = commands/i386/pc/drivemap.c
+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	5 Jun 2008 20:08:21 -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: include/grub/i386/loader.h
===================================================================
RCS file: /sources/grub/grub2/include/grub/i386/loader.h,v
retrieving revision 1.4
diff -u -r1.4 loader.h
--- include/grub/i386/loader.h	30 Mar 2008 18:04:39 -0000	1.4
+++ include/grub/i386/loader.h	5 Jun 2008 20:08:21 -0000
@@ -31,6 +31,15 @@
 extern grub_addr_t EXPORT_VAR(grub_os_area_addr);
 extern grub_size_t EXPORT_VAR(grub_os_area_size);
 
+extern grub_uint32_t EXPORT_VAR(grub_drivemap_int13_oldhandler); /* realm far ptr = 2 * 16b */
+extern grub_uint16_t EXPORT_VAR(grub_drivemap_int13_size);  /* Size of the section to be copied */
+/* This is NOT a typo: we just want this "void" var in order to take its
+ * address with &var - used for relative addressing within the int13 handler */
+extern void EXPORT_VAR(grub_drivemap_int13_handler_base);
+extern void EXPORT_VAR(grub_drivemap_int13_mapstart);
+
+void EXPORT_FUNC(grub_drivemap_int13_handler)(void);
+
 grub_err_t EXPORT_FUNC(grub_linux_boot) (void);
 
 /* The asm part of the multiboot loader.  */
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	5 Jun 2008 20:08:21 -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 ();
Index: kern/i386/loader.S
===================================================================
RCS file: /sources/grub/grub2/kern/i386/loader.S,v
retrieving revision 1.3
diff -u -r1.3 loader.S
--- kern/i386/loader.S	19 Feb 2008 16:40:44 -0000	1.3
+++ kern/i386/loader.S	5 Jun 2008 20:08:22 -0000
@@ -59,6 +59,142 @@
 VARIABLE(grub_linux_is_bzimage)
 	.long	0
 
+
+#define GRUB_DRIVEMAP_INT13H_OFFSET(x) ((x) - grub_drivemap_int13_handler_base)
+VARIABLE(grub_drivemap_int13_handler_base)  /* When deployed, this tag must be segment-aligned */
+VARIABLE(grub_drivemap_int13_oldhandler)
+  .word 0xdead, 0xbeef
+/* Drivemap module - INT 13h handler - BIOS HD map */
+/* Right now this function is just a passthrough to the old handler
+ * 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 */
+
+  /* DEBUG: print requested drive (DL) */
+  /*push %es
+  pushal
+  mov $4, %di
+  mov $0xb800, %bx
+  mov %bx, %es
+  
+  movb %dl, %bl
+  andb $0x0f, %bl
+  addb $0x30, %bl
+  shll $16, %ebx
+  movb %dl, %bl
+  shrb $4, %bl
+  addb $0x30, %bl
+  andl $0x00ff00ff, %ebx
+  orl $0x0f000f00, %ebx
+  movl %ebx, %es:(%di)
+  add $4, %di
+  popal
+  pop %es*/
+
+  /* Map the drive number (always in DL?) */
+  push %ax
+  push %bx
+  push %si
+  mov $GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_size), %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 */
+  
+  /* DEBUG: print map entry DRV (AL) and DST (AH) */  
+  /*push %es
+  pushal
+  mov $8, %di
+  mov $0xb800, %bx
+  mov %bx, %es
+  
+  movb -4(%bp), %bl
+  andb $0x0f, %bl
+  addb $0x30, %bl
+  shll $16, %ebx
+  movb -4(%bp), %bl
+  shrb $4, %bl
+  addb $0x30, %bl
+  andl $0x00ff00ff, %ebx
+  orl $0x0f000f00, %ebx
+  movl %ebx, %es:(%di)
+  add $4, %di
+   
+  movb -3(%bp), %bl
+  andb $0x0f, %bl
+  addb $0x30, %bl
+  shll $16, %ebx
+  movb -3(%bp), %bl
+  shrb $4, %bl
+  addb $0x30, %bl
+  andl $0x00ff00ff, %ebx
+  orl $0x0f000f00, %ebx
+  movl %ebx, %es:(%di)
+  add $4, %di
+
+  popal
+  pop %es*/
+
+  
+  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
+
+
 FUNCTION(grub_linux_boot)
 	/* Must be done before zImage copy.  */
 	call	EXT_C(grub_dl_unload_all)

Attachment: signature.asc
Description: Esta parte del mensaje está firmada digitalmente

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

Reply via email to