diff mbox series

[v4,2/4] x86/boot: Refactor BIOS/PVH start

Message ID 20240925060101.259244-3-frediano.ziglio@cloud.com (mailing list archive)
State Superseded
Headers show
Series x86/boot: Reduce assembly code | expand

Commit Message

Frediano Ziglio Sept. 25, 2024, 6 a.m. UTC
The 2 code paths were sharing quite some common code, reuse it instead
of having duplications.
Use %dl register to store boot type before running common code.
Using a 8 bit register reduces code size.

Signed-off-by: Frediano Ziglio <frediano.ziglio@cloud.com>
---
Changes since v1:
- use %dl instead of %ebp to reduce code size;
- fold cli instruction;
- update some comments.

Changes since v3:
- dropped %dl and constant, distinguish entry by magic.
---
 xen/arch/x86/boot/head.S | 108 +++++++++++++++------------------------
 1 file changed, 40 insertions(+), 68 deletions(-)

Comments

Andrew Cooper Sept. 25, 2024, 7:33 p.m. UTC | #1
On 25/09/2024 7:00 am, Frediano Ziglio wrote:
> The 2 code paths were sharing quite some common code, reuse it instead
> of having duplications.
> Use %dl register to store boot type before running common code.
> Using a 8 bit register reduces code size.

These final two lines are stale and can be dropped.

> diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
> index 267207e5a2..2d2f56ad22 100644
> --- a/xen/arch/x86/boot/head.S
> +++ b/xen/arch/x86/boot/head.S
> @@ -409,13 +411,27 @@ cs32_switch:
>  ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start))
>  
>  __pvh_start:
> -        cld
> +        mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */
> +        /*
> +         * Fall through into BIOS code.
> +         * We will use %eax to distinguish we came from PHV entry point.

PVH.

It occurs to me that we could actually have:

        mov     $XEN_HVM_START_MAGIC_VALUE, %eax

Given the cross-check against 0(%ebx) later, that's marginally more robust.

> @@ -449,62 +458,40 @@ __pvh_start:
>          mov     %ecx, %es
>          mov     %ecx, %ss
>  
> -        /* Skip bootloader setup and bios setup, go straight to trampoline */
> -        movb    $1, sym_esi(pvh_boot)
> -        movb    $1, sym_esi(skip_realmode)
> +        /* Load null selector to unused segment registers. */
> +        xor     %ecx, %ecx
> +        mov     %ecx, %fs
> +        mov     %ecx, %gs

Honestly, the more I look at this, the more bizarre it is.

We should just set up %fs/gs like we do %ds/es, which in this case is
simply to drop the comment and the xor.

>  
> -        /* Set trampoline_phys to use mfn 1 to avoid having a mapping at VA 0 */
> -        movw    $0x1000, sym_esi(trampoline_phys)
> -        mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */
> -        jmp     trampoline_setup
> -
> -#endif /* CONFIG_PVH_GUEST */
> -
> -.Linitialise_bss:
>          /* Initialise the BSS.  Preserve %eax (BOOTLOADER_MAGIC). */
>          mov     %eax, %ebp
> -
>          lea     sym_esi(__bss_start), %edi
>          lea     sym_esi(__bss_end), %ecx
>          sub     %edi, %ecx
>          xor     %eax, %eax
>          shr     $2, %ecx
>          rep stosl
> -
>          mov     %ebp, %eax

Are these two dropped lines intentional?

I did ask on the previous version; it's weird to introduce the code with
them present, then delete them in the subsequent patch.

