root/sys/arch/ia64/ia64/machdep.c
/*      $NetBSD: machdep.c,v 1.46 2025/03/16 15:34:59 riastradh Exp $   */

/*-
 * Copyright (c) 2003,2004 Marcel Moolenaar
 * Copyright (c) 2000,2001 Doug Rabson
 * 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.
 */

/*-
 * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center and by Chris G. Demetriou.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

/*
 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include <sys/cdefs.h>
/*__FBSDID("$FreeBSD: src/sys/ia64/ia64/machdep.c,v 1.203 2005/10/14 12:43:45 davidxu Exp $"); */

#include "opt_modular.h"

#include <sys/param.h>
#include <sys/cpu.h>
#include <sys/exec.h>
#include <sys/ksyms.h>
#include <sys/msgbuf.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/systm.h>

#include <machine/ia64_cpu.h>
#include <machine/pal.h>
#include <machine/sal.h>
#include <machine/ssc.h>

#include <machine/md_var.h>
#include <machine/fpu.h>
#include <machine/efi.h>
#include <machine/bootinfo.h>
#include <machine/vmparam.h>

#include <machine/atomic.h>
#include <machine/pte.h>
#include <machine/pcb.h>

#include <uvm/uvm.h>

#include <dev/cons.h>
#include <dev/mm.h>

#ifdef DEBUG
#define DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
#else
#define DPRINTF(fmt, args...)   ((void)0)
#endif

/* the following is used externally (sysctl_hw) */
char    machine[] = MACHINE;            /* from <machine/param.h> */
char    machine_arch[] = MACHINE_ARCH;  /* from <machine/param.h> */

#if NKSYMS || defined(DDB) || defined(MODULAR)
/* start and end of kernel symbol table */
void    *ksym_start, *ksym_end;
vaddr_t ia64_unwindtab;
vsize_t ia64_unwindtablen;
#endif

struct vm_map *phys_map = NULL;

void *msgbufaddr;

vaddr_t kernstart, kernend;

/* XXX: Move this stuff to cpu_info */

uint64_t processor_frequency;
uint64_t bus_frequency;
uint64_t itc_frequency;
uint64_t ia64_pal_base;
uint64_t ia64_pal_size;
uint64_t ia64_port_base;

int ia64_sync_icache_needed = 0;

extern uint64_t ia64_gateway_page[];

uint64_t pa_bootinfo;
struct bootinfo bootinfo;

extern vaddr_t kstack, kstack_top;
extern vaddr_t kernel_text, end;

struct fpswa_iface *fpswa_iface;


/*
 * Machine-dependent startup code
 */
void
cpu_startup(void)
{
        vaddr_t minaddr, maxaddr;

        /* XXX: startrtclock(); */
#ifdef PERFMON
        perfmon_init();
#endif
        printf("Detected memory  = %ld (%ld MB)\n", ia64_ptob(physmem),
            ptoa(physmem) / 1048576);

        /*
         * Display any holes after the first chunk of extended memory.
         */
        if (bootverbose) {
                int sizetmp, vm_nphysseg;
                uvm_physseg_t upm;

                printf("Physical memory chunk(s):\n");
                for (vm_nphysseg = 0, upm = uvm_physseg_get_first();
                     uvm_physseg_valid_p(upm);
                     vm_nphysseg++, upm = uvm_physseg_get_next(upm)) {
                        sizetmp = uvm_physseg_get_avail_end(upm) -
                            uvm_physseg_get_avail_start(upm);

                        printf("0x%016lx - 0x%016lx, %ld bytes (%d pages)\n",
                            ptoa(uvm_physseg_get_avail_start(upm)),
                            ptoa(uvm_physseg_get_avail_end(upm)) - 1,
                                    ptoa(sizetmp), sizetmp);
                }
                printf("Total number of segments: vm_nphysseg = %d \n",
                    vm_nphysseg);
        }

        minaddr = 0;

        /*
         * Allocate a submap for physio
         */
        phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
                                   VM_PHYS_SIZE, 0, false, NULL);

        /*
         * No need to allocate an mbuf cluster submap.  Mbuf clusters
         * are allocated via the pool allocator, and we use RR7 to
         * map those pages.
         */

        banner();

        if (fpswa_iface == NULL)
                printf("Warning: no FPSWA package supplied\n");
        else
                printf("FPSWA Revision = 0x%lx, Entry = %p\n",
                    (long)fpswa_iface->if_rev, (void *)fpswa_iface->if_fpswa);


        /*
         * Traverse the MADT to discover IOSAPIC and Local SAPIC
         * information.
         */
        ia64_probe_sapics();
}

