grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Drivemap module


From: Marco Gerards
Subject: Re: [PATCH] Drivemap module
Date: Tue, 05 Aug 2008 13:23:11 +0200
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

Hi Javier,

Javier Martín <address@hidden> writes:

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

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


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

Yes, I did notice it immediately.

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

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

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

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

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

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

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

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

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

>> Some comments can be found below.  Can you please fix the code mention
>> in the review and similar code?  I really want the code to be just
>> like any other GRUB code.  Please understand that we have to maintain
>> this in the future.  If everyone would use his own codingstyle, GRUB
>> would become unmaintainable.
>> 
>> > Index: commands/i386/pc/drivemap.c
>> > ===================================================================
>> > --- commands/i386/pc/drivemap.c    (revisión: 0)
>> > +++ commands/i386/pc/drivemap.c    (revisión: 0)
>> > @@ -0,0 +1,417 @@
>> > +/* drivemap.c - command to manage the BIOS drive mappings.  */
>> > +/*
>> > + *  GRUB  --  GRand Unified Bootloader
>> > + *  Copyright (C) 2008  Free Software Foundation, Inc.
>> > + *
>> > + *  GRUB is free software: you can redistribute it and/or modify
>> > + *  it under the terms of the GNU General Public License as published by
>> > + *  the Free Software Foundation, either version 3 of the License, or
>> > + *  (at your option) any later version.
>> > + *
>> > + *  GRUB is distributed in the hope that it will be useful,
>> > + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > + *  GNU General Public License for more details.
>> > + *
>> > + *  You should have received a copy of the GNU General Public License
>> > + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>> > + */
>> > +
>> > +#include <grub/normal.h>
>> > +#include <grub/dl.h>
>> > +#include <grub/mm.h>
>> > +#include <grub/misc.h>
>> > +#include <grub/disk.h>
>> > +#include <grub/loader.h>
>> > +#include <grub/machine/loader.h>
>> > +#include <grub/machine/biosdisk.h>
>> > +
>> > +#define MODNAME "drivemap"
>> > +
>> > +static const struct grub_arg_option options[] = {
>> > +  {"list", 'l', 0, "show the current mappings", 0, 0},
>> > +  {"reset", 'r', 0, "reset all mappings to the default values", 0, 0},
>> > +  {0, 0, 0, 0, 0, 0}
>> > +};
>> > +
>> > +/* Syms/vars/funcs exported from drivemap_int13h.S - start.  */
>> > +
>> > +/* Realmode far ptr = 2 * 16b */
>> > +extern grub_uint32_t grub_drivemap_int13_oldhandler;
>> > +/* Size of the section to be copied */
>> > +extern grub_uint16_t grub_drivemap_int13_size;
>> > +
>> > +/* This type is used for imported assembly labels, takes no storage and 
>> > is only
>> > +   used to take the symbol address with &label.  Do NOT put void* here.  
>> > */
>> > +typedef void grub_symbol_t;
>> > +extern grub_symbol_t grub_drivemap_int13_handler_base;
>> > +extern grub_symbol_t grub_drivemap_int13_mapstart;
>> > +
>> > +void grub_drivemap_int13_handler (void);
>> 
>> The lines above belong in a header file.
> True, but they are used in a single file in the whole project and thus I
> see it pointless to extract an unneeded header, which would become one
> more SVN object to track. However, if you insist I will split the header
> file at once. In particular, I think the grub_symbol_t typedef should go
> into the "standard" GRUB headers somehow (but not in symbol.h, which is
> included from assembly files).

Please do so.

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

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

Please do so anyways.

>> > +static grub_err_t install_int13_handler (void);
>> > +
>> > +/* Puts the specified mapping into the table, replacing an existing 
>> > mapping
>> > +   for newdrive or adding a new one if required.  */
>> > +static grub_err_t
>> > +drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
>> > +
>> 
>> Please do not add a newline here.
> Oops, sorry. I forgot to remove it when moving the comment

:-)