> -        ret
> -
> -__start:
> -        cld
> -        cli
> -
> -        /*
> -         * Multiboot (both 1 and 2) specify the stack pointer as undefined
> -         * when entering in BIOS circumstances.  This is unhelpful for
> -         * relocatable images, where one call (i.e. push) is required to
> -         * calculate the image's load address.
> -         *
> -         * This early in boot, there is one area of memory we know about with
> -         * reasonable confidence that it isn't overlapped by Xen, and that's
> -         * the Multiboot info structure in %ebx.  Use it as a temporary stack.
> -         */
>  
> -        /* Preserve the field we're about to clobber. */
> -        mov     (%ebx), %edx
> -        lea     4(%ebx), %esp
> +#ifdef CONFIG_PVH_GUEST
> +        cmp     $XEN_HVM_START_MAGIC_VALUE, %eax
> +        jne     1f
>  
> -        /* Calculate the load base address. */
> -        call    1f
> -1:      pop     %esi
> -        sub     $sym_offs(1b), %esi
> +        mov     %ebx, sym_esi(pvh_start_info_pa)
>  
> -        /* Restore the clobbered field. */
> -        mov     %edx, (%ebx)
> +        /* Force xen console.  Will revert to user choice in init code. */
> +        movb    $-1, sym_esi(opt_console_xen)
>  
> -        /* Set up stack. */
> -        lea     STACK_SIZE - CPUINFO_sizeof + sym_esi(cpu0_stack), %esp
> +        /* Skip bootloader setup and bios setup, go straight to trampoline */
> +        movb    $1, sym_esi(pvh_boot)
> +        movb    $1, sym_esi(skip_realmode)
>  
> -        call    .Linitialise_bss
> +        /* Set trampoline_phys to use mfn 1 to avoid having a mapping at VA 0 */
> +        movl    $PAGE_SIZE, sym_esi(trampoline_phys)
> +        mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */

This isn't needed any more.  It was previously reloading %eax after REP
STOSL, but that's sorted elsehow now.


Overall, this bit of the diff is still hard to read, but it's the best
we're going to get I think.  The end result is good.

I'm happy to adjust all of these on commit.

~Andrew
Jan Beulich Sept. 26, 2024, 6:50 a.m. UTC | #2
On 25.09.2024 21:33, Andrew Cooper wrote:
> On 25/09/2024 7:00 am, Frediano Ziglio wrote:
>> @@ -449,62 +458,40 @@ __pvh_start:
>>          mov     %ecx, %es
>>          mov     %ecx, %ss
>>  
>> -        /* Skip bootloader setup and bios setup, go straight to trampoline */
>> -        movb    $1, sym_esi(pvh_boot)
>> -        movb    $1, sym_esi(skip_realmode)
>> +        /* Load null selector to unused segment registers. */
>> +        xor     %ecx, %ecx
>> +        mov     %ecx, %fs
>> +        mov     %ecx, %gs
> 
> Honestly, the more I look at this, the more bizarre it is.
> 
> We should just set up %fs/gs like we do %ds/es, which in this case is
> simply to drop the comment and the xor.

What's bizarre here? As long as we don't use %fs/%gs, it doesn't matter
much what we set them to. So yes, they may be set to what %ds etc are set
to, but they may as well be marked unusable. Documentation-wise that's
cleaner imo, as down the road - when a need to use one arises - it then
won't require auditing of all code to figure where the register(s) is(are)
actually used (just to find: nowhere). Even if a comment to this effect
was left here, I for one wouldn't trust it in a couple of years time, but
rather fear it went stale.

Jan
Frediano Ziglio Sept. 26, 2024, 9:24 a.m. UTC | #3
On Thu, Sep 26, 2024 at 7:50 AM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 25.09.2024 21:33, Andrew Cooper wrote:
> > On 25/09/2024 7:00 am, Frediano Ziglio wrote:
> >> @@ -449,62 +458,40 @@ __pvh_start:
> >>          mov     %ecx, %es
> >>          mov     %ecx, %ss
> >>
> >> -        /* Skip bootloader setup and bios setup, go straight to trampoline */
> >> -        movb    $1, sym_esi(pvh_boot)
> >> -        movb    $1, sym_esi(skip_realmode)
> >> +        /* Load null selector to unused segment registers. */
> >> +        xor     %ecx, %ecx
> >> +        mov     %ecx, %fs
> >> +        mov     %ecx, %gs
> >
> > Honestly, the more I look at this, the more bizarre it is.
> >
> > We should just set up %fs/gs like we do %ds/es, which in this case is
> > simply to drop the comment and the xor.
>
> What's bizarre here? As long as we don't use %fs/%gs, it doesn't matter
> much what we set them to. So yes, they may be set to what %ds etc are set
> to, but they may as well be marked unusable. Documentation-wise that's
> cleaner imo, as down the road - when a need to use one arises - it then
> won't require auditing of all code to figure where the register(s) is(are)
> actually used (just to find: nowhere). Even if a comment to this effect
> was left here, I for one wouldn't trust it in a couple of years time, but
> rather fear it went stale.
>
> Jan

