root/sys/arch/ia64/stand/ia64/efi/main.c
/*      $NetBSD: main.c,v 1.12 2018/08/18 06:52:57 kre Exp $    */

/*-
 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
 * Copyright (c) 1998,2000 Doug Rabson <dfr@freebsd.org>
 * All rights reserved.
 *
 * 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/cdefs.h>

#include <lib/libsa/stand.h>
#include <lib/libsa/loadfile.h>

#include <machine/sal.h>
#include <machine/pal.h>
#include <machine/pte.h>
#include <machine/dig64.h>

#include <efi.h>
#include <efilib.h>
#include <efifsdev.h>

#include <machine/efilib.h>

#include "bootstrap.h"
#include "efiboot.h"

extern char bootprog_name[];
extern char bootprog_rev[];

struct efi_devdesc      currdev;        /* our current device */
struct arch_switch      archsw;         /* MI/MD interface boundary */

vaddr_t ia64_unwindtab;
vsize_t ia64_unwindtablen;

extern u_int64_t        ia64_pal_entry;

EFI_GUID acpi = ACPI_TABLE_GUID;
EFI_GUID acpi20 = ACPI_20_TABLE_GUID;
EFI_GUID devid = DEVICE_PATH_PROTOCOL;
EFI_GUID hcdp = HCDP_TABLE_GUID;
EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
EFI_GUID mps = MPS_TABLE_GUID;
EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
EFI_GUID sal = SAL_SYSTEM_TABLE_GUID;
EFI_GUID smbios = SMBIOS_TABLE_GUID;

static void
find_pal_proc(void)
{
        int i;
        struct sal_system_table *saltab = 0;
        static int sizes[6] = {
                48, 32, 16, 32, 16, 16
        };
        u_int8_t *p;

        saltab = efi_get_table(&sal);
        if (saltab == NULL) {
                printf("Can't find SAL System Table\n");
                return;
        }

        if (memcmp(saltab->sal_signature, "SST_", 4)) {
                printf("Bad signature for SAL System Table\n");
                return;
        }

        p = (u_int8_t *) (saltab + 1);
        for (i = 0; i < saltab->sal_entry_count; i++) {
                if (*p == 0) {
                        struct sal_entrypoint_descriptor *dp;
                        dp = (struct sal_entrypoint_descriptor *) p;
                        ia64_pal_entry = dp->sale_pal_proc;
                        return;
                }
                p += sizes[*p];
        }

        printf("Can't find PAL proc\n");
        return;
}

EFI_STATUS
main(int argc, CHAR16 *argv[])
{
        EFI_LOADED_IMAGE *img;
        int i;

        /* 
         * XXX Chicken-and-egg problem; we want to have console output
         * early, but some console attributes may depend on reading from
         * eg. the boot device, which we can't do yet.  We can use
         * printf() etc. once this is done.
         */
        cons_probe();

        /*
         * Initialise the block cache
         */
        /* bcache_init(32, 512); */             /* 16k XXX tune this */

        find_pal_proc();

        efifs_dev_init();

        efinet_init_driver();

        /* Get our loaded image protocol interface structure. */
        BS->HandleProtocol(IH, &imgid, (VOID**)&img);

        printf("Image base: 0x%lx\n", (u_long)img->ImageBase);

        printf("\n");
        printf("%s, Revision %s\n", bootprog_name, bootprog_rev);

        i = efifs_get_unit(img->DeviceHandle);
        if (i >= 0) {
                currdev.d_dev = &devsw[0];              /* XXX disk */
                currdev.d_kind.efidisk.unit = i;
                /* XXX should be able to detect this, default to autoprobe */
                currdev.d_kind.efidisk.slice = -1;
                currdev.d_kind.efidisk.partition = 0;
                currdev.d_type = DEVT_DISK;
        } else {
                currdev.d_dev = &devsw[1];              /* XXX net */
                currdev.d_kind.netif.unit = 0;          /* XXX */
                currdev.d_type = DEVT_NET;

                /* XXX overwrite disk ops with nfs ops */
                memcpy(&file_system[0], &file_system[1], sizeof(struct fs_ops));
        }


        /*
         * Disable the watchdog timer. By default the boot manager sets
         * the timer to 5 minutes before invoking a boot option. If we
         * want to return to the boot manager, we have to disable the
         * watchdog timer and since we're an interactive program, we don't
         * want to wait until the user types "quit". The timer may have
         * fired by then. We don't care if this fails. It does not prevent
         * normal functioning in any way...
         */
        BS->SetWatchdogTimer(0, 0, 0, NULL);

        env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
            (ev_sethook_t *) efi_setcurrdev, env_nounset);
        env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
            env_nounset);

        setenv("LINES", "24", 1);       /* optional */
    
        archsw.arch_autoload = efi_autoload;
        archsw.arch_getdev = efi_getdev;
        archsw.arch_copyin = efi_copyin;
        archsw.arch_copyout = efi_copyout;
        archsw.arch_readin = efi_readin;

        interact();                     /* doesn't return */

        return (EFI_SUCCESS);           /* keep compiler happy */
}

