qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Adopting abandoned patch?


From: David Woodhouse
Subject: Re: Adopting abandoned patch?
Date: Tue, 28 Feb 2023 15:12:42 +0000
User-agent: Evolution 3.44.4-0ubuntu1

On Tue, 2023-02-28 at 08:16 +0000, Daniel P. Berrangé wrote:
> On Mon, Feb 27, 2023 at 04:34:12PM -0500, Dinah B wrote:
> > It looks like the author didn't include a "Signed off" in their patch draft
> > and it doesn't look like Debian qemu-kvm maintainers ever merged it.
> > Does this change the patch adoption process?
> > 
> > On Mon, Feb 27, 2023 at 4:23 PM Dinah B <dinahbaum123@gmail.com> wrote:
> > 
> > > Thanks, here's the original patch:
> > > https://bugs.debian.org/cgi-bin/bugreport.cgi?att=2;bug=621529;filename=multiboot2.patch;msg=15
> 
> That is unfortunate. That patch is very large and complex, so I don't
> think we'd be willing to take it without a Signed-off-by from the
> original author.
> 
> It is quite old, but try emailing the original author, you might
> get lucky and find their email addr still works

We added multiboot2 support to kexec-tools which you are welcome to
take code from.

I see there's an option ROM in the patch you referenced; I'm not quite
sure how that gets used, but here's one you can have that we use for
loading the Xen shim (in a KVM-pretending-to-be-Xen-HVM guest) to
launch Xen PV guests.

We do the multiboot processing in the VMM, load the executable and
modules into guest RAM, then just install an oprom which will launch it
when the BIOS invokes INT 18h.

Feel free to use this in QEMU under GPLv2 if it's useful. I'll be happy
to add a signed-off-by if you work it into individual patches, but for
now,
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>

/******************************************************************************
 *
 * PV Shim as an expansion ROM.
 *
 * We already have an ELF and Multiboot loader in Brimstone, which is
 * precisely what Xen needs. We can't *just* go directly into the Xen
 * multiboot entry point though, since it does expect *some* form of
 * BIOS to be present.
 *
 * So we perform the Elf/Multiboot load, placing Xen where it wants to
 * be at 0x200000, and then allow xenloader/ROMBIOS to initialise as
 * normal. But we install an 'expansion ROM' at 0xE0000 which will
 * register itself as a bootable target (BEV), so ROMBIOS will invoke
 * it instead of trying to boot from the disk once everything's set up.
 *
 * When invoked, the BEV entry point just switches to 32-bit mode,
 * loads the two registers with the Multiboot magic and pointer to the
 * data structure that Brimstone set up earlier, and jumps to Xen's
 * entry point.
 */

#define OPROM_SIG 0xaa55;
struct oprom_hdr {
        uint16_t sig;
        uint8_t romlen; /* in 512-byte blocks */
        uint8_t entry[21];
        uint16_t pci;
        uint16_t expansion;
};
static_assert(sizeof(struct oprom_hdr) == 28);

#define PNP_SIG 0x506e5024 /* '$PnP' */
struct pnp_hdr {
        uint32_t sig;
        uint8_t rev;
        uint8_t len; /* in 16-byte counts */
        uint16_t next_header;
        uint8_t reserved1;
        uint8_t checksum;
        uint32_t device_id;
        uint16_t mfr_name;
        uint16_t product_name;
        uint8_t device_type_code[3];
        uint8_t device_indicators;
        uint16_t bcv;
        uint16_t bev;
        uint16_t reserved2;
        uint16_t resource_vector;
};
static_assert(sizeof(struct pnp_hdr) == 32);

union pvshim_rom {
        uint8_t bytes[512];
        struct {
                struct oprom_hdr oprom;
                struct pnp_hdr pnp;
                uint32_t mbi_gpa;
                uint16_t idt[3];
                uint16_t gdt_len;
                uint32_t gdt_addr;
                uint64_t gdt[3];
                uint8_t code16[64];
                uint8_t code32[64];
        };
};
static_assert(!(sizeof(union pvshim_rom) % 512));

/* The UMB location at which we install our expansion ROM */
#define PVSHIM_ROM_ADDR 0xe0000

/* The fixed entry point of PV Shim itself */
#define PVSHIM_ENTRY_ADDR 0x200000

/* Segments used by the GDT that we manually create */
#define CODE32_SEG 0x08
#define DATA32_SEG 0x10

