diff mbox series

[RFC,3/3] pvh: Boot uncompressed kernel using direct boot ABI

Message ID 1544049446-6359-4-git-send-email-liam.merwick@oracle.com (mailing list archive)
State New, archived
Headers show
Series QEMU changes to do PVH boot | expand

Commit Message

Liam Merwick Dec. 5, 2018, 10:37 p.m. UTC
These changes (along with corresponding qboot and Linux kernel changes)
enable a guest to be booted using the x86/HVM direct boot ABI.

This commit adds a load_elfboot() routine to pass the size and
location of the kernel entry point to qboot (which will fill in
the start_info struct information needed to to boot the guest).
Having loaded the ELF binary, load_linux() will run qboot
which continues the boot.

The address for the kernel entry point has already been read
from an ELF Note in the uncompressed kernel binary earlier
in pc_memory_init().

Signed-off-by: George Kennedy <George.Kennedy@oracle.com>
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
---
 hw/i386/pc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

Comments

Stefano Garzarella Dec. 11, 2018, 5:11 p.m. UTC | #1
Hi Liam,
in order to support PVH also with SeaBIOS, I'm going to work on a new
option rom (like linuxboot/multiboot) that can be used in this case.

I'll keep you updated on it!

Cheers,
Stefano
On Wed, Dec 5, 2018 at 11:38 PM Liam Merwick <liam.merwick@oracle.com> wrote:
>
> These changes (along with corresponding qboot and Linux kernel changes)
> enable a guest to be booted using the x86/HVM direct boot ABI.
>
> This commit adds a load_elfboot() routine to pass the size and
> location of the kernel entry point to qboot (which will fill in
> the start_info struct information needed to to boot the guest).
> Having loaded the ELF binary, load_linux() will run qboot
> which continues the boot.
>
> The address for the kernel entry point has already been read
> from an ELF Note in the uncompressed kernel binary earlier
> in pc_memory_init().
>
> Signed-off-by: George Kennedy <George.Kennedy@oracle.com>
> Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
> ---
>  hw/i386/pc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 72 insertions(+)
>
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index 056aa46d99b9..d3012cbd8597 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -54,6 +54,7 @@
>  #include "sysemu/qtest.h"
>  #include "kvm_i386.h"
>  #include "hw/xen/xen.h"
> +#include "hw/xen/start_info.h"
>  #include "ui/qemu-spice.h"
>  #include "exec/memory.h"
>  #include "exec/address-spaces.h"
> @@ -1098,6 +1099,50 @@ done:
>      return pvh_start_addr != 0;
>  }
>
> +static bool load_elfboot(const char *kernel_filename,
> +                   int kernel_file_size,
> +                   uint8_t *header,
> +                   size_t pvh_xen_start_addr,
> +                   FWCfgState *fw_cfg)
> +{
> +    uint32_t flags = 0;
> +    uint32_t mh_load_addr = 0;
> +    uint32_t elf_kernel_size = 0;
> +    uint64_t elf_entry;
> +    uint64_t elf_low, elf_high;
> +    int kernel_size;
> +
> +    if (ldl_p(header) != 0x464c457f) {
> +        return false; /* no elfboot */
> +    }
> +
> +    bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
> +    flags = elf_is64 ?
> +        ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
> +
> +    if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
> +        error_report("elfboot unsupported flags = %x", flags);
> +        exit(1);
> +    }
> +
> +    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
> +                           &elf_low, &elf_high, 0, I386_ELF_MACHINE,
> +                           0, 0);
> +
> +    if (kernel_size < 0) {
> +        error_report("Error while loading elf kernel");
> +        exit(1);
> +    }
> +    mh_load_addr = elf_low;
> +    elf_kernel_size = elf_high - elf_low;
> +
> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_xen_start_addr);
> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
> +
> +    return true;
> +}
> +
>  static void load_linux(PCMachineState *pcms,
>                         FWCfgState *fw_cfg)
>  {
> @@ -1138,6 +1183,33 @@ static void load_linux(PCMachineState *pcms,
>      if (ldl_p(header+0x202) == 0x53726448) {
>          protocol = lduw_p(header+0x206);
>      } else {
> +        /* If the kernel address for using the x86/HVM direct boot ABI has
> +         * been saved then proceed with booting the uncompressed kernel */
> +        if (pvh_start_addr) {
> +            if (load_elfboot(kernel_filename, kernel_size,
> +                             header, pvh_start_addr, fw_cfg)) {
> +                struct hvm_modlist_entry ramdisk_mod = { 0 };
> +
> +                fclose(f);
> +
> +                fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
> +                    strlen(kernel_cmdline) + 1);
> +                fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
> +
> +                assert(machine->device_memory != NULL);
> +                ramdisk_mod.paddr = machine->device_memory->base;
> +                ramdisk_mod.size =
> +                    memory_region_size(&machine->device_memory->mr);
> +
> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, &ramdisk_mod,
> +                                 sizeof(ramdisk_mod));
> +                fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
> +                                 header, sizeof(header));
> +
> +                return;
> +            }
> +        }
>          /* This looks like a multiboot kernel. If it is, let's stop
>             treating it like a Linux kernel. */
>          if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
> --
> 1.8.3.1
>
Maran Wilson Dec. 11, 2018, 6:35 p.m. UTC | #2
On 12/11/2018 9:11 AM, Stefano Garzarella wrote:
> Hi Liam,
> in order to support PVH also with SeaBIOS, I'm going to work on a new
> option rom (like linuxboot/multiboot) that can be used in this case.

