diff mbox series

[v4,1/3] multiboot2: parse vga= option when setting GOP mode

Message ID 20230705114741.11449-2-roger.pau@citrix.com (mailing list archive)
State New, archived
Headers show
Series x86/gfx: early boot improvements | expand

Commit Message

Roger Pau Monné July 5, 2023, 11:47 a.m. UTC
Introduce support for passing the command line to the efi_multiboot2()
helper, and parse the vga= option if present.

Add support for the 'gfx' and 'current' vga options, ignore the 'keep'
option, and print a warning message about other options not being
currently implemented.

Note that the multboot2 command line string must always be
zero-terminated according to the multiboot2 specification, and hence
there's no need to pass the string size to efi_multiboot2().

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Changes since v3:
 - s/last/cur/
 - asm style fixes.
 - Add note about cmdline always being 0 terminated.
 - Also allow \t as valid option separator (as space).
 - Expand comment about expected get_option() behavior.
 - Always check for valid option terminator.

Changes since v2:
 - Do not parse console=.
 - Allow width or height to be 0 as long as the gfx- option is well
   formed.

Changes since v1:
 - Do not return the last occurrence of a command line.
 - Rearrange the code for assembly processing of the cmdline and use
   lea.
 - Merge patches handling console= and vga= together.
---
 xen/arch/x86/boot/head.S          | 13 +++++-
 xen/arch/x86/efi/efi-boot.h       | 74 ++++++++++++++++++++++++++++++-
 xen/arch/x86/x86_64/asm-offsets.c |  1 +
 3 files changed, 84 insertions(+), 4 deletions(-)

Comments

Jan Beulich July 6, 2023, 10:41 a.m. UTC | #1
On 05.07.2023 13:47, Roger Pau Monne wrote:
> --- a/xen/arch/x86/efi/efi-boot.h
> +++ b/xen/arch/x86/efi/efi-boot.h
> @@ -795,7 +795,30 @@ static bool __init efi_arch_use_config_file(EFI_SYSTEM_TABLE *SystemTable)
>  
>  static void __init efi_arch_flush_dcache_area(const void *vaddr, UINTN size) { }
>  
> -void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
> +/* Return a pointer to the character after the first occurrence of opt in cmd */
> +static const char __init *get_option(const char *cmd, const char *opt)

Nit: __init and * want to change places.