/* Assembler trampoline. There are a whole 16 instructions here;
 * it would have been theoretically possible to build them from
 * a .S file or inline asm, and jump through various hoops to
 * get the relocations and internal/external references to work.
 * Or we could just emit them as bytes; life's too short. */

/* Emit a word or dword as consecutive bytes in little-endian order */
#define le16word(x) static_cast<uint8_t>((x) & 0xff), static_cast<uint8_t>((x) 
>> 8)
#define le32dword(x) le16word(x), le16word((x)>>16)

/* Helper macro to emit the offset of a field in the pvshim_rom
 * structure, as two bytes */
#define le16ofs(field) le16word(offsetof(union pvshim_rom, field))

/* ROMBIOS / CMOS boot sequence configuration */
#define PORT_CMOS_INDEX         0x70
#define PORT_CMOS_DATA          0x71

#define CMOS_BOOT_DEVICE_1_2    0x3d
#define CMOS_BOOT_DEVICE_BEV    0x04

/* Initial setup entry point of the PV Shim option ROM.
 * Just set the CMOS boot order to boot from the BEV. */
static uint8_t pvshim_init[] =
{
        0xb0, CMOS_BOOT_DEVICE_1_2,                     // mov 
$CMOS_BOOT_DEVICE_1_2, %al
        0xe6, PORT_CMOS_INDEX,                          // out %al, 
$PORT_CMOS_INDEX
        0xb0, CMOS_BOOT_DEVICE_BEV,                     // mov 
$CMOS_BOOT_DEVICE_BEV, %al
        0xe6, PORT_CMOS_DATA,                           // out %al, 
$PORT_CMOS_DATA
        0xcb                                            // lret
};

/* This is the 16-bit BEV entry point of the PV Shim "option ROM",
 * invoked when ROMBIOS is done with its setup, and it's time to
 * start PV Shim. */
static uint8_t pvshim_trampoline16[] =
{
        /* The Multiboot handoff expects the multiboot_info struct in %ebx.
         * Do that now from 16-bit mode so that we don't have to mess with
         * %rip-relative addressing to load it. */
        0x2e, 0x66, 0x8b, 0x1e, le16ofs(mbi_gpa),       // mov %cs:mbi_gpa, %ebx

        /* Clear interrupts, load the IDT and GDT that were set up for us. */
        0xfa,                                           // cli
        0x2e, 0x0f, 0x01, 0x1e, le16ofs(idt),           // lidtw %cs:idt
        0x2e, 0x0f, 0x01, 0x16, le16ofs(gdt_len),       // lgdtw %cs:gdt_len

        /* Set CR0.PE to enter protected mode. */
        0x31, 0xc0,                                     // xor %ax,%ax
        0x40,                                           // inc %ax
        0x0f, 0x01, 0xf0,                               // lmsw %ax

        /* Jump to the 32-bit mode trampoline. */
        0x66, 0xea, le16ofs(code32), le16word(PVSHIM_ROM_ADDR >> 16),
        le16word(CODE32_SEG)                            // ljmpl 
$CODE32_SEG,code32
};

static uint8_t pvshim_trampoline32[] =
{
        /* Load the 32-bit data segment into the segment registers */
        0xb8, le32dword(DATA32_SEG),                    // mov $DATA32_SEG, %eax
        0x8e, 0xd8,                                     // mov %eax, %ds
        0x8e, 0xc0,                                     // mov %eax, %es
        0x8e, 0xe0,                                     // mov %eax, %fs
        0x8e, 0xe8,                                     // mov %eax, %gs
        0x8e, 0xd0,                                     // mov %eax, %ss

        /* The Multiboot handoff requires the magic value in %eax */
        0xb8, le32dword(MULTIBOOT_BOOTLOADER_MAGIC),    // mov 
$MULTIBOOT_BOOTLOADER_MAGIC, %eax

        /* Go go go! */
        0xea, le32dword(PVSHIM_ENTRY_ADDR), le16word(CODE32_SEG)
                                                        // ljmpl 
$CODE32_SEG,$PVSHIM_ENTRY_ADDR
};

static const char *pvshim_cmdline =
        "loglvl=all guest_loglvl=none console=com1 console_timestamps=datems 
xsave=0 xpti=false";