>> > +  drivemap_node_t *mapping = 0;
>> > +  drivemap_node_t *search = drivemap;
>> > +  while (search)
>> > +    {
>> > +      if (search->newdrive == newdrive)
>> > +        {
>> > +          mapping = search;
>> > +          break;
>> > +        }
>> > +      search = search->next;
>> > +    }
>> > +
>> > +  
>> > +  /* Check for pre-existing mappings to modify before creating a new one. 
>> >  */
>> > +  if (mapping)
>> > +    mapping->redirto = redirto;
>> > +  else 
>> > +    {
>> > +      mapping = grub_malloc (sizeof (drivemap_node_t));
>> > +      if (!mapping)
>> > +        return grub_error (GRUB_ERR_OUT_OF_MEMORY,
>> > +                           "cannot allocate map entry, not enough 
>> > memory");
>> > +      mapping->newdrive = newdrive;
>> > +      mapping->redirto = redirto;
>> > +      mapping->next = drivemap;
>> > +      drivemap = mapping;
>> > +    }
>> > +  return GRUB_ERR_NONE;
>> > +}
>> > +
>> > +/* Removes the mapping for newdrive from the table.  If there is no 
>> > mapping,
>> > +   then this function behaves like a no-op on the map.  */
>> > +static void
>> > +drivemap_remove (grub_uint8_t newdrive)
>> > +{
>> > +  drivemap_node_t *mapping = 0;
>> > +  drivemap_node_t *search = drivemap;
>> > +  drivemap_node_t *previous = 0;
>> > +  while (search)
>> > +    {
>> > +      if (search->newdrive == newdrive)
>> > +        {
>> > +          mapping = search;
>> > +          break;
>> > +        }
>> > +      previous = search;
>> > +      search = search->next;
>> > +    }
>> > +  if (mapping) /* Found.  */
>> 
>> You forgot one.
> Corrected. Sorry.
>> 
>> > +    {
>> > +      if (previous)
>> > +        previous->next = mapping->next;
>> > +      else /* Entry was head of list.  */
>> > +        drivemap = mapping->next;
>> > +      grub_free (mapping);
>> > +    }
>> > +}
>> > +
>> > +/* Given a device name, resolves its BIOS disk number and stores it in the
>> > +   passed location, which should only be trusted if ERR_NONE is returned. 
>> >  */
>> > +static grub_err_t
>> > +parse_biosdisk (const char *name, grub_uint8_t *disknum)
>> > +{
>> > +  grub_disk_t disk;
>> > +  if (!name || 0 == *name)
>> > +    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name empty");
>> > +  /* Skip the first ( in (hd0) - disk_open wants just the name.  */
>> > +  if (*name == '(')
>> > +    name++;
>> > +  
>> > +  disk = grub_disk_open (name);
>> > +  if (!disk)
>> > +    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown device \"%s\"", 
>> > name);
>> > +  else
>> > +    {
>> > +      const enum grub_disk_dev_id id = disk->dev->id;
>> > +      /* The following assignment is only sound if the device is indeed a
>> > +         biosdisk.  The caller must check the return value.  */
>> > +      if (disknum)
>> > +        *disknum = disk->id;
>> > +      grub_disk_close (disk);
>> > +      if (GRUB_DISK_DEVICE_BIOSDISK_ID == id)
>> > +        return GRUB_ERR_NONE;
>> > +      else return grub_error (GRUB_ERR_BAD_DEVICE, "%s is not a BIOS 
>> > disk", name);
>> > +    }
>> > +}
>> > +
>> > +/* Given a BIOS disk number, returns its GRUB device name if it exists.
>> > +   For nonexisting BIOS dnums, this function returns ERR_UNKNOWN_DEVICE.  
>> > */
>> 
>> This is GRUB_ERR_UNKNOWN_DEVICE
> I know, I consciously left the GRUB_ part out because 1) it would
> require the line to be split and 2) that prefix is all over the place.
> Corrected, however.
>> 
>> > +static grub_err_t
>> > +revparse_biosdisk(const grub_uint8_t dnum, const char **output)
>> > +{
>> > +  int found = 0;
>> > +  auto int find (const char *name);
>> > +  int find (const char *name)
>> > +  {
>> > +    const grub_disk_t disk = grub_disk_open (name);
>> > +    if (!disk)
>> > +      return 0;
>> > +    else
>> > +      {
>> > +        if (disk->id == dnum && GRUB_DISK_DEVICE_BIOSDISK_ID == 
>> > disk->dev->id)
>> > +          {
>> > +            found = 1;
>> > +            if (output)
>> > +              *output = name;
>> > +          }
>> > +        grub_disk_close (disk);
>> > +        return found;
>> > +      }
>> > +  }
>> > +
>> > +  grub_disk_dev_iterate (find);
>> > +  if (found)
>> > +    return GRUB_ERR_NONE;
>> > +  else return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "BIOS disk %d not 
>> > found", dnum);
>> 
>> Please change this.
> Em... to what? What is the problem? Do you want me to reverse the
> comparison? i.e. if (!found) { return error; } else { return ok; }

The return is on the same line as the else.

>> > +/* Given a GRUB-like device name and a convenient location, stores the 
>> > related
>> > +   BIOS disk number.  Accepts devices like \((f|h)dN\), with 0 <= N < 
>> > 128.  */
>> > +static grub_err_t
>> > +tryparse_diskstring (const char *str, grub_uint8_t *output)
>> > +{
>> > +  if (!str || 0 == *str)
>> > +    goto fail;
>> > +  /* Skip opening paren in order to allow both (hd0) and hd0.  */
>> > +  if (*str == '(')
>> > +    str++;
>> > +  if ((str[0] == 'f' || str[0] == 'h') && str[1] == 'd')
>> > +    {
>> > +      grub_uint8_t bios_num = (str[0] == 'h')? 0x80 : 0x00;
>> > +      grub_errno = GRUB_ERR_NONE;
>> > +      unsigned long drivenum = grub_strtoul (str + 2, 0, 0);
>> > +      if (grub_errno != GRUB_ERR_NONE || drivenum > 127)
>> > +        /* N not a number or out of range */
>> > +        goto fail;
>> 
>> Can you put this between braces, now comment was added.
> Done.
>> 
>> > +      else
>> > +        {
>> > +          bios_num |= drivenum;
>> > +          if (output)
>> > +            *output = bios_num;
>> > +          return GRUB_ERR_NONE;
>> > +        }
>> > +    }
>> > +  else goto fail;
>> 
>> ...
> What's the problem here? The lack of braces? The goto (as used in the
> ext2 code)?

goto is on the same line as the else.

>> > +fail:
>> > +  return grub_error (GRUB_ERR_BAD_ARGUMENT, "device format \"%s\" 
>> > invalid: must"
>> > +                     "be (f|h)dN, with 0 <= N < 128", str);
>> > +}
>> > +
>> > +static grub_err_t
>> > +grub_cmd_drivemap (struct grub_arg_list *state, int argc, char **args)
>> > +{
>> > +  if (state[0].set)
>> > +    {
>> > +      /* Show: list mappings.  */
>> > +      if (!drivemap)
>> > +        grub_printf ("No drives have been remapped");
>> > +      else
>> > +        {
>> > +          grub_printf ("Showing only remapped drives.\n");
>> > +          grub_printf ("Mapped\tGRUB\n");
>> > +          drivemap_node_t *curnode = drivemap;
>> > +          while (curnode)
>> > +            {
>> > +              const char *dname = 0;
>> > +              grub_err_t err = revparse_biosdisk (curnode->redirto, 
>> > &dname);
>> > +              if (err != GRUB_ERR_NONE)
>> > +                return grub_error (err, "invalid mapping: non-existent 
>> > disk"
>> > +                                        "or not managed by the BIOS");
>> > +              grub_printf("0x%02x\t%4s\n", curnode->newdrive, dname);
>> > +              curnode = curnode->next;
>> > +            }
>> > +        }
>> > +    }
>> > +  else if (state[1].set)
>> > +    {
>> > +      /* Reset: just delete all mappings, freeing their memory.  */
>> > +      drivemap_node_t *curnode = drivemap;
>> > +      drivemap_node_t *prevnode = 0;
>> > +      while (curnode)
>> > +        {
>> > +          prevnode = curnode;
>> > +          curnode = curnode->next;
>> > +          grub_free (prevnode);
>> > +        }
>> > +      drivemap = 0;
>> > +    }
>> > +  else
>> > +    {
>> > +      /* Neither flag: put mapping */
>> 
>> ".  */
> Done
>> 
>> > +      grub_uint8_t mapfrom = 0;
>> > +      grub_uint8_t mapto = 0xFF;
>> > +      grub_err_t err;
>> > +      
>> > +      if (argc != 2)
>> > +        return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments 
>> > required");
>> > +
>> > +      err = parse_biosdisk (args[0], &mapfrom);
>> > +      if (err != GRUB_ERR_NONE)
>> > +        return err;
>> > +
>> > +      err = tryparse_diskstring (args[1], &mapto);
>> > +      if (err != GRUB_ERR_NONE) /* Not a disk string. Maybe a raw num 
>> > then?  */
>> 
>> Please move this up.
> Done.
>> 
>> > +        {    
>> > +          grub_errno = GRUB_ERR_NONE;
>> > +          unsigned long num = grub_strtoul (args[1], 0, 0);
>> > +          if (grub_errno != GRUB_ERR_NONE || num > 0xFF)  /* Not a raw 
>> > num or too high.  */
>> > +            return grub_error (grub_errno,
>> > +                              "Target specifier must be of the form (fdN) 
>> > or "
>> > +                              "(hdN), with 0 <= N < 128; or a plain 
>> > dec/hex "
>> > +                              "number between 0 and 255");
>> > +          else mapto = (grub_uint8_t)num;
>> > +        }
>> > +      
>> > +      if (mapto == mapfrom)  /* Reset to default.  */
>> 
>> Same here.
> Done.
>> 
>> > +        {
>> > +          grub_dprintf (MODNAME, "Removing the mapping for %s (%02x)", 
>> > args[0], mapfrom);
>> > +          drivemap_remove (mapfrom);
>> > +        }
>> > +      else  /* Map.  */
>> 
>> Please move the comment inside the braces below.
> Done, and reworded.
>> 
>> > +        {
>> > +          grub_dprintf (MODNAME, "Mapping %s (%02x) to %02x\n", args[0], 
>> > mapfrom, mapto);
>> > +          return drivemap_set ((grub_uint8_t)mapto, mapfrom);
>> > +        }
>> > +    }
>> > +
>> > +  return GRUB_ERR_NONE;
>> > +}
>> > +
>> > +typedef struct __attribute__ ((packed)) int13map_node
>> > +{
>> > +  grub_uint8_t disknum;
>> > +  grub_uint8_t mapto;
>> > +} int13map_node_t;
>> > +
>> > +/* The min amount of mem that must remain free after installing the 
>> > handler.
>> > +   32 KiB is just above 0x7C00-0x7E00, where the bootsector is loaded.  */
>> > +#define MIN_FREE_MEM_KB 32
>> > +#define INT13H_OFFSET(x) ( ((grub_uint8_t*)(x)) - 
>> > ((grub_uint8_t*)&grub_drivemap_int13_handler_base) )
>> > +#define INT13H_REBASE(x) ( (void*) (((grub_uint8_t*)handler_base) + (x)) )
>> > +#define INT13H_TONEWADDR(x) INT13H_REBASE( INT13H_OFFSET( x ) )
>> > +
>> > +/* Int13h handler installer - reserves conventional memory for the 
>> > handler,
>> > +   copies it over and sets the IVT entry for int13h.  
>> > +   This code rests on the assumption that GRUB does not activate any kind 
>> > of
>> > +   memory mapping apart from identity paging, since it accesses realmode
>> > +   structures by their absolute addresses, like the IVT at 0 or the BDA at
>> > +   0x400; and transforms a pmode pointer into a rmode seg:off far ptr.  */
>> > +static grub_err_t
>> > +install_int13_handler (void)
>> > +{
>> > +  grub_size_t entries = 0;
>> > +  drivemap_node_t *curentry = drivemap;
>> > +  while (curentry)  /* Count entries to prepare a contiguous map block.  
>> > */
>> 
>> ...
> Comment moved up.
>> 
>> > +    {
>> > +      entries++;
>> > +      curentry = curentry->next;
>> > +    }
>> > +  if (0 == entries)
>> 
>> I know this is what you prefer, but can you change this nevertheless?
> I refer to my objection near the top of the post.

I know you object, but did you change it?

>> > +      grub_dprintf (MODNAME, "No drives marked as remapped, installation 
>> > of"
>> > +                  "an int13h handler is not required.");
>> > +      return GRUB_ERR_NONE;  /* No need to install the int13h handler.  */
>> > +    }
>> > +  else
>> > +    {
>> > +      grub_dprintf (MODNAME, "Installing int13h handler...\n");
>> > +      grub_uint32_t *ivtslot = (grub_uint32_t*)0x0000004c;
>> > +      
>> > +      /* Save the pointer to the old int13h handler.  */
>> > +      grub_drivemap_int13_oldhandler = *ivtslot;
>> > +      grub_dprintf (MODNAME, "Old int13 handler at %04x:%04x\n",
>> > +                  (grub_drivemap_int13_oldhandler >> 16) & 0x0ffff,
>> > +                  grub_drivemap_int13_oldhandler & 0x0ffff);
>> > +
>> > +      /* Reserve a section of conventional memory as "BIOS memory" for 
>> > handler:
>> > +         BDA offset 0x13 contains the top of such memory.  */
>> > +      grub_uint16_t *bpa_freekb = (grub_uint16_t*)0x00000413;
>> > +      grub_dprintf (MODNAME, "Top of conventional memory: %u KiB\n", 
>> > *bpa_freekb);
>> > +      grub_size_t total_size = grub_drivemap_int13_size
>> > +                            + (entries + 1) * sizeof(int13map_node_t);
>> > +      grub_uint16_t payload_sizekb = (total_size >> 10) +
>> > +                                    (((total_size % 1024) == 0) ? 0 : 1);
>> > +      if ((*bpa_freekb - payload_sizekb) < MIN_FREE_MEM_KB)
>> > +        return grub_error (GRUB_ERR_OUT_OF_MEMORY, "refusing to install"
>> > +                           "int13 handler, not enough free memory after");
>> > +      grub_dprintf (MODNAME, "Payload is %u b long, reserving %u Kb\n",
>> > +                                  total_size, payload_sizekb);
>> > +      *bpa_freekb -= payload_sizekb;
>> > +
>> > +      /* Copy int13h handler chunk to reserved area.  */
>> > +      grub_uint8_t *handler_base = (grub_uint8_t*)(*bpa_freekb << 10);
>> > +      grub_dprintf (MODNAME, "Copying int13 handler to: %p\n", 
>> > handler_base);
>> > +      grub_memcpy (handler_base, &grub_drivemap_int13_handler_base,
>> > +                   grub_drivemap_int13_size);
>> > +
>> > +      /* Copy the mappings to the reserved area.  */
>> > +      curentry = drivemap;
>> > +      grub_size_t i;
>> > +      int13map_node_t *handler_map = (int13map_node_t*)
>> > +                      INT13H_TONEWADDR (&grub_drivemap_int13_mapstart);
>> > +      grub_dprintf (MODNAME, "Target map at %p, copying mappings...\n", 
>> > handler_map);
>> > +      for (i = 0; i < entries && curentry; i++, curentry = curentry->next)
>> > +        {
>> > +          handler_map[i].disknum = curentry->newdrive;
>> > +          handler_map[i].mapto = curentry->redirto;
>> > +          grub_dprintf (MODNAME, "\t#%d: 0x%02x <- 0x%02x\n", i,
>> > +                                          handler_map[i].disknum, 
>> > handler_map[i].mapto);
>> > +        }
>> > +      /* Signal end-of-map.  */
>> > +      handler_map[i].disknum = 0;
>> > +      handler_map[i].mapto = 0;
>> > +      grub_dprintf (MODNAME, "\t#%d: 0x%02x <- 0x%02x (end)\n", i,
>> > +                                  handler_map[i].disknum, 
>> > handler_map[i].mapto);
>> > +
>> > +      /* Install our function as the int13h handler in the IVT.  */
>> > +      grub_uint32_t ivtentry = ((grub_uint32_t)handler_base) << 12; /* 
>> > Segment address.  */
>> > +      ivtentry |= (grub_uint16_t) 
>> > INT13H_OFFSET(grub_drivemap_int13_handler);
>> > +      grub_dprintf (MODNAME, "New int13 handler IVT pointer: %04x:%04x\n",
>> > +                  (ivtentry >> 16) & 0x0ffff, ivtentry & 0x0ffff);
>> > +      *ivtslot = ivtentry;
>> > +      
>> > +      return GRUB_ERR_NONE;
>> > +    }
>> > +}
>> > +
>> > +GRUB_MOD_INIT (drivemap)
>> > +{
>> > +  (void) mod;                     /* Stop warning.  */
>> > +  grub_register_command (MODNAME, grub_cmd_drivemap,
>> > +                         GRUB_COMMAND_FLAG_BOTH,
>> > +                                     MODNAME " -s | -r | (hdX) 
>> > newdrivenum",
>> > +                         "Manage the BIOS drive mappings", options);
>> > +  insthandler_hook = grub_loader_register_preboot 
>> > (&install_int13_handler, 1);
>> > +}
>> > +
>> > +GRUB_MOD_FINI (drivemap)
>> > +{
>> > +  grub_loader_unregister_preboot (insthandler_hook);
>> > +  insthandler_hook = 0;
>> > +  grub_unregister_command (MODNAME);
>> > +}
>> > +
>> > Index: commands/i386/pc/drivemap_int13h.S
>> > ===================================================================
>> > --- commands/i386/pc/drivemap_int13h.S     (revisión: 0)
>> > +++ commands/i386/pc/drivemap_int13h.S     (revisión: 0)
>> > @@ -0,0 +1,118 @@
>> > +/*
>> > + *  GRUB  --  GRand Unified Bootloader
>> > + *  Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 Free 
>> > Software Foundation, Inc.
>> > + *
>> > + *  GRUB is free software: you can redistribute it and/or modify
>> > + *  it under the terms of the GNU General Public License as published by
>> > + *  the Free Software Foundation, either version 3 of the License, or
>> > + *  (at your option) any later version.
>> > + *
>> > + *  GRUB is distributed in the hope that it will be useful,
>> > + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > + *  GNU General Public License for more details.
>> > + *
>> > + *  You should have received a copy of the GNU General Public License
>> > + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>> > + */
>> > +
>> > +
>> > +/*
>> > + * Note: These functions defined in this file may be called from C.
>> > + *       Be careful of that you must not modify some registers. Quote
>> > + *       from gcc-2.95.2/gcc/config/i386/i386.h:
>> > +
>> > +   1 for registers not available across function calls.
>> > +   These must include the FIXED_REGISTERS and also any
>> > +   registers that can be used without being saved.
>> > +   The latter must include the registers where values are returned
>> > +   and the register where structure-value addresses are passed.
>> > +   Aside from that, you can include as many other registers as you like.
>> > +
>> > +  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
>> > +{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
>> > + */
>> > +
>> > +/*
>> > + * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
>> > + *       So the first three arguments are passed in %eax, %edx, and %ecx,
>> > + *       respectively, and if a function has a fixed number of arguments
>> > + *       and the number if greater than three, the function must return
>> > + *       with "ret $N" where N is ((the number of arguments) - 3) * 4.
>> > + */
>> > +
>> > +#include <grub/symbol.h>
>> > +
>> > +#define GRUB_DRIVEMAP_INT13H_OFFSET(x) ((x) - 
>> > grub_drivemap_int13_handler_base)
>> > +
>> > +/* Copy starts here. When deployed, this label must be segment-aligned */
>> > +VARIABLE(grub_drivemap_int13_handler_base)
>> > +
>> > +VARIABLE(grub_drivemap_int13_oldhandler)
>> > +  .word 0xdead, 0xbeef
>> > +/* Drivemap module - INT 13h handler - BIOS HD map */
>> > +/* We need to use relative addressing, and with CS to top it all, since we
>> > +   must make as few changes to the registers as possible.  IP-relative
>> > +   addressing like on amd64 would make life way easier here. */
>> > +.code16
>> > +FUNCTION(grub_drivemap_int13_handler)
>> > +  push %bp
>> > +  mov %sp, %bp
>> > +  push %ax  /* We'll need it later to determine the used BIOS function */
>> > +
>> > +  /* Map the drive number (always in DL?) */
>> > +  push %ax
>> > +  push %bx
>> > +  push %si
>> > +  mov $GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_mapstart), %bx
>> > +  xor %si, %si
>> > +1:movw %cs:(%bx,%si), %ax
>> > +  cmp %ah, %al
>> > +  jz 3f /* DRV=DST => map end - drive not remapped, leave DL as-is */
>> > +  cmp %dl, %al
>> > +  jz 2f /* Found - drive remapped, modify DL */
>> > +  add $2, %si
>> > +  jmp 1b /* Not found, but more remaining, loop  */
>> > +2:mov %ah, %dl
>> > +3:pop %si
>> > +  pop %bx
>> > +  xchgw %ax, -4(%bp) /* Recover the old AX and save the map entry for 
>> > later */
>> > +  
>> > +  push %bp
>> > +  /* Simulate interrupt call: push flags and do a far call in order to set
>> > +     the stack the way the old handler expects it so that its iret works 
>> > */
>> > +  push 6(%bp)
>> > +  movw (%bp), %bp  /* Restore the caller BP (is this needed and/or 
>> > sensible?) */
>> > +  lcall *%cs:GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_oldhandler)
>> > +  pop %bp /* The pushed flags were removed by iret */
>> > +  /* Set the saved flags to what the int13h handler returned */
>> > +  push %ax
>> > +  pushf
>> > +  pop %ax
>> > +  movw %ax, 6(%bp)
>> > +  pop %ax
>> > +
>> > +  /* Reverse map any returned drive number if the data returned includes 
>> > it.  
>> > +     The only func that does this seems to be origAH = 0x08, but many BIOS
>> > +     refs say retDL = # of drives connected.  However, the GRUB Legacy 
>> > code
>> > +     treats this as the _drive number_ and "undoes" the remapping.  Thus,
>> > +     this section has been disabled for testing if it's required */
>> > +#  cmpb $0x08, -1(%bp) /* Caller's AH */
>> > +#  jne 4f
>> > +#  xchgw %ax, -4(%bp) /* Map entry used */
>> > +#  cmp %ah, %al  /* DRV=DST => drive not remapped */
>> > +#  je 4f
>> > +#  mov %ah, %dl  /* Undo remap */
>> > +
>> > +4:mov %bp, %sp
>> > +  pop %bp
>> > +  iret
>> > +/* This label MUST be at the end of the copied block, since the installer 
>> > code
>> > +   reserves additional space for mappings at runtime and copies them over 
>> > it */
>> > +.align 2
>> > +VARIABLE(grub_drivemap_int13_mapstart)
>> > +/* Copy stops here */
>> > +.code32
>> > +VARIABLE(grub_drivemap_int13_size)
>> > +  .word GRUB_DRIVEMAP_INT13H_OFFSET(grub_drivemap_int13_size)
>> > +
>> > Index: conf/i386-pc.rmk
>> > ===================================================================
>> > --- conf/i386-pc.rmk       (revisión: 1766)
>> > +++ conf/i386-pc.rmk       (copia de trabajo)
>> > @@ -158,7 +158,7 @@
>> >    vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
>> >    videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod  \
>> >    ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \
>> > -  aout.mod _bsd.mod bsd.mod
>> > +  aout.mod _bsd.mod bsd.mod drivemap.mod
>> >  
>> >  # For biosdisk.mod.
>> >  biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
>> > @@ -325,4 +325,11 @@
>> >  bsd_mod_CFLAGS = $(COMMON_CFLAGS)
>> >  bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
>> >  
>> > +# For drivemap.mod.
>> > +drivemap_mod_SOURCES = commands/i386/pc/drivemap.c \
>> > +                       commands/i386/pc/drivemap_int13h.S
>> > +drivemap_mod_ASFLAGS = $(COMMON_ASFLAGS)
>> > +drivemap_mod_CFLAGS = $(COMMON_CFLAGS)
>> > +drivemap_mod_LDFLAGS = $(COMMON_LDFLAGS)
>> > +
>> >  include $(srcdir)/conf/common.mk
>> > Index: include/grub/loader.h
>> > ===================================================================
>> > --- include/grub/loader.h  (revisión: 1766)
>> > +++ include/grub/loader.h  (copia de trabajo)
>> > @@ -37,6 +37,22 @@
>> >  /* Unset current loader, if any.  */
>> >  void EXPORT_FUNC(grub_loader_unset) (void);
>> >  
>> > +typedef struct hooklist_node *grub_preboot_hookid;
>> > +
>> > +/* Register a function to be called before the boot hook.  Returns an id 
>> > that
>> > +   can be later used to unregister the preboot (i.e. on module unload).  
>> > If
>> > +   abort_on_error is set, the boot sequence will abort if any of the 
>> > registered
>> > +   functions return anything else than GRUB_ERR_NONE.
>> > +   On error, the return value will compare equal to 0 and the error 
>> > information
>> > +   will be available in errno and errmsg.  However, if the call is 
>> > successful
>> > +   those variables are _not_ modified. */
>> 
>> No need to mention errmsg, it's internal to GRUB.  As for errno (which
>> is grub_errno, actually) it does not need to be mentioned, otherwise
>> we would have to do so everywhere.  Please capitalize HOOK and
>> ABORT_ON_ERROR in the comments above.
> Done. "hook" removed because it referred to the loader module boot
> function.
>> 
>> > +grub_preboot_hookid EXPORT_FUNC(grub_loader_register_preboot)
>> > +           (grub_err_t (*hook) (void), int abort_on_error);
>> > +
>> > +/* Unregister a preboot hook by the id returned by 
>> > loader_register_preboot.
>> > +   This functions becomes a no-op if no such function is registered */
>> > +void EXPORT_FUNC(grub_loader_unregister_preboot) (grub_preboot_hookid id);
>> > +
>> >  /* Call the boot hook in current loader. This may or may not return,
>> >     depending on the setting by grub_loader_set.  */
>> 
>> Nitpick: "loader.  This..."
> Are you a bot? ¬¬ Corrected

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

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

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