void
cpu_reboot(int howto, char *bootstr)
{

        efi_reset_system();

        panic("XXX: Reset didn't work ? \n");
        /*NOTREACHED*/
}

bool
cpu_intr_p(void)
{
        return 0;
}

/*
 * This is called by main to set dumplo and dumpsize.
 * Dumps always skip the first PAGE_SIZE of disk space
 * in case there might be a disk label stored there.
 * If there is extra space, put dump at the end to
 * reduce the chance that swapping trashes it.
 */
void
cpu_dumpconf(void)
{
        return;
}

void
map_vhpt(uintptr_t vhpt)
{
        pt_entry_t pte;
        uint64_t psr;

        /*
         * XXX read pmap_vhpt_log2size before any memory translation
         * instructions to avoid "Data Nested TLB faults".  Not
         * exactly sure why this is needed with GCC 7.4
         */
        register uint64_t log2size = pmap_vhpt_log2size << 2;

        pte = PTE_PRESENT | PTE_MA_WB | PTE_ACCESSED | PTE_DIRTY |
                PTE_PL_KERN | PTE_AR_RW;
        pte |= vhpt & PTE_PPN_MASK;

        __asm __volatile("ptr.d %0,%1" :: "r"(vhpt), "r"(log2size));

        __asm __volatile("mov   %0=psr" : "=r"(psr));
        __asm __volatile("rsm   psr.ic|psr.i");
        ia64_srlz_i();
        ia64_set_ifa(vhpt);
        ia64_set_itir(log2size);
        ia64_srlz_d();
        __asm __volatile("itr.d dtr[%0]=%1" :: "r"(3), "r"(pte));
        __asm __volatile("mov   psr.l=%0" :: "r" (psr));
        ia64_srlz_i();
}

void
map_pal_code(void)
{
        pt_entry_t pte;
        uint64_t psr;

        if (ia64_pal_base == 0)
                return;

        pte = PTE_PRESENT | PTE_MA_WB | PTE_ACCESSED | PTE_DIRTY |
            PTE_PL_KERN | PTE_AR_RWX;
        pte |= ia64_pal_base & PTE_PPN_MASK;

        __asm __volatile("ptr.d %0,%1; ptr.i %0,%1" ::
                         "r"(IA64_PHYS_TO_RR7(ia64_pal_base)),
                         "r"(IA64_ID_PAGE_SHIFT<<2));

        __asm __volatile("mov   %0=psr" : "=r"(psr));
        __asm __volatile("rsm   psr.ic|psr.i");
        ia64_srlz_i();
        ia64_set_ifa(IA64_PHYS_TO_RR7(ia64_pal_base));
        ia64_set_itir(IA64_ID_PAGE_SHIFT << 2);
        ia64_srlz_d();
        __asm __volatile("itr.d dtr[%0]=%1" ::
                         "r"(1), "r"(*(pt_entry_t *)&pte));
        ia64_srlz_d();
        __asm __volatile("itr.i itr[%0]=%1" ::
                         "r"(1), "r"(*(pt_entry_t *)&pte));
        __asm __volatile("mov   psr.l=%0" :: "r" (psr));
        ia64_srlz_i();
}

void
map_gateway_page(void)
{
        pt_entry_t pte;
        uint64_t psr;

        pte = PTE_PRESENT | PTE_MA_WB | PTE_ACCESSED | PTE_DIRTY |
            PTE_PL_KERN | PTE_AR_X_RX;
        pte |= (uint64_t)ia64_gateway_page & PTE_PPN_MASK;

        __asm __volatile("ptr.d %0,%1; ptr.i %0,%1" ::
            "r"(VM_MAX_ADDRESS), "r"(PAGE_SHIFT << 2));

        __asm __volatile("mov   %0=psr" : "=r"(psr));
        __asm __volatile("rsm   psr.ic|psr.i");
        ia64_srlz_i();
        ia64_set_ifa(VM_MAX_ADDRESS);
        ia64_set_itir(PAGE_SHIFT << 2);
        ia64_srlz_d();
        __asm __volatile("itr.d dtr[%0]=%1" :: "r"(3), "r"(*(pt_entry_t*)&pte));
        ia64_srlz_d();
        __asm __volatile("itr.i itr[%0]=%1" :: "r"(3), "r"(*(pt_entry_t*)&pte));
        __asm __volatile("mov   psr.l=%0" :: "r" (psr));
        ia64_srlz_i();

        /* Expose the mapping to userland in ar.k5 */
        ia64_set_k5(VM_MAX_ADDRESS);
}

