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 17:42:41 +0200

El sáb, 09-05-2009 a las 16:04 +0200, Vladimir 'phcoder' Serbinenko
escribió:
> Hello
> 
> 2009/5/9 Javier Martín <address@hidden>
>         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 <address@hidden>

        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

Attachment: drivemap.12.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]