root/stand/kboot/kboot/arch/amd64/load_addr.c
/*-
 * Copyright (c) 2022 Netflix, Inc
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <machine/pc/bios.h>
#include <machine/metadata.h>

#include "stand.h"
#include "host_syscall.h"
#include "efi.h"
#include "kboot.h"
#include "bootstrap.h"

/*
 * Abbreviated x86 Linux struct boot_param for the so-called zero-page.
 * We have to use this to get systab and memmap since neither of those
 * are exposed in a sane way. We only define what we need and pad for
 * everything else to minimize cross-coupling.
 *
 * Transcribed in FreeBSD-ese from Linux's asm/bootparam.h for x86 as of
 * 6.15, but these details haven't changed in a long time.
 */

struct linux_efi_info {
        uint32_t efi_loader_signature;  /* 0x00 */
        uint32_t efi_systab;            /* 0x04 */
        uint32_t efi_memdesc_size;      /* 0x08 */
        uint32_t efi_memdesc_version;   /* 0x0c */
        uint32_t efi_memmap;            /* 0x10 */
        uint32_t efi_memmap_size;       /* 0x14 */
        uint32_t efi_systab_hi;         /* 0x18 */
        uint32_t efi_memmap_hi;         /* 0x1c */
} __packed;

struct linux_boot_params {
        uint8_t _pad1[0x1c0];                           /* 0x000 */
        struct linux_efi_info efi_info;                 /* 0x1c0 */
        uint8_t _pad2[0x1000 - 0x1c0 - sizeof(struct linux_efi_info)]; /* 0x1e0 */
} __packed;     /* Total size 4k, the page size on x86 */

bool
enumerate_memory_arch(void)
{
        struct linux_boot_params bp;

        /*
         * Sadly, there's no properly exported data for the EFI memory map nor
         * the system table. systab is passed in from the original boot loader.
         * memmap is obtained from boot time services (which are long gone) and
         * then modified and passed to SetVirtualAddressMap. Even though the
         * latter is in runtime services, it can only be called once and Linux
         * has already called it. So unless we can dig all this out from the
         * Linux kernel, there's no other wy to get it. A proper way would be to
         * publish these in /sys/firmware/efi, but that's not done yet. We can
         * only get the runtime subset and can't get systbl at all from today's
         * (6.15) Linux kernel. Linux's pandora boot loader will copy this same
         * information when it calls the new kernel, but since we don't use the
         * bzImage kexec vector, we have to harvest it here.
         */
        if (data_from_kernel("boot_params", &bp, sizeof(bp))) {
                uint64_t systbl, memmap;

                systbl = (uint64_t)bp.efi_info.efi_systab_hi << 32 |
                    bp.efi_info.efi_systab;
                memmap = (uint64_t)bp.efi_info.efi_memmap_hi << 32 |
                    bp.efi_info.efi_memmap;

                efi_set_systbl(systbl);
                efi_read_from_pa(memmap, bp.efi_info.efi_memmap_size,
                    bp.efi_info.efi_memdesc_size, bp.efi_info.efi_memdesc_version);
                printf("UEFI SYSTAB PA: %#lx\n", systbl);
                printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n",
                    bp.efi_info.efi_memdesc_version, bp.efi_info.efi_memdesc_size,
                    bp.efi_info.efi_memmap_size, memmap);
        }
        /*
         * So, we can't use the EFI map for this, so we have to fall back to
         * the proc iomem stuff to at least get started...
         */
        if (!populate_avail_from_iomem()) {
                printf("Populate from avail also failed.\n");
                return (false);
        } else {
                printf("Populate worked...\n");
        }
        print_avail();
        return (true);
}

/* XXX refactor with aarch64 */
uint64_t
kboot_get_phys_load_segment(void)
{
#define HOLE_SIZE       (64ul << 20)
#define KERN_ALIGN      (2ul << 20)
        static uint64_t s = 0;

        if (s != 0)
                return (s);

        print_avail();
        s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM);
        printf("KBOOT GET PHYS Using %#llx\n", (long long)s);
        if (s != 0)
                return (s);
        s = 0x40000000 | 0x4200000;     /* should never get here */
        /* XXX PANIC? XXX */
        printf("Falling back to the crazy address %#lx which works in qemu\n", s);
        return (s);
}

void
bi_loadsmap(struct preloaded_file *kfp)
{
        efi_bi_loadsmap(kfp);
}