Drivemap module
PATCH 1/1 Drivemap module
Since the last posts in the "reimplementing the legacy map command" thread seemingly died silently, I'm sending the improved version of the patch here. If the death of the thread was intentional, please just tell me (either nicely or rudely) and I'll be off.
Recapitulating the other thread, this is a new module for x86-pc that provides functionality similar to the legacy "map" command, i.e. allows BIOS drives to be remapped and have the second HD appear as 0x80 (the first), which is necessary because some OSes refuse to boot otherwise, ignoring the "boot drive" passed to their loaders in DL (Windows XP and other NTs); or can only boot with limited functionality (FreeDOS). The syntax of this new command is similar to the old "map", though the second argument must be the target BIOS drive number instead of a GRUB device, though a hack-patch parsing them would be simple.
Regarding the objections raised by Vesa, I followed most of the advices, removing chatter in the source and completely separating drivemap from the kernel. However, I've left the "abort_on_error" part of the new preboot-hook interface intact, mostly because I don't know how to replace it. As drivemap is the only module using it, the problem should be harmless for now, but a solution should be devised - not by me, though, finals are here (literally, I had one today).
I hope I didn't leave anything out this time. This code has been tested on the following situations:
* Emulators: Bochs & QEMU, booting FreeDOS in (hd1) with another fat
partition in (hd0) - works wonderfully, and even allows access to both drives if they are crossmapped and a faux root=(hd0) is set.
* Real hardware: an Athlon64 (939) CPU on a Gigabyte motherboard,
booting Windows XP Pro x64 in (hd1) with Ubuntu in (hd0) - works too, with no hacks required at all (by the way, booting XP was my reason to do this).
Well, here it is. Have a nice day!
Habbit
Index: commands/i386/pc/drivemap.c
===================================================================
RCS file: commands/i386/pc/drivemap.c
i386/pc/drivemap.c new file +348 -0
@@ -0,0 +1,348 @@ +/* drivemap.c - command to manage the BIOS drive mappings. */ +/* +* GRUB -- GRand Unified Bootloader +* Copyright (C) 2008 Free Software Foundation, Inc. +* +* GRUB is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GRUB is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GRUB. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <grub/normal.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/disk.h> +#include <grub/loader.h> +#include <grub/machine/loader.h> +#include <grub/machine/biosdisk.h> + +/* Uncomment the following line to enable debugging output */ +/* #define DRIVEMAP_DEBUG */ + +#ifdef DRIVEMAP_DEBUG +# define DBG_PRINTF (...) grub_printf(__VA_ARGS__) +#else +# define DBG_PRINTF (...) +#endif + +static const struct grub_arg_option options[] = + { + {"list" ,'l' ,0 ,"show the current mappings" ,0 ,0 }, + {"reset" ,'r' ,0 ,"reset all mappings to the default values" ,0 ,0 }, + {0 ,0 ,0 ,0 ,0 ,0 } + }; + +/* ASSEMBLY SYMBOLS/VARS/FUNCS - start */ + +/* realmode far ptr = 2 * 16b */ +extern grub_uint32_t EXPORT_VAR (grub_drivemap_int13_oldhandler ); +/* Size of the section to be copied */ +extern grub_uint16_t EXPORT_VAR (grub_drivemap_int13_size ); + +/* NOT a typo - just need the symbol's address with &symbol */ +typedef void grub_symbol_t ; +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_handler_base ); +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_mapstart ); + +void EXPORT_FUNC (grub_drivemap_int13_handler )(void ); + +/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */ + +static grub_preboot_hookid insthandler_hook =0 ; + +typedef struct drivemap_node +{ +grub_uint8_t newdrive; +grub_uint8_t redirto; +struct drivemap_node *next; +}drivemap_node_t ; + +static drivemap_node_t *drivemap =0 ; +static grub_err_t drivemap_install_int13_handler (void ); + +static grub_err_t +drivemap_set (grub_uint8_t newdrive,grub_uint8_t redirto) +/* Puts the specified mapping into the table, replacing an existing mapping +* for newdrive or adding a new one if required. */ +{ +drivemap_node_t *mapping =0 , *search = drivemap; +while (search) + { +if (search->newdrive == newdrive) + { + mapping = search; +break ; + } + search = search->next; + } + +if (mapping)/* There was a mapping already in place, modify it */ + mapping->redirto = redirto; +else /* Create a new mapping and add it to the head of the list */ + { + mapping =grub_malloc (sizeof (drivemap_node_t)); +if (!mapping) +return grub_error (GRUB_ERR_OUT_OF_MEMORY ,"cannot allocate map entry" ); + mapping->newdrive = newdrive; + mapping->redirto = redirto; + mapping->next = drivemap; + drivemap = mapping; + } +return GRUB_ERR_NONE ; +} + +static void +drivemap_remove (grub_uint8_t newdrive) +/* Removes the mapping for newdrive from the table. If there is no mapping, +* then this function behaves like a no-op on the map. */ +{ +drivemap_node_t *mapping =0 , *search = drivemap, *previous =0 ; +while (search) + { +if (search->newdrive == newdrive) + { + mapping = search; +break ; + } + previous = search; + search = search->next; + } +if (mapping)/* Found */ + { +if (previous) + previous->next = mapping->next; +else drivemap = mapping->next;/* Entry was head of list */ +grub_free (mapping); + } +} + +static grub_err_t parse_biosdisk (const char *name,grub_uint8_t *disknum) +{ +if (!name)return GRUB_ERR_BAD_ARGUMENT ; +if (name[0 ] =='(' ) + name++;/* Skip the first ( in (hd0) - disk_open wants just the name! */ +grub_disk_t disk =grub_disk_open (name); +if (!disk) +return GRUB_ERR_UNKNOWN_DEVICE ; +else + { +enum grub_disk_dev_id id = disk->dev->id; +if (disknum) + *disknum = disk->id;/* Only valid, of course if it's a biosdisk */ +grub_disk_close (disk); +return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ? +GRUB_ERR_BAD_DEVICE :GRUB_ERR_NONE ; + } +} + +static grub_err_t revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +grub_err_t retval =GRUB_ERR_UNKNOWN_DEVICE ; + 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 ; + *output = name; + retval =GRUB_ERR_NONE ; + } +grub_disk_close (disk); +return found; + } + } + +grub_disk_dev_iterate (&find); +return retval; +} + +static grub_err_t +grub_cmd_drivemap (struct grub_arg_list *state,int argc,char **args) +{ +if (state[0 ].set)/* Show: list mappings */ + { +if (!drivemap) +grub_printf ("No drives have been remapped" ); +else + { +grub_printf ("Showing only remapped drives. Drives that have had " +"their slot assigned to another one and have not been " +"themselves remapped will become inaccessible through " +"the BIOS routines to the booted OS.\n\n" ); +grub_printf ("Mapped\tGRUB\n" ); +drivemap_node_t *curnode = drivemap; +while (curnode) + { +const char *dname =0 ; +grub_err_t err =revparse_biosdisk (curnode->redirto, &dname); +if (err !=GRUB_ERR_NONE ) +return grub_error (err,"invalid mapping: non-existent disk or" +"not managed by the BIOS" ); +grub_printf ("0x%02x\t%4s\n" , curnode->newdrive, dname); + curnode = curnode->next; + } + } + } +else if (state[1 ].set)/* Reset: just delete all mappings */ + { +if (drivemap) + { +drivemap_node_t *curnode = drivemap, *prevnode =0 ; +while (curnode) + { + prevnode = curnode; + curnode = curnode->next; +grub_free (prevnode); + } + drivemap =0 ; + } + } +else + { +if (argc !=2 ) +return grub_error (GRUB_ERR_BAD_ARGUMENT ,"two arguments required" ); +grub_uint8_t mapfrom =0 ; +grub_err_t err =parse_biosdisk (args[0 ], &mapfrom); +if (err !=GRUB_ERR_NONE ) +return grub_error (err,"invalid disk or not managed by the BIOS" ); + + grub_errno =GRUB_ERR_NONE ; +unsigned long mapto =grub_strtoul (args[1 ],0 ,0 ); +if (grub_errno !=GRUB_ERR_NONE ) +return grub_error (grub_errno, +"BIOS disk number must be between 0 and 255" ); +else if (mapto == mapfrom)/* Reset to default */ + { +DBG_PRINTF ("Removing the mapping for %s (%02x)" , args[0 ], mapfrom); +drivemap_remove (mapfrom); + } +else /* Map */ + { +DBG_PRINTF ("Mapping %s (%02x) to %02x\n" , args[0 ], mapfrom, mapto); +return drivemap_set ((grub_uint8_t )mapto, mapfrom); + } + } + +return GRUB_ERR_NONE ; +} + +typedef struct __attribute__ ((packed))int13map_node +{ +grub_uint8_t disknum; +grub_uint8_t mapto; +}int13map_node_t ; + +/* The min amount of mem that must remain free after installing the handler. +* 32 KiB is just above 0x7C00-0x7E00, where the bootsector is loaded. */ +#define MIN_FREE_MEM_KB 32 +#define INT13H_OFFSET (x) ( ((grub_uint8_t*)(x)) - ((grub_uint8_t*)&grub_drivemap_int13_handler_base) ) +#define INT13H_REBASE (x) ( (void*) (((grub_uint8_t*)handler_base) + (x)) ) +#define INT13H_TONEWADDR (x) INT13H_REBASE( INT13H_OFFSET( x ) ) +/* Int13h handler installer - reserves conventional memory for the handler, +* copies it over and sets the IVT entry for int13h. +* This code rests on the assumption that GRUB does not activate any kind of +* memory mapping, since it accesses realmode structures by their absolute +* addresses, like the IVT at 0 or the BDA at 0x400 */ +static grub_err_t drivemap_install_int13_handler (void ) +{ +grub_size_t entries =0 ; +drivemap_node_t *curentry = drivemap; +while (curentry)/* Count entries to prepare a contiguous map block */ + { + entries++; + curentry = curentry->next; + } +if (0 == entries) + { +DBG_PRINTF ("drivemap: No drives remapped, int13h handler not installed" ); +return GRUB_ERR_NONE ;/* No need to install the int13h handler */ + } +else + { +DBG_PRINTF ("drivemap: Installing int13h handler...\n" ); +grub_uint32_t *ivtslot = (grub_uint32_t *)0x0000004c ; + +/* Save the pointer to the old int13h handler */ + grub_drivemap_int13_oldhandler = *ivtslot; +DBG_PRINTF ("Old int13 handler at %04x:%04x\n" , + (grub_drivemap_int13_oldhandler >>16 ) &0x0ffff , + grub_drivemap_int13_oldhandler &0x0ffff ); + +/* Reserve a section of conventional memory as "BIOS memory" for handler: +* BDA offset 0x13 contains the top of such memory */ +grub_uint16_t *bpaMemInKb = (grub_uint16_t *)0x00000413 ; +DBG_PRINTF ("Top of conventional memory: %u KiB\n" , *bpaMemInKb); +grub_size_t totalSize = grub_drivemap_int13_size + + (entries +1 ) *sizeof (int13map_node_t); +grub_uint16_t payloadSizeKb = (totalSize >>10 ) + + (((totalSize %1024 ) ==0 ) ?0 :1 ); +if ((*bpaMemInKb - payloadSizeKb) <MIN_FREE_MEM_KB ) +return grub_error (GRUB_ERR_OUT_OF_MEMORY ,"refusing to install int13 handler, not enough free memory after" ); +DBG_PRINTF ("Payload is %u b long, reserving %u Kb\n" , totalSize, payloadSizeKb); + *bpaMemInKb -= payloadSizeKb; + +/* Copy int13h handler chunk to reserved area */ +grub_uint8_t *handler_base = (grub_uint8_t *)(*bpaMemInKb <<10 ); +DBG_PRINTF ("Copying int13 handler to: %p\n" , handler_base); +grub_memcpy (handler_base, &grub_drivemap_int13_handler_base, grub_drivemap_int13_size); + +/* Copy the mappings to the reserved area */ + curentry = drivemap; +grub_size_t i; +int13map_node_t *handler_map = (int13map_node_t *)INT13H_TONEWADDR (&grub_drivemap_int13_mapstart); +DBG_PRINTF ("Target map at %p, copying mappings...\n" , handler_map); +for (i =0 ; i < entries && curentry; i++, curentry = curentry->next) + { + handler_map[i].disknum = curentry->newdrive; + handler_map[i].mapto = curentry->redirto; +DBG_PRINTF ("\t#%d: 0x%02x <- 0x%02x\n" , i, handler_map[i].disknum, handler_map[i].mapto); + } +/* Signal end-of-map */ + handler_map[i].disknum =0 ; + handler_map[i].mapto =0 ; +DBG_PRINTF ("\t#%d: 0x%02x <- 0x%02x (end)\n" , i, handler_map[i].disknum, handler_map[i].mapto); + +/* Install our function as the int13h handler in the IVT */ +grub_uint32_t ivtentry = ((grub_uint32_t )handler_base) <<12 ;/* Segment address */ + ivtentry |= (grub_uint16_t )INT13H_OFFSET (grub_drivemap_int13_handler); +DBG_PRINTF ("New int13 handler IVT pointer: %04x:%04x\n" , + (ivtentry >>16 ) &0x0ffff , ivtentry &0x0ffff ); + *ivtslot = ivtentry; + +return GRUB_ERR_NONE ; + } +} +#undef INT13H_TONEWADDR +#undef INT13H_REBASE +#undef INT13H_OFFSET + +GRUB_MOD_INIT (drivemap) +{ + (void )mod;/* To stop warning. */ +grub_register_command ("drivemap" , grub_cmd_drivemap,GRUB_COMMAND_FLAG_BOTH , +"drivemap -s | -r | (hdX) newdrivenum" ,"Manage the BIOS drive mappings" , options); + insthandler_hook =grub_loader_register_preboot (&drivemap_install_int13_handler,1 ); +} + +GRUB_MOD_FINI (drivemap) +{ +grub_loader_unregister_preboot (insthandler_hook); + insthandler_hook =0 ; +grub_unregister_command ("drivemap" ); +}
Index: commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file: commands/i386/pc/drivemap_int13h.S
i386/pc/drivemap_int13h.S new file +128 -0
128 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.119
i386-pc.rmk +8 -1
@@ -152,7 +152,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \ videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod \ ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \ - aout.mod _bsd.mod bsd.mod + aout.mod _bsd.mod bsd.mod drivemap.mod # For biosdisk.mod. biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -319,4 +319,11 @@ bsd_mod_SOURCES = loader/i386/bsd_normal bsd_mod_CFLAGS = $(COMMON_CFLAGS) bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For drivemap.mod. +drivemap_mod_SOURCES = commands/i386/pc/drivemap.c \ + commands/i386/pc/drivemap_int13h.S +drivemap_mod_ASFLAGS = $(COMMON_ASFLAGS) +drivemap_mod_CFLAGS = $(COMMON_CFLAGS) +drivemap_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk
Index: include/grub/loader.h
===================================================================
RCS file: /sources/grub/grub2/include/grub/loader.h,v retrieving revision 1.9
grub/loader.h +13 -0
@@ -37,6 +37,19 @@ void EXPORT_FUNC(grub_loader_set) (grub_/* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset ) (void ); +typedef struct hooklist_node *grub_preboot_hookid ; + +/* Register a function to be called before the boot hook. Returns an id that +can be later used to unregister the preboot (i.e. if module unloaded). If +abort_on_error is set, the boot sequence will abort if any of the registered +functions return anything else than GRUB_ERR_NONE */ +grub_preboot_hookid EXPORT_FUNC (grub_loader_register_preboot ) + (grub_err_t (*hook) (void ),int abort_on_error); + +/* Unregister a preboot hook by the id returned by loader_register_preboot. +This functions becomes a no-op if no such function is registered */ +void EXPORT_FUNC (grub_loader_unregister_preboot ) (grub_preboot_hookid id); +/* Call the boot hook in current loader. This may or may not return, depending on the setting by grub_loader_set. */ grub_err_t EXPORT_FUNC (grub_loader_boot ) (void );
Index: kern/loader.c
===================================================================
RCS file: /sources/grub/grub2/kern/loader.c,v
retrieving revision 1.9
loader.c +67 -0
78 lines not on this page · raw message
Quoted 4 lines · Javier Martín
Since the last posts in the "reimplementing the legacy map command" thread seemingly died silently, I'm sending the improved version of the patch here. If the death of the thread was intentional, please just tell me (either nicely or rudely) and I'll be off.
I'll answer this now and look at patch when I have better time.
Just keep in mind that while we appreciate new feature and bug fixes, we do not have all the time on earth to review them. Please be patient especially if you touch something that can be critical for stability and functionality of the software. We do not want to have feature on the system that no-one knows how it works, so we prefer to have understanding of the feature at hand and then that is gained then it is much easier to comment the patch and then diagnose if it has problems.
So please wait a bit, and lets see what kind of comments it gets.
Quoted 16 lines · Vesa Jääskeläinen
Quoted 4 lines · Javier Martín
Since the last posts in the "reimplementing the legacy map command" thread seemingly died silently, I'm sending the improved version of the patch here. If the death of the thread was intentional, please just tell me (either nicely or rudely) and I'll be off.
I'll answer this now and look at patch when I have better time.
Just keep in mind that while we appreciate new feature and bug fixes, we do not have all the time on earth to review them. Please be patient especially if you touch something that can be critical for stability and functionality of the software. We do not want to have feature on the system that no-one knows how it works, so we prefer to have understanding of the feature at hand and then that is gained then it is much easier to comment the patch and then diagnose if it has problems.
Quoted 2 lines
So please wait a bit, and lets see what kind of comments it gets.
Ok, I'm sorry and don't mean to be intrusive, I just thought the last messages might have got lost between mail filters - it's happened to me.
Quoted 2 lines · Javier Martín
Ok, I'm sorry and don't mean to be intrusive, I just thought the last messages might have got lost between mail filters - it's happened to me.
Here's my very superficial review.
Please don't add any trailing whitespace. Line in the patch that start with a plus should not end with a space or a tab.
Please avoid camelCase names, such as bpaMemInKb and retVal. Local variables should generally be short, like "ret" and "bpa_mem".
Some strings are excessively long. While there may be exception of the 80 column limit, I see a 118 character long line that can be trivially wrapped.
The patch add a new warning:
commands/i386/pc/drivemap_int13h.S: Assembler messages:commands/i386/pc/drivemap_int13h.S:124: Warning: .space repeat count is zero, ignored
I'm not sure what you meant there.
I don't think using #undef is a good idea. It's better to use macro names that would never be reused accidentally and thus never need to be undefined.
Signature · 2 lines
Regards, Pavel Roskin
Hi there, I checked your comment, reread the GNU coding guidelines and fixed some things (stated below the relevant parts of your message). Here is the new version of the patch.
Quoted 2 lines · Pavel Roskin
Please don't add any trailing whitespace. Line in the patch that start with a plus should not end with a space or a tab.
I could not find the line you were referring to. I could only find one line starting with a plus (in install_int13_handler), but it does not end with a space/tab. However, I did find one trailing tab after a comment, and removed it.
Quoted 7 lines
Please avoid camelCase names, such as bpaMemInKb and retVal. Local variables should generally be short, like "ret" and "
bpa_mem".Some strings are excessively long. While there may be exception of the 80 column limit, I see a 118 character long line that can be trivially wrapped.
Both corrected. Sorry, I work on a wide screen and, tough I tried to uphold the 80-char line rule, I'm not used to it. I think there was a switch in gedit to graphically display the 80-char limit, but I can't find it now...
OOC: do you know if there is a syntax highlighting mode for gas/x86 assembly code in gedit? Right now the program thinks the code is C, and the only opcode it highlights is "int". Even a gas-only highlighting (i.e. only the directives, not the opcodes) would be useful.
Quoted 8 lines
The patch add a new warning:
commands/i386/pc/drivemap_int13h.S: Assembler messages:commands/i386/pc/drivemap_int13h.S:124: Warning: .space repeat count is zero, ignoredI'm not sure what you meant there.
Removed. That was a leftover from the early stages when the "mappings" area of the int13h handler had a maximum size of 8 entries. Then, I decided that the space for the map was to be dynamically allocated, so I moved it to the end of the block and set the .space directive to zero.
Quoted 4 lines
I don't think using #undef is a good idea. It's better to use macro names that would never be reused accidentally and thus never need to be undefined.
Removed. Those are leftovers too, from the time the patch modified the loader.h and loader.S files, which are public and #included, so I didn't want to contaminate the namespace. Since Vesa's review, I moved the relevant code to its own files inside the module, and thus it's no longer public.
I also removed four lines from some old debug code that would have made the int13h handler crash if the "unmap" section had been uncommented.
Index: commands/i386/pc/drivemap.c
===================================================================
RCS file: commands/i386/pc/drivemap.c
i386/pc/drivemap.c new file +351 -0
@@ -0,0 +1,351 @@ +/* drivemap.c - command to manage the BIOS drive mappings. */ +/* +* GRUB -- GRand Unified Bootloader +* Copyright (C) 2008 Free Software Foundation, Inc. +* +* GRUB is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* GRUB is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GRUB. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <grub/normal.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/disk.h> +#include <grub/loader.h> +#include <grub/machine/loader.h> +#include <grub/machine/biosdisk.h> + +/* Uncomment the following line to enable debugging output */ +/* #define DRIVEMAP_DEBUG */ + +#ifdef DRIVEMAP_DEBUG +# define DBG_PRINTF (...) grub_printf(__VA_ARGS__) +#else +# define DBG_PRINTF (...) +#endif + +static const struct grub_arg_option options[] = + { + {"list" ,'l' ,0 ,"show the current mappings" ,0 ,0 }, + {"reset" ,'r' ,0 ,"reset all mappings to the default values" ,0 ,0 }, + {0 ,0 ,0 ,0 ,0 ,0 } + }; + +/* ASSEMBLY SYMBOLS/VARS/FUNCS - start */ + +/* realmode far ptr = 2 * 16b */ +extern grub_uint32_t EXPORT_VAR (grub_drivemap_int13_oldhandler ); +/* Size of the section to be copied */ +extern grub_uint16_t EXPORT_VAR (grub_drivemap_int13_size ); + +/* NOT a typo - just need the symbol's address with &symbol */ +typedef void grub_symbol_t ; +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_handler_base ); +extern grub_symbol_t EXPORT_VAR (grub_drivemap_int13_mapstart ); + +void EXPORT_FUNC (grub_drivemap_int13_handler )(void ); + +/* ASSEMBLY SYMBOLS/VARS/FUNCS - end */ + +static grub_preboot_hookid insthandler_hook =0 ; + +typedef struct drivemap_node +{ +grub_uint8_t newdrive; +grub_uint8_t redirto; +struct drivemap_node *next; +}drivemap_node_t ; + +static drivemap_node_t *drivemap =0 ; +static grub_err_t install_int13_handler (void ); + +static grub_err_t +drivemap_set (grub_uint8_t newdrive,grub_uint8_t redirto) +/* Puts the specified mapping into the table, replacing an existing mapping +* for newdrive or adding a new one if required. */ +{ +drivemap_node_t *mapping =0 , *search = drivemap; +while (search) + { +if (search->newdrive == newdrive) + { + mapping = search; +break ; + } + search = search->next; + } + +if (mapping)/* There was a mapping already in place, modify it */ + mapping->redirto = redirto; +else /* Create a new mapping and add it to the head of the list */ + { + mapping =grub_malloc (sizeof (drivemap_node_t)); +if (!mapping) +return grub_error (GRUB_ERR_OUT_OF_MEMORY ,"cannot allocate map entry" ); + mapping->newdrive = newdrive; + mapping->redirto = redirto; + mapping->next = drivemap; + drivemap = mapping; + } +return GRUB_ERR_NONE ; +} + +static void +drivemap_remove (grub_uint8_t newdrive) +/* Removes the mapping for newdrive from the table. If there is no mapping, +* then this function behaves like a no-op on the map. */ +{ +drivemap_node_t *mapping =0 , *search = drivemap, *previous =0 ; +while (search) + { +if (search->newdrive == newdrive) + { + mapping = search; +break ; + } + previous = search; + search = search->next; + } +if (mapping)/* Found */ + { +if (previous) + previous->next = mapping->next; +else drivemap = mapping->next;/* Entry was head of list */ +grub_free (mapping); + } +} + +static grub_err_t parse_biosdisk (const char *name,grub_uint8_t *disknum) +{ +if (!name)return GRUB_ERR_BAD_ARGUMENT ; +if (name[0 ] =='(' ) + name++;/* Skip the first ( in (hd0) - disk_open wants just the name! */ +grub_disk_t disk =grub_disk_open (name); +if (!disk) +return GRUB_ERR_UNKNOWN_DEVICE ; +else + { +enum grub_disk_dev_id id = disk->dev->id; +if (disknum) + *disknum = disk->id;/* Only valid, of course if it's a biosdisk */ +grub_disk_close (disk); +return (GRUB_DISK_DEVICE_BIOSDISK_ID != id) ? +GRUB_ERR_BAD_DEVICE :GRUB_ERR_NONE ; + } +} + +static grub_err_t revparse_biosdisk (const grub_uint8_t dnum,const char **output) +{ +grub_err_t retval =GRUB_ERR_UNKNOWN_DEVICE ; + 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 ; + *output = name; + retval =GRUB_ERR_NONE ; + } +grub_disk_close (disk); +return found; + } + } + +grub_disk_dev_iterate (&find); +return retval; +} + +static grub_err_t +grub_cmd_drivemap (struct grub_arg_list *state,int argc,char **args) +{ +if (state[0 ].set)/* Show: list mappings */ + { +if (!drivemap) +grub_printf ("No drives have been remapped" ); +else + { +grub_printf ("Showing only remapped drives. Drives that have had " +"their slot assigned to another one and have not been " +"themselves remapped will become inaccessible through " +"the BIOS routines to the booted OS.\n\n" ); +grub_printf ("Mapped\tGRUB\n" ); +drivemap_node_t *curnode = drivemap; +while (curnode) + { +const char *dname =0 ; +grub_err_t err =revparse_biosdisk (curnode->redirto, &dname); +if (err !=GRUB_ERR_NONE ) +return grub_error (err,"invalid mapping: non-existent disk or" +"not managed by the BIOS" ); +grub_printf ("0x%02x\t%4s\n" , curnode->newdrive, dname); + curnode = curnode->next; + } + } + } +else if (state[1 ].set)/* Reset: just delete all mappings */ + { +if (drivemap) + { +drivemap_node_t *curnode = drivemap, *prevnode =0 ; +while (curnode) + { + prevnode = curnode; + curnode = curnode->next; +grub_free (prevnode); + } + drivemap =0 ; + } + } +else + { +if (argc !=2 ) +return grub_error (GRUB_ERR_BAD_ARGUMENT ,"two arguments required" ); +grub_uint8_t mapfrom =0 ; +grub_err_t err =parse_biosdisk (args[0 ], &mapfrom); +if (err !=GRUB_ERR_NONE ) +return grub_error (err,"invalid disk or not managed by the BIOS" ); + + grub_errno =GRUB_ERR_NONE ; +unsigned long mapto =grub_strtoul (args[1 ],0 ,0 ); +if (grub_errno !=GRUB_ERR_NONE ) +return grub_error (grub_errno, +"BIOS disk number must be between 0 and 255" ); +else if (mapto == mapfrom)/* Reset to default */ + { +DBG_PRINTF ("Removing the mapping for %s (%02x)" , args[0 ], mapfrom); +drivemap_remove (mapfrom); + } +else /* Map */ + { +DBG_PRINTF ("Mapping %s (%02x) to %02x\n" , args[0 ], mapfrom, mapto); +return drivemap_set ((grub_uint8_t )mapto, mapfrom); + } + } + +return GRUB_ERR_NONE ; +} + +typedef struct __attribute__ ((packed))int13map_node +{ +grub_uint8_t disknum; +grub_uint8_t mapto; +}int13map_node_t ; + +/* The min amount of mem that must remain free after installing the handler. +* 32 KiB is just above 0x7C00-0x7E00, where the bootsector is loaded. */ +#define MIN_FREE_MEM_KB 32 +#define INT13H_OFFSET (x) ( ((grub_uint8_t*)(x)) - ((grub_uint8_t*)&grub_drivemap_int13_handler_base) ) +#define INT13H_REBASE (x) ( (void*) (((grub_uint8_t*)handler_base) + (x)) ) +#define INT13H_TONEWADDR (x) INT13H_REBASE( INT13H_OFFSET( x ) ) +/* Int13h handler installer - reserves conventional memory for the handler, +* copies it over and sets the IVT entry for int13h. +* This code rests on the assumption that GRUB does not activate any kind of +* memory mapping, since it accesses realmode structures by their absolute +* addresses, like the IVT at 0 or the BDA at 0x400 */ +static grub_err_t install_int13_handler (void ) +{ +grub_size_t entries =0 ; +drivemap_node_t *curentry = drivemap; +while (curentry)/* Count entries to prepare a contiguous map block */ + { + entries++; + curentry = curentry->next; + } +if (0 == entries) + { +DBG_PRINTF ("drivemap: No drives remapped, int13h handler not installed" ); +return GRUB_ERR_NONE ;/* No need to install the int13h handler */ + } +else + { +DBG_PRINTF ("drivemap: Installing int13h handler...\n" ); +grub_uint32_t *ivtslot = (grub_uint32_t *)0x0000004c ; + +/* Save the pointer to the old int13h handler */ + grub_drivemap_int13_oldhandler = *ivtslot; +DBG_PRINTF ("Old int13 handler at %04x:%04x\n" , + (grub_drivemap_int13_oldhandler >>16 ) &0x0ffff , + grub_drivemap_int13_oldhandler &0x0ffff ); + +/* Reserve a section of conventional memory as "BIOS memory" for handler: +* BDA offset 0x13 contains the top of such memory */ +grub_uint16_t *bpa_freekb = (grub_uint16_t *)0x00000413 ; +DBG_PRINTF ("Top of conventional memory: %u KiB\n" , *bpa_freekb); +grub_size_t total_size = grub_drivemap_int13_size + + (entries +1 ) *sizeof (int13map_node_t); +grub_uint16_t payload_sizekb = (total_size >>10 ) + + (((total_size %1024 ) ==0 ) ?0 :1 ); +if ((*bpa_freekb - payload_sizekb) <MIN_FREE_MEM_KB ) +return grub_error (GRUB_ERR_OUT_OF_MEMORY ,"refusing to install" +"int13 handler, not enough free memory after" ); +DBG_PRINTF ("Payload is %u b long, reserving %u Kb\n" , total_size, + payload_sizekb); + *bpa_freekb -= payload_sizekb; + +/* Copy int13h handler chunk to reserved area */ +grub_uint8_t *handler_base = (grub_uint8_t *)(*bpa_freekb <<10 ); +DBG_PRINTF ("Copying int13 handler to: %p\n" , handler_base); +grub_memcpy (handler_base, &grub_drivemap_int13_handler_base, + grub_drivemap_int13_size); + +/* Copy the mappings to the reserved area */ + curentry = drivemap; +grub_size_t i; +int13map_node_t *handler_map = (int13map_node_t *)INT13H_TONEWADDR (&grub_drivemap_int13_mapstart); +DBG_PRINTF ("Target map at %p, copying mappings...\n" , handler_map); +for (i =0 ; i < entries && curentry; i++, curentry = curentry->next) + { + handler_map[i].disknum = curentry->newdrive; + handler_map[i].mapto = curentry->redirto; +DBG_PRINTF ("\t#%d: 0x%02x <- 0x%02x\n" , i, handler_map[i].disknum, + handler_map[i].mapto); + } +/* Signal end-of-map */ + handler_map[i].disknum =0 ; + handler_map[i].mapto =0 ; +DBG_PRINTF ("\t#%d: 0x%02x <- 0x%02x (end)\n" , i, handler_map[i].disknum, + handler_map[i].mapto); + +/* Install our function as the int13h handler in the IVT */ +grub_uint32_t ivtentry = ((grub_uint32_t )handler_base) <<12 ;/* Segment address */ + ivtentry |= (grub_uint16_t )INT13H_OFFSET (grub_drivemap_int13_handler); +DBG_PRINTF ("New int13 handler IVT pointer: %04x:%04x\n" , + (ivtentry >>16 ) &0x0ffff , ivtentry &0x0ffff ); + *ivtslot = ivtentry; + +return GRUB_ERR_NONE ; + } +} + +GRUB_MOD_INIT (drivemap) +{ + (void )mod;/* To stop warning. */ +grub_register_command ("drivemap" , grub_cmd_drivemap,GRUB_COMMAND_FLAG_BOTH , +"drivemap -s | -r | (hdX) newdrivenum" , +"Manage the BIOS drive mappings" , options); + insthandler_hook =grub_loader_register_preboot (&install_int13_handler,1 ); +} + +GRUB_MOD_FINI (drivemap) +{ +grub_loader_unregister_preboot (insthandler_hook); + insthandler_hook =0 ; +grub_unregister_command ("drivemap" ); +}
Index: commands/i386/pc/drivemap_int13h.S
===================================================================
RCS file: commands/i386/pc/drivemap_int13h.S
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.119
i386-pc.rmk +8 -1
@@ -152,7 +152,7 @@ vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \ videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod \ ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \ - aout.mod _bsd.mod bsd.mod + aout.mod _bsd.mod bsd.mod drivemap.mod # For biosdisk.mod. biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -319,4 +319,11 @@ bsd_mod_CFLAGS = $(COMMON_CFLAGS) bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For drivemap.mod. +drivemap_mod_SOURCES = commands/i386/pc/drivemap.c \ + commands/i386/pc/drivemap_int13h.S +drivemap_mod_ASFLAGS = $(COMMON_ASFLAGS) +drivemap_mod_CFLAGS = $(COMMON_CFLAGS) +drivemap_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk
Index: include/grub/loader.h
===================================================================
RCS file: /sources/grub/grub2/include/grub/loader.h,v retrieving revision 1.9
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 11 lines · On Fri, 13 Jun 2008 00:43:31 +0200 Javier Martín <[email protected]> wrote:
Hi there, I checked your comment, reread the GNU coding guidelines and fixed some things (stated below the relevant parts of your message). Here is the new version of the patch.
Quoted 2 lines · Pavel Roskin
Please don't add any trailing whitespace. Line in the patch that start with a plus should not end with a space or a tab.
I could not find the line you were referring to. I could only find one line starting with a plus (in
install_int13_handler), but it does not end with a space/tab. However, I did find one trailing tab after a comment, and removed it.
I think Pavel meant the lines added by the patch, i.e., beginning with a plus in the patch, not in the original source. Here's an illustration to help find and visualize the invisible trailing whitespace:
cdb@gamma ~ $ perl -ne '/^\+.* +$/ and y/ \t/_~/ and
print' /home/cdb/drivemap.patch +______else_if_(mapto_==_mapfrom)__/*_Reset_to_default_*/______ +______ +______/*_Save_the_pointer_to_the_old_int13h_handler_*/____
+______
+VARIABLE(grub_drivemap_int13_handler_base)__
+__
+__push_%bp__ +___________(grub_err_t_(*hook)_(void),_int_abort_on_error);_
+__
I highly recommend running
indent --no-tabs
on your source code, as long as it doesn't cause too many spurious changes to pre-existing code. This should take care of most formatting inconsistencies and frees you a little but from concerning yourself with trivial details.
Colin
Quoted 2 lines · Colin D Bennett
I think Pavel meant the lines added by the patch, i.e., beginning with a plus in the patch, not in the original source.
Yes, I meant added lines. There is a lot of code that needs reformatting, but the patch shouldn't add more to that.
Quoted 3 lines
I highly recommend running
indent --no-tabs
Why no tabs? Tabs are heavily used in the code. GNU indent is supposed to default to GNU Coding Standards, and it uses tabs.
Quoted 2 lines
on your source code, as long as it doesn't cause too many spurious changes to pre-existing code.
It will.
Quoted 3 lines
This should take care of most formatting
inconsistencies and frees you a little but from concerning yourself with trivial details.
We can use a script to re-indent everything. Then new patches could be easily run through the same script. Otherwise, it's not going to be simple.
Signature · 2 lines
Regards, Pavel Roskin
Quoted 7 lines · On Thu, 12 Jun 2008 21:00:21 -0400 Pavel Roskin <[email protected]> wrote:
Quoted 3 lines · Colin D Bennett
I highly recommend running
indent --no-tabsWhy no tabs? Tabs are heavily used in the code. GNU indent is supposed to default to GNU Coding Standards, and it uses tabs.
The video module code uses only spaces for indentation. Since I've been working mostly on the video module, so far, I was misled to believe that spaces are the preferred GRUB style.
I checked all the C files in GRUB2 CVS and found that you are right, tabs are heavily used, except in the code that I have been focused
on. The following C files do not contains any tabs:
./kern/i386/efi/init.c./commands/videotest.c./util/powerpc/ieee1275/misc.c./util/i386/pc/misc.c./util/i386/get_disk_name.c./util/ieee1275/get_disk_name.c./util/lvm.c./video/video.c./video/i386/pc/vbeutil.c./video/i386/pc/vbefill.c./video/i386/pc/vbeblit.c./video/i386/pc/vbe.c./video/readers/tga.c./video/bitmap.c./disk/ieee1275/nand.c
Quoted 4 lines
Quoted 2 lines
on your source code, as long as it doesn't cause too many spurious changes to pre-existing code.
It will.
This depends on the file. Running format (with the same choice of tabs+spaces or spaces-only for indentation) on a GRUB source file usually results in minimal changes, in my experience.
Quoted 7 lines
Quoted 3 lines
This should take care of most formatting
inconsistencies and frees you a little but from concerning yourself with trivial details.We can use a script to re-indent everything. Then new patches could be easily run through the same script. Otherwise, it's not going to be simple.
Interesting idea!
Anyway, my point is not to get into the religious war of spaces vs. tabs in indentation, since I am comfortable with both, although I think that mixing spaces and tabs together (using TAB characters as a substitute for 8 spaces) negates the benefits of both approaches. But that's all I'll say on that.
I will indent code that I work on with the same style that it already has, so that my patches are clean and contain, in general, only changes related to actual features that the patch adds.
Colin
Quoted 7 lines · Javier Martín
Quoted 2 lines · Pavel Roskin
Please don't add any trailing whitespace. Line in the patch that start with a plus should not end with a space or a tab.
I could not find the line you were referring to. I could only find one line starting with a plus (in
install_int13_handler), but it does not end with a space/tab. However, I did find one trailing tab after a comment, and removed it.
I mean the lines your patch adds.
Quoted 7 lines
Quoted 6 lines
Please avoid camelCase names, such as bpaMemInKb and retVal. Local variables should generally be short, like "ret" and "
bpa_mem".Some strings are excessively long. While there may be exception of the 80 column limit, I see a 118 character long line that can be trivially wrapped.
Both corrected.
retVal is still there. Please call it "ret", it's the traditional name of the variable to be returned.
Quoted 4 lines
Sorry, I work on a wide screen and, tough I tried to
uphold the 80-char line rule, I'm not used to it. I think there was a switch in gedit to graphically display the 80-char limit, but I can't find it now...
Edit->Preferences->View->Right Margin
Quoted 4 lines
OOC: do you know if there is a syntax highlighting mode for gas/x86 assembly code in gedit? Right now the program thinks the code is C, and the only opcode it highlights is "int". Even a gas-only highlighting (i.e. only the directives, not the opcodes) would be useful.
There are some possibly useful attachments here:
http://bugzilla.gnome.org/show_bug.cgi?id=152267
I hope that others will comment on the patch contents.
Signature · 2 lines
Regards, Pavel Roskin
Quoted 4 lines · Pavel Roskin
I mean the lines your patch adds.
(...)
retVal is still there. Please call it "ret", it's the traditional name of the variable to be returned.
Ok, both of this completely corrected now thanks to Colin's script and the dear "find/replace" functionality that I miss so much when I'm handwriting. Didn't realize that retVal was in the "public" file loader.c. I also made other aesthetic changes based on the output of `indent', though it seems to distort "continuation" lines in C and completely borks assembly code. It is also overzealous wrt the 80-char line rule, breaking many lines with more than 75 chars and seriously unnerving me - particularly with its strange resetting of nested "if" blocks to the first indenting level.
Quoted 7 lines
Quoted 5 lines
Sorry, I work on a wide screen and, tough I tried to
uphold the 80-char line rule, I'm not used to it. I think there was a switch in gedit to graphically display the 80-char limit, but I can't find it now...
Edit->Preferences->View->RightMargin
Thanks! I knew I had seen it before.
Quoted 2 lines
I hope that others will comment on the patch contents.
lol... So do I, because until now we've just been discussing its aesthetic aspects. I think we don't really need the (cleaned) version of the patch to discuss its functionality, so I won't waste bandwith sending updated versions of it if the only change is two tabs and one variable (unless you devs wanted to commit it right away ^^). Well, I'm going to call this a night, since it's 4:26 AM and I have an exam tomorrow... Goodbye!
Hi Javier,
Quoted 4 lines · Javier Martín
Since the last posts in the "reimplementing the legacy map command" thread seemingly died silently, I'm sending the improved version of the patch here. If the death of the thread was intentional, please just tell me (either nicely or rudely) and I'll be off.
It's not intentional. This feature is appreciated! Please do this scare you away...
The problem is that many developers are really busy at the moment (unfortunately). I expect to have more time to review patches in about 2-3 weeks.
--
Marco