Drivemap module
PATCH 1/1 Drivemap module
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.
Index: commands/i386/pc/drivemap.c
===================================================================
RCS file: commands/i386/pc/drivemap.c
i386/pc/drivemap.c new file +391 -0
@@ -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 ; + autoint 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
i386/pc/drivemap_int13h.S new file +122 -0
122 lines not on this page · raw message
Index: conf/i386-pc.rmk
===================================================================
RCS file: /sources/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.120
i386-pc.rmk +8 -1
@@ -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
grub/loader.h +13 -0
@@ -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
loader.c +67 -0
78 lines not on this page · raw message
Quoted 5 lines · Javier Martín
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
Quoted 7 lines
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
Quoted 15 lines · Marco Gerards
Quoted 5 lines · Javier Martín
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) 0x81Quoted 7 lines
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.
Quoted 4 lines
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.
Quoted 5 lines · Javier Martín
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
Quoted 7 lines
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?
Quoted 3 lines
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 :-)
Quoted 33 lines
Index:
commands/i386/pc/drivemap.c
===================================================================
RCS file:commands/i386/pc/drivemap.c
diff -Ncommands/i386/pc/drivemap.ci386/pc/drivemap.c new file +26
-0@@ -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>
i386/pc/drivemap.c line 36
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+#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.
i386/pc/drivemap.c line 44
Quoted 7 lines
i386/pc/drivemap.c new file +7
-0+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
i386/pc/drivemap.c line 54
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+/* 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?
i386/pc/drivemap.c line 58
Quoted 3 lines
i386/pc/drivemap.c new file +3
-0+void EXPORT_FUNC (grub_drivemap_int13_handler ) (void ); + +/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */
Like before.
i386/pc/drivemap.c line 60
Quoted 1 line
i386/pc/drivemap.c new file +1
-0+static grub_preboot_hookid insthandler_hook =0 ;
You can leave the "= 0" away.
i386/pc/drivemap.c line 69
Quoted 8 lines
i386/pc/drivemap.c new file +8
-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 ;
Same here :-)
i386/pc/drivemap.c line 75
Quoted 6 lines
i386/pc/drivemap.c new file +6
-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. */
Please place the comments before the function.
i386/pc/drivemap.c line 89
Quoted 13 lines
i386/pc/drivemap.c new file +13
-0+{ +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?
i386/pc/drivemap.c line 85
Quoted 2 lines
i386/pc/drivemap.c new file +2
-0+else /* Create a new mapping and add it to the head of the list */ + {
I would move the comment inside the braces.
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+ 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;
i386/pc/drivemap.c line 107
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+ 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.
i386/pc/drivemap.c line 103
Quoted 2 lines
i386/pc/drivemap.c new file +2
-0+{ +drivemap_node_t *mapping =0 , *search = drivemap, *previous =0 ;
Please define one variable per line, can you break this down to 3 lines?
Quoted 5 lines
i386/pc/drivemap.c new file +5
-0+while (search) + { +if (search->newdrive == newdrive) + { + mapping = search;
i386/pc/drivemap.c line 124
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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.
i386/pc/drivemap.c line 132
Quoted 8 lines
i386/pc/drivemap.c new file +8
-0+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.
i386/pc/drivemap.c line 135
Quoted 3 lines
i386/pc/drivemap.c new file +3
-0+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.
i386/pc/drivemap.c line 137
Quoted 2 lines
i386/pc/drivemap.c new file +2
-0+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.
i386/pc/drivemap.c line 142
Quoted 5 lines
i386/pc/drivemap.c new file +5
-0+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.
i386/pc/drivemap.c line 145
Quoted 3 lines
i386/pc/drivemap.c new file +3
-0+grub_disk_close (disk); +return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ? +GRUB_ERR_BAD_DEVICE :GRUB_ERR_NONE ;
Please use grub_error.
Quoted 7 lines
i386/pc/drivemap.c new file +7
-0+ } +} + +static grub_err_t +revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +grub_err_t ret =GRUB_ERR_UNKNOWN_DEVICE ;
i386/pc/drivemap.c line 162
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+ autoint 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
i386/pc/drivemap.c line 174
Quoted 12 lines
i386/pc/drivemap.c new file +12
-0+ { + 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...
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+return ret; +} + +static grub_err_t +tryparse_diskstring (const char *str,grub_uint8_t *output) +{
i386/pc/drivemap.c line 190
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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
Quoted 5 lines
i386/pc/drivemap.c new file +5
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num;
i386/pc/drivemap.c line 205
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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
i386/pc/drivemap.c line 214
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+ { +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? :-)
Quoted 23 lines
i386/pc/drivemap.c new file +23
-0+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;
i386/pc/drivemap.c line 247
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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.
Quoted 7 lines
i386/pc/drivemap.c new file +7
-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? */
i386/pc/drivemap.c line 264
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+ { + 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.
i386/pc/drivemap.c line 275
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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
Quoted 12 lines
i386/pc/drivemap.c new file +12
-0+ } + +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.
i386/pc/drivemap.c line 297
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+* 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.
i386/pc/drivemap.c line 303
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+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?
i386/pc/drivemap.c line 308
Quoted 5 lines
i386/pc/drivemap.c new file +5
-0+ { + entries++; + curentry = curentry->next; + } +if (0 == entries)
if (! entries)
Quoted 73 lines
i386/pc/drivemap.c new file +73
-0+ { +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",
i386/pc/drivemap.c line 391
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+ "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 :-)
Quoted 40 lines
Index:
commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file:commands/i386/pc/drivemap_int13h.S
diff -Ncommands/i386/pc/drivemap_int13h.Si386/pc/drivemap_int13h.S new file +32
-0@@ -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
i386/pc/drivemap_int13h.S line 42
Quoted 10 lines
i386/pc/drivemap_int13h.S new file +10
-0+{ 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.
i386/pc/drivemap_int13h.S line 46
Quoted 3 lines
i386/pc/drivemap_int13h.S new file +3
-0+/* + * This is the area for all of the special variables. + */
I don't think this comment is useful.
Quoted 138 lines
i386/pc/drivemap_int13h.S new file +75
-0+#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.rmki386-pc.rmk +8
-1@@ -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.mkIndex:
include/grub/loader.h
===================================================================
RCS file: /sources/grub/grub2/include/grub/loader.h,v retrieving revision 1.9
diff -u -r1.9loader.hgrub/loader.h +13
-0@@ -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.9loader.c
loader.c line 69
Quoted 9 lines
loader.c +6
-0@@ -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
loader.c line 76
Quoted 6 lines
loader.c +6
-0+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)
loader.c line 78
Quoted 2 lines
loader.c +2
-0+ { +grub_error (GRUB_ERR_BAD_ARGUMENT ,"preboot hoook must not be null" );
hoook
null -> NULL
Quoted 15 lines
loader.c +15
-0+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;
loader.c line 136
Quoted 10 lines
loader.c +9
-0+ } +} + +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)
Quoted 18 lines
loader.c +18
-0+ { +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); + } +} +
loader.c line 131
Quoted 10 lines
loader.c +5
-0grub_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"
Quoted 11 lines
loader.c +4
-0+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
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Quoted 1 line · Marco Gerards
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.
Quoted 15 lines
Quoted 4 lines
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 be0x80, like Windows XP, with non-standard boot configurations.
Quoted 7 lines
"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 bydrivemap.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
Quoted 3 lines
* 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.
Quoted 8 lines
*
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"
Quoted 3 lines
*
kern/loader.c: Implement the preboot-hook describedWhich 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
Quoted 3 lines
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.
Quoted 7 lines
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 ^^
Quoted 12 lines
(...)
i386/pc/drivemap.c line 36
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+ +/* 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_dprintfinfrastructure.
Done. I didn't even know it existed :S
Quoted 13 lines
i386/pc/drivemap.c line 54
Quoted 10 lines
i386/pc/drivemap.c new file +9
-0+/* 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.
Quoted 8 lines
Quoted 2 lines
+static
grub_preboot_hookidinsthandler_hook= 0;You can leave the "= 0" away.
(...)Quoted 1 line
+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.
Quoted 8 lines
(...)
i386/pc/drivemap.c line 75
Quoted 4 lines
i386/pc/drivemap.c new file +4
-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.
(...)
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ 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)
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ 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
Quoted 9 lines
(...)
i386/pc/drivemap.c line 107
Quoted 4 lines
i386/pc/drivemap.c new file +4
-0+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.
Quoted 5 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+{ + 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.
Quoted 4 lines
(...)
Quoted 1 line
+ else drivemap =
mapping->next; /* Entry was head of list */Please place the stuff after "else" on a new line.
Done, I think.
Quoted 12 lines
i386/pc/drivemap.c line 132
Quoted 9 lines
i386/pc/drivemap.c new file +8
-0+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, usegrub_errorso 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).
Quoted 6 lines
i386/pc/drivemap.c line 135
Quoted 3 lines
i386/pc/drivemap.c new file +3
-0+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.
Quoted 8 lines
i386/pc/drivemap.c line 142
Quoted 6 lines
i386/pc/drivemap.c new file +5
-0+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.
Quoted 7 lines
(...)
Quoted 1 line
+ 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.
Quoted 2 lines
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).
Quoted 4 lines
(...)
Quoted 1 line
+
grub_disk_dev_iterate(&find);No need for the &. I even wonder if it would work this way...
Corrected.
Quoted 10 lines
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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' )
Quoted 8 lines
i386/pc/drivemap.c line 190
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+ { +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?).
Quoted 9 lines
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num; +return GRUB_ERR_NONE ; + } + } +else return GRUB_ERR_BAD_ARGUMENT ;
Quoted 8 lines
i386/pc/drivemap.c line 205
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+} + +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.
Quoted 11 lines
i386/pc/drivemap.c line 214
Quoted 10 lines
i386/pc/drivemap.c new file +9
-0+ { +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" );
Quoted 3 lines
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?
Quoted 34 lines
Quoted 23 lines
i386/pc/drivemap.c new file +23
-0+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;i386/pc/drivemap.c line 247
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+grub_free (prevnode); + } + drivemap =0 ; + } + } +else + { +if (argc !=2 ) +return grub_error (GRUB_ERR_BAD_ARGUMENT ,"two arguments required" ); +grub_uint8_t mapfrom =0 ;
Quoted 1 line
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.
Quoted 18 lines
Quoted 7 lines
i386/pc/drivemap.c new file +7
-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? */ i386/pc/drivemap.c line 264
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+ { + 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; + }
Quoted 2 lines
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.
Quoted 2 lines
Can you add newlines between the #defines and the comments, this will get quite messy like this.
Done.
Quoted 8 lines
i386/pc/drivemap.c line 308
Quoted 6 lines
i386/pc/drivemap.c new file +5
-0+ { + 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.
Quoted 10 lines
(...)
i386/pc/drivemap.c line 391
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+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.
Quoted 6 lines
i386/pc/drivemap_int13h.S
Quoted 6 lines
Index:
commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file:commands/i386/pc/drivemap_int13h.S
diff -Ncommands/i386/pc/drivemap_int13h.Si386/pc/drivemap_int13h.S new file +0
-0
And here we go for the second round xD
Quoted 22 lines
(...)
Quoted 21 lines
i386/pc/drivemap_int13h.S new file +21
-0+ + +/* + * 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,
Quoted 8 lines
Quoted 5 lines
+ * 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.
Quoted 5 lines
Quoted 3 lines
+/*
+ * 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.
Quoted 67 lines
Quoted 67 lines
i386/pc/drivemap_int13h.S new file +67
-0+#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
Quoted 8 lines
i386/pc/drivemap_int13h.S line 122
Quoted 8 lines
i386/pc/drivemap_int13h.S new file +8
-0+/* 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?? ;)
Quoted 12 lines
(...)
Quoted 11 lines
Index:
kern/loader.c
===================================================================
RCS file: /sources/grub/grub2/kern/loader.c,v
retrieving revision 1.9
diff -u -r1.9loader.cloader.c +0
-0@@ -61,11 +61,78 @@ grub_loader_loaded =0 ; }
Quoted 8 lines
loader.c line 69
Quoted 6 lines
loader.c +6
-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
Quoted 8 lines
loader.c line 76
Quoted 6 lines
loader.c +6
-0+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.
Quoted 6 lines
loader.c line 78
Quoted 2 lines
loader.c +2
-0+ { +grub_error (GRUB_ERR_BAD_ARGUMENT ,"preboot hoook must not be null" );hoook
null -> NULL
Changed too.
Quoted 10 lines
(...)
loader.c line 102
Quoted 8 lines
loader.c +7
-0+ +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?
Quoted 4 lines
(...)
Quoted 1 line
+
grub_err_tpossible_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.
Quoted 3 lines · Javier Martín
(...)
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
i386/pc/drivemap.c +393 -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 ; + autoint 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 ); +}
i386/pc/drivemap_int13h.S +118 -0
118 lines not on this page · raw message
i386-pc.rmk +8 -1
@@ -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
grub/loader.h +13 -0
@@ -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 );
loader.c +69 -0
80 lines not on this page · raw message
Quoted 1 line · Javier Martín
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.
Signature · 5 lines
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 /.)
Quoted 6 lines · Javier Martín
Quoted 1 line · Marco Gerards
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...
Quoted 20 lines
Quoted 20 lines
Quoted 3 lines
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 be0x80, 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 bydrivemap.c, performs the actual redirection of BIOS drives.
Quoted 7 lines
Quoted 1 line
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.
Quoted 11 lines
Quoted 3 lines
* 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 ;)
Quoted 20 lines
Quoted 7 lines
*
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"Quoted 3 lines
*
kern/loader.c: Implement the preboot-hook describedWhich 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 loaderQuoted 1 line
Quoted 4 lines
Quoted 2 lines
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.
Quoted 7 lines
Quoted 5 lines
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 ^^
:-)
Quoted 12 lines
Quoted 12 lines
(...)
i386/pc/drivemap.c line 36
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+ +/* 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_dprintfinfrastructure.
Quoted 1 line
Done. I didn't even know it existed :S
:-)
Quoted 15 lines
Quoted 4 lines
Quoted 4 lines
i386/pc/drivemap.c new file +4
-0+/* 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 );Quoted 8 lines
i386/pc/drivemap.c line 54
Quoted 5 lines
i386/pc/drivemap.c new file +5
-0+ +/* 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).
Quoted 6 lines
What's not a typo is the definition of
grub_symbol_tas "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.
Quoted 11 lines
Quoted 7 lines
Quoted 1 line
+static
grub_preboot_hookidinsthandler_hook= 0;You can leave the "= 0" away.
(...)Quoted 1 line
+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.
Quoted 8 lines
Quoted 8 lines
(...)
i386/pc/drivemap.c line 75
Quoted 4 lines
i386/pc/drivemap.c new file +4
-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.
(...)
Quoted 8 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ 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.
Quoted 3 lines
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.
Quoted 6 lines
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ 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.
Quoted 11 lines
Quoted 9 lines
(...)
i386/pc/drivemap.c line 107
Quoted 4 lines
i386/pc/drivemap.c new file +4
-0+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.
Quoted 25 lines
Quoted 5 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+{ + 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.
Quoted 4 lines
(...)
Quoted 1 line
+ else drivemap =
mapping->next; /* Entry was head of list */Please place the stuff after "else" on a new line.
Done, I think.
Quoted 12 lines
i386/pc/drivemap.c line 132
Quoted 9 lines
i386/pc/drivemap.c new file +8
-0+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, usegrub_errorso you can also define a string for the error.
Quoted 4 lines
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.
Quoted 10 lines
Quoted 6 lines
i386/pc/drivemap.c line 135
Quoted 3 lines
i386/pc/drivemap.c new file +3
-0+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.
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
i386/pc/drivemap.c new file +5
-0+else + { +enum grub_disk_dev_id id = disk->dev->id; +if (disknum) + *disknum = disk->id;/* Only valid, of course if it's a biosdisk */
Quoted 6 lines
Quoted 1 line
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?
Quoted 4 lines
Quoted 4 lines
(...)
Quoted 1 line
+ if (dnum ==
disk->id&&GRUB_DISK_DEVICE_BIOSDISK_ID==disk->dev->id)
Quoted 8 lines
Quoted 3 lines
This is not rally wrong, but doesn't feel right. I would use:
disk->dev->id==GRUB_DISK_DEVICE_BIOSDISK_IDI 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?
Quoted 3 lines
Quoted 1 line
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 :-)
Quoted 24 lines
Quoted 4 lines
(...)
Quoted 1 line
+
grub_disk_dev_iterate(&find);No need for the &. I even wonder if it would work this way...
Corrected.
Quoted 10 lines
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+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' )Quoted 8 lines
i386/pc/drivemap.c line 190
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+ { +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
Quoted 5 lines
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_errorprinting 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?
Quoted 29 lines
Quoted 9 lines
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num; +return GRUB_ERR_NONE ; + } + } +else return GRUB_ERR_BAD_ARGUMENT ;Quoted 8 lines
i386/pc/drivemap.c line 205
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+} + +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 */ (...) }
Quoted 2 lines
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.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
i386/pc/drivemap.c new file +9
-0+ { +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" );
Quoted 6 lines
Quoted 3 lines
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.
Quoted 40 lines
Quoted 34 lines
Quoted 23 lines
i386/pc/drivemap.c new file +23
-0+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;i386/pc/drivemap.c line 247
Quoted 10 lines
i386/pc/drivemap.c new file +10
-0+grub_free (prevnode); + } + drivemap =0 ; + } + } +else + { +if (argc !=2 ) +return grub_error (GRUB_ERR_BAD_ARGUMENT ,"two arguments required" ); +grub_uint8_t mapfrom =0 ;Quoted 1 line
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.
Quoted 5 lines
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.
Quoted 18 lines
Quoted 18 lines
Quoted 17 lines
i386/pc/drivemap.c new file +17
-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; + }
Quoted 4 lines
Quoted 2 lines
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.
Quoted 18 lines
Quoted 2 lines
Can you add newlines between the #defines and the comments, this will get quite messy like this.
Done.
Quoted 8 lines
i386/pc/drivemap.c line 308
Quoted 6 lines
i386/pc/drivemap.c new file +5
-0+ { + 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.
Quoted 8 lines
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? :)
Quoted 10 lines
Quoted 10 lines
(...)
i386/pc/drivemap.c line 391
Quoted 6 lines
i386/pc/drivemap.c new file +6
-0+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 :-)
Quoted 2 lines
AFAIK the "fini" functions are called by
grub_machine_fini, which is called _after_ the preboot hook.
ok
Quoted 38 lines
Quoted 6 lines
i386/pc/drivemap_int13h.S
Quoted 6 lines
Index:
commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file:commands/i386/pc/drivemap_int13h.S
diff -Ncommands/i386/pc/drivemap_int13h.Si386/pc/drivemap_int13h.S new file +0
-0And here we go for the second round xD
Quoted 22 lines
(...)
Quoted 21 lines
i386/pc/drivemap_int13h.S new file +21
-0+ + +/* + * 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,Quoted 8 lines
Quoted 5 lines
+ * 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.
Quoted 3 lines
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
Quoted 76 lines
Quoted 5 lines
Quoted 3 lines
+/*
+ * 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.
Quoted 68 lines
Quoted 68 lines
i386/pc/drivemap_int13h.S new file +68
-0+#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
Quoted 8 lines
Quoted 7 lines
i386/pc/drivemap_int13h.S line 122
Quoted 7 lines
+ * reserves additional space for mappings at runtime and copies them over it */ +.align 2
i386/pc/drivemap_int13h.S new file +5
-0+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 ;-)
Quoted 20 lines
Quoted 12 lines
(...)
Quoted 11 lines
Index:
kern/loader.c
===================================================================
RCS file: /sources/grub/grub2/kern/loader.c,v
retrieving revision 1.9
diff -u -r1.9loader.cloader.c +0
-0@@ -61,11 +61,78 @@ grub_loader_loaded =0 ; }Quoted 8 lines
loader.c line 69
Quoted 6 lines
loader.c +6
-0+struct hooklist_node +{ +grub_err_t (*hook) (void ); +int abort_on_error; +struct hooklist_node *next; +};Isn't there something
Quoted 1 line
Woot? What do you mean with that?
o_O
I think I was distracted when I typed this, so I did not finish the
Quoted 29 lines
Quoted 8 lines
loader.c line 76
Quoted 6 lines
loader.c +6
-0+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.
Quoted 6 lines
loader.c line 78
Quoted 2 lines
loader.c +2
-0+ { +grub_error (GRUB_ERR_BAD_ARGUMENT ,"preboot hoook must not be null" );hoook
null -> NULL
Changed too.
Quoted 10 lines
(...)
loader.c line 102
Quoted 8 lines
loader.c +7
-0+ +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?
Quoted 6 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.
Quoted 9 lines
Quoted 4 lines
(...)
Quoted 1 line
+
grub_err_tpossible_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...
Quoted 2 lines
PS: I already signed the copyright assignment papers and sent them to the FSF. They should arrive within this week.
Cool :-)
--
Marco
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 <[email protected]>
* 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
i386/pc/drivemap.c +417 -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 ; + autoint 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 ); +} +
i386/pc/drivemap_int13h.S +118 -0
118 lines not on this page · raw message
i386-pc.rmk +8 -1
@@ -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
grub/loader.h +16 -0
@@ -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 );
loader.c +71 -0
82 lines not on this page · raw message
Quoted 8 lines · Javier Martín
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_handlerfunction 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:
Quoted 7 lines
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.
Quoted 11 lines
- 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. returngrub_error(err, fmt, ...)
- Comment about the strange "void" type instead of "void*" rephrased to be clearer
Thanks a lot!
Quoted 7 lines
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?
Quoted 5 lines
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 :-)
Quoted 3 lines
The new version of the patch is attached, and here is my suggested CLog:
2008-08-XX Javier Martin <[email protected]>
(newline)
Quoted 9 lines
*
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.
Quoted 1 line
(
grub_loader_unregister_preboot) : Likewise. Unregister handler
Same here.
Quoted 1 line
(
grub_preboot_hookid) : New typedef. Registered hook "handle"
Same here.
Quoted 3 lines
*
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.
Quoted 2 lines
(
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.
Quoted 45 lines
Index:
commands/i386/pc/drivemap.c
===================================================================i386/pc/drivemap.c +40
-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;
i386/pc/drivemap.c line 50
Quoted 10 lines
i386/pc/drivemap.c +10
-0+/* 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.
i386/pc/drivemap.c line 64
Quoted 13 lines
i386/pc/drivemap.c +13
-0+/* 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.
i386/pc/drivemap.c line 71
Quoted 7 lines
i386/pc/drivemap.c +7
-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) +
Please do not add a newline here.
Quoted 40 lines
i386/pc/drivemap.c +40
-0+{ +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)
i386/pc/drivemap.c line 121
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ { +if (search->newdrive == newdrive) + { + mapping = search; +break ; + } + previous = search; + search = search->next; + } +if (mapping)/* Found. */
You forgot one.
Quoted 30 lines
i386/pc/drivemap.c +30
-0+ { +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)
i386/pc/drivemap.c line 161
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ *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
Quoted 17 lines
i386/pc/drivemap.c +17
-0+static grub_err_t +revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +int found =0 ; + autoint 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;
i386/pc/drivemap.c line 188
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ } +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.
Quoted 10 lines
i386/pc/drivemap.c +10
-0+} + +/* 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. */
i386/pc/drivemap.c line 208
Quoted 10 lines
i386/pc/drivemap.c +10
-0+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.
Quoted 9 lines
i386/pc/drivemap.c +9
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num; +return GRUB_ERR_NONE ; + } + } +else goto fail;
...
Quoted 36 lines
i386/pc/drivemap.c +36
-0+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)
i386/pc/drivemap.c line 264
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ { + prevnode = curnode; + curnode = curnode->next; +grub_free (prevnode); + } + drivemap =0 ; + } +else + { +/* Neither flag: put mapping */
". */
i386/pc/drivemap.c line 277
Quoted 13 lines
i386/pc/drivemap.c +13
-0+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.
i386/pc/drivemap.c line 289
Quoted 12 lines
i386/pc/drivemap.c +12
-0+ { + 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.
i386/pc/drivemap.c line 294
Quoted 5 lines
i386/pc/drivemap.c +5
-0+ { +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.
Quoted 34 lines
i386/pc/drivemap.c +34
-0+ { +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. */
...
i386/pc/drivemap.c line 333
Quoted 5 lines
i386/pc/drivemap.c +5
-0+ { + entries++; + curentry = curentry->next; + } +if (0 == entries)
I know this is what you prefer, but can you change this nevertheless?
Quoted 239 lines
i386/pc/drivemap.c +84
-0+ { +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
===================================================================i386/pc/drivemap_int13h.S +118
-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
===================================================================i386-pc.rmk +8
-1@@ -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.mkIndex:
include/grub/loader.h
===================================================================grub/loader.h +0
-0@@ -37,6 +37,22 @@/* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset ) (void );
grub/loader.h line 48
Quoted 10 lines
grub/loader.h +9
-0+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/loader.h line 57
Quoted 9 lines
grub/loader.h +7
-0+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..."
Quoted 17 lines
grub/loader.h +0
-0grub_err_t EXPORT_FUNC (grub_loader_boot ) (void );Index:
kern/loader.c
===================================================================loader.c +8
-0@@ -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 ;
loader.c line 81
Quoted 10 lines
loader.c +10
-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.
Quoted 7 lines
loader.c +7
-0+if (!newentry) + { +grub_error (GRUB_ERR_OUT_OF_MEMORY ,"cannot alloc a hookinfo structure" ); +return 0 ; + } +else + {
loader.c line 98
Quoted 10 lines
loader.c +10
-0+ 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"
Quoted 7 lines
loader.c +7
-0+{ +grub_preboot_hookid entry =0 ; +grub_preboot_hookid search = preboot_hooks; +grub_preboot_hookid previous =0 ; + +if (0 == id) +return ;
...
Quoted 11 lines
loader.c +11
-0+while (search) + { +if (search == id) + { + entry = search; +break ; + } + previous = search; + search = search->next; + } +if (entry)/* Found */
...
Quoted 5 lines
loader.c +5
-0+ { +if (previous) + previous->next = entry->next; +else preboot_hooks = entry->next;/* Entry was head of list */ +grub_free (entry);
loader.c line 132
Quoted 10 lines
loader.c +5
-0+ } +} +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.
Quoted 14 lines
loader.c +7
-0+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
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Hi there,
Quoted 18 lines · Marco Gerards
Quoted 8 lines · Javier Martín
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_handlerfunction 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:Quoted 7 lines
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;
Quoted 7 lines
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.
Quoted 23 lines
Quoted 12 lines
- 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. returngrub_error(err, fmt, ...)
- Comment about the strange "void" type instead of "void*" rephrased to be clearerThanks a lot!
Quoted 7 lines
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.
Quoted 1 line
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.
Quoted 7 lines
Quoted 6 lines
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.
Quoted 6 lines
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.
Quoted 33 lines
Or so, perhaps the long argument names can be chosen in a more clever way :-)
Quoted 3 lines
The new version of the patch is attached, and here is my suggested CLog:
2008-08-XX Javier Martin <[email protected]>
(newline)
Quoted 9 lines
*
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 handlerNo need how the change is used or why it was added.
Quoted 1 line
(
grub_loader_unregister_preboot) : Likewise. Unregister handlerSame here.
Quoted 1 line
(
grub_preboot_hookid) : New typedef. Registered hook "handle"Same here.
Quoted 3 lines
*
kern/loader.c(grub_loader_register_preboot) : New function. (grub_loader_unregister_preboot) : Likewise. (preboot_hooks) : New variable. Linked list of preboot hooks
Quoted 6 lines
Same here.
Quoted 2 lines
(
grub_loader_boot) : Call the list of preboot-hooks before the actual loaderWhat's the `'?
The what? o_O
Quoted 2 lines
Please do not add a space before the ":"
Ok, everything corrected. New CL entry:
2008-08-XX Javier Martin <[email protected]>
* 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
Quoted 57 lines
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.
Quoted 49 lines
Index:
commands/i386/pc/drivemap.c
===================================================================i386/pc/drivemap.c +44
-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
Quoted 8 lines
i386/pc/drivemap.c line 50
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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).
Quoted 16 lines
Quoted 4 lines
i386/pc/drivemap.c +3
-0+/* Syms/vars/funcs exported from drivemap_int13h.S - end. */ + +i386/pc/drivemap.c line 63
Quoted 10 lines
i386/pc/drivemap.c +10
-0+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.
Quoted 10 lines
i386/pc/drivemap.c line 70
Quoted 8 lines
i386/pc/drivemap.c +7
-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) +Please do not add a newline here.
Oops, sorry. I forgot to remove it when moving the comment
Quoted 45 lines
Quoted 45 lines
i386/pc/drivemap.c +44
-0+{ +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;
Quoted 8 lines
i386/pc/drivemap.c line 120
Quoted 6 lines
i386/pc/drivemap.c +6
-0+break ; + } + previous = search; + search = search->next; + } +if (mapping)/* Found. */ You forgot one.
Corrected. Sorry.
Quoted 35 lines
Quoted 35 lines
i386/pc/drivemap.c +34
-0+ { +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 ;
Quoted 8 lines
i386/pc/drivemap.c line 160
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 22 lines
Quoted 22 lines
i386/pc/drivemap.c +21
-0+static grub_err_t +revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +int found =0 ; + autoint 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; + }
Quoted 8 lines
i386/pc/drivemap.c line 187
Quoted 6 lines
i386/pc/drivemap.c +6
-0+ } + +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; }
Quoted 15 lines
Quoted 15 lines
i386/pc/drivemap.c +14
-0+} + +/* 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' ) + {
Quoted 8 lines
i386/pc/drivemap.c line 207
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 11 lines
i386/pc/drivemap.c line 216
Quoted 10 lines
i386/pc/drivemap.c +9
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num; +return GRUB_ERR_NONE ; + } + } +else goto fail;
Quoted 1 line
...
What's the problem here? The lack of braces? The goto (as used in the ext2 code)?
Quoted 41 lines
Quoted 41 lines
i386/pc/drivemap.c +40
-0+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);
Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +6
-0+ } + drivemap =0 ; + } +else + { +/* Neither flag: put mapping */ ". */
Done
Quoted 16 lines
Quoted 4 lines
i386/pc/drivemap.c +3
-0+grub_uint8_t mapfrom =0 ; +grub_uint8_t mapto =0xFF ; +grub_err_t err;i386/pc/drivemap.c
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ +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.
Quoted 14 lines
i386/pc/drivemap.c
Quoted 13 lines
i386/pc/drivemap.c +12
-0+ { + 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. */
Quoted 1 line
Same here.
Done.
Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +5
-0+ { +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.
Quoted 29 lines
Quoted 29 lines
i386/pc/drivemap.c +28
-0+ { +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. */
Quoted 8 lines
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +5
-0+ { + 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.
Quoted 241 lines
Quoted 241 lines
i386/pc/drivemap.c +84
-0+ { +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
===================================================================i386/pc/drivemap_int13h.S +118
-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
===================================================================i386-pc.rmk +8
-1@@ -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.mkIndex:
include/grub/loader.h
===================================================================grub/loader.h +0
-0@@ -37,6 +37,22 @@/* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset ) (void );
Quoted 14 lines
grub/loader.h line 48
Quoted 9 lines
grub/loader.h +9
-0+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 andABORT_ON_ERRORin the comments above.
Done. "hook" removed because it referred to the loader module boot function.
Quoted 12 lines
grub/loader.h line 57
Quoted 10 lines
grub/loader.h +7
-0+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
Quoted 22 lines
Quoted 22 lines
grub/loader.h +0
-0grub_err_t EXPORT_FUNC (grub_loader_boot ) (void );Index:
kern/loader.c
===================================================================loader.c +12
-0@@ -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) +{
Quoted 8 lines
loader.c line 81
Quoted 6 lines
loader.c +6
-0+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.
Quoted 12 lines
Quoted 12 lines
loader.c +11
-0+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;
Quoted 8 lines
loader.c line 98
Quoted 6 lines
loader.c +6
-0+return newentry; + } +} + +void +grub_loader_unregister_preboot (grub_preboot_hookid id)"preboot (grub"
Corrected on both functions ;)
Quoted 10 lines
Quoted 8 lines
loader.c +7
-0+{ +grub_preboot_hookid entry =0 ; +grub_preboot_hookid search = preboot_hooks; +grub_preboot_hookid previous =0 ; + +if (0 == id) +return ;...
... xD
Quoted 13 lines
loader.c
Quoted 12 lines
loader.c +11
-0+while (search) + { +if (search == id) + { + entry = search; +break ; + } + previous = search; + search = search->next; + } +if (entry)/* Found */
Quoted 1 line
...
Comment removed, was unnecessary.
Quoted 10 lines
Quoted 10 lines
loader.c +8
-0+ { +if (previous) + previous->next = entry->next; +else preboot_hooks = entry->next;/* Entry was head of list */ +grub_free (entry); + } +} + grub_err_t
Quoted 8 lines
loader.c
Quoted 6 lines
grub_loader_boot(void)
{
if (!grub_loader_loaded)return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");loader.c +2
-0+ +grub_preboot_hookid entry = preboot_hooks;Mixed declarations/code.
Moved the whole line up.
Quoted 18 lines
Quoted 11 lines
loader.c +7
-0+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 ();Quoted 4 lines
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Quoted 4 lines
_______________________________________________
Grub-devel mailing list
[email protected]
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? ^^
i386/pc/drivemap.c +439 -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 ; + autoint 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 ); +} +
i386/pc/drivemap_int13h.S +118 -0
118 lines not on this page · raw message
i386-pc.rmk +8 -1
@@ -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
grub/loader.h +17 -1
@@ -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 );
loader.c +74 -0
85 lines not on this page · raw message
Quoted 5 lines · Javier Martín
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
Hi there and thanks for playing The Game!
Quoted 7 lines · Isaac Dupree
Quoted 5 lines · Javier Martín
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).
Quoted 13 lines
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 becomeint32_tand 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.
Quoted 5 lines
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...
Quoted 11 lines
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
Hi,
Quoted 21 lines · Javier Martín
Quoted 10 lines
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; }
Quoted 5 lines
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
Hi Javier,
Quoted 26 lines · Javier Martín
Quoted 18 lines · Marco Gerards
Quoted 8 lines · Javier Martín
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_handlerfunction 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:Quoted 7 lines
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;Quoted 7 lines
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.
Quoted 8 lines
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.
Quoted 22 lines
Quoted 22 lines
Quoted 11 lines
- 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. returngrub_error(err, fmt, ...)
- Comment about the strange "void" type instead of "void*" rephrased to be clearerThanks a lot!
Quoted 7 lines
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.
Quoted 6 lines
Quoted 1 line
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.
Quoted 8 lines
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 ;-)
Quoted 9 lines
Quoted 9 lines
Quoted 5 lines
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.
Quoted 8 lines
Quoted 3 lines
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
0x80in the BIOS to grub's (hd1) drive which will most likely be BIOS drive0x81.
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 :-)
Quoted 2 lines
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?
Quoted 32 lines
Quoted 32 lines
Or so, perhaps the long argument names can be chosen in a more clever way :-)
Quoted 3 lines
The new version of the patch is attached, and here is my suggested CLog:
2008-08-XX Javier Martin <[email protected]>
(newline)
Quoted 9 lines
*
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 handlerNo need how the change is used or why it was added.
Quoted 1 line
(
grub_loader_unregister_preboot) : Likewise. Unregister handlerSame here.
Quoted 1 line
(
grub_preboot_hookid) : New typedef. Registered hook "handle"Same here.
Quoted 3 lines
*
kern/loader.c(grub_loader_register_preboot) : New function. (grub_loader_unregister_preboot) : Likewise. (preboot_hooks) : New variable. Linked list of preboot hooks
Quoted 7 lines
Quoted 6 lines
Same here.
Quoted 2 lines
(
grub_loader_boot) : Call the list of preboot-hooks before the actual loaderWhat's the `'?
The what?
o_O
I see some weird character in your text. My font shows it as a block before every `*'.
Quoted 5 lines
Quoted 1 line
Please do not add a space before the ":"
Ok, everything corrected. New CL entry:
2008-08-XX Javier Martin <[email protected]>
Quoted 16 lines
*
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 :-).
Quoted 63 lines
Quoted 55 lines
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.
Quoted 49 lines
Index:
commands/i386/pc/drivemap.c
===================================================================i386/pc/drivemap.c +44
-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 Quoted 8 lines
i386/pc/drivemap.c line 50
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 6 lines
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_ttypedef should go into the "standard" GRUB headers somehow (but not insymbol.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?
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
i386/pc/drivemap.c +7
-0+/* Syms/vars/funcs exported from drivemap_int13h.S - end. */ + + +static grub_preboot_hookid insthandler_hook; + +typedef struct drivemap_node +{Quoted 8 lines
i386/pc/drivemap.c line 64
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 6 lines
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.
Quoted 10 lines
Quoted 9 lines
i386/pc/drivemap.c line 83
Quoted 7 lines
i386/pc/drivemap.c +7
-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) +Please do not add a newline here.
Oops, sorry. I forgot to remove it when moving the comment
:-)
Quoted 128 lines
Quoted 43 lines
Quoted 43 lines
i386/pc/drivemap.c +43
-0+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;Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +6
-0+break ; + } + previous = search; + search = search->next; + } +if (mapping)/* Found. */ You forgot one.
Corrected. Sorry.
Quoted 35 lines
Quoted 35 lines
i386/pc/drivemap.c +34
-0+ { +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 ;Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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_DEVICEI 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.Quoted 22 lines
Quoted 22 lines
i386/pc/drivemap.c +21
-0+static grub_err_t +revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +int found =0 ; + autoint 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; + }Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +6
-0+ } + +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.
Quoted 2 lines
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.
Quoted 32 lines
Quoted 12 lines
Quoted 12 lines
i386/pc/drivemap.c +12
-0+/* 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' ) + {Quoted 8 lines
i386/pc/drivemap.c line 211
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 11 lines
Quoted 10 lines
i386/pc/drivemap.c +9
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num; +return GRUB_ERR_NONE ; + } + } +else goto fail;
Quoted 3 lines
Quoted 1 line
...
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.
Quoted 130 lines
Quoted 40 lines
Quoted 40 lines
i386/pc/drivemap.c +40
-0+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);Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +6
-0+ } + drivemap =0 ; + } +else + { +/* Neither flag: put mapping */ ". */
Done
Quoted 16 lines
Quoted 4 lines
i386/pc/drivemap.c +3
-0+grub_uint8_t mapfrom =0 ; +grub_uint8_t mapto =0xFF ; +grub_err_t err;i386/pc/drivemap.c
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ +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.
Quoted 14 lines
i386/pc/drivemap.c
Quoted 13 lines
i386/pc/drivemap.c +12
-0+ { + 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. */ Quoted 1 line
Same here.
Done.
Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +5
-0+ { +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.
Quoted 29 lines
Quoted 29 lines
i386/pc/drivemap.c +28
-0+ { +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. */ Quoted 8 lines
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 1 line
Quoted 8 lines
Quoted 7 lines
i386/pc/drivemap.c line 342
Quoted 5 lines
i386/pc/drivemap.c +5
-0+ { + 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?
Quoted 267 lines
Quoted 239 lines
Quoted 239 lines
i386/pc/drivemap.c +83
-0+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
===================================================================i386/pc/drivemap_int13h.S +118
-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
===================================================================i386-pc.rmk +8
-1@@ -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.mkIndex:
include/grub/loader.h
===================================================================grub/loader.h +0
-0@@ -37,6 +37,22 @@/* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset ) (void );Quoted 14 lines
grub/loader.h line 48
Quoted 9 lines
grub/loader.h +9
-0+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 andABORT_ON_ERRORin the comments above.Done. "hook" removed because it referred to the loader module boot function.
Quoted 12 lines
grub/loader.h line 57
Quoted 10 lines
grub/loader.h +7
-0+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..."
Quoted 1 line
Are you a bot? ¬¬ Corrected
It would make like much simpler if I were ;-). What makes you think so?
Quoted 29 lines
Quoted 21 lines
Quoted 21 lines
grub/loader.h +0
-0grub_err_t EXPORT_FUNC (grub_loader_boot ) (void );Index:
kern/loader.c
===================================================================loader.c +12
-0@@ -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) +{Quoted 8 lines
loader.c
Quoted 6 lines
loader.c +6
-0+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.
Quoted 2 lines
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...
Quoted 76 lines
Quoted 11 lines
Quoted 11 lines
loader.c +11
-0+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;Quoted 8 lines
loader.c
Quoted 6 lines
loader.c +6
-0+return newentry; + } +} + +void +grub_loader_unregister_preboot (grub_preboot_hookid id)"preboot (grub"
Corrected on both functions ;)
Quoted 10 lines
Quoted 8 lines
loader.c +7
-0+{ +grub_preboot_hookid entry =0 ; +grub_preboot_hookid search = preboot_hooks; +grub_preboot_hookid previous =0 ; + +if (0 == id) +return ;...
... xD
Quoted 13 lines
loader.c
Quoted 12 lines
loader.c +11
-0+while (search) + { +if (search == id) + { + entry = search; +break ; + } + previous = search; + search = search->next; + } +if (entry)/* Found */ Quoted 1 line
...
Comment removed, was unnecessary.
Quoted 10 lines
Quoted 10 lines
loader.c +8
-0+ { +if (previous) + previous->next = entry->next; +else preboot_hooks = entry->next;/* Entry was head of list */ +grub_free (entry); + } +} + grub_err_tQuoted 8 lines
loader.c line 131
Quoted 6 lines
grub_loader_boot(void)
{
if (!grub_loader_loaded)return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");loader.c +2
-0+ +grub_preboot_hookid entry = preboot_hooks;Mixed declarations/code.
Moved the whole line up.
Quoted 11 lines
Quoted 11 lines
loader.c +7
-0+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 ();
Quoted 12 lines · On Tue, 05 Aug 2008 13:23:11 +0200 Marco Gerards <[email protected]> wrote:
Hi Javier,
Quoted 8 lines · Javier Martín
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.
Quoted 2 lines
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
Hi,
Quoted 8 lines · Javier Martín
Quoted 8 lines
Quoted 7 lines
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.
Quoted 6 lines
Quoted 1 line
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
Ok, I will reply to your last three messages on a single post:
Hi there!
Quoted 31 lines · Marco Gerards
Hi,
Quoted 21 lines · Javier Martín
Quoted 10 lines
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; }Quoted 5 lines
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
Quoted 2 lines
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.
Quoted 10 lines
--
Marco_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Quoted 39 lines · Marco Gerards
Hi Javier,
Quoted 26 lines · Javier Martín
Quoted 18 lines · Marco Gerards
Quoted 8 lines · Javier Martín
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_handlerfunction 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:Quoted 7 lines
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;Quoted 7 lines
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.
Quoted 8 lines
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.
Quoted 4 lines
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.
Quoted 31 lines
Quoted 24 lines
Quoted 24 lines
Quoted 13 lines
- 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. returngrub_error(err, fmt, ...)
- Comment about the strange "void" type instead of "void*" rephrased to be clearerThanks a lot!
Quoted 7 lines
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.
Quoted 6 lines
Quoted 1 line
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).
Quoted 6 lines
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
Quoted 10 lines
Quoted 9 lines
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.
Quoted 2 lines
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.
Quoted 25 lines
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
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.
Quoted 8 lines
Quoted 3 lines
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
0x80in the BIOS to grub's (hd1) drive which will most likely be BIOS drive0x81.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 :-)
Quoted 4 lines
Quoted 2 lines
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.
Quoted 35 lines
Quoted 35 lines
Quoted 35 lines
Or so, perhaps the long argument names can be chosen in a more clever way :-)
Quoted 3 lines
The new version of the patch is attached, and here is my suggested CLog:
2008-08-XX Javier Martin <[email protected]>
(newline)
Quoted 9 lines
*
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 handlerNo need how the change is used or why it was added.
Quoted 1 line
(
grub_loader_unregister_preboot) : Likewise. Unregister handlerSame here.
Quoted 1 line
(
grub_preboot_hookid) : New typedef. Registered hook "handle"Same here.
Quoted 3 lines
*
kern/loader.c(grub_loader_register_preboot) : New function. (grub_loader_unregister_preboot) : Likewise. (preboot_hooks) : New variable. Linked list of preboot hooksSame here.
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines
(
grub_loader_boot) : Call the list of preboot-hooks before the actual loaderWhat's the `'?
The what?
o_OI 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?
Quoted 23 lines
Quoted 6 lines
Quoted 2 lines
Please do not add a space before the ":"
Ok, everything corrected. New CL entry:
2008-08-XX Javier Martin <[email protected]>
Quoted 16 lines
*
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
Quoted 3 lines
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.
Quoted 71 lines
Quoted 64 lines
Quoted 56 lines
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.
Quoted 49 lines
Index:
commands/i386/pc/drivemap.c
===================================================================i386/pc/drivemap.c +44
-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 Quoted 8 lines
i386/pc/drivemap.c line 50
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 6 lines
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_ttypedef should go into the "standard" GRUB headers somehow (but not insymbol.h, which is included from assembly files).
Quoted 6 lines
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.
Quoted 166 lines
Quoted 16 lines
Quoted 16 lines
Quoted 4 lines
i386/pc/drivemap.c +3
-0+/* Syms/vars/funcs exported from drivemap_int13h.S - end. */ + +i386/pc/drivemap.c line 63
Quoted 10 lines
i386/pc/drivemap.c +10
-0+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.
Quoted 6 lines
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.
Quoted 10 lines
Quoted 9 lines
i386/pc/drivemap.c line 70
Quoted 7 lines
i386/pc/drivemap.c +7
-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) +Please do not add a newline here.
Oops, sorry. I forgot to remove it when moving the comment
:-)
Quoted 128 lines
Quoted 43 lines
Quoted 43 lines
i386/pc/drivemap.c +43
-0+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;Quoted 8 lines
i386/pc/drivemap.c line 119
Quoted 6 lines
i386/pc/drivemap.c +6
-0+break ; + } + previous = search; + search = search->next; + } +if (mapping)/* Found. */ You forgot one.
Corrected. Sorry.
Quoted 35 lines
Quoted 35 lines
i386/pc/drivemap.c +34
-0+ { +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 ;Quoted 8 lines
i386/pc/drivemap.c line 159
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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_DEVICEI 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.Quoted 30 lines
Quoted 18 lines
i386/pc/drivemap.c +17
-0+static grub_err_t +revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +int found =0 ; + autoint 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;i386/pc/drivemap.c line 186
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ } +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.
Quoted 5 lines
Quoted 2 lines
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.
Quoted 33 lines
Quoted 33 lines
Quoted 13 lines
Quoted 13 lines
i386/pc/drivemap.c +12
-0+/* 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' ) + {Quoted 8 lines
i386/pc/drivemap.c line 204
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 11 lines
Quoted 10 lines
i386/pc/drivemap.c +9
-0+else + { + bios_num |= drivenum; +if (output) + *output = bios_num; +return GRUB_ERR_NONE ; + } + } +else goto fail;
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
...
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;
Quoted 137 lines
Quoted 137 lines
Quoted 41 lines
Quoted 41 lines
i386/pc/drivemap.c +40
-0+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);Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +6
-0+ } + drivemap =0 ; + } +else + { +/* Neither flag: put mapping */ ". */
Done
Quoted 16 lines
Quoted 4 lines
i386/pc/drivemap.c +3
-0+grub_uint8_t mapfrom =0 ; +grub_uint8_t mapto =0xFF ; +grub_err_t err;i386/pc/drivemap.c
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ +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.
Quoted 14 lines
i386/pc/drivemap.c
Quoted 13 lines
i386/pc/drivemap.c +12
-0+ { + 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. */ Quoted 1 line
Same here.
Done.
Quoted 8 lines
i386/pc/drivemap.c
Quoted 6 lines
i386/pc/drivemap.c +5
-0+ { +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.
Quoted 29 lines
Quoted 29 lines
i386/pc/drivemap.c +28
-0+ { +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. */ Quoted 8 lines
Quoted 6 lines
i386/pc/drivemap.c +6
-0+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.
Quoted 7 lines
Quoted 6 lines
i386/pc/drivemap.c +5
-0+ { + entries++; + curentry = curentry->next; + } +if (0 == entries)
Quoted 4 lines
Quoted 2 lines
Quoted 1 line
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.
Quoted 268 lines
Quoted 268 lines
Quoted 240 lines
Quoted 240 lines
i386/pc/drivemap.c +83
-0+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
===================================================================i386/pc/drivemap_int13h.S +118
-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
===================================================================i386-pc.rmk +8
-1@@ -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.mkIndex:
include/grub/loader.h
===================================================================grub/loader.h +0
-0@@ -37,6 +37,22 @@/* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset ) (void );Quoted 14 lines
grub/loader.h line 48
Quoted 9 lines
grub/loader.h +9
-0+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 andABORT_ON_ERRORin the comments above.Done. "hook" removed because it referred to the loader module boot function.
Quoted 12 lines
grub/loader.h line 57
Quoted 10 lines
grub/loader.h +7
-0+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..."
Quoted 4 lines
Quoted 1 line
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!
Quoted 118 lines
Quoted 30 lines
Quoted 22 lines
Quoted 22 lines
grub/loader.h +0
-0grub_err_t EXPORT_FUNC (grub_loader_boot ) (void );Index:
kern/loader.c
===================================================================loader.c +12
-0@@ -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) +{Quoted 8 lines
loader.c line 81
Quoted 6 lines
loader.c +6
-0+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.
Quoted 2 lines
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...
Quoted 64 lines
Quoted 11 lines
Quoted 11 lines
loader.c +11
-0+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;Quoted 8 lines
loader.c line 98
Quoted 6 lines
loader.c +6
-0+return newentry; + } +} + +void +grub_loader_unregister_preboot (grub_preboot_hookid id)"preboot (grub"
Corrected on both functions ;)
Quoted 10 lines
Quoted 8 lines
loader.c +7
-0+{ +grub_preboot_hookid entry =0 ; +grub_preboot_hookid search = preboot_hooks; +grub_preboot_hookid previous =0 ; + +if (0 == id) +return ;...
... xD
Quoted 13 lines
loader.c
Quoted 12 lines
loader.c +11
-0+while (search) + { +if (search == id) + { + entry = search; +break ; + } + previous = search; + search = search->next; + } +if (entry)/* Found */ Quoted 1 line
...
Comment removed, was unnecessary.
Quoted 10 lines
Quoted 10 lines
loader.c +8
-0+ { +if (previous) + previous->next = entry->next; +else preboot_hooks = entry->next;/* Entry was head of list */ +grub_free (entry); + } +} + grub_err_tQuoted 8 lines
loader.c
Quoted 6 lines
grub_loader_boot(void)
{
if (!grub_loader_loaded)return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");loader.c +2
-0+ +grub_preboot_hookid entry = preboot_hooks;Mixed declarations/code.
Quoted 12 lines
Moved the whole line up.
Quoted 11 lines
Quoted 11 lines
loader.c +7
-0+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
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Quoted 19 lines · Marco Gerards
Hi,
Quoted 8 lines · Javier Martín
Quoted 8 lines
Quoted 7 lines
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.
Quoted 6 lines
Quoted 1 line
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).
Quoted 6 lines
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 ^^
Quoted 7 lines
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.
Quoted 10 lines
--
Marco_______________________________________________
Grub-devel mailing list
[email protected]
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 <[email protected]>
* 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
i386/pc/drivemap.c +423 -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 ; + autoint 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 ); +} +
i386/pc/drivemap_int13h.S +121 -0
121 lines not on this page · raw message
i386-pc.rmk +9 -1
@@ -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
grub/loader.h +17 -1
@@ -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 );
grub/i386/pc/drivemap.h +37 -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;
loader.c +74 -0
85 lines not on this page · raw message
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
i386/pc/drivemap.c +433 -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 ; + autoint 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 ); +} +
i386/pc/drivemap_int13h.S +121 -0
121 lines not on this page · raw message
i386-pc.rmk +9 -1
@@ -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
grub/loader.h +17 -1
@@ -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 );
grub/i386/pc/drivemap.h +37 -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;
loader.c +74 -0
85 lines not on this page · raw message
Quoted 15 lines · Javier Martín
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
hd10x80" 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 :-)
Quoted 5 lines
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 thedrivemap_cmdfunction proper, and there are cosmetic changes elsewhere (spurious tabs removed, etc.).
You only forgot the changelog entry ;-)
Quoted 143 lines
-Habbit
Index:
commands/i386/pc/drivemap.c
===================================================================i386/pc/drivemap.c +136
-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
i386/pc/drivemap.c line 146
Quoted 10 lines
i386/pc/drivemap.c +10
-0+ { +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.
Quoted 74 lines
i386/pc/drivemap.c +74
-0+ } +} + +/* 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 ; + autoint 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)
i386/pc/drivemap.c line 230
Quoted 10 lines
i386/pc/drivemap.c +10
-0+{ +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?
Quoted 397 lines
i386/pc/drivemap.c +203
-0+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
===================================================================i386/pc/drivemap_int13h.S +121
-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
===================================================================i386-pc.rmk +9
-1@@ -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.mkIndex:
include/grub/loader.h
===================================================================grub/loader.h +17
-1@@ -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
===================================================================grub/i386/pc/drivemap.h +8
-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
grub/i386/pc/drivemap.h line 18
Quoted 10 lines
grub/i386/pc/drivemap.h +10
-0+* (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.
Quoted 76 lines
grub/i386/pc/drivemap.h +18
-0+#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
===================================================================loader.c +50
-0@@ -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 ;
loader.c line 123
Quoted 10 lines
loader.c +10
-0+ } + 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.
Quoted 26 lines
loader.c +14
-0+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
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Quoted 20 lines · Marco Gerards
Quoted 15 lines · Javier Martín
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
hd10x80" 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 :-)
Quoted 7 lines
Quoted 5 lines
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 thedrivemap_cmdfunction 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 <[email protected]>
* 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:
Quoted 15 lines
+static
grub_err_tReview
Quoted 11 lines
(quoted diff) +11
-0+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?
Quoted 1 line
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
i386/pc/drivemap.c +434 -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 ; + autoint 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 ); +} +
i386/pc/drivemap_int13h.S +121 -0
121 lines not on this page · raw message
i386-pc.rmk +9 -1
@@ -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
grub/loader.h +17 -1
@@ -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 );
grub/i386/pc/drivemap.h +42 -0
42 lines not on this page · raw message
loader.c +77 -0
88 lines not on this page · raw message
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.
i386/pc/drivemap.c
Quoted 5 lines · Javier Martín
i386/pc/drivemap.c +4
-0+ +#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.
i386/pc/drivemap.c line 328
Quoted 9 lines
i386/pc/drivemap.c +9
-0+/* 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).
Quoted 1 line
+
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.
i386/pc/drivemap_int13h.S line 54
Quoted 4 lines
i386/pc/drivemap_int13h.S +4
-0+/* 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).
i386/pc/drivemap_int13h.S line 64
Quoted 4 lines
i386/pc/drivemap_int13h.S +4
-0+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).
Quoted 18 lines
Index:
include/grub/loader.h
===================================================================grub/loader.h +9
-1@@ -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/loader.h line 58
Quoted 10 lines
grub/loader.h +8
-0+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?
grub/loader.h
Quoted 3 lines
grub/loader.h +3
-0+/* 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.
Quoted 11 lines
Index:
kern/loader.c
===================================================================loader.c +3
-0@@ -61,11 +61,88 @@ grub_loader_loaded =0 ; } +struct hooklist_node +{ +grub_err_t (*hook) (void );
loader.c line 75
Quoted 10 lines
loader.c +9
-0+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?
Signature · 5 lines
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."
Quoted 3 lines · Robert Millan
Hi,
Marco asked me to review this.
So he finally got fed up of me... Understandable ^^
Quoted 15 lines
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.i386/pc/drivemap.c
Quoted 5 lines · Javier Martín
i386/pc/drivemap.c +4
-0+ +#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.
Quoted 4 lines
[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";
Quoted 14 lines
i386/pc/drivemap.c line 328
Quoted 10 lines
i386/pc/drivemap.c +9
-0+/* 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.
Quoted 4 lines
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.
Quoted 5 lines
Quoted 2 lines
+
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.
Quoted 9 lines
i386/pc/drivemap_int13h.S line 54
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+/* 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, 0xbeefIs this a signature? Then a macro would be preferred, so that it can be shared with whoever checks for it.
Quoted 3 lines
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.
Quoted 7 lines
i386/pc/drivemap_int13h.S line 64
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+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].
Quoted 24 lines
Quoted 24 lines
Index:
include/grub/loader.h
===================================================================grub/loader.h +14
-1@@ -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 */
Quoted 8 lines
grub/loader.h line 58
Quoted 5 lines
grub/loader.h +3
-0+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.
Quoted 6 lines
grub/loader.h
Quoted 4 lines
grub/loader.h +3
-0+/* 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"?
Quoted 26 lines
Quoted 12 lines
Index:
kern/loader.c
===================================================================loader.c +3
-0@@ -61,11 +61,88 @@ grub_loader_loaded =0 ; } +struct hooklist_node +{ +grub_err_t (*hook) (void );loader.c line 75
Quoted 10 lines
loader.c +9
-0+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.
Quoted 1 line
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.
Quoted 5 lines · Robert Millan
loader.c
Quoted 2 lines · Javier Martín
loader.c +2
-0+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
Hi,
Quoted 5 lines · Javier Martín
Quoted 3 lines · Robert Millan
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 ;-)
Quoted 15 lines
Quoted 15 lines
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.i386/pc/drivemap.c
Quoted 5 lines · Javier Martín
i386/pc/drivemap.c +4
-0+ +#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.
Quoted 7 lines
Quoted 4 lines
[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 ;)
Quoted 25 lines
Quoted 13 lines
i386/pc/drivemap.c line 328
Quoted 9 lines
i386/pc/drivemap.c +9
-0+/* 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.Quoted 4 lines
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.
Quoted 3 lines
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.
Quoted 29 lines
Quoted 4 lines
Quoted 1 line
+
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.
Quoted 9 lines
i386/pc/drivemap_int13h.S line 54
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+/* 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, 0xbeefIs this a signature? Then a macro would be preferred, so that it can be shared with whoever checks for it.
Quoted 3 lines
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.Quoted 6 lines
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+FUNCTION(grub_drivemap_int13_handler) + push %bp + mov %sp, %bp + push %ax /* We'll need it later to determine the used BIOS function. */
Quoted 7 lines
Quoted 1 line
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 :-)
Quoted 31 lines
Quoted 23 lines
Quoted 23 lines
Index:
include/grub/loader.h
===================================================================grub/loader.h +14
-1@@ -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 */ Quoted 8 lines
grub/loader.h line 58
Quoted 5 lines
grub/loader.h +3
-0+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?
Quoted 6 lines
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.
Quoted 11 lines
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.
Quoted 6 lines
grub/loader.h
Quoted 4 lines
grub/loader.h +3
-0+/* 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? :)
Quoted 38 lines
Quoted 26 lines
Quoted 12 lines
Index:
kern/loader.c
===================================================================loader.c +3
-0@@ -61,11 +61,88 @@ grub_loader_loaded =0 ; } +struct hooklist_node +{ +grub_err_t (*hook) (void );loader.c line 75
Quoted 10 lines
loader.c +9
-0+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.
Quoted 1 line
Would the same functionality work if put inside a module?
For the reasons discussed above in the
loader.hsnippet, I don't think
so: the only "lighter" solution would be to just put adrivemap_hookvariable 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 onloader_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.
Quoted 8 lines
Quoted 5 lines · Robert Millan
loader.c
Quoted 2 lines · Javier Martín
loader.c +2
-0+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
Quoted 4 lines
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
Quoted 5 lines · Javier Martín
Quoted 5 lines
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.
Quoted 7 lines
Quoted 4 lines
[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.
Quoted 10 lines
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?
Quoted 8 lines
Quoted 8 lines
i386/pc/drivemap_int13h.S line 54
Quoted 4 lines
i386/pc/drivemap_int13h.S +4
-0+/* 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, 0xbeefIs this a signature? Then a macro would be preferred, so that it can be shared with whoever checks for it.
Quoted 7 lines
Quoted 3 lines
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.
Quoted 8 lines
Quoted 7 lines
i386/pc/drivemap_int13h.S line 64
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+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).
Quoted 23 lines
Quoted 2 lines
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.
[...]
Quoted 4 lines
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.hsnippet, I don't think
so: the only "lighter" solution would be to just put adrivemap_hookvariable that would be called before boot, but as I mentioned before, this solution can be employed by other modules as well.
Quoted 5 lines
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 onloader_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).
Quoted 6 lines
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+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 ;-)
Quoted 4 lines
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 :-)
Signature · 5 lines
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."
Robert Millan <[email protected]> writes:
[...]
Quoted 19 lines
Quoted 9 lines
Quoted 4 lines
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.hsnippet, I don't think
so: the only "lighter" solution would be to just put adrivemap_hookvariable that would be called before boot, but as I mentioned before, this solution can be employed by other modules as well.Quoted 5 lines
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 onloader_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.
Quoted 5 lines
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
Ok, making a mixup reply...
Quoted 9 lines · Robert Millan
Quoted 8 lines · Javier Martín
Quoted 8 lines
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!
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
[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?
Quoted 8 lines
Quoted 8 lines
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.
Quoted 6 lines
Quoted 3 lines
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.
Quoted 25 lines
Quoted 9 lines
Quoted 9 lines
i386/pc/drivemap_int13h.S line 54
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+/* 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, 0xbeefIs this a signature? Then a macro would be preferred, so that it can be shared with whoever checks for it.
Quoted 7 lines
Quoted 3 lines
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.
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
i386/pc/drivemap_int13h.S +4
-0+FUNCTION(grub_drivemap_int13_handler) + push %bp + mov %sp, %bp + push %ax /* We'll need it later to determine the used BIOS function. */
Quoted 6 lines
Quoted 2 lines
Quoted 1 line
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.
Quoted 3 lines
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).
Quoted 34 lines · Marco Gerards
Robert Millan <[email protected]> writes:
[...]
Quoted 19 lines
Quoted 9 lines
Quoted 4 lines
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.hsnippet, I don't think
so: the only "lighter" solution would be to just put adrivemap_hookvariable that would be called before boot, but as I mentioned before, this solution can be employed by other modules as well.Quoted 5 lines
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 onloader_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.
Quoted 5 lines
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.
Quoted 4 lines
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
Quoted 10 lines
--
Marco_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
/*
* 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. */
.code16FUNCTION(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 2VARIABLE(grub_drivemap_int13_mapstart)
/* Copy stops here. */
.code32VARIABLE(grub_drivemap_int13_size)
.word GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_size)
/*
* 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. */
.code16FUNCTION(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 2VARIABLE(grub_drivemap_int13_mapstart)
/* Copy stops here. */
.code32VARIABLE(grub_drivemap_int13_size)
.word GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_size)
Quoted 1 line · Javier Martín
Ok, making a mixup reply...
Please don't. Unless you do not like it that your mails go unread.
Quoted 20 lines
Quoted 15 lines
Quoted 9 lines
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.
Quoted 4 lines
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.
Quoted 5 lines
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
Quoted 32 lines · Marco Gerards
Quoted 1 line · Javier Martín
Ok, making a mixup reply...
Please don't. Unless you do not like it that your mails go unread.
Quoted 20 lines
Quoted 15 lines
Quoted 9 lines
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.
Quoted 4 lines
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.
Quoted 5 lines
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.
Quoted 7 lines
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.
Quoted 8 lines
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.
Quoted 5 lines
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.
Quoted 6 lines
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
Quoted 10 lines
--
Marco_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Review
Quoted 2 lines · Javier Martín
(quoted diff) +2
-0+static grub_err_t +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
Ah, and please separate function names from parenthesis ;-)
Signature · 5 lines
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."
Quoted 3 lines · On Tue, 05 Aug 2008 01:10:25 +0200 Javier Martín <[email protected]> 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
$ Quoted 3 lines
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