> @@ -816,7 +839,54 @@ void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
>  
>      if ( gop )
>      {
> -        gop_mode = efi_find_gop_mode(gop, 0, 0, 0);
> +        const char *cur = cmdline;
> +        unsigned int width = 0, height = 0, depth = 0;
> +        bool keep_current = false;
> +
> +        while ( (cur = get_option(cur, "vga=")) != NULL )
> +        {
> +#define VALID_TERMINATOR(c) \
> +    (*(c) == ' ' || *(c) == '\t' || *(c) == '\0' || *(c) == ',')
> +            if ( !strncmp(cur, "gfx-", 4) )
> +            {
> +                width = simple_strtoul(cur + 4, &cur, 10);
> +
> +                if ( *cur == 'x' )
> +                    height = simple_strtoul(cur + 1, &cur, 10);
> +                else
> +                    goto error;
> +
> +                if ( *cur == 'x' )
> +                    depth = simple_strtoul(cur + 1, &cur, 10);
> +                else
> +                    goto error;
> +
> +                if ( !VALID_TERMINATOR(cur) )
> +                {
> +error:

Nit: Labels want to be indented by at least one blank. Here I'm
inclined to suggest indenting to the level of the enclosing curly
braces.

> +                    PrintStr(L"Warning: Invalid gfx- option detected.\r\n");

Maybe better PrintErr() and no trailing full stop?

> +                    width = height = depth = 0;
> +                }
> +                keep_current = false;
> +            }
> +            else if ( !strncmp(cur, "current", 7) && VALID_TERMINATOR(cur + 7) )
> +                keep_current = true;
> +            else if ( !strncmp(cur, "keep", 4) && VALID_TERMINATOR(cur + 4) )
> +            {
> +                /* Ignore, handled in later vga= parsing. */
> +            }
> +            else
> +            {
> +                /* Fallback to defaults if unimplemented. */
> +                width = height = depth = 0;
> +                keep_current = false;
> +                PrintStr(L"Warning: Cannot use selected vga option.\r\n");

Same here then?

With these addressed (which are all mechanical and hence can probably
be done while committing, as long as we can reach agreement)
Reviewed-by: Jan Beulich <jbeulich@suse.com>

Jan
Roger Pau Monné July 7, 2023, 10:13 a.m. UTC | #2
On Thu, Jul 06, 2023 at 12:41:58PM +0200, Jan Beulich wrote:
> On 05.07.2023 13:47, Roger Pau Monne wrote:
> > --- a/xen/arch/x86/efi/efi-boot.h
> > +++ b/xen/arch/x86/efi/efi-boot.h
> > @@ -795,7 +795,30 @@ static bool __init efi_arch_use_config_file(EFI_SYSTEM_TABLE *SystemTable)
> >  
> >  static void __init efi_arch_flush_dcache_area(const void *vaddr, UINTN size) { }
> >  
> > -void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
> > +/* Return a pointer to the character after the first occurrence of opt in cmd */
> > +static const char __init *get_option(const char *cmd, const char *opt)
> 
> Nit: __init and * want to change places.

Hm, yes.  I assume that placing it before the return type is not OK?
(static const __init char ...)

> > @@ -816,7 +839,54 @@ void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
> >  
> >      if ( gop )
> >      {
> > -        gop_mode = efi_find_gop_mode(gop, 0, 0, 0);
> > +        const char *cur = cmdline;
> > +        unsigned int width = 0, height = 0, depth = 0;
> > +        bool keep_current = false;
> > +
> > +        while ( (cur = get_option(cur, "vga=")) != NULL )
> > +        {
> > +#define VALID_TERMINATOR(c) \
> > +    (*(c) == ' ' || *(c) == '\t' || *(c) == '\0' || *(c) == ',')
> > +            if ( !strncmp(cur, "gfx-", 4) )
> > +            {
> > +                width = simple_strtoul(cur + 4, &cur, 10);
> > +
> > +                if ( *cur == 'x' )
> > +                    height = simple_strtoul(cur + 1, &cur, 10);
> > +                else
> > +                    goto error;
> > +
> > +                if ( *cur == 'x' )
> > +                    depth = simple_strtoul(cur + 1, &cur, 10);
> > +                else
> > +                    goto error;
> > +
> > +                if ( !VALID_TERMINATOR(cur) )
> > +                {
> > +error:
> 
> Nit: Labels want to be indented by at least one blank. Here I'm
> inclined to suggest indenting to the level of the enclosing curly
> braces.
> 
> > +                    PrintStr(L"Warning: Invalid gfx- option detected.\r\n");
> 
> Maybe better PrintErr() and no trailing full stop?

Yes, please adjust if you don't mind.

> 
> > +                    width = height = depth = 0;
> > +                }
> > +                keep_current = false;
> > +            }
> > +            else if ( !strncmp(cur, "current", 7) && VALID_TERMINATOR(cur + 7) )
> > +                keep_current = true;
> > +            else if ( !strncmp(cur, "keep", 4) && VALID_TERMINATOR(cur + 4) )
> > +            {
> > +                /* Ignore, handled in later vga= parsing. */
> > +            }
> > +            else
> > +            {
> > +                /* Fallback to defaults if unimplemented. */
> > +                width = height = depth = 0;
> > +                keep_current = false;
> > +                PrintStr(L"Warning: Cannot use selected vga option.\r\n");
> 
> Same here then?
> 
> With these addressed (which are all mechanical and hence can probably
> be done while committing, as long as we can reach agreement)
> Reviewed-by: Jan Beulich <jbeulich@suse.com>

LGTM, please adjust if you don't mind, otherwise I can send an
adjusted version.

Thanks, Roger.
Jan Beulich July 7, 2023, 10:24 a.m. UTC | #3
On 07.07.2023 12:13, Roger Pau Monné wrote:
> On Thu, Jul 06, 2023 at 12:41:58PM +0200, Jan Beulich wrote:
>> On 05.07.2023 13:47, Roger Pau Monne wrote:
>>> --- a/xen/arch/x86/efi/efi-boot.h
>>> +++ b/xen/arch/x86/efi/efi-boot.h
>>> @@ -795,7 +795,30 @@ static bool __init efi_arch_use_config_file(EFI_SYSTEM_TABLE *SystemTable)
>>>  
>>>  static void __init efi_arch_flush_dcache_area(const void *vaddr, UINTN size) { }
>>>  
>>> -void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
>>> +/* Return a pointer to the character after the first occurrence of opt in cmd */
>>> +static const char __init *get_option(const char *cmd, const char *opt)
>>
>> Nit: __init and * want to change places.
> 
> Hm, yes.  I assume that placing it before the return type is not OK?
> (static const __init char ...)

That's still in the middle of the return type then. Technically gcc
accepts it being placed anywhere, but they reserve the right to change
meaning when not placed appropriately. Recall that you may alter both
attributes of a function (or variable) and attributes of types. Hence
to disambiguate both, proper placement may become necessary down the
road. And while it might be that

static __init const char *...

would also be okay-ish (albeit I'm not certain), that's still against
how we do things commonly (i.e. a not written down style aspect).

>>>[...]
> 
> LGTM, please adjust if you don't mind, otherwise I can send an
> adjusted version.

No need to send an update.

Jan
Roger Pau Monné July 7, 2023, 10:42 a.m. UTC | #4
On Fri, Jul 07, 2023 at 12:24:10PM +0200, Jan Beulich wrote:
> On 07.07.2023 12:13, Roger Pau Monné wrote:
> > On Thu, Jul 06, 2023 at 12:41:58PM +0200, Jan Beulich wrote:
> >> On 05.07.2023 13:47, Roger Pau Monne wrote:
> >>> --- a/xen/arch/x86/efi/efi-boot.h
> >>> +++ b/xen/arch/x86/efi/efi-boot.h
> >>> @@ -795,7 +795,30 @@ static bool __init efi_arch_use_config_file(EFI_SYSTEM_TABLE *SystemTable)
> >>>  
> >>>  static void __init efi_arch_flush_dcache_area(const void *vaddr, UINTN size) { }
> >>>  
> >>> -void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
> >>> +/* Return a pointer to the character after the first occurrence of opt in cmd */
> >>> +static const char __init *get_option(const char *cmd, const char *opt)
> >>
> >> Nit: __init and * want to change places.
> > 
> > Hm, yes.  I assume that placing it before the return type is not OK?
> > (static const __init char ...)
> 
> That's still in the middle of the return type then. Technically gcc
> accepts it being placed anywhere, but they reserve the right to change
> meaning when not placed appropriately. Recall that you may alter both
> attributes of a function (or variable) and attributes of types. Hence
> to disambiguate both, proper placement may become necessary down the
> road. And while it might be that
> 
> static __init const char *...
> 
> would also be okay-ish (albeit I'm not certain), that's still against
> how we do things commonly (i.e. a not written down style aspect).

Thanks for the explanation.

Roger.
diff mbox series

Patch

diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index e03f52c75535..1eb829ab419f 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -237,9 +237,10 @@  __efi64_mb2_start:
         jmp     x86_32_switch
 
 .Lefi_multiboot2_proto:
-        /* Zero EFI SystemTable and EFI ImageHandle addresses. */
+        /* Zero EFI SystemTable, EFI ImageHandle addresses and cmdline. */
         xor     %esi,%esi
         xor     %edi,%edi
+        xor     %edx,%edx
 
         /* Skip Multiboot2 information fixed part. */
         lea     (MB2_fixed_sizeof+MULTIBOOT2_TAG_ALIGN-1)(%rbx),%ecx
@@ -277,6 +278,13 @@  __efi64_mb2_start:
         cmove   MB2_efi64_ih(%rcx),%rdi
         je      .Lefi_mb2_next_tag
 
+        /* Get command line from Multiboot2 information. */
+        cmpl    $MULTIBOOT2_TAG_TYPE_CMDLINE, MB2_tag_type(%rcx)
+        jne     .Lno_cmdline
+        lea     MB2_tag_string(%rcx), %rdx
+        jmp     .Lefi_mb2_next_tag
+.Lno_cmdline:
+
         /* Is it the end of Multiboot2 information? */
         cmpl    $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%rcx)
         je      .Lrun_bs
@@ -340,7 +348,8 @@  __efi64_mb2_start:
 
         /*
          * efi_multiboot2() is called according to System V AMD64 ABI:
-         *   - IN:  %rdi - EFI ImageHandle, %rsi - EFI SystemTable.
+         *   - IN:  %rdi - EFI ImageHandle, %rsi - EFI SystemTable,
+         *          %rdx - MB2 cmdline
          */
         call    efi_multiboot2
 
diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
index 64c1a02cf10a..4394c7276aa3 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -795,7 +795,30 @@  static bool __init efi_arch_use_config_file(EFI_SYSTEM_TABLE *SystemTable)
 
 static void __init efi_arch_flush_dcache_area(const void *vaddr, UINTN size) { }
 
-void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
+/* Return a pointer to the character after the first occurrence of opt in cmd */
+static const char __init *get_option(const char *cmd, const char *opt)
+{
+    const char *s = cmd, *o = NULL;
+
+    if ( !cmd || !opt )
+        return NULL;
+
+    while ( (s = strstr(s, opt)) != NULL )
+    {
+        if ( s == cmd || *(s - 1) == ' ' || *(s - 1) == '\t' )
+        {
+            o = s + strlen(opt);
+            break;
+        }
+
+        s += strlen(opt);
+    }
+
+    return o;
+}
+
+void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable,
+                           const char *cmdline)
 {
     EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
     EFI_HANDLE gop_handle;
@@ -816,7 +839,54 @@  void __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
 
     if ( gop )
     {
-        gop_mode = efi_find_gop_mode(gop, 0, 0, 0);
+        const char *cur = cmdline;
+        unsigned int width = 0, height = 0, depth = 0;
+        bool keep_current = false;
+
+        while ( (cur = get_option(cur, "vga=")) != NULL )
+        {
+#define VALID_TERMINATOR(c) \
+    (*(c) == ' ' || *(c) == '\t' || *(c) == '\0' || *(c) == ',')
+            if ( !strncmp(cur, "gfx-", 4) )
+            {
+                width = simple_strtoul(cur + 4, &cur, 10);
+
+                if ( *cur == 'x' )
+                    height = simple_strtoul(cur + 1, &cur, 10);
+                else
+                    goto error;
+
+                if ( *cur == 'x' )
+                    depth = simple_strtoul(cur + 1, &cur, 10);
+                else
+                    goto error;
+
+                if ( !VALID_TERMINATOR(cur) )
+                {
+error:
+                    PrintStr(L"Warning: Invalid gfx- option detected.\r\n");
+                    width = height = depth = 0;
+                }
+                keep_current = false;
+            }
+            else if ( !strncmp(cur, "current", 7) && VALID_TERMINATOR(cur + 7) )
+                keep_current = true;
+            else if ( !strncmp(cur, "keep", 4) && VALID_TERMINATOR(cur + 4) )
+            {
+                /* Ignore, handled in later vga= parsing. */
+            }
+            else
+            {
+                /* Fallback to defaults if unimplemented. */
+                width = height = depth = 0;
+                keep_current = false;
+                PrintStr(L"Warning: Cannot use selected vga option.\r\n");
+            }
+#undef VALID_TERMINATOR
+        }
+
+        if ( !keep_current )
+            gop_mode = efi_find_gop_mode(gop, width, height, depth);
 
         efi_arch_edid(gop_handle);
     }
diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c
index 287dac101ad4..fbd6c54188db 100644
--- a/xen/arch/x86/x86_64/asm-offsets.c
+++ b/xen/arch/x86/x86_64/asm-offsets.c
@@ -175,6 +175,7 @@  void __dummy__(void)
     OFFSET(MB2_mem_lower, multiboot2_tag_basic_meminfo_t, mem_lower);
     OFFSET(MB2_efi64_st, multiboot2_tag_efi64_t, pointer);
     OFFSET(MB2_efi64_ih, multiboot2_tag_efi64_ih_t, pointer);
+    OFFSET(MB2_tag_string, multiboot2_tag_string_t, string);
     BLANK();
 
     OFFSET(DOMAIN_vm_assist, struct domain, vm_assist);