static void
calculate_frequencies(void)
{
        struct ia64_sal_result sal;
        struct ia64_pal_result pal;

        sal = ia64_sal_entry(SAL_FREQ_BASE, 0, 0, 0, 0, 0, 0, 0);
        pal = ia64_call_pal_static(PAL_FREQ_RATIOS, 0, 0, 0);
        if (sal.sal_status == 0 && pal.pal_status == 0) {
                if (bootverbose) {
                        printf("Platform clock frequency %ld Hz\n",
                            sal.sal_result[0]);
                        printf("Processor ratio %ld/%ld, Bus ratio %ld/%ld, "
                               "ITC ratio %ld/%ld\n",
                            pal.pal_result[0] >> 32,
                            pal.pal_result[0] & ((1L << 32) - 1),
                            pal.pal_result[1] >> 32,
                            pal.pal_result[1] & ((1L << 32) - 1),
                            pal.pal_result[2] >> 32,
                            pal.pal_result[2] & ((1L << 32) - 1));
                }
                processor_frequency =
                        sal.sal_result[0] * (pal.pal_result[0] >> 32)
                        / (pal.pal_result[0] & ((1L << 32) - 1));
                bus_frequency =
                        sal.sal_result[0] * (pal.pal_result[1] >> 32)
                        / (pal.pal_result[1] & ((1L << 32) - 1));
                itc_frequency =
                        sal.sal_result[0] * (pal.pal_result[2] >> 32)
                        / (pal.pal_result[2] & ((1L << 32) - 1));
        }

}