That is awesome. Yes, please keep us posted when you have something working.

Just FYI, before switching over to using Qemu+qboot, we had been using a 
Qemu only solution (but not using an option rom) internally that worked 
very well using no FW at all. We had Qemu simply parse the ELF file and 
jump to the PVH entry point if one is found. The only gotcha was that we 
had to include a pair of patches that were originally written by folks 
at Intel as part of the clear containers work. Specifically, in order to 
be able to skip firmware entirely, we had to do 2 additional things: (1) 
ACPI tables generated by Qemu are usually patched up by FW. Since we 
were running no FW, we needed to do that patching up of the ACPI tables 
in Qemu when it was detected that we were going to enter the OS via the 
PVH entry point. (2) We also needed to add a patch to Qemu to enable a 
few PM registers -- something typically done by FW.

But if SeaBIOS is involved in the solution you are working on, I guess 
you won't really need those extra patches. Just figured I'd mention it 
so you have the full picture.

Thanks,
-Maran

> I'll keep you updated on it!
>
> Cheers,
> Stefano
> On Wed, Dec 5, 2018 at 11:38 PM Liam Merwick <liam.merwick@oracle.com> wrote:
>> These changes (along with corresponding qboot and Linux kernel changes)
>> enable a guest to be booted using the x86/HVM direct boot ABI.
>>
>> This commit adds a load_elfboot() routine to pass the size and
>> location of the kernel entry point to qboot (which will fill in
>> the start_info struct information needed to to boot the guest).
>> Having loaded the ELF binary, load_linux() will run qboot
>> which continues the boot.
>>
>> The address for the kernel entry point has already been read
>> from an ELF Note in the uncompressed kernel binary earlier
>> in pc_memory_init().
>>
>> Signed-off-by: George Kennedy <George.Kennedy@oracle.com>
>> Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
>> ---
>>   hw/i386/pc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 72 insertions(+)
>>
>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>> index 056aa46d99b9..d3012cbd8597 100644
>> --- a/hw/i386/pc.c
>> +++ b/hw/i386/pc.c
>> @@ -54,6 +54,7 @@
>>   #include "sysemu/qtest.h"
>>   #include "kvm_i386.h"
>>   #include "hw/xen/xen.h"
>> +#include "hw/xen/start_info.h"
>>   #include "ui/qemu-spice.h"
>>   #include "exec/memory.h"
>>   #include "exec/address-spaces.h"
>> @@ -1098,6 +1099,50 @@ done:
>>       return pvh_start_addr != 0;
>>   }
>>
>> +static bool load_elfboot(const char *kernel_filename,
>> +                   int kernel_file_size,
>> +                   uint8_t *header,
>> +                   size_t pvh_xen_start_addr,
>> +                   FWCfgState *fw_cfg)
>> +{
>> +    uint32_t flags = 0;
>> +    uint32_t mh_load_addr = 0;
>> +    uint32_t elf_kernel_size = 0;
>> +    uint64_t elf_entry;
>> +    uint64_t elf_low, elf_high;
>> +    int kernel_size;
>> +
>> +    if (ldl_p(header) != 0x464c457f) {
>> +        return false; /* no elfboot */
>> +    }
>> +
>> +    bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
>> +    flags = elf_is64 ?
>> +        ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
>> +
>> +    if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
>> +        error_report("elfboot unsupported flags = %x", flags);
>> +        exit(1);
>> +    }
>> +
>> +    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
>> +                           &elf_low, &elf_high, 0, I386_ELF_MACHINE,
>> +                           0, 0);
>> +
>> +    if (kernel_size < 0) {
>> +        error_report("Error while loading elf kernel");
>> +        exit(1);
>> +    }
>> +    mh_load_addr = elf_low;
>> +    elf_kernel_size = elf_high - elf_low;
>> +
>> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_xen_start_addr);
>> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
>> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
>> +
>> +    return true;
>> +}
>> +
>>   static void load_linux(PCMachineState *pcms,
>>                          FWCfgState *fw_cfg)
>>   {
>> @@ -1138,6 +1183,33 @@ static void load_linux(PCMachineState *pcms,
>>       if (ldl_p(header+0x202) == 0x53726448) {
>>           protocol = lduw_p(header+0x206);
>>       } else {
>> +        /* If the kernel address for using the x86/HVM direct boot ABI has
>> +         * been saved then proceed with booting the uncompressed kernel */
>> +        if (pvh_start_addr) {
>> +            if (load_elfboot(kernel_filename, kernel_size,
>> +                             header, pvh_start_addr, fw_cfg)) {
>> +                struct hvm_modlist_entry ramdisk_mod = { 0 };
>> +
>> +                fclose(f);
>> +
>> +                fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
>> +                    strlen(kernel_cmdline) + 1);
>> +                fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
>> +
>> +                assert(machine->device_memory != NULL);
>> +                ramdisk_mod.paddr = machine->device_memory->base;
>> +                ramdisk_mod.size =
>> +                    memory_region_size(&machine->device_memory->mr);
>> +
>> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, &ramdisk_mod,
>> +                                 sizeof(ramdisk_mod));
>> +                fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
>> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
>> +                                 header, sizeof(header));
>> +
>> +                return;
>> +            }
>> +        }
>>           /* This looks like a multiboot kernel. If it is, let's stop
>>              treating it like a Linux kernel. */
>>           if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
>> --
>> 1.8.3.1
>>
>
Stefano Garzarella Dec. 12, 2018, 3:28 p.m. UTC | #3
On Tue, Dec 11, 2018 at 7:35 PM Maran Wilson <maran.wilson@oracle.com> wrote:
>
> On 12/11/2018 9:11 AM, Stefano Garzarella wrote:
> > Hi Liam,
> > in order to support PVH also with SeaBIOS, I'm going to work on a new
> > option rom (like linuxboot/multiboot) that can be used in this case.
>
> That is awesome. Yes, please keep us posted when you have something working.

