El sáb, 09-05-2009 a las 16:04 +0200, Vladimir 'phcoder' Serbinenko escribió: > Hello > > 2009/5/9 Javier Martín <lordhab...@gmail.com> > El sáb, 09-05-2009 a las 11:17 +0200, Vladimir 'phcoder' > Serbinenko > escribió: > > +/* Realmode far ptr (2 * 16b) to the previous INT13h > handler. */ > > +extern grub_uint32_t grub_drivemap_int13_oldhandler; > > I prefer it to be 2 variables because of the way they > interract so > > nobody would do something like (char *) > > grub_drivemap_int13_oldhandler; > > Two variables? I don't understand. You want the CS and IP > parts of the > far pointer separated? > Yes. Especially that in .S you declared it as two words. But it's not > an absolute must I don't see the advantage, particularly taking into account that the current code is very happy to treat it as a black box that is taken from the IVT and not touch it. However, if you insist, I'll change it.
> > Why would anyone try to use it like you said? The > comment explicits that it is a realmode pointer, and as such > it is not > usable as a linear pmode pointer. > > > +/* The type "void" is used for imported assembly labels, > takes no > > storage and > > + serves just to take the address with &label. Do NOT put > void* > > here. */ > > +/* Start of the handler bundle. */ > > +extern void grub_drivemap_int13_handler_base; > > The common practice is to use declarations like > > extern char grub_drivemap_int13_handler_base[]; > > it makes pointer arithmetics easy > > That variable is used only twice in the code: one in a call to > memcpy > and another one inside the macro INT13_OFFSET. It would still > be so even > if it were changed to a char array, with the added risk that a > char > array can be modified with almost nothing to gain (as casts > would still > be needed). > The casts are needed if you declare it as char foo[]; > If someone modifies an array he does it on purpose I guess you were trying to say that the casts are _not_ needed if I declared it as such. What I'm trying to say is that in its only _direct_ use (in memcpy), its "const void" signature is perfectly fine for the callee. All other uses are hidden within a nasty macro "INT13_OFFSET", which would still be used with your proposed change. Thus, nothing is gained, but a way to modify the int13h handler code on memory opens, without requiring a cast. Thus, I think such a change would be for the worse. > > It's not necessary to try to open the disk actually because > for some > > reason biosdisk module may be not loaded (e.g. grub may use > ata) and > > loading it may cause nasty effects (e.g. conflict between > ata and > > biosdisk) > > Same applies for revparse_biosdisk. And if user wants to map > to > > unexistant disk it's ok. So just use tryparse_diskstring > > Hmm... this is a profound design issue: you are arguing that > drivemap > should not care about actual BIOS disks, but just their drive > numbers, > and let you map 0x86 to 0x80 even if you have no (hd6). I do > not agree, > since the main use for GRUB is a bootloader, not a test > platform (which > would be the only reason to perform that kind of mappings > which are sure > to cause havoc). Thus, by default drivemap only lets you > perform > mappings that will work - any stress tester is free to modify > the code. > > > Regarding opening the disk, the biosdisk interface does not > assure in > any way (and again there is almost no API documentation) that > hd0 will > be 0x80, though it's the sensible thing to expect. However, > expecting > sensible things from an unstable API prone to redesigns (like > the > command API, for example) > Mapping something to non-existant drive makes the drive non-existant. > I think it's fair > It's almost guaranteed that numbering of disks won't change AfaIr, most operating systems only implement very basic support for BIOS disks (because they switch to their own drivers as soon as possible). Many, among them Linux iIrc, just probe them sequentially and stop when they find the first non-existant drive, while others probe hd0 thru hd7 and ignore the rest of them. In the first kind of OSes, mapping hd0=hd6 when you have no hd6 will make hd1 disappear along with hd0, which may be very confusing to the user. Also, making a BIOS disk disappear will not banish it from being detected by the OS normal drivers, so the utility of this is pretty much restricted to DOS-like systems. > > > usually leads to nasty surprises later on, so > unless 1) the biosdisk interface so specifies and 2) hdN will > _always_ > refer to biosdisk and not another possible handler, I think > the check is > a Good Thing (tm). > It's not if it may hang when you use ata.mod Well, I don't like to put things like this, but then biosdisk and ata should be fixed to avoid these kind of bugs. > > > > > + if ((str[0] == 'f' || str[0] == 'h') && str[1] == 'd') > > change this to something like > > if (! ((str[0] == 'f' || str[0] == 'h') && str[1] == 'd')) > > return ... > > It makes code nicer by avoiding unnecessary long else > > I think the "else" you were talking about was one line long, > but it was > not needed anyways and so I've removed it. > > > > + grub_errno = GRUB_ERR_NONE; > > No need to set grub_errno explicitely unless you want to > ignore an > > error > > I may have got it wrong, but I think that functions like > strtoul only > _set_ errno if there _has_ been an error, but leave it > unchanged if > there hasn't. As I want to check if the conversion failed, I > need to > have the variable to check in a known pre-state. > If grub_errno is set at the begining of the function then either > scripting or your code has a bug So... I can assume that errno=GRUB_ERR_NONE at the start of my function, right? Ok then, I'll remove that extra line. The check, however, has to remain, since a failed call to strtoul can return either ULONG_MAX (on an overflow) or 0 (if the conversion could not be performed), and 0 is one of our acceptable values. > > > > + unsigned long drivenum = grub_strtoul (str + 2, 0, > 0); > > + if (grub_errno != GRUB_ERR_NONE || drivenum > 127) > > I think it's enough to just check the range > > See above. > > > + { > > + /* N not a number or out of range. */ > > + goto fail; > > Since at fail you have just a return, just put a return here > with > > informative error message > > The function was rearranged so only the first goto is required > now. > +fail: > + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device format \"%s\" " > + "invalid: must be (f|h)dN, with 0 <= N < 128", str); > I don't see a clear benefit of using goto here. Perhaps providing a > separate informative messages for 2 fail cases is a better option After a more thorough analysis, the initial check is not necessary because the caller never passes a null pointer and the second check will fail with the empty string. Removed initial "if", the goto and the label. > > > > > + } > > + else > > else is unnecessary, just continue with the code > > I fear you don't give me enough context to find this one. If > it was > inside tryparse_diskstring, it has been removed. > > > + if (cmd->state[OPTIDX_LIST].set || 0 == argc) > > argc == 0 sounds better. Also moving list function to a > separate > > function is a good idea > > Ok, changed. I'd just like to remark that if someone slipped > and changed > the line to "argc = 0" it would pass unnoticed by the > compiler, while "0 > = argc" would create a compile-time error. > It's what warnings are for This kind of construct: if (argc = 0) is allowed by the standard, even if it seems nonsensical, to allow for things like: if (! (hFile = fopen("my_file.txt")) ) exit(2); Thus most compilers will give no warning even with their maximum warning settings. On the other hand, this: if (0 = argc) is guaranteed to cause a compile-time error. However, the code has already been changed to what you requested. > > Regarding slicing the listing > functionality, I don't see the advantages. > It makes code more readable and avoids long else clause The else clause was removed. > > > + push %bp > > + mov %sp, %bp > > You don't need to change bp. Just use sp. In this case it > makes code > > easier > > That would make it more difficult to address passed parameters > like the > flags at [BP+6] because their location would depend on the > current > status of SP (which might change depending on code above the > relevant > lines). > I checked and can say that removing bp code makes the rest actually > easier. If you don't see how tell me I'll modify it > I think that trying to shave off those 4 or so bytes would > make > the code unnecessarily more complicated, which is usually a > no-no when > assembly is involved. Besides, this prolog sequence and the > corresponding epilog before returning is practically a > standard in x86. > > > + lcall *% > > > cs:GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_oldhandler) > > + pop %bp /* The pushed flags were removed by > iret. */ > > Use rather: > > pushf > > lcall *% > > > cs:GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_oldhandler) > > Why would we want to save our current flags? We want the old > int13 > handler to receive the flags it would have received if the > drivemap > int13 handler were not present, that is, the original caller > flags which > were stored above CS:IP in the stack when the "int $0x13" call > was > issued. The comment means that "iret" pops not just CS:IP but > also the > flags, so we can proceed directly to popping %bp. > > > > Also remember to restore original %dl after the call > > I think there is no need to do so, because BIOS calls modify % > dl anyway. > > Then you can remove %bp and just make a long jump. This way int 13 > handler recieves the original stack intact So what you want is to make control flow return to the caller directly and seamlessly... Let's see if you like the new version of the handler better. The con here is that we lost its "normal-function" structure, so any modification to check anything after the BIOS handler returns would require its complete revamping into a form... just like what I had been using. I can't say that I don't like the elegance of your idea, but in non performance-critical assembly code I usually prefer standard forms to structural beauty. > > > > > Could you also prepare a changelog entry for it? > > Hmm... I don't really know how to write it, given that both > files are > new and the patch does not modify any other GRUB function. Am > I supposed > to also declare the modifications in the rmk files? > (written in mailer so identation is wrong) > > <your contact line> > > <short description> > * conf/i386-pc.rmk (pkglib_MODULES): add drivemap.mod > (drivemap_mod_SOURCES): new variable > (drivemap_mod_CFLAGS): likewise > (drivemap_mod_LDFLAGS): likewise > (drivemap_mod_ASFLAGS): likewise > > * commands/i386/pc/drivemap.c: new file > * commands/i386/pc/drivemap_int13h.S: likewise Ok then, I'm copying your template: 2009-05-09 Javier Martin <lordhab...@gmail.com> New module drivemap: allows remapping of BIOS disks * conf/i386-pc.rmk (pkglib_MODULES): add drivemap.mod (drivemap_mod_SOURCES): new variable (drivemap_mod_CFLAGS): likewise (drivemap_mod_LDFLAGS): likewise (drivemap_mod_ASFLAGS): likewise * commands/i386/pc/drivemap.c: new file * commands/i386/pc/drivemap_int13h.S: likewise OK, I have a good feeling about this version of the patch. Most importantly, it still works!! Cheers! -- -- Lazy, Oblivious, Recurrent Disaster -- Habbit
Index: conf/i386-pc.rmk =================================================================== --- conf/i386-pc.rmk (revision 2201) +++ conf/i386-pc.rmk (working copy) @@ -185,8 +185,15 @@ aout.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \ datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \ usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod \ - efiemu.mod mmap.mod acpi.mod + efiemu.mod mmap.mod acpi.mod drivemap.mod +# 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) + # For efiemu.mod. efiemu_mod_SOURCES = efiemu/main.c efiemu/i386/loadcore32.c \ efiemu/i386/loadcore64.c efiemu/i386/pc/cfgtables.c \ Index: commands/i386/pc/drivemap.c =================================================================== --- commands/i386/pc/drivemap.c (revision 0) +++ commands/i386/pc/drivemap.c (revision 0) @@ -0,0 +1,443 @@ +/* drivemap.c - command to manage the BIOS drive mappings. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008, 2009 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/extcmd.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/disk.h> +#include <grub/machine/biosdisk.h> +#include <grub/loader.h> +#include <grub/machine/memory.h> + + +#define MODNAME "drivemap" + +static grub_extcmd_t cmd_reghandle; + +/* Remember to update enum opt_idxs accordingly. */ +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} +}; + +/* Remember to update options[] accordingly. */ +enum opt_idxs +{ + OPTIDX_LIST = 0, + OPTIDX_RESET, + OPTIDX_SWAP, +}; + +/* Realmode far ptr (2 * 16b) to the previous INT13h handler. */ +extern grub_uint32_t grub_drivemap_int13_oldhandler; + +/* The type "void" is used for imported assembly labels, takes no storage and + serves just to take the address with &label. Do NOT put void* here. */ +/* Start of the handler bundle. */ +extern const void grub_drivemap_int13_handler_base; +/* Start of the drive mappings area (space reserved at runtime). */ +extern const void 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 const void grub_drivemap_int13_handler; + +typedef struct drivemap_node +{ + struct drivemap_node *next; + grub_uint8_t newdrive; + grub_uint8_t redirto; +} drivemap_node_t; + +static drivemap_node_t *map_head; +static void *insthandler_hook; +static int handlermem_hnd; +static grub_err_t install_int13_handler (int noret __attribute__ ((unused))); +static grub_err_t uninstall_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 = map_head; + 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 = map_head; + map_head = 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 = map_head; + 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 + map_head = 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; + /* 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); + 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_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS disk", name); + return GRUB_ERR_NONE; +} + +/* Given a BIOS disk number, returns its GRUB device name if it exists. + If the call succeeds, the resulting device string must be freed. + For nonexisting BIOS disk numbers, this function returns + GRUB_ERR_UNKNOWN_DEVICE. */ +static grub_err_t +revparse_biosdisk (const grub_uint8_t dnum, const char **output) +{ + int found = 0; + auto int find (const char *name); + int find (const char *name) + { + const grub_disk_t disk = grub_disk_open (name); + if (! disk) + return 0; + if (disk->id == dnum && disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID) + { + found = 1; + if (output) + *output = grub_strdup (name); + } + grub_disk_close (disk); + return found; + } + + grub_disk_dev_iterate (find); + if (found) + return GRUB_ERR_NONE; + 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) +{ + /* 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; + unsigned long drivenum = grub_strtoul (str + 2, 0, 0); + if (grub_errno == GRUB_ERR_NONE && drivenum < 128) + { + bios_num |= drivenum; + if (output) + *output = bios_num; + return GRUB_ERR_NONE; + } + } + 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_extcmd *cmd, int argc, char **args) +{ + if (cmd->state[OPTIDX_LIST].set || argc == 0) + { + /* Show: list mappings. */ + if (! map_head) + { + grub_printf ("No drives have been remapped"); + return GRUB_ERR_NONE; + } + grub_printf ("Showing only remapped drives.\n"); + grub_printf ("BIOS disk #num ----> GRUB device\n"); + drivemap_node_t *curnode = map_head; + while (curnode) + { + const char *dname = 0; + grub_err_t err = revparse_biosdisk (curnode->redirto, &dname); + if (err != GRUB_ERR_NONE) + return err; + grub_printf ("%cD #%-3u (0x%02x) %s\n", + (curnode->newdrive & 0x80) ? 'H' : 'F', + curnode->newdrive & 0x7F, curnode->newdrive, + dname); + curnode = curnode->next; + grub_free ((char *) dname); + } + return GRUB_ERR_NONE; + } + else if (cmd->state[OPTIDX_RESET].set) + { + /* Reset: just delete all mappings, freeing their memory. */ + drivemap_node_t *curnode = map_head; + drivemap_node_t *prevnode = 0; + while (curnode) + { + prevnode = curnode; + curnode = curnode->next; + grub_free (prevnode); + } + map_head = 0; + return GRUB_ERR_NONE; + } + + /* 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 (cmd->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); + return GRUB_ERR_NONE; + } + /* Set the mapping for the disk (overwrites any existing mapping). */ + grub_dprintf (MODNAME, "%s %s (%02x) = %s (%02x)\n", + cmd->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 (cmd->state[OPTIDX_SWAP].set && err == GRUB_ERR_NONE) + err = drivemap_set (mapfrom, mapto); + return err; +} + +typedef struct __attribute__ ((packed)) int13map_node +{ + grub_uint8_t disknum; + grub_uint8_t mapto; +} int13map_node_t; + +#define INT13H_OFFSET(x) ( ((grub_uint8_t*)(x)) - ((grub_uint8_t*)&grub_drivemap_int13_handler_base) ) +#define INT13H_REBASE(x) ( (void*) (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; + and transforms a pmode pointer into a rmode seg:off far ptr. */ +static grub_err_t +install_int13_handler (int noret __attribute__ ((unused))) +{ + grub_size_t entries = 0; + drivemap_node_t *curentry = map_head; + + /* 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 = UINT_TO_PTR (0x0000004c); + /* Size of the full int13 handler "bundle", including code and map. */ + grub_uint64_t total_size; + /* Base address of the space reserved for the handler bundle. */ + grub_uint8_t *handler_base = 0; + /* 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); + + /* Find a rmode-segment-aligned zone in conventional memory big + enough to hold the handler and its data. */ + total_size = INT13H_OFFSET(&grub_drivemap_int13_mapstart) + + (entries + 1) * sizeof (int13map_node_t); + grub_dprintf (MODNAME, "Payload is %llu bytes long\n", total_size); + handler_base = grub_mmap_malign_and_register (16, total_size, + &handlermem_hnd, + GRUB_MACHINE_MEMORY_RESERVED, + GRUB_MMAP_MALLOC_LOW); + if (! handler_base) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not reserve " + "memory for the int13h handler"); + + /* Copy int13h handler bundle to reserved area. */ + grub_dprintf (MODNAME, "Reserved memory at %p, copying handler...\n", + handler_base); + grub_memcpy (handler_base, &grub_drivemap_int13_handler_base, + INT13H_OFFSET (&grub_drivemap_int13_mapstart)); + + /* Copy the mappings to the reserved area. */ + curentry = map_head; + grub_size_t i; + handler_map = INT13H_TONEWADDR (&grub_drivemap_int13_mapstart); + grub_dprintf (MODNAME, "Target map at %p, copying mappings...\n", + handler_map); + for (i = 0; i < entries; ++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: 0x00 <- 0x00 (end)\n", i); + + /* 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; + } +} + +static grub_err_t +uninstall_int13_handler (void) +{ + grub_uint32_t *ivtslot = UINT_TO_PTR (0x0000004c); + + if (grub_drivemap_int13_oldhandler != 0) + return GRUB_ERR_NONE; + + *ivtslot = grub_drivemap_int13_oldhandler; + grub_mmap_free_and_unregister (handlermem_hnd); + grub_drivemap_int13_oldhandler = 0; + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT (drivemap) +{ + cmd_reghandle = grub_register_extcmd (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_hook (&install_int13_handler, + &uninstall_int13_handler, + GRUB_LOADER_PREBOOT_HOOK_PRIO_NORMAL); +} + +GRUB_MOD_FINI (drivemap) +{ + grub_loader_unregister_preboot_hook (insthandler_hook); + insthandler_hook = 0; + grub_unregister_extcmd (cmd_reghandle); +} Index: commands/i386/pc/drivemap_int13h.S =================================================================== --- commands/i386/pc/drivemap_int13h.S (revision 0) +++ commands/i386/pc/drivemap_int13h.S (revision 0) @@ -0,0 +1,70 @@ +/* drivemap_int13h.S - interrupt handler for the BIOS drive remapper */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008, 2009 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/symbol.h> + +#define INT13H_OFFSET(x) ((x) - grub_drivemap_int13_handler_base) + +.code16 + +/* 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 0x0, 0x0 + +/* Actual int13 handler. We use relative addressing with CS in order to be as + unintrusive as possible with registers and the stack. */ +FUNCTION(grub_drivemap_int13_handler) + /* Map the drive number (always in DL). */ + push %ax + push %bx + push %si + movw $INT13H_OFFSET(grub_drivemap_int13_mapstart), %bx + xorw %si, %si + +more_remaining: + movw %cs:(%bx,%si), %ax + cmpb %ah, %al + jz not_found /* DRV=DST => map end - drive not remapped, keep DL. */ + cmpb %dl, %al + jz found /* Found - drive remapped, modify DL. */ + addw $2, %si + jmp more_remaining /* Not found, but more remaining, loop. */ + +found: + movb %ah, %dl + +not_found: + pop %si + pop %bx + pop %ax + + /* Upon arrival to this point the stack must be exactly like at entry. + This long jump will transfer the caller's stack to the old INT13 + handler, thus making it return directly to the original caller. */ + ljmp *%cs:INT13H_OFFSET(grub_drivemap_int13_oldhandler) + + +/* 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)
signature.asc
Description: Esto es una parte de mensaje firmado digitalmente
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel