diff mbox

[v3,14/16] x86/boot: implement early command line parser in C

Message ID 1460723596-13261-15-git-send-email-daniel.kiper@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Kiper April 15, 2016, 12:33 p.m. UTC
Current early command line parser implementation in assembler
is very difficult to change to relocatable stuff using segment
registers. This requires a lot of changes in very weird and
fragile code. So, reimplement this functionality in C. This
way code will be relocatable out of the box (without playing
with segment registers) and much easier to maintain.

Suggested-by: Andrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: Daniel Kiper <daniel.kiper@oracle.com>
---
v3 - suggestions/fixes:
   - optimize some code
     (suggested by Jan Beulich),
   - put VESA data into early_boot_opts_t members
     (suggested by Jan Beulich),
   - rename some functions and variables
     (suggested by Jan Beulich),
   - move around video.h include in xen/arch/x86/boot/trampoline.S
     (suggested by Jan Beulich),
   - fix coding style
     (suggested by Jan Beulich),
   - fix build with older GCC
     (suggested by Konrad Rzeszutek Wilk),
   - remove redundant comments
     (suggested by Jan Beulich),
   - add some comments
   - improve commit message
     (suggested by Jan Beulich).
---
 .gitignore                     |    5 +-
 xen/arch/x86/Makefile          |    2 +-
 xen/arch/x86/boot/Makefile     |    7 +-
 xen/arch/x86/boot/build32.lds  |    1 +
 xen/arch/x86/boot/build32.mk   |    4 +-
 xen/arch/x86/boot/cmdline.S    |  367 ----------------------------------------
 xen/arch/x86/boot/cmdline.c    |  357 ++++++++++++++++++++++++++++++++++++++
 xen/arch/x86/boot/edd.S        |    3 -
 xen/arch/x86/boot/head.S       |   17 ++
 xen/arch/x86/boot/trampoline.S |   12 ++
 xen/arch/x86/boot/video.S      |    6 -
 11 files changed, 400 insertions(+), 381 deletions(-)
 delete mode 100644 xen/arch/x86/boot/cmdline.S
 create mode 100644 xen/arch/x86/boot/cmdline.c

Comments

Jan Beulich May 25, 2016, 10:33 a.m. UTC | #1
>>> On 15.04.16 at 14:33, <daniel.kiper@oracle.com> wrote:
> --- a/xen/arch/x86/boot/build32.lds
> +++ b/xen/arch/x86/boot/build32.lds
> @@ -25,6 +25,7 @@ SECTIONS
>          *(.text)
>          *(.text.*)
>          *(.rodata)
> +        *(.rodata.*)
>    }

Interesting - didn't you say you don't want this for now?

> --- /dev/null
> +++ b/xen/arch/x86/boot/cmdline.c
> @@ -0,0 +1,357 @@
> +/*
> + * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
> + *      Daniel Kiper <daniel.kiper@oracle.com>
> + *
> + * This program 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 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * strlen(), strncmp(), strspn() and strcspn() were copied from
> + * Linux kernel source (linux/lib/string.c).

Any reason you can't just #include ".../common/string.c" here?

> +/*
> + * Space and TAB are obvious delimiters. However, I am
> + * adding "\n" and "\r" here too. Just in case when
> + * crazy bootloader/user put them somewhere.
> + */
> +#define DELIM_CHARS		" \n\r\t"
> +#define DELIM_CHARS_COMMA	DELIM_CHARS ","

static const char[] variables (or really just one, with the comma put
first and the non-comma variant indexing into that variable by 1)?

> +#define __packed	__attribute__((__packed__))

No way to include compiler.h here?

> +/*
> + * Compiler is not able to optimize regular strlen()
> + * if argument is well known string during build.
> + * Hence, introduce optimized strlen_opt().
> + */
> +#define strlen_opt(s) (sizeof(s) - 1)

Do we really care in this code?

> +/* Keep in sync with trampoline.S:early_boot_opts label! */
> +typedef struct __packed {
> +    u8 skip_realmode;
> +    u8 opt_edd;
> +    u8 opt_edid;
> +    u16 boot_vid_mode;
> +    u16 vesa_width;
> +    u16 vesa_height;
> +    u16 vesa_depth;
> +} early_boot_opts_t;

This "keeping in sync" should be automated in some way, e.g. via
a new header and suitable macroization.

> +static int strtoi(const char *s, const char *stop, const char **next)
> +{
> +    int base = 10, i, ores = 0, res = 0;

You don't even handle a '-' on the numbers here, so all the variables
and the function return type should be unsigned int afaict. And the
function name perhaps be strtoui().

> +    if ( *s == '0' )
> +      base = (tolower(*++s) == 'x') ? (++s, 16) : 8;
> +
> +    for ( ; *s != '\0'; ++s )
> +    {
> +        for ( i = 0; stop && stop[i] != '\0'; ++i )
> +            if ( *s == stop[i] )
> +                goto out;

strchr()?

> +        if ( *s < '0' || (*s > '7' && base == 8) )
> +        {
> +            res = -1;
> +            goto out;
> +        }
> +
> +        if ( *s > '9' && (base != 16 || tolower(*s) < 'a' || tolower(*s) > 'f') )
> +        {
> +            res = -1;
> +            goto out;
> +        }
> +
> +        res *= base;
> +        res += (tolower(*s) >= 'a') ? (tolower(*s) - 'a' + 10) : (*s - '0');

With the four instances, how about latching tolower(*s) into a local
variable?

> +static u8 skip_realmode(const char *cmdline)
> +{
> +    return !!find_opt(cmdline, "no-real-mode", 0) || !!find_opt(cmdline, "tboot=", 1);

The || makes the two !! pointless.

Also please settle on which type you want to use for boolean
(find_opt()'s last parameter is "int", yet here you use "u8"), and
perhaps make yourself a bool_t.

> +static void vga_parse(const char *cmdline, early_boot_opts_t *ebo)
> +{
> +    const char *c;
> +    int tmp;
> +
> +    c = find_opt(cmdline, "vga=", 1);
> +
> +    if ( !c )
> +        return;
> +
> +    ebo->boot_vid_mode = ASK_VGA;
> +
> +    if ( !strmaxcmp(c, "current", DELIM_CHARS_COMMA) )
> +        ebo->boot_vid_mode = VIDEO_CURRENT_MODE;
> +    else if ( !strsubcmp(c, "text-80x") )
> +    {
> +        c += strlen_opt("text-80x");
> +        ebo->boot_vid_mode = rows2vmode(strtoi(c, DELIM_CHARS_COMMA, NULL));
> +    }
> +    else if ( !strsubcmp(c, "gfx-") )
> +    {
> +        tmp = strtoi(c + strlen_opt("gfx-"), "x", &c);
> +
> +        if ( tmp < 0 || tmp > U16_MAX )
> +            return;
> +
> +        ebo->vesa_width = tmp;
> +
> +        /*
> +         * Increment c outside of strtoi() because otherwise some
> +         * compiler may complain with following message:
> +         * warning: operation on ‘c’ may be undefined.
> +         */
> +        ++c;
> +        tmp = strtoi(c, "x", &c);

The comment is pointless - the operation is firmly undefined if you
put it in the strtoi() invocation.

> +        if ( tmp < 0 || tmp > U16_MAX )
> +            return;
> +
> +        ebo->vesa_height = tmp;
> +
> +        tmp = strtoi(++c, DELIM_CHARS_COMMA, NULL);
> +
> +        if ( tmp < 0 || tmp > U16_MAX )
> +            return;
> +
> +        ebo->vesa_depth = tmp;
> +
> +        ebo->boot_vid_mode = VIDEO_VESA_BY_SIZE;

I realize this is reflecting original behavior, but now that you do it in
C, please leverage that fact and defer storing width and height until
you know the entire option was good.

> --- a/xen/arch/x86/boot/head.S
> +++ b/xen/arch/x86/boot/head.S
> @@ -436,8 +436,24 @@ trampoline_setup:
>          cmp     $sym_phys(__trampoline_seg_stop),%edi
>          jb      1b
>  
> +        /* Do not parse command line on EFI platform here. */
> +        cmpb    $1,sym_phys(skip_realmode)
> +        je      1f

Doesn't this belong in an earlier patch? It certainly doesn't get
moved here from cmdline.S.

> +        /* Bail if there is no command line to parse. */
> +        mov     sym_phys(multiboot_ptr),%ebx
> +        testl   $MBI_CMDLINE,MB_flags(%ebx)
> +        jz      1f
> +
> +        cmpl    $0,MB_cmdline(%ebx)
> +        jz      1f

Any reason not to leave at least this check to the C code?

> +        pushl   $sym_phys(early_boot_opts)
> +        pushl   MB_cmdline(%ebx)
>          call    cmdline_parse_early
> +        add     $8,%esp             /* Remove cmdline_parse_early() args from stack. */

I don't think such a comment is really useful (seems like I overlooked
a similar one in an earlier patch, on the reloc() invocation).

Jan
Daniel Kiper May 25, 2016, 9:36 p.m. UTC | #2
On Wed, May 25, 2016 at 04:33:54AM -0600, Jan Beulich wrote:
> >>> On 15.04.16 at 14:33, <daniel.kiper@oracle.com> wrote:
> > --- a/xen/arch/x86/boot/build32.lds
> > +++ b/xen/arch/x86/boot/build32.lds
> > @@ -25,6 +25,7 @@ SECTIONS
> >          *(.text)
> >          *(.text.*)
> >          *(.rodata)
> > +        *(.rodata.*)
> >    }
>
> Interesting - didn't you say you don't want this for now?

Yep, however, I also said that we should add "*(.rodata.*)"
if it is needed. And it is needed here.

> > --- /dev/null
> > +++ b/xen/arch/x86/boot/cmdline.c
> > @@ -0,0 +1,357 @@
> > +/*
> > + * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
> > + *      Daniel Kiper <daniel.kiper@oracle.com>
> > + *
> > + * This program 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 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * strlen(), strncmp(), strspn() and strcspn() were copied from
> > + * Linux kernel source (linux/lib/string.c).
>
> Any reason you can't just #include ".../common/string.c" here?

I am confused. Sometimes you request to reduce number of such strange
includes and sometimes you ask me to do something contrary? So, what
is the rule behind these requests?

Additionally, there is a chance that final xen ELF file will be
unnecessary larger because it will contain unused functions. Wait,
maybe there is a linker option which allows us to remove unreferenced
objects from final output.

> > +/*
> > + * Space and TAB are obvious delimiters. However, I am
> > + * adding "\n" and "\r" here too. Just in case when
> > + * crazy bootloader/user put them somewhere.
> > + */
> > +#define DELIM_CHARS		" \n\r\t"
> > +#define DELIM_CHARS_COMMA	DELIM_CHARS ","
>
> static const char[] variables (or really just one, with the comma put
> first and the non-comma variant indexing into that variable by 1)?

OK.

> > +#define __packed	__attribute__((__packed__))
>
> No way to include compiler.h here?

Ditto.

> > +/*
> > + * Compiler is not able to optimize regular strlen()
> > + * if argument is well known string during build.
> > + * Hence, introduce optimized strlen_opt().
> > + */
> > +#define strlen_opt(s) (sizeof(s) - 1)
>
> Do we really care in this code?

Not to strongly but why not?

> > +/* Keep in sync with trampoline.S:early_boot_opts label! */
> > +typedef struct __packed {
> > +    u8 skip_realmode;
> > +    u8 opt_edd;
> > +    u8 opt_edid;
> > +    u16 boot_vid_mode;
> > +    u16 vesa_width;
> > +    u16 vesa_height;
> > +    u16 vesa_depth;
> > +} early_boot_opts_t;
>
> This "keeping in sync" should be automated in some way, e.g. via
> a new header and suitable macroization.

OK.

> > +static int strtoi(const char *s, const char *stop, const char **next)
> > +{
> > +    int base = 10, i, ores = 0, res = 0;
>
> You don't even handle a '-' on the numbers here, so all the variables

Yep, however, ...

> and the function return type should be unsigned int afaict. And the
> function name perhaps be strtoui().

... we return -1 in case of error.

> > +    if ( *s == '0' )
> > +      base = (tolower(*++s) == 'x') ? (++s, 16) : 8;
> > +
> > +    for ( ; *s != '\0'; ++s )
> > +    {
> > +        for ( i = 0; stop && stop[i] != '\0'; ++i )
> > +            if ( *s == stop[i] )
> > +                goto out;
>
> strchr()?

Could be.

> > +        if ( *s < '0' || (*s > '7' && base == 8) )
> > +        {
> > +            res = -1;
> > +            goto out;
> > +        }
> > +
> > +        if ( *s > '9' && (base != 16 || tolower(*s) < 'a' || tolower(*s) > 'f') )
> > +        {
> > +            res = -1;
> > +            goto out;
> > +        }
> > +
> > +        res *= base;
> > +        res += (tolower(*s) >= 'a') ? (tolower(*s) - 'a' + 10) : (*s - '0');
>
> With the four instances, how about latching tolower(*s) into a local
> variable?

Make sense.

> > +static u8 skip_realmode(const char *cmdline)
> > +{
> > +    return !!find_opt(cmdline, "no-real-mode", 0) || !!find_opt(cmdline, "tboot=", 1);
>
> The || makes the two !! pointless.
>
> Also please settle on which type you want to use for boolean
> (find_opt()'s last parameter is "int", yet here you use "u8"), and

Could be u8.

> perhaps make yourself a bool_t.

I do not think it make sense here.

> > +static void vga_parse(const char *cmdline, early_boot_opts_t *ebo)
> > +{
> > +    const char *c;
> > +    int tmp;
> > +
> > +    c = find_opt(cmdline, "vga=", 1);
> > +
> > +    if ( !c )
> > +        return;
> > +
> > +    ebo->boot_vid_mode = ASK_VGA;
> > +
> > +    if ( !strmaxcmp(c, "current", DELIM_CHARS_COMMA) )
> > +        ebo->boot_vid_mode = VIDEO_CURRENT_MODE;
> > +    else if ( !strsubcmp(c, "text-80x") )
> > +    {
> > +        c += strlen_opt("text-80x");
> > +        ebo->boot_vid_mode = rows2vmode(strtoi(c, DELIM_CHARS_COMMA, NULL));
> > +    }
> > +    else if ( !strsubcmp(c, "gfx-") )
> > +    {
> > +        tmp = strtoi(c + strlen_opt("gfx-"), "x", &c);
> > +
> > +        if ( tmp < 0 || tmp > U16_MAX )
> > +            return;
> > +
> > +        ebo->vesa_width = tmp;
> > +
> > +        /*
> > +         * Increment c outside of strtoi() because otherwise some
> > +         * compiler may complain with following message:
> > +         * warning: operation on ‘c’ may be undefined.
> > +         */
> > +        ++c;
> > +        tmp = strtoi(c, "x", &c);
>
> The comment is pointless - the operation is firmly undefined if you
> put it in the strtoi() invocation.

In terms of C spec you are right. However, it is quite surprising that older
GCC complains and newer ones do not. Should not we investigate this?

> > +        if ( tmp < 0 || tmp > U16_MAX )
> > +            return;
> > +
> > +        ebo->vesa_height = tmp;
> > +
> > +        tmp = strtoi(++c, DELIM_CHARS_COMMA, NULL);
> > +
> > +        if ( tmp < 0 || tmp > U16_MAX )
> > +            return;
> > +
> > +        ebo->vesa_depth = tmp;
> > +
> > +        ebo->boot_vid_mode = VIDEO_VESA_BY_SIZE;
>
> I realize this is reflecting original behavior, but now that you do it in
> C, please leverage that fact and defer storing width and height until
> you know the entire option was good.

OK.

> > --- a/xen/arch/x86/boot/head.S
> > +++ b/xen/arch/x86/boot/head.S
> > @@ -436,8 +436,24 @@ trampoline_setup:
> >          cmp     $sym_phys(__trampoline_seg_stop),%edi
> >          jb      1b
> >
> > +        /* Do not parse command line on EFI platform here. */
> > +        cmpb    $1,sym_phys(skip_realmode)
> > +        je      1f
>
> Doesn't this belong in an earlier patch? It certainly doesn't get
> moved here from cmdline.S.

Potentially.

> > +        /* Bail if there is no command line to parse. */
> > +        mov     sym_phys(multiboot_ptr),%ebx
> > +        testl   $MBI_CMDLINE,MB_flags(%ebx)
> > +        jz      1f
> > +
> > +        cmpl    $0,MB_cmdline(%ebx)
> > +        jz      1f
>
> Any reason not to leave at least this check to the C code?

OK.

> > +        pushl   $sym_phys(early_boot_opts)
> > +        pushl   MB_cmdline(%ebx)
> >          call    cmdline_parse_early
> > +        add     $8,%esp             /* Remove cmdline_parse_early() args from stack. */
>
> I don't think such a comment is really useful (seems like I overlooked
> a similar one in an earlier patch, on the reloc() invocation).

This thing is quite obvious but I do not think that this comment hurts.

Daniel
Jan Beulich May 27, 2016, 9:33 a.m. UTC | #3
>>> On 25.05.16 at 23:36, <daniel.kiper@oracle.com> wrote:
> On Wed, May 25, 2016 at 04:33:54AM -0600, Jan Beulich wrote:
>> >>> On 15.04.16 at 14:33, <daniel.kiper@oracle.com> wrote:
>> > --- /dev/null
>> > +++ b/xen/arch/x86/boot/cmdline.c
>> > @@ -0,0 +1,357 @@
>> > +/*
>> > + * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
>> > + *      Daniel Kiper <daniel.kiper@oracle.com>
>> > + *
>> > + * This program 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 2 of the License, or
>> > + * (at your option) any later version.
>> > + *
>> > + * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
>> > + *
>> > + * strlen(), strncmp(), strspn() and strcspn() were copied from
>> > + * Linux kernel source (linux/lib/string.c).
>>
>> Any reason you can't just #include ".../common/string.c" here?
> 
> I am confused. Sometimes you request to reduce number of such strange
> includes and sometimes you ask me to do something contrary? So, what
> is the rule behind these requests?

The rule is "whatever fits best for the current purpose". Such
"strange" includes should be avoided if their purpose can be
fulfilled by other means. Them being avoided at the price of
quite a bit of code duplication, otoh, doesn't seem well advised
to me.

>> > +/*
>> > + * Compiler is not able to optimize regular strlen()
>> > + * if argument is well known string during build.
>> > + * Hence, introduce optimized strlen_opt().
>> > + */
>> > +#define strlen_opt(s) (sizeof(s) - 1)
>>
>> Do we really care in this code?
> 
> Not to strongly but why not?

Keep things as readable as possible. In fact I wouldn't mind hard
coded literal numbers for the string lengths, if they sit right next
to the respective string literal.

>> > +static int strtoi(const char *s, const char *stop, const char **next)
>> > +{
>> > +    int base = 10, i, ores = 0, res = 0;
>>
>> You don't even handle a '-' on the numbers here, so all the variables
> 
> Yep, however, ...
> 
>> and the function return type should be unsigned int afaict. And the
>> function name perhaps be strtoui().
> 
> ... we return -1 in case of error.

Which - having looked at some of the callers - could easily be
UINT_MAX as it seems.

>> > +static u8 skip_realmode(const char *cmdline)
>> > +{
>> > +    return !!find_opt(cmdline, "no-real-mode", 0) || !!find_opt(cmdline, 
> "tboot=", 1);
>>
>> The || makes the two !! pointless.
>>
>> Also please settle on which type you want to use for boolean
>> (find_opt()'s last parameter is "int", yet here you use "u8"), and
> 
> Could be u8.
> 
>> perhaps make yourself a bool_t.
> 
> I do not think it make sense here.

I think it makes as much or as little sense as having NULL available.

>> > +        /*
>> > +         * Increment c outside of strtoi() because otherwise some
>> > +         * compiler may complain with following message:
>> > +         * warning: operation on ‘c’ may be undefined.
>> > +         */
>> > +        ++c;
>> > +        tmp = strtoi(c, "x", &c);
>>
>> The comment is pointless - the operation is firmly undefined if you
>> put it in the strtoi() invocation.
> 
> In terms of C spec you are right. However, it is quite surprising that older
> GCC complains and newer ones do not. Should not we investigate this?

Actually I think I was wrong here. A function call like func(c++, c)
would be undefined, but func(c++, &c) isn't. So I guess if there are
compiler versions getting this wrong, then you should just disregard
my comment.

>> > +        pushl   $sym_phys(early_boot_opts)
>> > +        pushl   MB_cmdline(%ebx)
>> >          call    cmdline_parse_early
>> > +        add     $8,%esp             /* Remove cmdline_parse_early() args from stack. */
>>
>> I don't think such a comment is really useful (seems like I overlooked
>> a similar one in an earlier patch, on the reloc() invocation).
> 
> This thing is quite obvious but I do not think that this comment hurts.

It may not really hurt, but it draws needless attention to something
that is to b expected after any function call getting arguments
passed on the stack. You could, btw., make cmdline_parse_early
a stdcall function, so you wouldn't have to do that adjustment
here.

Jan
Daniel Kiper June 2, 2016, 8:15 a.m. UTC | #4
On Fri, May 27, 2016 at 03:33:49AM -0600, Jan Beulich wrote:
> >>> On 25.05.16 at 23:36, <daniel.kiper@oracle.com> wrote:
> > On Wed, May 25, 2016 at 04:33:54AM -0600, Jan Beulich wrote:
> >> >>> On 15.04.16 at 14:33, <daniel.kiper@oracle.com> wrote:

[...]

> >> > +/*
> >> > + * Compiler is not able to optimize regular strlen()
> >> > + * if argument is well known string during build.
> >> > + * Hence, introduce optimized strlen_opt().
> >> > + */
> >> > +#define strlen_opt(s) (sizeof(s) - 1)
> >>
> >> Do we really care in this code?
> >
> > Not to strongly but why not?
>
> Keep things as readable as possible. In fact I wouldn't mind hard
> coded literal numbers for the string lengths, if they sit right next
> to the respective string literal.

As separate variable? Does it pays? I prefer standard strlen() call
instead of that.

> >> > +static int strtoi(const char *s, const char *stop, const char **next)
> >> > +{
> >> > +    int base = 10, i, ores = 0, res = 0;
> >>
> >> You don't even handle a '-' on the numbers here, so all the variables
> >
> > Yep, however, ...
> >
> >> and the function return type should be unsigned int afaict. And the
> >> function name perhaps be strtoui().
> >
> > ... we return -1 in case of error.
>
> Which - having looked at some of the callers - could easily be
> UINT_MAX as it seems.

Here it looks safe.

> >> > +static u8 skip_realmode(const char *cmdline)
> >> > +{
> >> > +    return !!find_opt(cmdline, "no-real-mode", 0) || !!find_opt(cmdline,
> > "tboot=", 1);
> >>
> >> The || makes the two !! pointless.
> >>
> >> Also please settle on which type you want to use for boolean
> >> (find_opt()'s last parameter is "int", yet here you use "u8"), and
> >
> > Could be u8.
> >
> >> perhaps make yourself a bool_t.
> >
> > I do not think it make sense here.
>
> I think it makes as much or as little sense as having NULL available.

:-)))

> >> > +        /*
> >> > +         * Increment c outside of strtoi() because otherwise some
> >> > +         * compiler may complain with following message:
> >> > +         * warning: operation on ‘c’ may be undefined.
> >> > +         */
> >> > +        ++c;
> >> > +        tmp = strtoi(c, "x", &c);
> >>
> >> The comment is pointless - the operation is firmly undefined if you
> >> put it in the strtoi() invocation.
> >
> > In terms of C spec you are right. However, it is quite surprising that older
> > GCC complains and newer ones do not. Should not we investigate this?
>
> Actually I think I was wrong here. A function call like func(c++, c)

Because argument evaluation order is undefined in C. Am I correct?

> would be undefined, but func(c++, &c) isn't. So I guess if there are
> compiler versions getting this wrong, then you should just disregard
> my comment.

By the way, here is quite good description of these problems:
  http://en.cppreference.com/w/c/language/eval_order

> >> > +        pushl   $sym_phys(early_boot_opts)
> >> > +        pushl   MB_cmdline(%ebx)
> >> >          call    cmdline_parse_early
> >> > +        add     $8,%esp             /* Remove cmdline_parse_early() args from stack. */
> >>
> >> I don't think such a comment is really useful (seems like I overlooked
> >> a similar one in an earlier patch, on the reloc() invocation).
> >
> > This thing is quite obvious but I do not think that this comment hurts.
>
> It may not really hurt, but it draws needless attention to something
> that is to b expected after any function call getting arguments
> passed on the stack. You could, btw., make cmdline_parse_early
> a stdcall function, so you wouldn't have to do that adjustment
> here.

If it is acceptable by you then I can do that.

Daniel
Jan Beulich June 2, 2016, 8:39 a.m. UTC | #5
>>> On 02.06.16 at 10:15, <daniel.kiper@oracle.com> wrote:
> On Fri, May 27, 2016 at 03:33:49AM -0600, Jan Beulich wrote:
>> >>> On 25.05.16 at 23:36, <daniel.kiper@oracle.com> wrote:
>> > On Wed, May 25, 2016 at 04:33:54AM -0600, Jan Beulich wrote:
>> >> >>> On 15.04.16 at 14:33, <daniel.kiper@oracle.com> wrote:
>> >> > +/*
>> >> > + * Compiler is not able to optimize regular strlen()
>> >> > + * if argument is well known string during build.
>> >> > + * Hence, introduce optimized strlen_opt().
>> >> > + */
>> >> > +#define strlen_opt(s) (sizeof(s) - 1)
>> >>
>> >> Do we really care in this code?
>> >
>> > Not to strongly but why not?
>>
>> Keep things as readable as possible. In fact I wouldn't mind hard
>> coded literal numbers for the string lengths, if they sit right next
>> to the respective string literal.
> 
> As separate variable? Does it pays? I prefer standard strlen() call
> instead of that.

Variable? I said literal numbers. As in

	strncmp(str, "xyz", 3);

From such code it is visible at the first glance what the 3 stands for,
and is imo better readable than

	strncmp(str, "xyz", strlen("xyz"));

>> >> > +        pushl   $sym_phys(early_boot_opts)
>> >> > +        pushl   MB_cmdline(%ebx)
>> >> >          call    cmdline_parse_early
>> >> > +        add     $8,%esp             /* Remove cmdline_parse_early() args from stack. */
>> >>
>> >> I don't think such a comment is really useful (seems like I overlooked
>> >> a similar one in an earlier patch, on the reloc() invocation).
>> >
>> > This thing is quite obvious but I do not think that this comment hurts.
>>
>> It may not really hurt, but it draws needless attention to something
>> that is to b expected after any function call getting arguments
>> passed on the stack. You could, btw., make cmdline_parse_early
>> a stdcall function, so you wouldn't have to do that adjustment
>> here.
> 
> If it is acceptable by you then I can do that.

There are two possible issues, which would need checking (which
I only thought of after having written that reply):
- Do all gcc versions we care about support stdcall?
- What's the disposition of those asm() stubs on the callee side?
  (Remember that Andrew had asked for them to get dropped?)

Jan
diff mbox

Patch

diff --git a/.gitignore b/.gitignore
index 91f690c..6919546 100644
--- a/.gitignore
+++ b/.gitignore
@@ -237,9 +237,10 @@  xen/arch/arm/xen.lds
 xen/arch/x86/asm-offsets.s
 xen/arch/x86/boot/mkelf32
 xen/arch/x86/xen.lds
+xen/arch/x86/boot/cmdline.S
 xen/arch/x86/boot/reloc.S
-xen/arch/x86/boot/reloc.bin
-xen/arch/x86/boot/reloc.lnk
+xen/arch/x86/boot/*.bin
+xen/arch/x86/boot/*.lnk
 xen/arch/x86/efi.lds
 xen/arch/x86/efi/check.efi
 xen/arch/x86/efi/disabled
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 1bcb08b..32d2407 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -176,4 +176,4 @@  clean::
 	rm -f asm-offsets.s *.lds boot/*.o boot/*~ boot/core boot/mkelf32
 	rm -f $(BASEDIR)/.xen-syms.[0-9]* boot/.*.d
 	rm -f $(BASEDIR)/.xen.efi.[0-9]* efi/*.o efi/.*.d efi/*.efi efi/disabled efi/mkreloc
-	rm -f boot/reloc.S boot/reloc.lnk boot/reloc.bin
+	rm -f boot/cmdline.S boot/reloc.S boot/*.lnk boot/*.bin
diff --git a/xen/arch/x86/boot/Makefile b/xen/arch/x86/boot/Makefile
index 06893d8..d73cc76 100644
--- a/xen/arch/x86/boot/Makefile
+++ b/xen/arch/x86/boot/Makefile
@@ -1,9 +1,14 @@ 
 obj-bin-y += head.o
 
+CMDLINE_DEPS = video.h
+
 RELOC_DEPS = $(BASEDIR)/include/asm-x86/config.h $(BASEDIR)/include/xen/multiboot.h \
 	     $(BASEDIR)/include/xen/multiboot2.h
 
-head.o: reloc.S
+head.o: cmdline.S reloc.S
+
+cmdline.S: cmdline.c $(CMDLINE_DEPS)
+	$(MAKE) -f build32.mk $@ CMDLINE_DEPS="$(CMDLINE_DEPS)"
 
 reloc.S: reloc.c $(RELOC_DEPS)
 	$(MAKE) -f build32.mk $@ RELOC_DEPS="$(RELOC_DEPS)"
diff --git a/xen/arch/x86/boot/build32.lds b/xen/arch/x86/boot/build32.lds
index 47db9c4..6a234ea 100644
--- a/xen/arch/x86/boot/build32.lds
+++ b/xen/arch/x86/boot/build32.lds
@@ -25,6 +25,7 @@  SECTIONS
         *(.text)
         *(.text.*)
         *(.rodata)
+        *(.rodata.*)
   }
 
   /DISCARD/ : {
diff --git a/xen/arch/x86/boot/build32.mk b/xen/arch/x86/boot/build32.mk
index eb02b4b..58e27e1 100644
--- a/xen/arch/x86/boot/build32.mk
+++ b/xen/arch/x86/boot/build32.mk
@@ -23,7 +23,7 @@  CFLAGS := $(filter-out -flto,$(CFLAGS))
 	$(OBJDUMP) -h $< | sed -n '/[0-9]/{s,00*,0,g;p;}' |\
 		while read idx name sz rest; do \
 			case "$$name" in \
-			.data|.data.*|.rodata.*|.bss|.bss.*) \
+			.data|.data.*|.bss|.bss.*) \
 				test $$sz != 0 || continue; \
 				echo "Error: non-empty $$name: 0x$$sz" >&2; \
 				exit $$(expr $$idx + 1);; \
@@ -34,6 +34,8 @@  CFLAGS := $(filter-out -flto,$(CFLAGS))
 %.o: %.c
 	$(CC) $(CFLAGS) -c -fpic $< -o $@
 
+cmdline.o: cmdline.c $(CMDLINE_DEPS)
+
 reloc.o: reloc.c $(RELOC_DEPS)
 
 .PRECIOUS: %.bin %.lnk
diff --git a/xen/arch/x86/boot/cmdline.S b/xen/arch/x86/boot/cmdline.S
deleted file mode 100644
index 00687eb..0000000
--- a/xen/arch/x86/boot/cmdline.S
+++ /dev/null
@@ -1,367 +0,0 @@ 
-/******************************************************************************
- * cmdline.S
- *
- * Early command-line parsing.
- */
-
-        .code32
-
-#include "video.h"
-
-# NB. String pointer on stack is modified to point past parsed digits.
-.Latoi:
-        push    %ebx
-        push    %ecx
-        push    %edx
-        push    %esi
-        xor     %ebx,%ebx       /* %ebx = accumulator */
-        mov     $10,%ecx        /* %ecx = base (default base 10) */
-        mov     16+4(%esp),%esi /* %esi = pointer into ascii string. */
-        lodsb
-        cmpb    $'0',%al
-        jne     2f
-        mov     $8,%ecx         /* Prefix '0' => octal (base 8) */
-        lodsb
-        cmpb    $'x',%al
-        jne     2f
-        mov     $16,%ecx        /* Prefix '0x' => hex (base 16) */
-1:      lodsb
-2:      sub     $'0',%al
-        jb      4f
-        cmp     $9,%al
-        jbe     3f
-        sub     $'A'-'0'-10,%al
-        jb      4f
-        cmp     $15,%al
-        jbe     3f
-        sub     $'a'-'A',%al
-        jb      4f
-3:      cmp     %cl,%al
-        jae     4f
-        movzbl  %al,%eax
-        xchg    %eax,%ebx
-        mul     %ecx
-        xchg    %eax,%ebx
-        add     %eax,%ebx
-        jmp     1b
-4:      mov     %ebx,%eax
-        dec     %esi
-        mov     %esi,16+4(%esp)
-        pop     %esi
-        pop     %edx
-        pop     %ecx
-        pop     %ebx
-        ret
-
-.Lstrstr:
-        push    %ecx
-        push    %edx
-        push    %esi
-        push    %edi
-        xor     %eax,%eax
-        xor     %ecx,%ecx
-        not     %ecx
-        mov     16+4(%esp),%esi
-        mov     16+8(%esp),%edi
-        repne   scasb
-        not     %ecx
-        dec     %ecx
-        mov     %ecx,%edx
-1:      mov     16+8(%esp),%edi
-        mov     %esi,%eax
-        mov     %edx,%ecx
-        repe    cmpsb
-        je      2f
-        xchg    %eax,%esi
-        inc     %esi
-        cmpb    $0,-1(%eax)
-        jne     1b
-        xor     %eax,%eax
-2:      pop     %edi
-        pop     %esi
-        pop     %edx
-        pop     %ecx
-        ret
-
-.Lstr_prefix:
-        push    %esi
-        push    %edi
-        mov     8+4(%esp),%esi /* 1st arg is prefix string */
-        mov     8+8(%esp),%edi /* 2nd arg is main string */
-1:      lodsb
-        test    %al,%al
-        jz      2f
-        scasb
-        je      1b
-        sbb     %eax,%eax
-        or      $1,%al
-        jmp     3f
-2:      xor     %eax,%eax
-3:      pop     %edi
-        pop     %esi
-        ret
-
-.Lstrlen:
-        push    %ecx
-        push    %esi
-        push    %edi
-        xor     %eax,%eax
-        xor     %ecx,%ecx
-        not     %ecx
-        mov     12+4(%esp),%edi
-        repne   scasb
-        not     %ecx
-        dec     %ecx
-        mov     %ecx,%eax
-        pop     %edi
-        pop     %esi
-        pop     %ecx
-        ret
-
-.Lfind_option:
-        mov     4(%esp),%eax
-        dec     %eax
-        push    %ebx
-1:      pushl   4+8(%esp)
-        inc     %eax
-        push    %eax
-        call    .Lstrstr
-        add     $8,%esp
-        test    %eax,%eax
-        jz      3f
-        cmp     %eax,4+4(%esp)
-        je      2f
-        cmpb    $' ',-1(%eax)
-        jne     1b
-2:      mov     %eax,%ebx
-        pushl   4+8(%esp)
-        call    .Lstrlen
-        add     $4,%esp
-        xadd    %eax,%ebx
-        /* NUL check (as $'\0' == 0x30 in GAS) */
-        cmpb    $0,(%ebx)
-        je      3f
-        cmpb    $' ',(%ebx)
-        je      3f
-        cmpb    $'=',(%ebx)
-        jne     1b
-3:      pop     %ebx
-        ret
-
-cmdline_parse_early:
-        pusha
-
-        /* Bail if there is no command line to parse. */
-        mov     sym_phys(multiboot_ptr),%ebx
-        mov     MB_flags(%ebx),%eax
-        test    $4,%al
-        jz      .Lcmdline_exit
-        mov     MB_cmdline(%ebx),%eax
-        test    %eax,%eax
-        jz      .Lcmdline_exit
-
-        /* Check for 'no-real-mode' command-line option. */
-        pushl   $sym_phys(.Lno_rm_opt)
-        pushl   MB_cmdline(%ebx)
-        call    .Lfind_option
-        test    %eax,%eax
-        setnz   %al
-        or      %al,sym_phys(skip_realmode)
-
-        /* Check for 'tboot=' command-line option. */
-        movl    $sym_phys(.Ltboot_opt),4(%esp)
-        call    .Lfind_option
-        test    %eax,%eax
-        setnz   %al
-        or      %al,sym_phys(skip_realmode) /* tboot= implies no-real-mode */
-
-.Lparse_edd:
-        /* Check for 'edd=' command-line option. */
-        movl    $sym_phys(.Ledd_opt),4(%esp)
-        call    .Lfind_option
-        test    %eax,%eax
-        jz      .Lparse_edid
-        cmpb    $'=',3(%eax)
-        jne     .Lparse_edid
-        add     $4,%eax
-        movb    $2,sym_phys(opt_edd)  /* opt_edd=2: edd=off */
-        cmpw    $0x666f,(%eax)            /* 0x666f == "of" */
-        je      .Lparse_edid
-        decb    sym_phys(opt_edd)     /* opt_edd=1: edd=skipmbr */
-        cmpw    $0x6b73,(%eax)            /* 0x6b73 == "sk" */
-        je      .Lparse_edid
-        decb    sym_phys(opt_edd)     /* opt_edd=0: edd=on (default) */
-
-.Lparse_edid:
-        /* Check for 'edid=' command-line option. */
-        movl    $sym_phys(.Ledid_opt),4(%esp)
-        call    .Lfind_option
-        test    %eax,%eax
-        jz      .Lparse_vga
-        cmpb    $'=',4(%eax)
-        jne     .Lparse_vga
-        add     $5,%eax
-        mov     %eax,%ebx
-        push    %ebx
-        pushl   $sym_phys(.Ledid_force)
-        call    .Lstr_prefix
-        add     $8,%esp
-        movb    $2,sym_phys(opt_edid) /* opt_edid=2: edid=force */
-        test    %eax,%eax
-        jz      .Lparse_vga
-        push    %ebx
-        pushl   $sym_phys(.Ledid_no)
-        call    .Lstr_prefix
-        add     $8,%esp
-        decb    sym_phys(opt_edid)    /* opt_edid=1: edid=no */
-        test    %eax,%eax
-        jz      .Lparse_vga
-        decb    sym_phys(opt_edid)    /* opt_edid=0: default */
-
-.Lparse_vga:
-        /* Check for 'vga=' command-line option. */
-        movl    $sym_phys(.Lvga_opt),4(%esp)
-        call    .Lfind_option
-        add     $8,%esp
-        test    %eax,%eax
-        jz      .Lcmdline_exit
-        cmpb    $'=',3(%eax)
-        jne     .Lcmdline_exit
-        add     $4,%eax
-
-        /* Found the 'vga=' option. Default option is to display vga menu. */
-        movw    $ASK_VGA,sym_phys(boot_vid_mode)
-
-        /* Check for 'vga=text-80x<rows>. */
-        mov     %eax,%ebx
-        push    %ebx
-        pushl   $sym_phys(.Lvga_text80)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lparse_vga_gfx
-
-        /* We have 'vga=text-80x<rows>'. */
-        add     $8,%ebx
-        push    %ebx
-        call    .Latoi
-        add     $4,%esp
-        mov     %ax,%bx
-        lea     sym_phys(.Lvga_text_modes),%esi
-1:      lodsw
-        test    %ax,%ax
-        jz      .Lcmdline_exit
-        cmp     %ax,%bx
-        lodsw
-        jne     1b
-        mov     %ax,sym_phys(boot_vid_mode)
-        jmp     .Lcmdline_exit
-
-.Lparse_vga_gfx:
-        /* Check for 'vga=gfx-<width>x<height>x<depth>'. */
-        push    %ebx
-        pushl   $sym_phys(.Lvga_gfx)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lparse_vga_mode
-
-        /* We have 'vga=gfx-<width>x<height>x<depth>'. */
-        /* skip 'gfx-' */
-        add     $4,%ebx
-        /* parse <width> */
-        push    %ebx
-        call    .Latoi
-        pop     %esi
-        mov     %ax,sym_phys(vesa_size)+0
-        /* skip 'x' */
-        lodsb
-        cmpb    $'x',%al
-        jne     .Lcmdline_exit
-        /* parse <height> */
-        push    %esi
-        call    .Latoi
-        pop     %esi
-        mov     %ax,sym_phys(vesa_size)+2
-        /* skip 'x' */
-        lodsb
-        cmpb    $'x',%al
-        jne     .Lcmdline_exit
-        /* parse <depth> */
-        push    %esi
-        call    .Latoi
-        pop     %esi
-        mov     %ax,sym_phys(vesa_size)+4
-        /* commit to vesa mode */
-        movw    $VIDEO_VESA_BY_SIZE,sym_phys(boot_vid_mode)
-        jmp     .Lcmdline_exit
-
-.Lparse_vga_mode:
-        /* Check for 'vga=mode-<mode>'. */
-        push    %ebx
-        pushl   $sym_phys(.Lvga_mode)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lparse_vga_current
-
-        /* We have 'vga=mode-<mode>'. */
-        add     $5,%ebx
-        push    %ebx
-        call    .Latoi
-        add     $4,%esp
-        mov     %ax,sym_phys(boot_vid_mode)
-        jmp     .Lcmdline_exit
-
-.Lparse_vga_current:
-        /* Check for 'vga=current'. */
-        push    %ebx
-        pushl   $sym_phys(.Lvga_current)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lcmdline_exit
-
-        /* We have 'vga=current'. */
-        movw    $VIDEO_CURRENT_MODE,sym_phys(boot_vid_mode)
-
-.Lcmdline_exit:
-        popa
-        ret
-
-        .pushsection .init.rodata, "a", @progbits
-
-.Lvga_text_modes: /* rows, mode_number */
-        .word   25,VIDEO_80x25
-        .word   50,VIDEO_80x50
-        .word   43,VIDEO_80x43
-        .word   28,VIDEO_80x28
-        .word   30,VIDEO_80x30
-        .word   34,VIDEO_80x34
-        .word   60,VIDEO_80x60
-        .word   0
-
-.Lvga_opt:
-        .asciz  "vga"
-.Lvga_text80:
-        .asciz  "text-80x"
-.Lvga_gfx:
-        .asciz  "gfx-"
-.Lvga_mode:
-        .asciz  "mode-"
-.Lvga_current:
-        .asciz  "current"
-.Lno_rm_opt:
-        .asciz  "no-real-mode"
-.Ltboot_opt:
-        .asciz  "tboot"
-.Ledid_opt:
-        .asciz  "edid"
-.Ledid_force:
-        .asciz  "force"
-.Ledid_no:
-        .asciz  "no"
-.Ledd_opt:
-        .asciz  "edd"
-
-        .popsection
diff --git a/xen/arch/x86/boot/cmdline.c b/xen/arch/x86/boot/cmdline.c
new file mode 100644
index 0000000..a624fa9
--- /dev/null
+++ b/xen/arch/x86/boot/cmdline.c
@@ -0,0 +1,357 @@ 
+/*
+ * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
+ *      Daniel Kiper <daniel.kiper@oracle.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * strlen(), strncmp(), strspn() and strcspn() were copied from
+ * Linux kernel source (linux/lib/string.c).
+ *
+ * max() was copied from xen/xen/include/xen/kernel.h.
+ */
+
+/*
+ * This entry point is entered from xen/arch/x86/boot/head.S with:
+ *   - 0x4(%esp) = &cmdline,
+ *   - 0x8(%esp) = &early_boot_opts.
+ */
+asm (
+    "    .text                         \n"
+    "    .globl _start                 \n"
+    "_start:                           \n"
+    "    jmp  cmdline_parse_early      \n"
+    );
+
+#include "video.h"
+
+#define NULL	((void *)0)
+
+/*
+ * Space and TAB are obvious delimiters. However, I am
+ * adding "\n" and "\r" here too. Just in case when
+ * crazy bootloader/user put them somewhere.
+ */
+#define DELIM_CHARS		" \n\r\t"
+#define DELIM_CHARS_COMMA	DELIM_CHARS ","
+
+#define __packed	__attribute__((__packed__))
+
+#define max(x,y) ({ \
+        const typeof(x) _x = (x);       \
+        const typeof(y) _y = (y);       \
+        (void) (&_x == &_y);            \
+        _x > _y ? _x : _y; })
+
+#define tolower(c) ((c) | 0x20)
+
+/*
+ * Compiler is not able to optimize regular strlen()
+ * if argument is well known string during build.
+ * Hence, introduce optimized strlen_opt().
+ */
+#define strlen_opt(s) (sizeof(s) - 1)
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int size_t;
+
+#define U16_MAX	((u16)(~0U))
+
+/* Keep in sync with trampoline.S:early_boot_opts label! */
+typedef struct __packed {
+    u8 skip_realmode;
+    u8 opt_edd;
+    u8 opt_edid;
+    u16 boot_vid_mode;
+    u16 vesa_width;
+    u16 vesa_height;
+    u16 vesa_depth;
+} early_boot_opts_t;
+
+static size_t strlen(const char *s)
+{
+    const char *sc;
+
+    for ( sc = s; *sc != '\0'; ++sc )
+        /* nothing */;
+    return sc - s;
+}
+
+static int strncmp(const char *cs, const char *ct, size_t count)
+{
+    unsigned char c1, c2;
+
+    while ( count )
+    {
+        c1 = *cs++;
+        c2 = *ct++;
+        if ( c1 != c2 )
+            return c1 < c2 ? -1 : 1;
+        if ( !c1 )
+            break;
+        count--;
+    }
+    return 0;
+}
+
+static size_t strspn(const char *s, const char *accept)
+{
+    const char *p;
+    const char *a;
+    size_t count = 0;
+
+    for ( p = s; *p != '\0'; ++p )
+    {
+        for ( a = accept; *a != '\0'; ++a )
+        {
+            if ( *p == *a )
+                break;
+        }
+        if ( *a == '\0' )
+            return count;
+        ++count;
+    }
+    return count;
+}
+
+static size_t strcspn(const char *s, const char *reject)
+{
+    const char *p;
+    const char *r;
+    size_t count = 0;
+
+    for ( p = s; *p != '\0'; ++p )
+    {
+        for ( r = reject; *r != '\0'; ++r )
+        {
+            if ( *p == *r )
+                return count;
+        }
+        ++count;
+    }
+    return count;
+}
+
+static int strtoi(const char *s, const char *stop, const char **next)
+{
+    int base = 10, i, ores = 0, res = 0;
+
+    if ( *s == '0' )
+      base = (tolower(*++s) == 'x') ? (++s, 16) : 8;
+
+    for ( ; *s != '\0'; ++s )
+    {
+        for ( i = 0; stop && stop[i] != '\0'; ++i )
+            if ( *s == stop[i] )
+                goto out;
+
+        if ( *s < '0' || (*s > '7' && base == 8) )
+        {
+            res = -1;
+            goto out;
+        }
+
+        if ( *s > '9' && (base != 16 || tolower(*s) < 'a' || tolower(*s) > 'f') )
+        {
+            res = -1;
+            goto out;
+        }
+
+        res *= base;
+        res += (tolower(*s) >= 'a') ? (tolower(*s) - 'a' + 10) : (*s - '0');
+
+        if ( ores > res )
+        {
+            res = -1;
+            goto out;
+        }
+
+        ores = res;
+    }
+
+ out:
+    if ( next )
+      *next = s;
+
+    return res;
+}
+
+static int strmaxcmp(const char *cs, const char *ct, const char *delim_chars)
+{
+    return strncmp(cs, ct, max(strcspn(cs, delim_chars), strlen(ct)));
+}
+
+static int strsubcmp(const char *cs, const char *ct)
+{
+    return strncmp(cs, ct, strlen(ct));
+}
+
+static const char *find_opt(const char *cmdline, const char *opt, int arg)
+{
+    size_t lc, lo;
+
+    lo = strlen(opt);
+
+    for ( ; ; )
+    {
+        cmdline += strspn(cmdline, DELIM_CHARS);
+
+        if ( *cmdline == '\0' )
+            return NULL;
+
+        if ( !strmaxcmp(cmdline, "--", DELIM_CHARS) )
+            return NULL;
+
+        lc = strcspn(cmdline, DELIM_CHARS);
+
+        if ( !strncmp(cmdline, opt, arg ? lo : max(lc, lo)) )
+            return cmdline + lo;
+
+        cmdline += lc;
+    }
+}
+
+static u8 skip_realmode(const char *cmdline)
+{
+    return !!find_opt(cmdline, "no-real-mode", 0) || !!find_opt(cmdline, "tboot=", 1);
+}
+
+static u8 edd_parse(const char *cmdline)
+{
+    const char *c;
+
+    c = find_opt(cmdline, "edd=", 1);
+
+    if ( !c )
+        return 0;
+
+    if ( !strmaxcmp(c, "off", DELIM_CHARS) )
+        return 2;
+
+    return !strmaxcmp(c, "skipmbr", DELIM_CHARS);
+}
+
+static u8 edid_parse(const char *cmdline)
+{
+    const char *c;
+
+    c = find_opt(cmdline, "edid=", 1);
+
+    if ( !c )
+        return 0;
+
+    if ( !strmaxcmp(c, "force", DELIM_CHARS) )
+        return 2;
+
+    return !strmaxcmp(c, "no", DELIM_CHARS);
+}
+
+static u16 rows2vmode(int rows)
+{
+    switch ( rows )
+    {
+    case 25:
+        return VIDEO_80x25;
+
+    case 28:
+        return VIDEO_80x28;
+
+    case 30:
+        return VIDEO_80x30;
+
+    case 34:
+        return VIDEO_80x34;
+
+    case 43:
+        return VIDEO_80x43;
+
+    case 50:
+        return VIDEO_80x50;
+
+    case 60:
+        return VIDEO_80x60;
+
+    default:
+        return ASK_VGA;
+    }
+}
+
+static void vga_parse(const char *cmdline, early_boot_opts_t *ebo)
+{
+    const char *c;
+    int tmp;
+
+    c = find_opt(cmdline, "vga=", 1);
+
+    if ( !c )
+        return;
+
+    ebo->boot_vid_mode = ASK_VGA;
+
+    if ( !strmaxcmp(c, "current", DELIM_CHARS_COMMA) )
+        ebo->boot_vid_mode = VIDEO_CURRENT_MODE;
+    else if ( !strsubcmp(c, "text-80x") )
+    {
+        c += strlen_opt("text-80x");
+        ebo->boot_vid_mode = rows2vmode(strtoi(c, DELIM_CHARS_COMMA, NULL));
+    }
+    else if ( !strsubcmp(c, "gfx-") )
+    {
+        tmp = strtoi(c + strlen_opt("gfx-"), "x", &c);
+
+        if ( tmp < 0 || tmp > U16_MAX )
+            return;
+
+        ebo->vesa_width = tmp;
+
+        /*
+         * Increment c outside of strtoi() because otherwise some
+         * compiler may complain with following message:
+         * warning: operation on ‘c’ may be undefined.
+         */
+        ++c;
+        tmp = strtoi(c, "x", &c);
+
+        if ( tmp < 0 || tmp > U16_MAX )
+            return;
+
+        ebo->vesa_height = tmp;
+
+        tmp = strtoi(++c, DELIM_CHARS_COMMA, NULL);
+
+        if ( tmp < 0 || tmp > U16_MAX )
+            return;
+
+        ebo->vesa_depth = tmp;
+
+        ebo->boot_vid_mode = VIDEO_VESA_BY_SIZE;
+    }
+    else if ( !strsubcmp(c, "mode-") )
+    {
+        tmp = strtoi(c + strlen_opt("mode-"), DELIM_CHARS_COMMA, NULL);
+
+        if ( tmp < 0 || tmp > U16_MAX )
+            return;
+
+        ebo->boot_vid_mode = tmp;
+    }
+}
+
+void cmdline_parse_early(const char *cmdline, early_boot_opts_t *ebo)
+{
+    ebo->skip_realmode = skip_realmode(cmdline);
+    ebo->opt_edd = edd_parse(cmdline);
+    ebo->opt_edid = edid_parse(cmdline);
+    vga_parse(cmdline, ebo);
+}
diff --git a/xen/arch/x86/boot/edd.S b/xen/arch/x86/boot/edd.S
index 5c80da6..73371f9 100644
--- a/xen/arch/x86/boot/edd.S
+++ b/xen/arch/x86/boot/edd.S
@@ -142,9 +142,6 @@  edd_next:
 edd_done:
         ret
 
-opt_edd:
-        .byte   0                               # edd=on/off/skipmbr
-
 GLOBAL(boot_edd_info_nr)
         .byte   0
 GLOBAL(boot_mbr_signature_nr)
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index efb0614..964851b 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -436,8 +436,24 @@  trampoline_setup:
         cmp     $sym_phys(__trampoline_seg_stop),%edi
         jb      1b
 
+        /* Do not parse command line on EFI platform here. */
+        cmpb    $1,sym_phys(skip_realmode)
+        je      1f
+
+        /* Bail if there is no command line to parse. */
+        mov     sym_phys(multiboot_ptr),%ebx
+        testl   $MBI_CMDLINE,MB_flags(%ebx)
+        jz      1f
+
+        cmpl    $0,MB_cmdline(%ebx)
+        jz      1f
+
+        pushl   $sym_phys(early_boot_opts)
+        pushl   MB_cmdline(%ebx)
         call    cmdline_parse_early
+        add     $8,%esp             /* Remove cmdline_parse_early() args from stack. */
 
+1:
         /* Switch to low-memory stack.  */
         mov     sym_phys(trampoline_phys),%edi
         lea     0x10000(%edi),%esp
@@ -453,6 +469,7 @@  trampoline_setup:
         /* Jump into the relocated trampoline. */
         lret
 
+cmdline_parse_early:
 #include "cmdline.S"
 
 reloc:
diff --git a/xen/arch/x86/boot/trampoline.S b/xen/arch/x86/boot/trampoline.S
index b013614..8a32728 100644
--- a/xen/arch/x86/boot/trampoline.S
+++ b/xen/arch/x86/boot/trampoline.S
@@ -220,8 +220,20 @@  trampoline_boot_cpu_entry:
         /* Jump to the common bootstrap entry point. */
         jmp     trampoline_protmode_entry
 
+#include "video.h"
+
+/* Keep in sync with cmdline.c:early_boot_opts_t type! */
+early_boot_opts:
 skip_realmode:
         .byte   0
+opt_edd:
+        .byte   0                               /* edd=on/off/skipmbr */
+opt_edid:
+        .byte   0                               /* EDID parsing option (force/no/default). */
+GLOBAL(boot_vid_mode)
+        .word   VIDEO_80x25                     /* If we don't run at all, assume basic video mode 3 at 80x25. */
+vesa_size:
+        .word   0,0,0                           /* width x depth x height */
 
 GLOBAL(kbd_shift_flags)
         .byte   0
diff --git a/xen/arch/x86/boot/video.S b/xen/arch/x86/boot/video.S
index b238bf3..335a51c 100644
--- a/xen/arch/x86/boot/video.S
+++ b/xen/arch/x86/boot/video.S
@@ -945,7 +945,6 @@  store_edid:
 #endif
         ret
 
-opt_edid:       .byte   0       # EDID parsing option (force/no/default)
 mt_end:         .word   0       # End of video mode table if built
 edit_buf:       .space  6       # Line editor buffer
 card_name:      .word   0       # Pointer to adapter name
@@ -991,11 +990,6 @@  name_bann:      .asciz  "Video adapter: "
 
 force_size:     .word   0       # Use this size instead of the one in BIOS vars
 
-vesa_size:      .word   0,0,0   # width x depth x height
-
-/* If we don't run at all, assume basic video mode 3 at 80x25. */
-GLOBAL(boot_vid_mode)
-        .word   VIDEO_80x25
 GLOBAL(boot_vid_info)
         .byte   0, 0    /* orig_x, orig_y */
         .byte   3       /* text mode 3    */