Yes, I'll keep you updated!

>
> Just FYI, before switching over to using Qemu+qboot, we had been using a
> Qemu only solution (but not using an option rom) internally that worked
> very well using no FW at all. We had Qemu simply parse the ELF file and
> jump to the PVH entry point if one is found. The only gotcha was that we
> had to include a pair of patches that were originally written by folks
> at Intel as part of the clear containers work. Specifically, in order to
> be able to skip firmware entirely, we had to do 2 additional things: (1)
> ACPI tables generated by Qemu are usually patched up by FW. Since we
> were running no FW, we needed to do that patching up of the ACPI tables
> in Qemu when it was detected that we were going to enter the OS via the
> PVH entry point. (2) We also needed to add a patch to Qemu to enable a
> few PM registers -- something typically done by FW.

I had a look of qemu-lite, are you referring to this?

>
> But if SeaBIOS is involved in the solution you are working on, I guess
> you won't really need those extra patches. Just figured I'd mention it
> so you have the full picture.

Thank you very much to share with me these details!

Cheers,
Stefano

>
> Thanks,
> -Maran
>
> > I'll keep you updated on it!
> >
> > Cheers,
> > Stefano
> > On Wed, Dec 5, 2018 at 11:38 PM Liam Merwick <liam.merwick@oracle.com> wrote:
> >> These changes (along with corresponding qboot and Linux kernel changes)
> >> enable a guest to be booted using the x86/HVM direct boot ABI.
> >>
> >> This commit adds a load_elfboot() routine to pass the size and
> >> location of the kernel entry point to qboot (which will fill in
> >> the start_info struct information needed to to boot the guest).
> >> Having loaded the ELF binary, load_linux() will run qboot
> >> which continues the boot.
> >>
> >> The address for the kernel entry point has already been read
> >> from an ELF Note in the uncompressed kernel binary earlier
> >> in pc_memory_init().
> >>
> >> Signed-off-by: George Kennedy <George.Kennedy@oracle.com>
> >> Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
> >> ---
> >>   hw/i386/pc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>   1 file changed, 72 insertions(+)
> >>
> >> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> >> index 056aa46d99b9..d3012cbd8597 100644
> >> --- a/hw/i386/pc.c
> >> +++ b/hw/i386/pc.c
> >> @@ -54,6 +54,7 @@
> >>   #include "sysemu/qtest.h"
> >>   #include "kvm_i386.h"
> >>   #include "hw/xen/xen.h"
> >> +#include "hw/xen/start_info.h"
> >>   #include "ui/qemu-spice.h"
> >>   #include "exec/memory.h"
> >>   #include "exec/address-spaces.h"
> >> @@ -1098,6 +1099,50 @@ done:
> >>       return pvh_start_addr != 0;
> >>   }
> >>
> >> +static bool load_elfboot(const char *kernel_filename,
> >> +                   int kernel_file_size,
> >> +                   uint8_t *header,
> >> +                   size_t pvh_xen_start_addr,
> >> +                   FWCfgState *fw_cfg)
> >> +{
> >> +    uint32_t flags = 0;
> >> +    uint32_t mh_load_addr = 0;
> >> +    uint32_t elf_kernel_size = 0;
> >> +    uint64_t elf_entry;
> >> +    uint64_t elf_low, elf_high;
> >> +    int kernel_size;
> >> +
> >> +    if (ldl_p(header) != 0x464c457f) {
> >> +        return false; /* no elfboot */
> >> +    }
> >> +
> >> +    bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
> >> +    flags = elf_is64 ?
> >> +        ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
> >> +
> >> +    if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
> >> +        error_report("elfboot unsupported flags = %x", flags);
> >> +        exit(1);
> >> +    }
> >> +
> >> +    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
> >> +                           &elf_low, &elf_high, 0, I386_ELF_MACHINE,
> >> +                           0, 0);
> >> +
> >> +    if (kernel_size < 0) {
> >> +        error_report("Error while loading elf kernel");
> >> +        exit(1);
> >> +    }
> >> +    mh_load_addr = elf_low;
> >> +    elf_kernel_size = elf_high - elf_low;
> >> +
> >> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_xen_start_addr);
> >> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
> >> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
> >> +
> >> +    return true;
> >> +}
> >> +
> >>   static void load_linux(PCMachineState *pcms,
> >>                          FWCfgState *fw_cfg)
> >>   {
> >> @@ -1138,6 +1183,33 @@ static void load_linux(PCMachineState *pcms,
> >>       if (ldl_p(header+0x202) == 0x53726448) {
> >>           protocol = lduw_p(header+0x206);
> >>       } else {
> >> +        /* If the kernel address for using the x86/HVM direct boot ABI has
> >> +         * been saved then proceed with booting the uncompressed kernel */
> >> +        if (pvh_start_addr) {
> >> +            if (load_elfboot(kernel_filename, kernel_size,
> >> +                             header, pvh_start_addr, fw_cfg)) {
> >> +                struct hvm_modlist_entry ramdisk_mod = { 0 };
> >> +
> >> +                fclose(f);
> >> +
> >> +                fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
> >> +                    strlen(kernel_cmdline) + 1);
> >> +                fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
> >> +
> >> +                assert(machine->device_memory != NULL);
> >> +                ramdisk_mod.paddr = machine->device_memory->base;
> >> +                ramdisk_mod.size =
> >> +                    memory_region_size(&machine->device_memory->mr);
> >> +
> >> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, &ramdisk_mod,
> >> +                                 sizeof(ramdisk_mod));
> >> +                fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
> >> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
> >> +                                 header, sizeof(header));
> >> +
> >> +                return;
> >> +            }
> >> +        }
> >>           /* This looks like a multiboot kernel. If it is, let's stop
> >>              treating it like a Linux kernel. */
> >>           if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
> >> --
> >> 1.8.3.1
> >>
> >
>
Maran Wilson Dec. 12, 2018, 5:36 p.m. UTC | #4
On 12/12/2018 7:28 AM, Stefano Garzarella wrote:
> On Tue, Dec 11, 2018 at 7:35 PM Maran Wilson <maran.wilson@oracle.com> wrote:
>> On 12/11/2018 9:11 AM, Stefano Garzarella wrote:
>>> Hi Liam,
>>> in order to support PVH also with SeaBIOS, I'm going to work on a new
>>> option rom (like linuxboot/multiboot) that can be used in this case.
>> That is awesome. Yes, please keep us posted when you have something working.
> Yes, I'll keep you updated!
>
>> Just FYI, before switching over to using Qemu+qboot, we had been using a
>> Qemu only solution (but not using an option rom) internally that worked
>> very well using no FW at all. We had Qemu simply parse the ELF file and
>> jump to the PVH entry point if one is found. The only gotcha was that we
>> had to include a pair of patches that were originally written by folks
>> at Intel as part of the clear containers work. Specifically, in order to
>> be able to skip firmware entirely, we had to do 2 additional things: (1)
>> ACPI tables generated by Qemu are usually patched up by FW. Since we
>> were running no FW, we needed to do that patching up of the ACPI tables
>> in Qemu when it was detected that we were going to enter the OS via the
>> PVH entry point. (2) We also needed to add a patch to Qemu to enable a
>> few PM registers -- something typically done by FW.
> I had a look of qemu-lite, are you referring to this?