/* XXXX: Don't allocate 'ci' on stack. */
register struct cpu_info *ci __asm__("r13");
struct ia64_init_return
ia64_init(void)
{
        struct ia64_init_return ret;
        paddr_t kernstartpfn, kernendpfn, pfn0, pfn1;
        struct pcb *pcb0;
        struct efi_md *md;
        vaddr_t v;

        /* NO OUTPUT ALLOWED UNTIL FURTHER NOTICE */
        ia64_set_fpsr(IA64_FPSR_DEFAULT);

        /*
         * Region 6 is direct mapped UC and region 7 is direct mapped
         * WC. The details of this is controlled by the Alt {I,D}TLB
         * handlers. Here we just make sure that they have the largest
         * possible page size to minimise TLB usage.
         */
        ia64_set_rr(IA64_RR_BASE(6), (6 << 8) | (LOG2_ID_PAGE_SIZE << 2));
        ia64_set_rr(IA64_RR_BASE(7), (7 << 8) | (LOG2_ID_PAGE_SIZE << 2));
        ia64_srlz_d();

        /*
         * TODO: Get critical system information (if possible, from the
         * information provided by the boot program).
         */

        /*
         * pa_bootinfo is the physical address of the bootinfo block as
         * passed to us by the loader and set in locore.s.
         */
        bootinfo = *(struct bootinfo *)(IA64_PHYS_TO_RR7(pa_bootinfo));

        if (bootinfo.bi_magic != BOOTINFO_MAGIC || bootinfo.bi_version != 1) {
                memset(&bootinfo, 0, sizeof(bootinfo));
                bootinfo.bi_kernend = (vaddr_t) round_page((vaddr_t)&end);
        }


        /*
         * Look for the I/O ports first - we need them for console
         * probing.
         */
        for (md = efi_md_first(); md != NULL; md = efi_md_next(md)) {
                switch (md->md_type) {
                case EFI_MD_TYPE_IOPORT:
                        ia64_port_base = IA64_PHYS_TO_RR6(md->md_phys);
                        break;
                case EFI_MD_TYPE_PALCODE:
                        ia64_pal_base = md->md_phys;
                        break;
                }
        }


        /* XXX: We need to figure out whether/how much of the FreeBSD
         *      getenv/setenv stuff we need. The info we get from ski
         *      is too trivial to go to the hassle of importing the
         *      FreeBSD environment stuff.
         */

        /*
         * Look at arguments passed to us and compute boothowto.
         */
        boothowto = bootinfo.bi_boothowto;

        /* XXX: Debug: Override to verbose */
        boothowto |= AB_VERBOSE;


        /*
         * Initialize the console before we print anything out.
         */
        cninit();

        /* OUTPUT NOW ALLOWED */

        if (ia64_pal_base != 0) {
                ia64_pal_base &= ~IA64_ID_PAGE_MASK;
                /*
                 * We use a TR to map the first 256M of memory - this might
                 * cover the palcode too.
                 */
                if (ia64_pal_base == 0)
                        printf("PAL code mapped by the kernel's TR\n");
        } else
                printf("PAL code not found\n");

        /*
         * Wire things up so we can call the firmware.
         */
        map_pal_code();
        efi_boot_minimal(bootinfo.bi_systab);
        ia64_sal_init();
        calculate_frequencies();

        /*
         * Find the beginning and end of the kernel.
         */

        kernstart = trunc_page((vaddr_t) &kernel_text);
#ifdef DDB
        ia64_unwindtab = (uint64_t)bootinfo.bi_unwindtab;
        ia64_unwindtablen = (uint64_t)bootinfo.bi_unwindtablen;
        ksym_start = (void *)bootinfo.bi_symtab;
        ksym_end   = (void *)bootinfo.bi_esymtab;
        kernend = (vaddr_t)round_page((vaddr_t)bootinfo.bi_kernend);
#else
        kernend = (vaddr_t)round_page(bootinfo.bi_kernend);
#endif
        kernstartpfn = atop(IA64_RR_MASK(kernstart));
        kernendpfn = atop(IA64_RR_MASK(kernend));

        /*
         * Find out this system's page size, and initialize
         * PAGE_SIZE-dependent variables.
         */

        uvmexp.pagesize = PAGE_SIZE;
        uvm_md_init();


        /*
         * Find out how much memory is available, by looking at
         * the memory descriptors.
         */

        physmem = 0;

        for (md = efi_md_first(); md != NULL; md = efi_md_next(md)) {

                DPRINTF("MD %p: type %d pa 0x%lx cnt 0x%lx\n", md,
                        md->md_type, md->md_phys, md->md_pages);

                pfn0 = ia64_btop(round_page(md->md_phys));
                pfn1 = ia64_btop(trunc_page(md->md_phys + md->md_pages * 4096));
                if (pfn1 <= pfn0)
                        continue;

                if (md->md_type != EFI_MD_TYPE_FREE)
                        continue;

                /*
                 * Wimp out for now since we do not DTRT here with
                 * pci bus mastering (no bounce buffering, for example).
                 */
                if (pfn0 >= ia64_btop(0x100000000UL)) {
                        printf("Skipping memory chunk start 0x%lx\n",
                            md->md_phys);
                        continue;
                }
                if (pfn1 >= ia64_btop(0x100000000UL)) {
                        printf("Skipping memory chunk end 0x%lx\n",
                            md->md_phys + md->md_pages * 4096);
                        continue;
                }

                /*
                 * We have a memory descriptor that describes conventional
                 * memory that is for general use. We must determine if the
                 * loader has put the kernel in this region.
                 */
                physmem += (pfn1 - pfn0);
                if (pfn0 <= kernendpfn && kernstartpfn <= pfn1) {
                        /*
                         * Must compute the location of the kernel
                         * within the segment.
                         */
                        DPRINTF("Descriptor %p contains kernel\n", md);

                        if (pfn0 < kernstartpfn) {
                                /*
                                 * There is a chunk before the kernel.
                                 */
                                DPRINTF("Loading chunk before kernel: "
                                        "0x%lx / 0x%lx\n", pfn0, kernstartpfn);

                                uvm_page_physload(pfn0, kernstartpfn,
                                    pfn0, kernstartpfn, VM_FREELIST_DEFAULT);

                        }
                        if (kernendpfn < pfn1) {
                                /*
                                 * There is a chunk after the kernel.
                                 */
                                DPRINTF("Loading chunk after kernel: "
                                        "0x%lx / 0x%lx\n", kernendpfn, pfn1);

                                uvm_page_physload(kernendpfn, pfn1,
                                    kernendpfn, pfn1, VM_FREELIST_DEFAULT);

                        }
                } else {
                        /*
                         * Just load this cluster as one chunk.
                         */
                        DPRINTF("Loading descriptor %p: 0x%lx / 0x%lx\n",
                                md, pfn0, pfn1);

                        uvm_page_physload(pfn0, pfn1, pfn0, pfn1,
                            VM_FREELIST_DEFAULT);

                }
        }

        if (physmem == 0)
                panic("can't happen: system seems to have no memory!");

        /*
         * Initialize error message buffer (at end of core).
         */
        msgbufaddr = (void *) uvm_pageboot_alloc(MSGBUFSIZE);
        initmsgbuf(msgbufaddr, MSGBUFSIZE);

        /*
         * Init mapping for u page(s) for proc 0.  use memory area
         * already set up in locore.S
         */
        v = (vaddr_t)&kstack;
        uvm_lwp_setuarea(&lwp0, v);

        /*
         * Set the kernel sp, reserving space for an (empty) trapframe,
         * and make lwp0's trapframe pointer point to it for sanity.
         */
        lwp0.l_md.md_tf = (struct trapframe *)(v + UAREA_TF_OFFSET);
        lwp0.l_md.md_tf->tf_length = sizeof(struct trapframe);
        lwp0.l_md.md_tf->tf_flags = FRAME_SYSCALL;

        lwp0.l_md.user_stack = NULL;
        lwp0.l_md.user_stack_size = 0;

        pcb0 = lwp_getpcb(&lwp0);
        pcb0->pcb_special.sp = v + UAREA_SP_OFFSET;
        pcb0->pcb_special.bspstore = v + UAREA_BSPSTORE_OFFSET;

        /*
         * Setup global data for the bootstrap cpu.
         */
        ci = curcpu();

        /* ar.k4 contains the cpu_info pointer to the
         * current cpu.
         */
        ia64_set_k4((uint64_t) ci);
        ci->ci_cpuid = cpu_number();

        /*
         * Initialise process context. XXX: This should really be in cpu_switchto
         *
         * No membar needed because we're not switching from a
         * previous lwp, and the idle lwp we're switching to can't be
         * holding locks already; see cpu_switchto.
         */
        ci->ci_curlwp = &lwp0;

        /*
         * Initialize the primary CPU's idle PCB to proc0's.  In a
         * MULTIPROCESSOR configuration, each CPU will later get
         * its own idle PCB when autoconfiguration runs.
         */
        ci->ci_idle_pcb = pcb0;

        /* Indicate that proc0 has a CPU. */
        lwp0.l_cpu = ci;

        ia64_set_tpr(0);
        ia64_srlz_d();

        mutex_init(&pcb0->pcb_fpcpu_slock, MUTEX_DEFAULT, 0);

        /*
         * Save our current context so that we have a known (maybe even
         * sane) context as the initial context for new threads that are
         * forked from us.
         */
        if (savectx(pcb0))
                panic("savectx failed");

        /*
         * Initialize the virtual memory system.
         */
        pmap_bootstrap();

        /*
         * Initialize debuggers, and break into them if appropriate.
         */
#if NKSYMS || defined(DDB) || defined(MODULAR)
        ksyms_addsyms_elf((int)((uint64_t)ksym_end - (uint64_t)ksym_start),
            ksym_start, ksym_end);
#endif

#ifdef DDB
        if (boothowto & RB_KDB)
                Debugger();
#endif

        ret.bspstore = pcb0->pcb_special.bspstore;
        ret.sp = pcb0->pcb_special.sp;

        return (ret);
}

