diff mbox series

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

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

Commit Message

Frediano Ziglio Sept. 24, 2024, 10:28 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.
---
 xen/arch/x86/boot/head.S | 117 +++++++++++++++------------------------
 1 file changed, 45 insertions(+), 72 deletions(-)

Comments

Andrew Cooper Sept. 24, 2024, 1:25 p.m. UTC | #1
On 24/09/2024 11:28 am, Frediano Ziglio wrote:
> diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
> index fa21024042..80bba6ff21 100644
> --- a/xen/arch/x86/boot/head.S
> +++ b/xen/arch/x86/boot/head.S
>  ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start))
>  
>  __pvh_start:
> -        cld
> +        mov     $BOOT_TYPE_PVH, %dl
> +        jmp     .Lcommon_bios_pvh
> +#endif /* CONFIG_PVH_GUEST */
> +
> +__start:
> +        mov     $BOOT_TYPE_BIOS, %dl

Even if we're generally using %dl, these must be full %edx writes.

%edx commonly contains FMS on entry, and we don't want part of FMS left
in the upper half of the register.

> +
> +.Lcommon_bios_pvh:
>          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. */
> -        mov     (%ebx), %edx
> +        mov     (%ebx), %ecx

Both here, and ...

>          lea     4(%ebx), %esp
>  
>          /* Calculate the load base address. */
> @@ -449,62 +459,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)
> -
> -        /* 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 */
> +        /* Load null selector to unused segment registers. */
> +        xor     %ecx, %ecx
> +        mov     %ecx, %fs
> +        mov     %ecx, %gs
>  
> -.Linitialise_bss:
>  	/* Initialise the BSS. */
> -        mov     %eax, %edx
> -
> +        mov     %eax, %ebp

... here, we've got changes caused by now using %edx for a long-lived
purpose (and a change in linebreaks.)

For this, %ebp should be used straight away in patch 1.  I've not
committed it yet, so can fix that up.


I have to admit that I think this patch would be easier if the "use %ebx
for BOOT_TYPE_*" change was split out of "better merge the BIOS/PVH
paths".  That would at least get the incidental %edx changes out of the way.

Also, inserting

#ifdef CONFIG_PVH_GUEST
        cmp     $BOOT_TYPE_PVH, %dl
        jne     1f
1:
#endif /* CONFIG_PVH_GUEST */

in the same patch will probably make the subsequent diff far more legible.

Thoughts?

I might give this a quick go, and see how it ends up looking...

~Andrew
Andrew Cooper Sept. 24, 2024, 1:30 p.m. UTC | #2
On 24/09/2024 2:25 pm, Andrew Cooper wrote:
> On 24/09/2024 11:28 am, Frediano Ziglio wrote:
>> diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
>> index fa21024042..80bba6ff21 100644
>> --- a/xen/arch/x86/boot/head.S
>> +++ b/xen/arch/x86/boot/head.S
>>  ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start))
>>  
>>  __pvh_start:
>> -        cld
>> +        mov     $BOOT_TYPE_PVH, %dl
>> +        jmp     .Lcommon_bios_pvh
>> +#endif /* CONFIG_PVH_GUEST */
>> +
>> +__start:
>> +        mov     $BOOT_TYPE_BIOS, %dl
> Even if we're generally using %dl, these must be full %edx writes.
>
> %edx commonly contains FMS on entry, and we don't want part of FMS left
> in the upper half of the register.
>
>> +
>> +.Lcommon_bios_pvh:
>>          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. */
>> -        mov     (%ebx), %edx
>> +        mov     (%ebx), %ecx
> Both here, and ...
>
>>          lea     4(%ebx), %esp
>>  
>>          /* Calculate the load base address. */
>> @@ -449,62 +459,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)
>> -
>> -        /* 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 */
>> +        /* Load null selector to unused segment registers. */
>> +        xor     %ecx, %ecx
>> +        mov     %ecx, %fs
>> +        mov     %ecx, %gs
>>  
>> -.Linitialise_bss:
>>  	/* Initialise the BSS. */
>> -        mov     %eax, %edx
>> -
>> +        mov     %eax, %ebp
> ... here, we've got changes caused by now using %edx for a long-lived
> purpose (and a change in linebreaks.)
>
> For this, %ebp should be used straight away in patch 1.  I've not
> committed it yet, so can fix that up.
>
>
> I have to admit that I think this patch would be easier if the "use %ebx
> for BOOT_TYPE_*" change was split out of "better merge the BIOS/PVH
> paths".  That would at least get the incidental %edx changes out of the way.
>
> Also, inserting
>
> #ifdef CONFIG_PVH_GUEST
>         cmp     $BOOT_TYPE_PVH, %dl
>         jne     1f
> 1:
> #endif /* CONFIG_PVH_GUEST */
>
> in the same patch will probably make the subsequent diff far more legible.
>
> Thoughts?
>
> I might give this a quick go, and see how it ends up looking...

Actually, why do we need BOOT_TYPE_* at all?  We've already got
BOOTLOADER_MAGIC in %eax which can be used to identify PVH.