Yes. More specifically, we were using a modified version of this patch:
    acpi: patch guest ACPI when loading firmware is skipped
But unlike qemu-lite, we were not using a -nofw flag, instead, just 
choosing PVH vs legacy boot based on which -kernel binary was provided 
and whether it contained the PVH ELF note.

So apply the above patch, you also need to pick up:
    acpi: expose acpi_checksum()

For a while, we had also been using patch:
    ich9: enable pm registers when there is no firmware
But that last patch can be avoided by simply selecting Hardware-Reduced 
ACPI mode when building the FADT in Qemu, when PVH boot is selected.

But you probably wont need those patches at all if you are actually 
running some version of minimized SeaBIOS.

Thanks,
-Maran


>> But if SeaBIOS is involved in the solution you are working on, I guess
>> you won't really need those extra patches. Just figured I'd mention it
>> so you have the full picture.
> Thank you very much to share with me these details!
>
> Cheers,
> Stefano
>
>> Thanks,
>> -Maran
>>
>>> I'll keep you updated on it!
>>>
>>> Cheers,
>>> Stefano
>>> On Wed, Dec 5, 2018 at 11:38 PM Liam Merwick <liam.merwick@oracle.com> wrote:
>>>> These changes (along with corresponding qboot and Linux kernel changes)
>>>> enable a guest to be booted using the x86/HVM direct boot ABI.
>>>>
>>>> This commit adds a load_elfboot() routine to pass the size and
>>>> location of the kernel entry point to qboot (which will fill in
>>>> the start_info struct information needed to to boot the guest).
>>>> Having loaded the ELF binary, load_linux() will run qboot
>>>> which continues the boot.
>>>>
>>>> The address for the kernel entry point has already been read
>>>> from an ELF Note in the uncompressed kernel binary earlier
>>>> in pc_memory_init().
>>>>
>>>> Signed-off-by: George Kennedy <George.Kennedy@oracle.com>
>>>> Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
>>>> ---
>>>>    hw/i386/pc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 72 insertions(+)
>>>>
>>>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>>>> index 056aa46d99b9..d3012cbd8597 100644
>>>> --- a/hw/i386/pc.c
>>>> +++ b/hw/i386/pc.c
>>>> @@ -54,6 +54,7 @@
>>>>    #include "sysemu/qtest.h"
>>>>    #include "kvm_i386.h"
>>>>    #include "hw/xen/xen.h"
>>>> +#include "hw/xen/start_info.h"
>>>>    #include "ui/qemu-spice.h"
>>>>    #include "exec/memory.h"
>>>>    #include "exec/address-spaces.h"
>>>> @@ -1098,6 +1099,50 @@ done:
>>>>        return pvh_start_addr != 0;
>>>>    }
>>>>
>>>> +static bool load_elfboot(const char *kernel_filename,
>>>> +                   int kernel_file_size,
>>>> +                   uint8_t *header,
>>>> +                   size_t pvh_xen_start_addr,
>>>> +                   FWCfgState *fw_cfg)
>>>> +{
>>>> +    uint32_t flags = 0;
>>>> +    uint32_t mh_load_addr = 0;
>>>> +    uint32_t elf_kernel_size = 0;
>>>> +    uint64_t elf_entry;
>>>> +    uint64_t elf_low, elf_high;
>>>> +    int kernel_size;
>>>> +
>>>> +    if (ldl_p(header) != 0x464c457f) {
>>>> +        return false; /* no elfboot */
>>>> +    }
>>>> +
>>>> +    bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
>>>> +    flags = elf_is64 ?
>>>> +        ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
>>>> +
>>>> +    if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
>>>> +        error_report("elfboot unsupported flags = %x", flags);
>>>> +        exit(1);
>>>> +    }
>>>> +
>>>> +    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
>>>> +                           &elf_low, &elf_high, 0, I386_ELF_MACHINE,
>>>> +                           0, 0);
>>>> +
>>>> +    if (kernel_size < 0) {
>>>> +        error_report("Error while loading elf kernel");
>>>> +        exit(1);
>>>> +    }
>>>> +    mh_load_addr = elf_low;
>>>> +    elf_kernel_size = elf_high - elf_low;
>>>> +
>>>> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_xen_start_addr);
>>>> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
>>>> +    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
>>>> +
>>>> +    return true;
>>>> +}
>>>> +
>>>>    static void load_linux(PCMachineState *pcms,
>>>>                           FWCfgState *fw_cfg)
>>>>    {
>>>> @@ -1138,6 +1183,33 @@ static void load_linux(PCMachineState *pcms,
>>>>        if (ldl_p(header+0x202) == 0x53726448) {
>>>>            protocol = lduw_p(header+0x206);
>>>>        } else {
>>>> +        /* If the kernel address for using the x86/HVM direct boot ABI has
>>>> +         * been saved then proceed with booting the uncompressed kernel */
>>>> +        if (pvh_start_addr) {
>>>> +            if (load_elfboot(kernel_filename, kernel_size,
>>>> +                             header, pvh_start_addr, fw_cfg)) {
>>>> +                struct hvm_modlist_entry ramdisk_mod = { 0 };
>>>> +
>>>> +                fclose(f);
>>>> +
>>>> +                fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
>>>> +                    strlen(kernel_cmdline) + 1);
>>>> +                fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
>>>> +
>>>> +                assert(machine->device_memory != NULL);
>>>> +                ramdisk_mod.paddr = machine->device_memory->base;
>>>> +                ramdisk_mod.size =
>>>> +                    memory_region_size(&machine->device_memory->mr);
>>>> +
>>>> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, &ramdisk_mod,
>>>> +                                 sizeof(ramdisk_mod));
>>>> +                fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
>>>> +                fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
>>>> +                                 header, sizeof(header));
>>>> +
>>>> +                return;
>>>> +            }
>>>> +        }
>>>>            /* This looks like a multiboot kernel. If it is, let's stop
>>>>               treating it like a Linux kernel. */
>>>>            if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
>>>> --
>>>> 1.8.3.1
>>>>
>
diff mbox series