static void pvshim_load()
{
        service_vm::bz_image_metadata bz_image_metadata;
        std::string pvshim_image_name = "pvshim";
        unique_fd pvshim_fd = service_image_fetch(pvshim_image_name, 
bz_image_metadata);
        die_on(pvshim_fd < 0, "Failed to open PV Shim image");

        size_t mmap_size = PAGE_ROUND_UP(bz_image_metadata.offset + 
bz_image_metadata.size);
        void *image = mmap(NULL, mmap_size, PROT_READ, MAP_SHARED, pvshim_fd, 
0);
        die_on(image == MAP_FAILED, "failed to mmap PV Shim image");

        unsigned char *bytes = static_cast<unsigned char *>(image) + 
bz_image_metadata.offset;
        size_t bytes_size = bz_image_metadata.size;

        struct gzip_tail gz_tail;
        unsigned char *gz_bytes = parse_gzip_header(bytes, bytes_size, 
&gz_tail);
        if (gz_bytes) {
                size_t gz_size = bytes_size - (gz_bytes - bytes);

                /* Impose a sane size limit on the decompressed PV Shim image */
                die_on(gz_tail.size > 4 * MiB, "PV Shim decompressed image size 
too large (%d bytes)",
                       gz_tail.size);

                void *elf_bytes = malloc(gz_tail.size);
                die_on(!elf_bytes, "Failed to allocate %d bytes for PV Shim 
decompression",
                       gz_tail.size);

                decompress_gzip_stream(&gz_tail, elf_bytes, gz_bytes, gz_size);
                log_info("Decompressed %d bytes of PV Shim image", 
gz_tail.size);

                load_multiboot(elf_bytes, gz_tail.size, pvshim_cmdline);
                free(elf_bytes);
        } else {
                /* Not compressed; load it directly */
                load_multiboot(bytes, bytes_size, pvshim_cmdline);
        }
        die_on(munmap(image, mmap_size) != 0, "Failed to munmap PV Shim image 
data %p", image);

        /* Install an expansion ROM which will invoke it once ROMBIOS has done 
the setup */
        union pvshim_rom rom = { };

        rom.oprom.sig = OPROM_SIG;
        rom.oprom.romlen = sizeof(rom) / 512;
        rom.oprom.expansion = offsetof(union pvshim_rom, pnp);

        rom.pnp.sig = PNP_SIG;
        rom.pnp.rev = 1;
        rom.pnp.len = sizeof(rom.pnp) / 16;
        rom.pnp.bev = offsetof(union pvshim_rom, code16);

        /* Grab the Multiboot info struct from where load_multiboot() put it */
        rom.mbi_gpa = static_cast<uint32_t>(thread_pvcpu->kvm.regs.rbx);
        die_on(thread_pvcpu->kvm.regs.rip != PVSHIM_ENTRY_ADDR,
               "PV Shim entry point 0x%x is not expected 0x%x",
               static_cast<uint32_t>(thread_pvcpu->kvm.regs.rip),
               PVSHIM_ENTRY_ADDR);

        rom.gdt_len = sizeof(rom.gdt) - 1;
        rom.gdt_addr = PVSHIM_ROM_ADDR + offsetof(union pvshim_rom, gdt);
        rom.gdt[1] = 0x00cf9a000000ffff; /* CODE32_SEG (0x0008) */
        rom.gdt[2] = 0x00cf92000000ffff; /* DATA32_SEG (0x0010) */

        /* Copy assembler code into place */
        static_assert(sizeof(rom.oprom.entry) >= sizeof(pvshim_init));
        memcpy(rom.oprom.entry, pvshim_init, sizeof(pvshim_init));

        static_assert(sizeof(rom.code16) >= sizeof(pvshim_trampoline16));
        memcpy(rom.code16, pvshim_trampoline16, sizeof(pvshim_trampoline16));

        static_assert(sizeof(rom.code32) >= sizeof(pvshim_trampoline32));
        memcpy(rom.code32, pvshim_trampoline32, sizeof(pvshim_trampoline32));

        /* Calculate the OpROM checksum */
        uint8_t sum = 0;
        for (unsigned i = 0; i < sizeof(rom) - 1; i++)
                sum = static_cast<uint8_t>(sum + rom.bytes[i]);
        rom.bytes[sizeof(rom) - 1] = static_cast<uint8_t>(-sum);

        /* Drop into guest memory where ROMBIOS will find it as an OpROM */
        memory_write_region(PVSHIM_ROM_ADDR, &rom, sizeof(rom));
}

Attachment: smime.p7s
Description: S/MIME cryptographic signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]