COMMAND_SET(quit, "quit", "exit the loader", command_quit);

static int
command_quit(int argc, char *argv[])
{
        exit(0);
        return (CMD_OK);
}

COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);

static int
command_memmap(int argc, char *argv[])
{
        UINTN sz;
        EFI_MEMORY_DESCRIPTOR *map, *p;
        UINTN key, dsz;
        UINT32 dver;
        EFI_STATUS status;
        int i, ndesc;
        static char *types[] = {
            "Reserved",
            "LoaderCode",
            "LoaderData",
            "BootServicesCode",
            "BootServicesData",
            "RuntimeServicesCode",
            "RuntimeServicesData",
            "ConventionalMemory",
            "UnusableMemory",
            "ACPIReclaimMemory",
            "ACPIMemoryNVS",
            "MemoryMappedIO",
            "MemoryMappedIOPortSpace",
            "PalCode"
        };

        sz = 0;
        status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
        if (status != EFI_BUFFER_TOO_SMALL) {
                printf("Can't determine memory map size\n");
                return CMD_ERROR;
        }
        map = alloc(sz);
        status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
        if (EFI_ERROR(status)) {
                printf("Can't read memory map\n");
                return CMD_ERROR;
        }

        ndesc = sz / dsz;
        printf("%23s %12s %12s %8s %4s\n",
               "Type", "Physical", "Virtual", "#Pages", "Attr");
               
        for (i = 0, p = map; i < ndesc;
             i++, p = NextMemoryDescriptor(p, dsz)) {
            printf("%23s %012lx %012lx %08lx ",
                   types[p->Type],
                   p->PhysicalStart,
                   p->VirtualStart,
                   p->NumberOfPages);
            if (p->Attribute & EFI_MEMORY_UC)
                printf("UC ");
            if (p->Attribute & EFI_MEMORY_WC)
                printf("WC ");
            if (p->Attribute & EFI_MEMORY_WT)
                printf("WT ");
            if (p->Attribute & EFI_MEMORY_WB)
                printf("WB ");
            if (p->Attribute & EFI_MEMORY_UCE)
                printf("UCE ");
            if (p->Attribute & EFI_MEMORY_WP)
                printf("WP ");
            if (p->Attribute & EFI_MEMORY_RP)
                printf("RP ");
            if (p->Attribute & EFI_MEMORY_XP)
                printf("XP ");
            if (p->Attribute & EFI_MEMORY_RUNTIME)
                printf("RUNTIME");
            printf("\n");
        }

        return CMD_OK;
}

COMMAND_SET(configuration, "configuration",
            "print configuration tables", command_configuration);

static const char *
guid_to_string(EFI_GUID *guid)
{
        static char buf[40];

        snprintf(buf, sizeof(buf),
            "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
            guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
            guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
            guid->Data4[5], guid->Data4[6], guid->Data4[7]);
        return (buf);
}

static int
command_configuration(int argc, char *argv[])
{
        int i;

        printf("NumberOfTableEntries=%ld\n", ST->NumberOfTableEntries);
        for (i = 0; i < ST->NumberOfTableEntries; i++) {
                EFI_GUID *guid;

                printf("  ");
                guid = &ST->ConfigurationTable[i].VendorGuid;
                if (!memcmp(guid, &mps, sizeof(EFI_GUID)))
                        printf("MPS Table");
                else if (!memcmp(guid, &acpi, sizeof(EFI_GUID)))
                        printf("ACPI Table");
                else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID)))
                        printf("ACPI 2.0 Table");
                else if (!memcmp(guid, &smbios, sizeof(EFI_GUID)))
                        printf("SMBIOS Table");
                else if (!memcmp(guid, &sal, sizeof(EFI_GUID)))
                        printf("SAL System Table");
                else if (!memcmp(guid, &hcdp, sizeof(EFI_GUID)))
                        printf("DIG64 HCDP Table");
                else
                        printf("Unknown Table (%s)", guid_to_string(guid));
                printf(" at %p\n", ST->ConfigurationTable[i].VendorTable);
        }

        return CMD_OK;
}    