Hi,
  are you against this change and asking to roll it back?

Frediano
Jan Beulich Sept. 26, 2024, 10:38 a.m. UTC | #4
On 26.09.2024 11:24, Frediano Ziglio wrote:
> On Thu, Sep 26, 2024 at 7:50 AM Jan Beulich <jbeulich@suse.com> wrote:
>>
>> On 25.09.2024 21:33, Andrew Cooper wrote:
>>> On 25/09/2024 7:00 am, Frediano Ziglio wrote:
>>>> @@ -449,62 +458,40 @@ __pvh_start:
>>>>          mov     %ecx, %es
>>>>          mov     %ecx, %ss
>>>>
>>>> -        /* Skip bootloader setup and bios setup, go straight to trampoline */
>>>> -        movb    $1, sym_esi(pvh_boot)
>>>> -        movb    $1, sym_esi(skip_realmode)
>>>> +        /* Load null selector to unused segment registers. */
>>>> +        xor     %ecx, %ecx
>>>> +        mov     %ecx, %fs
>>>> +        mov     %ecx, %gs
>>>
>>> Honestly, the more I look at this, the more bizarre it is.
>>>
>>> We should just set up %fs/gs like we do %ds/es, which in this case is
>>> simply to drop the comment and the xor.
>>
>> What's bizarre here? As long as we don't use %fs/%gs, it doesn't matter
>> much what we set them to. So yes, they may be set to what %ds etc are set
>> to, but they may as well be marked unusable. Documentation-wise that's
>> cleaner imo, as down the road - when a need to use one arises - it then
>> won't require auditing of all code to figure where the register(s) is(are)
>> actually used (just to find: nowhere). Even if a comment to this effect
>> was left here, I for one wouldn't trust it in a couple of years time, but
>> rather fear it went stale.
> 
> Hi,
>   are you against this change and asking to roll it back?

Well, first of all I'm hoping for a reply by Andrew. Maybe I'm overlooking
something. As it stands right now I indeed think we'd be better off keeping
nul selectors in %fs and %gs.

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index 267207e5a2..2d2f56ad22 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -25,6 +25,8 @@ 
 #define MB2_HT(name)      (MULTIBOOT2_HEADER_TAG_##name)
 #define MB2_TT(name)      (MULTIBOOT2_TAG_TYPE_##name)
 
+#define XEN_HVM_START_MAGIC_VALUE 0x336ec578
+
         .macro mb2ht_args arg:req, args:vararg
         .long \arg
         .ifnb \args
@@ -409,13 +411,27 @@  cs32_switch:
 ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start))
 
 __pvh_start:
-        cld
+        mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */
+        /*
+         * Fall through into BIOS code.
+         * We will use %eax to distinguish we came from PHV entry point.
+         */
+#endif /* CONFIG_PVH_GUEST */
+
+__start:
         cli
+        cld
 
         /*
-         * We need one call (i.e. push) to determine the load address.  See
-         * __start for a discussion on how to do this safely using the PVH
-         * info structure.
+         * Multiboot (both 1 and 2) and PVH specify the stack pointer as
+         * undefined.  This is unhelpful for relocatable images, where one
+         * call (i.e. push) is required to calculate the image's load address.
+         *
+         * Durig BIOS boot, there is one area of memory we know about with
+         * reasonable confidence that it isn't overlapped by Xen, and that's
+         * the Multiboot info structure in %ebx.  Use it as a temporary stack.
+         *
+         * During PVH boot use info structure in %ebx.
          */
 
         /* Preserve the field we're about to clobber. */
@@ -433,14 +449,7 @@  __pvh_start:
         /* Set up stack. */
         lea     STACK_SIZE - CPUINFO_sizeof + sym_esi(cpu0_stack), %esp
 
-        call    .Linitialise_bss
-
-        mov     %ebx, sym_esi(pvh_start_info_pa)
-
-        /* Force xen console.  Will revert to user choice in init code. */
-        movb    $-1, sym_esi(opt_console_xen)
-
-        /* Prepare gdt and segments */
+        /* Initialize GDTR and basic data segments. */
         add     %esi, sym_esi(gdt_boot_base)
         lgdt    sym_esi(gdt_boot_descr)
 