~Andrew
Andrew Cooper Sept. 24, 2024, 1:45 p.m. UTC | #3
On 24/09/2024 2:30 pm, Andrew Cooper wrote:
> On 24/09/2024 2:25 pm, Andrew Cooper wrote:
>> On 24/09/2024 11:28 am, Frediano Ziglio wrote:
>>> diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
>>> index fa21024042..80bba6ff21 100644
>>> --- a/xen/arch/x86/boot/head.S
>>> +++ b/xen/arch/x86/boot/head.S
>>>  ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start))
>>>  
>>>  __pvh_start:
>>> -        cld
>>> +        mov     $BOOT_TYPE_PVH, %dl
>>> +        jmp     .Lcommon_bios_pvh
>>> +#endif /* CONFIG_PVH_GUEST */
>>> +
>>> +__start:
>>> +        mov     $BOOT_TYPE_BIOS, %dl
>> Even if we're generally using %dl, these must be full %edx writes.
>>
>> %edx commonly contains FMS on entry, and we don't want part of FMS left
>> in the upper half of the register.
>>
>>> +
>>> +.Lcommon_bios_pvh:
>>>          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. */
>>> -        mov     (%ebx), %edx
>>> +        mov     (%ebx), %ecx
>> Both here, and ...
>>
>>>          lea     4(%ebx), %esp
>>>  
>>>          /* Calculate the load base address. */
>>> @@ -449,62 +459,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)
>>> -
>>> -        /* 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 */
>>> +        /* Load null selector to unused segment registers. */
>>> +        xor     %ecx, %ecx
>>> +        mov     %ecx, %fs
>>> +        mov     %ecx, %gs
>>>  
>>> -.Linitialise_bss:
>>>  	/* Initialise the BSS. */
>>> -        mov     %eax, %edx
>>> -
>>> +        mov     %eax, %ebp
>> ... here, we've got changes caused by now using %edx for a long-lived
>> purpose (and a change in linebreaks.)
>>
>> For this, %ebp should be used straight away in patch 1.  I've not
>> committed it yet, so can fix that up.
>>
>>
>> I have to admit that I think this patch would be easier if the "use %ebx
>> for BOOT_TYPE_*" change was split out of "better merge the BIOS/PVH
>> paths".  That would at least get the incidental %edx changes out of the way.
>>
>> Also, inserting
>>
>> #ifdef CONFIG_PVH_GUEST
>>         cmp     $BOOT_TYPE_PVH, %dl
>>         jne     1f
>> 1:
>> #endif /* CONFIG_PVH_GUEST */
>>
>> in the same patch will probably make the subsequent diff far more legible.
>>
>> Thoughts?
>>
>> I might give this a quick go, and see how it ends up looking...
> Actually, why do we need BOOT_TYPE_* at all?  We've already got
> BOOTLOADER_MAGIC in %eax which can be used to identify PVH.

Summary of the "quick look".

The suggested empty ifdefary doesn't really help.

However, moving the

    mov     (%ebx), %eax /* mov $XEN_HVM_START_MAGIC_VALUE, %eax */

into __pvh_entry avoids the need for any BOOT_TYPE_* being held in %edx.

The one place where it's needed can be `cmp $XEN_ ...; jne` and this
avoids needing to shuffle the register scheduling.

~Andrew
diff mbox series

Patch

diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index fa21024042..80bba6ff21 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -25,6 +25,9 @@ 
 #define MB2_HT(name)      (MULTIBOOT2_HEADER_TAG_##name)
 #define MB2_TT(name)      (MULTIBOOT2_TAG_TYPE_##name)
 
+#define BOOT_TYPE_BIOS 1
+#define BOOT_TYPE_PVH 2
+
         .macro mb2ht_args arg:req, args:vararg
         .long \arg
         .ifnb \args
@@ -409,17 +412,31 @@  cs32_switch:
 ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start))
 
 __pvh_start:
-        cld
+        mov     $BOOT_TYPE_PVH, %dl
+        jmp     .Lcommon_bios_pvh
+#endif /* CONFIG_PVH_GUEST */
+
+__start:
+        mov     $BOOT_TYPE_BIOS, %dl
+
+.Lcommon_bios_pvh:
         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. */
-        mov     (%ebx), %edx
+        mov     (%ebx), %ecx
         lea     4(%ebx), %esp
 
         /* Calculate the load base address. */
@@ -428,19 +445,12 @@  __pvh_start:
         sub     $sym_offs(1b), %esi
 
         /* Restore the clobbered field. */
-        mov     %edx, (%ebx)
+        mov     %ecx, (%ebx)
 
         /* 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 +459,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)
-
-        /* 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 */
+        /* Load null selector to unused segment registers. */
+        xor     %ecx, %ecx
+        mov     %ecx, %fs
+        mov     %ecx, %gs
 
-.Linitialise_bss:
 	/* Initialise the BSS. */
-        mov     %eax, %edx
-
+        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
 
-        mov     %edx, %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     $BOOT_TYPE_PVH, %dl
+        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 +551,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 */