COMMAND_SET(sal, "sal", "print SAL System Table", command_sal);

static int
command_sal(int argc, char *argv[])
{
        int i;
        struct sal_system_table *saltab = 0;
        static int sizes[6] = {
                48, 32, 16, 32, 16, 16
        };
        u_int8_t *p;

        saltab = efi_get_table(&sal);
        if (saltab == NULL) {
                printf("Can't find SAL System Table\n");
                return CMD_ERROR;
        }

        if (memcmp(saltab->sal_signature, "SST_", 4)) {
                printf("Bad signature for SAL System Table\n");
                return CMD_ERROR;
        }

        printf("SAL Revision %x.%02x\n",
               saltab->sal_rev[1],
               saltab->sal_rev[0]);
        printf("SAL A Version %x.%02x\n",
               saltab->sal_a_version[1],
               saltab->sal_a_version[0]);
        printf("SAL B Version %x.%02x\n",
               saltab->sal_b_version[1],
               saltab->sal_b_version[0]);

        p = (u_int8_t *) (saltab + 1);
        for (i = 0; i < saltab->sal_entry_count; i++) {
                printf("  Desc %d", *p);
                if (*p == 0) {
                        struct sal_entrypoint_descriptor *dp;
                        dp = (struct sal_entrypoint_descriptor *) p;
                        printf("\n");
                        printf("    PAL Proc at 0x%lx\n",
                               dp->sale_pal_proc);
                        printf("    SAL Proc at 0x%lx\n",
                               dp->sale_sal_proc);
                        printf("    SAL GP at 0x%lx\n",
                               dp->sale_sal_gp);
                } else if (*p == 1) {
                        struct sal_memory_descriptor *dp;
                        dp = (struct sal_memory_descriptor *) p;
                        printf(" Type %d.%d, ",
                               dp->sale_memory_type[0],
                               dp->sale_memory_type[1]);
                        printf("Address 0x%lx, ",
                               dp->sale_physical_address);
                        printf("Length 0x%x\n",
                               dp->sale_length);
                } else if (*p == 5) {
                        struct sal_ap_wakeup_descriptor *dp;
                        dp = (struct sal_ap_wakeup_descriptor *) p;
                        printf("\n");
                        printf("    Mechanism %d\n", dp->sale_mechanism);
                        printf("    Vector 0x%lx\n", dp->sale_vector);
                } else
                        printf("\n");

                p += sizes[*p];
        }

        return CMD_OK;
}