uint64_t
ia64_get_hcdp(void)
{

        return bootinfo.bi_hcdp;
}

/*
 * Set registers on exec.
 */
void
setregs(register struct lwp *l, struct exec_package *pack, vaddr_t stack)
{
        struct trapframe *tf;
        uint64_t *ksttop, *kst, regstkp;
        vaddr_t uv = uvm_lwp_getuarea(l);

        tf = l->l_md.md_tf;
        memset(tf, 0, sizeof(*tf));

        regstkp = uv + sizeof(struct pcb);

        ksttop =
            (uint64_t*)(regstkp + tf->tf_special.ndirty +
                                        (tf->tf_special.bspstore & 0x1ffUL));

        /* XXX: tf_special.ndirty on a new stack frame ??? */

        /*
         * We can ignore up to 8KB of dirty registers by masking off the
         * lower 13 bits in exception_restore() or epc_syscall(). This
         * should be enough for a couple of years, but if there are more
         * than 8KB of dirty registers, we lose track of the bottom of
         * the kernel stack. The solution is to copy the active part of
         * the kernel stack down 1 page (or 2, but not more than that)
         * so that we always have less than 8KB of dirty registers.
         */
        KASSERT((tf->tf_special.ndirty & ~PAGE_MASK) == 0);

        memset(&tf->tf_special, 0, sizeof(tf->tf_special));
        if ((tf->tf_flags & FRAME_SYSCALL) == 0) {      /* break syscalls. */
                memset(&tf->tf_scratch, 0, sizeof(tf->tf_scratch));
                memset(&tf->tf_scratch_fp, 0, sizeof(tf->tf_scratch_fp));
                tf->tf_special.cfm = (1UL<<63) | (3UL<<7) | 3UL;
                tf->tf_special.bspstore = IA64_BACKINGSTORE;
                /*
                 * Copy the arguments onto the kernel register stack so that
                 * they get loaded by the loadrs instruction. Skip over the
                 * NaT collection points.
                 */
                kst = ksttop - 1;
                if (((uintptr_t)kst & 0x1ff) == 0x1f8)
                        *kst-- = 0;
                *kst-- = stack;                         /* in3 = sp */
                if (((uintptr_t)kst & 0x1ff) == 0x1f8)
                        *kst-- = 0;
                *kst-- = l->l_proc->p_psstrp;           /* in2 = ps_strings */
                if (((uintptr_t)kst & 0x1ff) == 0x1f8)
                        *kst-- = 0;
                *kst-- = 0;                             /* in1 = *obj */
                if (((uintptr_t)kst & 0x1ff) == 0x1f8)
                        *kst-- = 0;
                *kst = 0;                               /* in0 = *cleanup */
                tf->tf_special.ndirty = (ksttop - kst) << 3;
        } else {                                /* epc syscalls (default). */
                tf->tf_special.cfm = (3UL<<62) | (3UL<<7) | 3UL;
                tf->tf_special.bspstore = IA64_BACKINGSTORE + 24;
                /*
                 * Write values for out0, out1, out2 and out3 to the user's
                 * backing store and arrange for them to be restored into
                 * the user's initial register frame.
                 * Assumes that (bspstore & 0x1f8) < 0x1e0.
                 */

                /* in0 = *cleanup */
                ustore_long((u_long *)(tf->tf_special.bspstore - 32), 0);

                /* in1 == *obj */
                ustore_long((u_long *)(tf->tf_special.bspstore -  24), 0);

                /* in2 == ps_strings */
                ustore_long((u_long *)(tf->tf_special.bspstore -  16),
                    l->l_proc->p_psstrp);

                /* in3 = sp */
                ustore_long((u_long *)(tf->tf_special.bspstore - 8),
                    stack);

        }

        tf->tf_special.iip = pack->ep_entry;
        tf->tf_special.sp = (stack & ~15) - 16;
        tf->tf_special.rsc = 0xf;
        tf->tf_special.fpsr = IA64_FPSR_DEFAULT;
        tf->tf_special.psr = IA64_PSR_IC | IA64_PSR_I | IA64_PSR_IT |
            IA64_PSR_DT | IA64_PSR_RT | IA64_PSR_DFH | IA64_PSR_BN |
            IA64_PSR_CPL_USER;
        return;
}