@@ -449,62 +458,40 @@  __pvh_start:
         mov     %ecx, %es
         mov     %ecx, %ss
 
-        /* Skip bootloader setup and bios setup, go straight to trampoline */
-        movb    $1, sym_esi(pvh_boot)
-        movb    $1, sym_esi(skip_realmode)
+        /* Load null selector to unused segment registers. */
+        xor     %ecx, %ecx
+        mov     %ecx, %fs
+        mov     %ecx, %gs
 
-        /* Set trampoline_phys to use mfn 1 to avoid having a mapping at VA 0 */
-        movw    $0x1000, sym_esi(trampoline_phys)
-        mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */
-        jmp     trampoline_setup
-
-#endif /* CONFIG_PVH_GUEST */
-
-.Linitialise_bss:
         /* Initialise the BSS.  Preserve %eax (BOOTLOADER_MAGIC). */
         mov     %eax, %ebp
-
         lea     sym_esi(__bss_start), %edi
         lea     sym_esi(__bss_end), %ecx
         sub     %edi, %ecx
         xor     %eax, %eax
         shr     $2, %ecx
         rep stosl
-
         mov     %ebp, %eax
-        ret
-
-__start:
-        cld
-        cli
-
-        /*
-         * Multiboot (both 1 and 2) specify the stack pointer as undefined
-         * when entering in BIOS circumstances.  This is unhelpful for
-         * relocatable images, where one call (i.e. push) is required to
-         * calculate the image's load address.
-         *
-         * This early in boot, there is one area of memory we know about with
-         * reasonable confidence that it isn't overlapped by Xen, and that's
-         * the Multiboot info structure in %ebx.  Use it as a temporary stack.
-         */
 
-        /* Preserve the field we're about to clobber. */
-        mov     (%ebx), %edx
-        lea     4(%ebx), %esp
+#ifdef CONFIG_PVH_GUEST
+        cmp     $XEN_HVM_START_MAGIC_VALUE, %eax
+        jne     1f
 
-        /* Calculate the load base address. */
-        call    1f
-1:      pop     %esi
-        sub     $sym_offs(1b), %esi
+        mov     %ebx, sym_esi(pvh_start_info_pa)
 
-        /* Restore the clobbered field. */
-        mov     %edx, (%ebx)
+        /* Force xen console.  Will revert to user choice in init code. */
+        movb    $-1, sym_esi(opt_console_xen)
 
-        /* Set up stack. */
-        lea     STACK_SIZE - CPUINFO_sizeof + sym_esi(cpu0_stack), %esp
+        /* Skip bootloader setup and bios setup, go straight to trampoline */
+        movb    $1, sym_esi(pvh_boot)
+        movb    $1, sym_esi(skip_realmode)
 
-        call    .Linitialise_bss
+        /* Set trampoline_phys to use mfn 1 to avoid having a mapping at VA 0 */
+        movl    $PAGE_SIZE, sym_esi(trampoline_phys)
+        mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */
+        jmp     trampoline_setup
+1:
+#endif /* CONFIG_PVH_GUEST */
 
         /* Bootloaders may set multiboot{1,2}.mem_lower to a nonzero value. */
         xor     %edx,%edx
@@ -563,22 +550,7 @@  __start:
 trampoline_bios_setup:
         /*
          * Called on legacy BIOS platforms only.
-         *
-         * Initialize GDTR and basic data segments.
          */
-        add     %esi,sym_esi(gdt_boot_base)
-        lgdt    sym_esi(gdt_boot_descr)
-
-        mov     $BOOT_DS,%ecx
-        mov     %ecx,%ds
-        mov     %ecx,%es
-        mov     %ecx,%ss
-        /* %esp is initialized later. */
-
-        /* Load null descriptor to unused segment registers. */
-        xor     %ecx,%ecx
-        mov     %ecx,%fs
-        mov     %ecx,%gs
 
         /* Set up trampoline segment 64k below EBDA */
         movzwl  0x40e,%ecx          /* EBDA segment */