int
print_trs(int type)
{
        struct ia64_pal_result res;
        int i, maxtr;
        struct {
                pt_entry_t      pte;
                uint64_t        itir;
                uint64_t        ifa;
                struct ia64_rr  rr;
        } buf;
        static const char *psnames[] = {
                "1B",   "2B",   "4B",   "8B",
                "16B",  "32B",  "64B",  "128B",
                "256B", "512B", "1K",   "2K",
                "4K",   "8K",   "16K",  "32K",
                "64K",  "128K", "256K", "512K",
                "1M",   "2M",   "4M",   "8M",
                "16M",  "32M",  "64M",  "128M",
                "256M", "512M", "1G",   "2G"
        };
        static const char *manames[] = {
                "WB",   "bad",  "bad",  "bad",
                "UC",   "UCE",  "WC",   "NaT",
        };

        res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0);
        if (res.pal_status != 0) {
                printf("Can't get VM summary\n");
                return CMD_ERROR;
        }

        if (type == 0)
                maxtr = (res.pal_result[0] >> 40) & 0xff;
        else
                maxtr = (res.pal_result[0] >> 32) & 0xff;

        printf("%d translation registers\n", maxtr);

        pager_open();
        pager_output("TR# RID    Virtual Page  Physical Page PgSz ED AR PL D A MA  P KEY\n");
        for (i = 0; i <= maxtr; i++) {
                char lbuf[128];

                memset(&buf, 0, sizeof(buf));
                res = ia64_call_pal_stacked(PAL_VM_TR_READ, i, type,
                                            (u_int64_t) &buf);
                if (res.pal_status != 0)
                        break;

                /* Only display valid translations */
                if ((buf.ifa & 1) == 0)
                        continue;

                if (!(res.pal_result[0] & 1))
                        buf.pte &= ~PTE_AR_MASK;
                if (!(res.pal_result[0] & 2))
                        buf.pte &= ~PTE_PL_MASK;
                if (!(res.pal_result[0] & 4))
                        buf.pte &= ~PTE_DIRTY;
                if (!(res.pal_result[0] & 8))
                        buf.pte &= ~PTE_MA_MASK;
                snprintf(lbuf, sizeof(lbuf),
                    "%03d %06x %013lx %013lx %4s %d  %d  %d  %d %d "
                    "%-3s %d %06x\n", i, buf.rr.rr_rid, buf.ifa >> 12,
                    (buf.pte & PTE_PPN_MASK) >> 12,
                    psnames[(buf.itir & ITIR_PS_MASK) >> 2],
                    (buf.pte & PTE_ED) ? 1 : 0,
                    (int)(buf.pte & PTE_AR_MASK) >> 9,
                    (int)(buf.pte & PTE_PL_MASK) >> 7,
                    (buf.pte & PTE_DIRTY) ? 1 : 0,
                    (buf.pte & PTE_ACCESSED) ? 1 : 0,
                    manames[(buf.pte & PTE_MA_MASK) >> 2],
                    (buf.pte & PTE_PRESENT) ? 1 : 0,
                    (int)((buf.itir & ITIR_KEY_MASK) >> 8));
                pager_output(lbuf);
        }
        pager_close();

        if (res.pal_status != 0) {
                printf("Error while getting TR contents\n");
                return CMD_ERROR;
        }
        return CMD_OK;
}

COMMAND_SET(itr, "itr", "print instruction TRs", command_itr);

static int
command_itr(int argc, char *argv[])
{
        return print_trs(0);
}

COMMAND_SET(dtr, "dtr", "print data TRs", command_dtr);

static int
command_dtr(int argc, char *argv[])
{
        return print_trs(1);
}

COMMAND_SET(hcdp, "hcdp", "Dump HCDP info", command_hcdp);

static char *
hcdp_string(char *s, u_int len)
{
        static char buffer[256];

        memcpy(buffer, s, len);
        buffer[len] = 0;
        return (buffer);
}
        