>> > +  if (!newentry)
>> > +    {
>> > +      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot alloc a hookinfo 
>> > structure");
>> > +      return 0;
>> > +    }
>> > +  else
>> > +    {
>> > +      newentry->hook = hook;
>> > +      newentry->abort_on_error = abort_on_error;
>> > +      newentry->next = preboot_hooks;
>> > +      preboot_hooks = newentry;
>> > +      return newentry;
>> > +    }
>> > +}
>> > +
>> > +void
>> > +grub_loader_unregister_preboot(grub_preboot_hookid id)
>> 
>> "preboot (grub"
> Corrected on both functions ;)
>> 
>> > +{
>> > +  grub_preboot_hookid entry = 0;
>> > +  grub_preboot_hookid search = preboot_hooks;
>> > +  grub_preboot_hookid previous = 0;
>> > +
>> > +  if (0 == id)
>> > +    return;
>> 
>> ...
> ... xD
>> 
>> > +  while (search)
>> > +    {
>> > +      if (search == id)
>> > +        {
>> > +          entry = search;
>> > +          break;
>> > +        }
>> > +      previous = search;
>> > +      search = search->next;
>> > +    }
>> > +  if (entry) /* Found */
>> 
>> ...
> Comment removed, was unnecessary.
>> 
>> > +    {
>> > +      if (previous)
>> > +        previous->next = entry->next;
>> > +      else preboot_hooks = entry->next; /* Entry was head of list */
>> > +      grub_free (entry);
>> > +    }
>> > +}
>> > +
>> >  grub_err_t
>> >  grub_loader_boot (void)
>> >  {
>> >    if (! grub_loader_loaded)
>> >      return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");
>> > +  
>> > +  grub_preboot_hookid entry = preboot_hooks;
>> 
>> Mixed declarations/code.
> Moved the whole line up.
>> 
>> > +  while (entry)
>> > +    {
>> > +      grub_err_t possible_error = entry->hook();
>> > +      if (possible_error != GRUB_ERR_NONE && entry->abort_on_error)
>> > +        return possible_error;
>> > +      entry = entry->next;
>> > +    }
>> >  
>> >    if (grub_loader_noreturn)
>> >      grub_machine_fini ();





reply via email to

[Prev in Thread] Current Thread [Next in Thread]