void
sendsig_siginfo(const ksiginfo_t *ksi, const sigset_t *mask)
{
        return;
}

void
cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags)
{
        return;
}

int
cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
{
        return EINVAL;
}

int
cpu_mcontext_validate(struct lwp *l, const mcontext_t *mcp)
{
        return EINVAL;
}

int
mm_md_physacc(paddr_t pa, vm_prot_t prot)
{

        return 0; /* TODO: Implement. */
}

void
ia64_sync_icache(vaddr_t va, vsize_t sz)
{
        vaddr_t lim;

        if (!ia64_sync_icache_needed)
                return;

        lim = va + sz;
        while (va < lim) {
                ia64_fc_i(va);
                va += 32;       /* XXX */
        }

        ia64_sync_i();
        ia64_srlz_i();
}

/*
 * Construct a PCB from a trapframe. This is called from kdb_trap() where
 * we want to start a backtrace from the function that caused us to enter
 * the debugger. We have the context in the trapframe, but base the trace
 * on the PCB. The PCB doesn't have to be perfect, as long as it contains
 * enough for a backtrace.
 */
void
makectx(struct trapframe *tf, struct pcb *pcb)
{
        pcb->pcb_special = tf->tf_special;
        pcb->pcb_special.__spare = ~0UL;        /* XXX see unwind.c */
        save_callee_saved(&pcb->pcb_preserved);
        save_callee_saved_fp(&pcb->pcb_preserved_fp);
}