static int
command_hcdp(int argc, char *argv[])
{
        struct dig64_hcdp_table *tbl;
        union dev_desc *desc;
        int i, m, n;

        tbl = efi_get_table(&hcdp);
        if (tbl == NULL) {
                printf("No HCDP table present\n");
                return (CMD_OK);
        }
        if (memcmp(tbl->signature, HCDP_SIGNATURE, sizeof(tbl->signature))) {
                printf("HCDP table has invalid signature\n");
                return (CMD_OK);
        }
        printf("HCDP table at 0x%lx\n", (u_long)tbl);
        printf("Signature  = %s\n", hcdp_string(tbl->signature, 4));
        printf("Length     = %u\n", tbl->length);
        printf("Revision   = %u\n", tbl->revision);
        printf("Checksum   = %u\n", tbl->checksum);
        printf("OEM Id     = %s\n", hcdp_string(tbl->oem_id, 6));
        printf("Table Id   = %s\n", hcdp_string(tbl->oem_tbl_id, 8));
        printf("OEM rev    = %u\n", tbl->oem_rev);
        printf("Creator Id = %s\n", hcdp_string(tbl->creator_id, 4));
        printf("Creator rev= %u\n", tbl->creator_rev);
        printf("Entries    = %u\n", tbl->entries);
        n = 0;
        m = tbl->length - sizeof(struct dig64_hcdp_table);
        i = 1;
        while (n < m) {
                printf("Entry #%d:\n", i);
                desc = (union dev_desc *)((char *)tbl->entry + n);
                printf("    Type      = %u\n", desc->type);
                if (desc->type == DIG64_ENTRYTYPE_TYPE0 ||
                    desc->type == DIG64_ENTRYTYPE_TYPE1) {
                        struct dig64_hcdp_entry *ent = &desc->uart;
                        struct dig64_gas *gas;
                        printf("    Databits  = %u\n", ent->databits);
                        printf("    Parity    = %u\n", ent->parity);
                        printf("    Stopbits  = %u\n", ent->stopbits);
                        printf("    PCI seg   = %u\n", ent->pci_segment);
                        printf("    PCI bus   = %u\n", ent->pci_bus);
                        printf("    PCI dev   = %u\n", ent->pci_device);
                        printf("    PCI func  = %u\n", ent->pci_function);
                        printf("    Interrupt = %u\n", ent->interrupt);
                        printf("    PCI flag  = %u\n", ent->pci_flag);
                        printf("    Baudrate  = %lu\n",
                            ((u_long)ent->baud_high << 32) +
                            (u_long)ent->baud_low);
                        gas = &ent->address;
                        printf("    Addr space= %u\n", gas->addr_space);
                        printf("    Bit width = %u\n", gas->bit_width);
                        printf("    Bit offset= %u\n", gas->bit_offset);
                        printf("    Address   = 0x%lx\n",
                            ((u_long)gas->addr_high << 32) +
                            (u_long)gas->addr_low);
                        printf("    PCI type  = %u\n", ent->pci_devid);
                        printf("    PCI vndr  = %u\n", ent->pci_vendor);
                        printf("    IRQ       = %u\n", ent->irq);
                        printf("    PClock    = %u\n", ent->pclock);
                        printf("    PCI iface = %u\n", ent->pci_interface);

                        n += sizeof(struct dig64_hcdp_entry);
                } else {
                        struct dig64_pcdp_entry *pcdp = &desc->pcdp;

                        if (tbl->revision < 3) {
                                printf("PCDP not support\n");
                                return (CMD_OK);
                        }

                        printf("    Length    = %u\n", pcdp->length);
                        printf("    Index EFI = %u\n", pcdp->index);
                        printf("    Interconn = %u", pcdp->specs.type);

                        switch (pcdp->specs.type) {
                        case DIG64_PCDP_SPEC_ACPI:
                        {
                                struct dig64_acpi_spec *acpi =
                                    &pcdp->specs.acpi;

                                printf("(ACPI)\n");
                                printf("    Length    = %u\n", acpi->length);
                                printf("    ACPI_UID  = %x\n", acpi->uid);
                                printf("    ACPI_HID  = %x\n", acpi->hid);
                                printf("    ACPI GSI  = %x\n", acpi->acpi_gsi);
                                printf("    MMIO_TRA  = %lx\n", acpi->mmio_tra);
                                printf("    IOPort_TRA= %lx\n",
                                    acpi->ioport_tra);
                                printf("    Flags     = %x\n", acpi->flags);
                                break;
                        }
                        case DIG64_PCDP_SPEC_PCI:
                        {
                                struct dig64_pci_spec *pci = &pcdp->specs.pci;

                                printf("(PCI)\n");
                                printf("    Length    = %u\n", pci->length);
                                printf("    Seg GrpNum= %u\n", pci->sgn);
                                printf("    Bus       = %u\n", pci->bus);
                                printf("    Device    = %u\n", pci->device);
                                printf("    Function  = %u\n", pci->function);
                                printf("    Device ID = %u\n", pci->device_id);
                                printf("    Vendor ID = %u\n", pci->vendor_id);
                                printf("    ACPI GSI  = %x\n", pci->acpi_gsi);
                                printf("    MMIO_TRA  = %lx\n", pci->mmio_tra);
                                printf("    IOPort_TRA= %lx\n",
                                    pci->ioport_tra);
                                printf("    Flags     = %x\n", pci->flags);
                                break;
                        }
                        }

                        n += pcdp->length;
                }
        }
        printf("<EOT>\n");
        return (CMD_OK);
}

struct bootblk_command commands[] = {
        COMMON_COMMANDS,
        { "quit",       "exit the loader",      command_quit },
        { "memmap",     "print memory map",     command_memmap },
        { "configuration", "print configuration tables", command_configuration },
        { "sal",        "print SAL System Table", command_sal },
        { "itr",        "print instruction TRs", command_itr },
        { "dtr",        "print data TRs",       command_dtr },
        { "hcdp",       "Dump HCDP info",       command_hcdp },
        { NULL,         NULL,                   NULL         },
};