grub-devel
[Top][All Lists]
Advanced

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

Re: status grub2 port of grub-legasy map command


From: Javier Martín
Subject: Re: status grub2 port of grub-legasy map command
Date: Sat, 09 May 2009 15:27:01 +0200

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? 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). In fact, I'm declaring all the labels as "const void" so
no-one tries to overwrite the "master" data instead of the deployed
data.

> +typedef struct drivemap_node 
> +{ 
> +  grub_uint8_t newdrive; 
> +  grub_uint8_t redirto; 
> +  struct drivemap_node *next; 
> +} drivemap_node_t; 
> Here you could reuse Bean's list functions
After examining the API, I think such a change would be too invasive for
a "mature" patch right now. However, once the patch is in and drivemap
is working for everyone, I can work on modifying it to use
<grub/list.h>. The biggest hurdle I see is that there is no way to
automatically maintain "key" uniqueness like the current methods do, so
an iteration over the list would be required.

As a side note/rant, <grub/list.h> desperately needs documentation
comments for other developers to be able to actually use it without
wondering if they're going into the darkness. In particular I need to
know what grub_list_insert does without delving into "list.c" (for
example: "if all tests fail, will the item be inserted last or not at
all?"). This rant also applies to other grub include files.

> +      if (!mapping)
> Put a space after !. I know GCS can be PITA but grub has chosen to
> follow it. Not my decision
Oops... I see it has happened in every (! something) test in the file.
I've checked and definitely `indent' did it... I thought that its
default invocation was supposed to adhere to the GCS by default (I'm
using GNU indent 2.2.10).

> +      else            /* Entry was head of list.  */ 
> +    map_head = mapping->next; 
> put the comment on a separate line 
I removed the comment.

> +  if (!name || *name == 0) 
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name empty");
> This check is unnecessary since your function is static and calling
> function already ensured that name isn't empty
I've checked, and actually the main function only ensures that the
pointer is valid, not that it is nonempty (try for example `drivemap
"" (hd1)'). However, trying to grub_disk_open the empty string will fail
and the error will be caught anyway, so the check has been removed.

> +  /* Skip the first ( in (hd0) - disk_open wants just the name.  */ 
> +  if (*name == '(') 
> +    name++;
> AFAIK you need to remove ')' as well
No you don't, it seems that grub_disk_open chokes on the GRUB device
name opening paren but ignores the closing paren kinda like strtoul
stops parsing on the first non-digit. As there is no API documentation I
can't tell whether this is a precondition checking glitch or an intended
effect, but it sure makes the caller logic way simpler.

> 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) 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).

> +  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.

> +      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.

> +    } 
> +      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. Regarding slicing the listing
functionality, I don't see the advantages.

> +        return grub_error (err, "invalid mapping: non-existent disk" 
> +                   "or not managed by the BIOS");
> If you change error message change error number too. Use either
> return err;
> or 
> return grub_error (GRUB_ERR_..., message);
Changed to "return err;"

> +      map_head = 0; 
> +    } 
> +  else
> Use return rather then else
> +      return GRUB_ERR_NONE; 
> +    } 
> +  else 
> no need for "else"
Function reworked so as to add "return"s and remove "else"s.

> +      for (i = 0; i < entries && curentry; ++i, curentry =
> curentry->next)
> You don't need to test for both conditions since theirequivalencyis
> ensured by preceding code
True. Changed.

> +  if (0 != grub_drivemap_int13_oldhandler)
> Better swap the parts
Changed. See my reasoning above, though.

> +                    MODNAME 
> +                    " -l | -r | [-s] grubdev biosdisk", 
> Here MODNAME doesn't save any space and makes code less readable. Just
> write the command name explicitely 
> +       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 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.

> I'm looking forward for your improved patch
Here it goes.

> Thank you for your effort
Thank _you_ for reviewing it, so many things slip past me (including the
convoluted GCS ._.)

> 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?

-- 
-- Lazy, Oblivious, Recurrent Disaster -- Habbit

Attachment: drivemap.11.patch
Description: Text Data

Attachment: signature.asc
Description: Esto es una parte de mensaje firmado digitalmente


reply via email to

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