Patch

diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 056aa46d99b9..d3012cbd8597 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -54,6 +54,7 @@ 
 #include "sysemu/qtest.h"
 #include "kvm_i386.h"
 #include "hw/xen/xen.h"
+#include "hw/xen/start_info.h"
 #include "ui/qemu-spice.h"
 #include "exec/memory.h"
 #include "exec/address-spaces.h"
@@ -1098,6 +1099,50 @@  done:
     return pvh_start_addr != 0;
 }
 
+static bool load_elfboot(const char *kernel_filename,
+                   int kernel_file_size,
+                   uint8_t *header,
+                   size_t pvh_xen_start_addr,
+                   FWCfgState *fw_cfg)
+{
+    uint32_t flags = 0;
+    uint32_t mh_load_addr = 0;
+    uint32_t elf_kernel_size = 0;
+    uint64_t elf_entry;
+    uint64_t elf_low, elf_high;
+    int kernel_size;
+
+    if (ldl_p(header) != 0x464c457f) {
+        return false; /* no elfboot */
+    }
+
+    bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
+    flags = elf_is64 ?
+        ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
+
+    if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
+        error_report("elfboot unsupported flags = %x", flags);
+        exit(1);
+    }
+
+    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                           &elf_low, &elf_high, 0, I386_ELF_MACHINE,
+                           0, 0);
+
+    if (kernel_size < 0) {
+        error_report("Error while loading elf kernel");
+        exit(1);
+    }
+    mh_load_addr = elf_low;
+    elf_kernel_size = elf_high - elf_low;
+
+    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_xen_start_addr);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
+
+    return true;
+}
+
 static void load_linux(PCMachineState *pcms,
                        FWCfgState *fw_cfg)
 {
@@ -1138,6 +1183,33 @@  static void load_linux(PCMachineState *pcms,
     if (ldl_p(header+0x202) == 0x53726448) {
         protocol = lduw_p(header+0x206);
     } else {
+        /* If the kernel address for using the x86/HVM direct boot ABI has
+         * been saved then proceed with booting the uncompressed kernel */
+        if (pvh_start_addr) {
+            if (load_elfboot(kernel_filename, kernel_size,
+                             header, pvh_start_addr, fw_cfg)) {
+                struct hvm_modlist_entry ramdisk_mod = { 0 };
+
+                fclose(f);
+
+                fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+                    strlen(kernel_cmdline) + 1);
+                fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
+
+                assert(machine->device_memory != NULL);
+                ramdisk_mod.paddr = machine->device_memory->base;
+                ramdisk_mod.size =
+                    memory_region_size(&machine->device_memory->mr);
+
+                fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, &ramdisk_mod,
+                                 sizeof(ramdisk_mod));
+                fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
+                fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
+                                 header, sizeof(header));
+
+                return;
+            }
+        }
         /* This looks like a multiboot kernel. If it is, let's stop
            treating it like a Linux kernel. */
         if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,