Index: stable/10/sys/ia64/acpica/OsdEnvironment.c =================================================================== --- stable/10/sys/ia64/acpica/OsdEnvironment.c (revision 270295) +++ stable/10/sys/ia64/acpica/OsdEnvironment.c (revision 270296) @@ -1,77 +1,77 @@ /*- * Copyright (c) 2000,2001 Michael Smith * Copyright (c) 2000 BSDi * 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 __FBSDID("$FreeBSD$"); #include +#include #include -#include #include static u_long acpi_root_phys; SYSCTL_ULONG(_machdep, OID_AUTO, acpi_root, CTLFLAG_RD, &acpi_root_phys, 0, "The physical address of the RSDP"); ACPI_STATUS AcpiOsInitialize(void) { return (AE_OK); } ACPI_STATUS AcpiOsTerminate(void) { return (AE_OK); } static u_long acpi_get_root_from_efi(void) { static struct uuid acpi_root_uuid = EFI_TABLE_ACPI20; void *acpi_root; acpi_root = efi_get_table(&acpi_root_uuid); if (acpi_root != NULL) return (IA64_RR_MASK((uintptr_t)acpi_root)); return (0); } ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer(void) { if (acpi_root_phys == 0) acpi_root_phys = acpi_get_root_from_efi(); return (acpi_root_phys); } Index: stable/10/sys/ia64/ia64/clock.c =================================================================== --- stable/10/sys/ia64/ia64/clock.c (revision 270295) +++ stable/10/sys/ia64/ia64/clock.c (revision 270296) @@ -1,199 +1,199 @@ /*- * Copyright (c) 2005, 2009-2011 Marcel Moolenaar * 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 ``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 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 __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define CLOCK_ET_OFF 0 #define CLOCK_ET_PERIODIC 1 #define CLOCK_ET_ONESHOT 2 static struct eventtimer ia64_clock_et; static u_int ia64_clock_xiv; #ifndef SMP static timecounter_get_t ia64_get_timecount; static struct timecounter ia64_timecounter = { ia64_get_timecount, /* get_timecount */ 0, /* no poll_pps */ ~0u, /* counter_mask */ 0, /* frequency */ "ITC" /* name */ }; static u_int ia64_get_timecount(struct timecounter* tc) { return ia64_get_itc(); } #endif static u_int ia64_ih_clock(struct thread *td, u_int xiv, struct trapframe *tf) { struct eventtimer *et; struct trapframe *stf; uint64_t itc, load; uint32_t mode; PCPU_INC(md.stats.pcs_nclks); intrcnt[INTRCNT_CLOCK]++; itc = ia64_get_itc(); PCPU_SET(md.clock, itc); mode = PCPU_GET(md.clock_mode); if (mode == CLOCK_ET_PERIODIC) { load = PCPU_GET(md.clock_load); ia64_set_itm(itc + load); } else ia64_set_itv((1 << 16) | xiv); ia64_set_eoi(0); ia64_srlz_d(); et = &ia64_clock_et; if (et->et_active) { stf = td->td_intr_frame; td->td_intr_frame = tf; et->et_event_cb(et, et->et_arg); td->td_intr_frame = stf; } return (1); } /* * Event timer start method. */ static int ia64_clock_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { u_long itc, load; register_t is; if (period != 0) { PCPU_SET(md.clock_mode, CLOCK_ET_PERIODIC); load = (et->et_frequency * period) >> 32; } else { PCPU_SET(md.clock_mode, CLOCK_ET_ONESHOT); load = 0; } PCPU_SET(md.clock_load, load); if (first != 0) load = (et->et_frequency * first) >> 32; is = intr_disable(); itc = ia64_get_itc(); ia64_set_itm(itc + load); ia64_set_itv(ia64_clock_xiv); ia64_srlz_d(); intr_restore(is); return (0); } /* * Event timer stop method. */ static int ia64_clock_stop(struct eventtimer *et) { ia64_set_itv((1 << 16) | ia64_clock_xiv); ia64_srlz_d(); PCPU_SET(md.clock_mode, CLOCK_ET_OFF); PCPU_SET(md.clock_load, 0); return (0); } /* * We call cpu_initclocks() on the APs as well. It allows us to * group common initialization in the same function. */ void cpu_initclocks() { ia64_clock_stop(NULL); if (PCPU_GET(cpuid) == 0) cpu_initclocks_bsp(); else cpu_initclocks_ap(); } static void clock_configure(void *dummy) { struct eventtimer *et; u_long itc_freq; ia64_clock_xiv = ia64_xiv_alloc(PI_REALTIME, IA64_XIV_IPI, ia64_ih_clock); if (ia64_clock_xiv == 0) panic("No XIV for clock interrupts"); itc_freq = (u_long)ia64_itc_freq() * 1000000ul; et = &ia64_clock_et; et->et_name = "ITC"; et->et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; et->et_quality = 1000; et->et_frequency = itc_freq; et->et_min_period = SBT_1S / (10 * hz); et->et_max_period = (0xfffffffeul << 32) / itc_freq; et->et_start = ia64_clock_start; et->et_stop = ia64_clock_stop; et->et_priv = NULL; et_register(et); #ifndef SMP ia64_timecounter.tc_frequency = itc_freq; tc_init(&ia64_timecounter); #endif } SYSINIT(clkcfg, SI_SUB_CONFIGURE, SI_ORDER_SECOND, clock_configure, NULL); Index: stable/10/sys/ia64/ia64/dump_machdep.c =================================================================== --- stable/10/sys/ia64/ia64/dump_machdep.c (revision 270295) +++ stable/10/sys/ia64/ia64/dump_machdep.c (revision 270296) @@ -1,440 +1,440 @@ /*- * Copyright (c) 2002 Marcel Moolenaar * 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 ``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 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 __FBSDID("$FreeBSD$"); #include "opt_watchdog.h" #include #include #include #include +#include #include #include #ifdef SW_WATCHDOG #include #endif #include #include #include -#include #include #include CTASSERT(sizeof(struct kerneldumpheader) == 512); /* * Don't touch the first SIZEOF_METADATA bytes on the dump device. This * is to protect us from metadata and to protect metadata from us. */ #define SIZEOF_METADATA (64*1024) #define MD_ALIGN(x) (((off_t)(x) + EFI_PAGE_MASK) & ~EFI_PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) static int minidump = 0; TUNABLE_INT("debug.minidump", &minidump); SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &minidump, 0, "Enable mini crash dumps"); static struct kerneldumpheader kdh; static off_t dumplo, fileofs; /* Handle buffered writes. */ static char buffer[DEV_BSIZE]; static size_t fragsz; static int buf_write(struct dumperinfo *di, char *ptr, size_t sz) { size_t len; int error; while (sz) { len = DEV_BSIZE - fragsz; if (len > sz) len = sz; bcopy(ptr, buffer + fragsz, len); fragsz += len; ptr += len; sz -= len; if (fragsz == DEV_BSIZE) { error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); if (error) return (error); dumplo += DEV_BSIZE; fragsz = 0; } } return (0); } static int buf_flush(struct dumperinfo *di) { int error; if (fragsz == 0) return (0); error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); dumplo += DEV_BSIZE; fragsz = 0; return (error); } /* * Physical dump support */ typedef int phys_callback_t(struct efi_md*, int, void*); static int phys_cb_dumpdata(struct efi_md *mdp, int seqnr, void *arg) { struct dumperinfo *di = (struct dumperinfo*)arg; vm_offset_t pa; uint64_t pgs; size_t counter, sz; int c, error, twiddle; error = 0; /* catch case in which mdp->md_pages is 0 */ counter = 0; /* Update twiddle every 16MB */ twiddle = 0; pgs = mdp->md_pages; pa = IA64_PHYS_TO_RR7(mdp->md_phys); printf(" chunk %d: %ld pages ", seqnr, (long)pgs); while (pgs) { sz = (pgs > (DFLTPHYS >> EFI_PAGE_SHIFT)) ? DFLTPHYS : pgs << EFI_PAGE_SHIFT; counter += sz; if (counter >> 24) { printf("%c\b", "|/-\\"[twiddle++ & 3]); counter &= (1<<24) - 1; } #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif error = dump_write(di, (void*)pa, 0, dumplo, sz); if (error) break; dumplo += sz; pgs -= sz >> EFI_PAGE_SHIFT; pa += sz; /* Check for user abort. */ c = cncheckc(); if (c == 0x03) return (ECANCELED); if (c != -1) printf("(CTRL-C to abort) "); } printf("... %s\n", (error) ? "fail" : "ok"); return (error); } static int phys_cb_dumphdr(struct efi_md *mdp, int seqnr, void *arg) { struct dumperinfo *di = (struct dumperinfo*)arg; Elf64_Phdr phdr; int error; bzero(&phdr, sizeof(phdr)); phdr.p_type = PT_LOAD; phdr.p_flags = PF_R; /* XXX */ phdr.p_offset = fileofs; phdr.p_vaddr = (uintptr_t)mdp->md_virt; /* XXX probably bogus. */ phdr.p_paddr = mdp->md_phys; phdr.p_filesz = mdp->md_pages << EFI_PAGE_SHIFT; phdr.p_memsz = mdp->md_pages << EFI_PAGE_SHIFT; phdr.p_align = EFI_PAGE_SIZE; error = buf_write(di, (char*)&phdr, sizeof(phdr)); fileofs += phdr.p_filesz; return (error); } static int phys_cb_size(struct efi_md *mdp, int seqnr, void *arg) { uint64_t *sz = (uint64_t*)arg; *sz += (uint64_t)mdp->md_pages << EFI_PAGE_SHIFT; return (0); } static int phys_foreach(phys_callback_t cb, void *arg) { struct efi_md *mdp; int error, seqnr; seqnr = 0; mdp = efi_md_first(); while (mdp != NULL) { if (mdp->md_type == EFI_MD_TYPE_FREE || mdp->md_type == EFI_MD_TYPE_DATA || mdp->md_type == EFI_MD_TYPE_CODE || mdp->md_type == EFI_MD_TYPE_BS_DATA || mdp->md_type == EFI_MD_TYPE_BS_CODE) { error = (*cb)(mdp, seqnr++, arg); if (error) return (-error); } mdp = efi_md_next(mdp); } return (seqnr); } /* * Virtual dump (aka minidump) support */ typedef int virt_callback_t(vm_offset_t, vm_size_t, int, void*); static int virt_cb_size(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) { uint64_t *dumpsize = (uint64_t *)arg; *dumpsize += sz; return (0); } static int virt_cb_dumphdr(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) { struct dumperinfo *di = (struct dumperinfo *)arg; Elf64_Phdr phdr; int error; bzero(&phdr, sizeof(phdr)); phdr.p_type = PT_LOAD; phdr.p_flags = PF_R; /* XXX */ phdr.p_offset = fileofs; phdr.p_vaddr = va; phdr.p_paddr = ~0UL; phdr.p_filesz = sz; phdr.p_memsz = sz; phdr.p_align = PAGE_SIZE; error = buf_write(di, (char*)&phdr, sizeof(phdr)); fileofs += phdr.p_filesz; return (error); } static int virt_cb_dumpdata(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) { struct dumperinfo *di = (struct dumperinfo *)arg; size_t counter, iosz; int c, error, twiddle; error = 0; /* catch case in which pgs is 0 */ counter = 0; /* Update twiddle every 16MB */ twiddle = 0; printf(" chunk %d: %ld pages ", seqnr, atop(sz)); while (sz) { iosz = (sz > DFLTPHYS) ? DFLTPHYS : sz; counter += iosz; if (counter >> 24) { printf("%c\b", "|/-\\"[twiddle++ & 3]); counter &= (1<<24) - 1; } #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif error = dump_write(di, (void*)va, 0, dumplo, iosz); if (error) break; dumplo += iosz; sz -= iosz; va += iosz; /* Check for user abort. */ c = cncheckc(); if (c == 0x03) return (ECANCELED); if (c != -1) printf("(CTRL-C to abort) "); } printf("... %s\n", (error) ? "fail" : "ok"); return (error); } static int virt_foreach(virt_callback_t cb, void *arg) { vm_offset_t va; vm_size_t sz; int error, seqnr; seqnr = 0; while (1) { switch (seqnr) { case 0: va = IA64_PBVM_BASE; sz = round_page(bootinfo->bi_kernend) - va; break; default: va = 0; sz = 0; break; } if (va == 0 && sz == 0) break; error = (*cb)(va, sz, seqnr, arg); if (error) return (-error); seqnr++; } return (seqnr); } /* * main entry point. */ void dumpsys(struct dumperinfo *di) { Elf64_Ehdr ehdr; uint64_t dumpsize; off_t hdrgap; size_t hdrsz; int error, status; bzero(&ehdr, sizeof(ehdr)); ehdr.e_ident[EI_MAG0] = ELFMAG0; ehdr.e_ident[EI_MAG1] = ELFMAG1; ehdr.e_ident[EI_MAG2] = ELFMAG2; ehdr.e_ident[EI_MAG3] = ELFMAG3; ehdr.e_ident[EI_CLASS] = ELFCLASS64; #if BYTE_ORDER == LITTLE_ENDIAN ehdr.e_ident[EI_DATA] = ELFDATA2LSB; #else ehdr.e_ident[EI_DATA] = ELFDATA2MSB; #endif ehdr.e_ident[EI_VERSION] = EV_CURRENT; ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ ehdr.e_type = ET_CORE; ehdr.e_machine = EM_IA_64; ehdr.e_entry = (minidump) ? (uintptr_t)bootinfo : ia64_tpa((uintptr_t)bootinfo); ehdr.e_phoff = sizeof(ehdr); ehdr.e_flags = (minidump) ? 0 : EF_IA_64_ABSOLUTE; /* XXX misuse? */ ehdr.e_ehsize = sizeof(ehdr); ehdr.e_phentsize = sizeof(Elf64_Phdr); ehdr.e_shentsize = sizeof(Elf64_Shdr); /* Calculate dump size. */ dumpsize = 0L; status = (minidump) ? virt_foreach(virt_cb_size, &dumpsize) : phys_foreach(phys_cb_size, &dumpsize); if (status < 0) { error = -status; goto fail; } ehdr.e_phnum = status; hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; fileofs = (minidump) ? round_page(hdrsz) : MD_ALIGN(hdrsz); dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); /* Determine dump offset on device. */ if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { error = ENOSPC; goto fail; } dumplo = di->mediaoffset + di->mediasize - dumpsize; dumplo -= sizeof(kdh) * 2; mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize); printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, ehdr.e_phnum); /* Dump leader */ error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); if (error) goto fail; dumplo += sizeof(kdh); /* Dump ELF header */ error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); if (error) goto fail; /* Dump program headers */ status = (minidump) ? virt_foreach(virt_cb_dumphdr, di) : phys_foreach(phys_cb_dumphdr, di); if (status < 0) { error = -status; goto fail; } buf_flush(di); /* * All headers are written using blocked I/O, so we know the * current offset is (still) block aligned. Skip the alignment * in the file to have the segment contents aligned at page * boundary. For physical dumps, it's the EFI page size (= 4K). * For minidumps it's the kernel's page size (= 8K). */ dumplo += hdrgap; /* Dump memory chunks (updates dumplo) */ status = (minidump) ? virt_foreach(virt_cb_dumpdata, di) : phys_foreach(phys_cb_dumpdata, di); if (status < 0) { error = -status; goto fail; } /* Dump trailer */ error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); if (error) goto fail; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); printf("\nDump complete\n"); return; fail: if (error == ECANCELED) printf("\nDump aborted\n"); else printf("\n** DUMP FAILED (ERROR %d) **\n", error); } Index: stable/10/sys/ia64/ia64/efi.c =================================================================== --- stable/10/sys/ia64/ia64/efi.c (revision 270295) +++ stable/10/sys/ia64/ia64/efi.c (revision 270296) @@ -1,270 +1,270 @@ /*- * Copyright (c) 2004 Marcel Moolenaar * Copyright (c) 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. */ #include __FBSDID("$FreeBSD$"); #include +#include #include #include -#include #include #include #include #include static struct efi_systbl *efi_systbl; static struct efi_cfgtbl *efi_cfgtbl; static struct efi_rt *efi_runtime; static int efi_status2err[25] = { 0, /* EFI_SUCCESS */ ENOEXEC, /* EFI_LOAD_ERROR */ EINVAL, /* EFI_INVALID_PARAMETER */ ENOSYS, /* EFI_UNSUPPORTED */ EMSGSIZE, /* EFI_BAD_BUFFER_SIZE */ EOVERFLOW, /* EFI_BUFFER_TOO_SMALL */ EBUSY, /* EFI_NOT_READY */ EIO, /* EFI_DEVICE_ERROR */ EROFS, /* EFI_WRITE_PROTECTED */ EAGAIN, /* EFI_OUT_OF_RESOURCES */ EIO, /* EFI_VOLUME_CORRUPTED */ ENOSPC, /* EFI_VOLUME_FULL */ ENXIO, /* EFI_NO_MEDIA */ ESTALE, /* EFI_MEDIA_CHANGED */ ENOENT, /* EFI_NOT_FOUND */ EACCES, /* EFI_ACCESS_DENIED */ ETIMEDOUT, /* EFI_NO_RESPONSE */ EADDRNOTAVAIL, /* EFI_NO_MAPPING */ ETIMEDOUT, /* EFI_TIMEOUT */ EDOOFUS, /* EFI_NOT_STARTED */ EALREADY, /* EFI_ALREADY_STARTED */ ECANCELED, /* EFI_ABORTED */ EPROTO, /* EFI_ICMP_ERROR */ EPROTO, /* EFI_TFTP_ERROR */ EPROTO /* EFI_PROTOCOL_ERROR */ }; static int efi_status_to_errno(efi_status status) { u_long code; int error; code = status & 0x3ffffffffffffffful; error = (code < 25) ? efi_status2err[code] : EDOOFUS; return (error); } void efi_boot_finish(void) { } /* * Collect the entry points for PAL and SAL. Be extra careful about NULL * pointer values. We're running pre-console, so it's better to return * error values than to cause panics, machine checks and other traps and * faults. Keep this minimal... */ int efi_boot_minimal(uint64_t systbl) { ia64_efi_f setvirt; struct efi_md *md; efi_status status; if (systbl == 0) return (EINVAL); efi_systbl = (struct efi_systbl *)IA64_PHYS_TO_RR7(systbl); if (efi_systbl->st_hdr.th_sig != EFI_SYSTBL_SIG) { efi_systbl = NULL; return (EFAULT); } efi_cfgtbl = (efi_systbl->st_cfgtbl == 0) ? NULL : (struct efi_cfgtbl *)IA64_PHYS_TO_RR7(efi_systbl->st_cfgtbl); if (efi_cfgtbl == NULL) return (ENOENT); efi_runtime = (efi_systbl->st_rt == 0) ? NULL : (struct efi_rt *)IA64_PHYS_TO_RR7(efi_systbl->st_rt); if (efi_runtime == NULL) return (ENOENT); /* * Relocate runtime memory segments for firmware. */ md = efi_md_first(); while (md != NULL) { if (md->md_attr & EFI_MD_ATTR_RT) { md->md_virt = (md->md_attr & EFI_MD_ATTR_WB) ? (void *)IA64_PHYS_TO_RR7(md->md_phys) : (void *)IA64_PHYS_TO_RR6(md->md_phys); } md = efi_md_next(md); } setvirt = (void *)IA64_PHYS_TO_RR7((u_long)efi_runtime->rt_setvirtual); status = ia64_efi_physical(setvirt, bootinfo->bi_memmap_size, bootinfo->bi_memdesc_size, bootinfo->bi_memdesc_version, ia64_tpa(bootinfo->bi_memmap)); return ((status < 0) ? EFAULT : 0); } void * efi_get_table(struct uuid *uuid) { struct efi_cfgtbl *ct; u_long count; if (efi_cfgtbl == NULL) return (NULL); count = efi_systbl->st_entries; ct = efi_cfgtbl; while (count--) { if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid))) return ((void *)IA64_PHYS_TO_RR7(ct->ct_data)); ct++; } return (NULL); } void efi_get_time(struct efi_tm *tm) { efi_runtime->rt_gettime(tm, NULL); } struct efi_md * efi_md_first(void) { struct efi_md *md; if (bootinfo->bi_memmap == 0) return (NULL); md = (struct efi_md *)bootinfo->bi_memmap; return (md); } struct efi_md * efi_md_last(void) { struct efi_md *md; if (bootinfo->bi_memmap == 0) return (NULL); md = (struct efi_md *)(bootinfo->bi_memmap + bootinfo->bi_memmap_size - bootinfo->bi_memdesc_size); return (md); } struct efi_md * efi_md_next(struct efi_md *md) { struct efi_md *lim; lim = efi_md_last(); md = (struct efi_md *)((uintptr_t)md + bootinfo->bi_memdesc_size); return ((md > lim) ? NULL : md); } struct efi_md * efi_md_prev(struct efi_md *md) { struct efi_md *lim; lim = efi_md_first(); md = (struct efi_md *)((uintptr_t)md - bootinfo->bi_memdesc_size); return ((md < lim) ? NULL : md); } struct efi_md * efi_md_find(vm_paddr_t pa) { static struct efi_md *last = NULL; struct efi_md *md, *p0, *p1; md = (last != NULL) ? last : efi_md_first(); p1 = p0 = NULL; while (md != NULL && md != p1) { if (pa >= md->md_phys && pa < md->md_phys + md->md_pages * EFI_PAGE_SIZE) { last = md; return (md); } p1 = p0; p0 = md; md = (pa < md->md_phys) ? efi_md_prev(md) : efi_md_next(md); } return (NULL); } void efi_reset_system(void) { if (efi_runtime != NULL) efi_runtime->rt_reset(EFI_RESET_WARM, 0, 0, NULL); panic("%s: unable to reset the machine", __func__); } int efi_set_time(struct efi_tm *tm) { return (efi_status_to_errno(efi_runtime->rt_settime(tm))); } int efi_var_get(efi_char *name, struct uuid *vendor, uint32_t *attrib, size_t *datasize, void *data) { efi_status status; status = efi_runtime->rt_getvar(name, vendor, attrib, datasize, data); return (efi_status_to_errno(status)); } int efi_var_nextname(size_t *namesize, efi_char *name, struct uuid *vendor) { efi_status status; status = efi_runtime->rt_scanvar(namesize, name, vendor); return (efi_status_to_errno(status)); } int efi_var_set(efi_char *name, struct uuid *vendor, uint32_t attrib, size_t datasize, void *data) { efi_status status; status = efi_runtime->rt_setvar(name, vendor, attrib, datasize, data); return (efi_status_to_errno(status)); } Index: stable/10/sys/ia64/ia64/iodev_machdep.c =================================================================== --- stable/10/sys/ia64/ia64/iodev_machdep.c (revision 270295) +++ stable/10/sys/ia64/ia64/iodev_machdep.c (revision 270296) @@ -1,208 +1,208 @@ /*- * Copyright (c) 2010 Marcel Moolenaar * 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 __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include #include #include #include -#include #include static int iodev_efivar_getvar(struct iodev_efivar_req *req); static int iodev_efivar_nextname(struct iodev_efivar_req *req); static int iodev_efivar_setvar(struct iodev_efivar_req *req); /* ARGSUSED */ int iodev_open(struct thread *td __unused) { return (0); } /* ARGSUSED */ int iodev_close(struct thread *td __unused) { return (0); } int iodev_ioctl(u_long cmd, caddr_t data) { struct iodev_efivar_req *efivar_req; int error; switch (cmd) { case IODEV_EFIVAR: efivar_req = (struct iodev_efivar_req *)data; efivar_req->result = 0; /* So it's well-defined */ switch (efivar_req->access) { case IODEV_EFIVAR_GETVAR: error = iodev_efivar_getvar(efivar_req); break; case IODEV_EFIVAR_NEXTNAME: error = iodev_efivar_nextname(efivar_req); break; case IODEV_EFIVAR_SETVAR: error = iodev_efivar_setvar(efivar_req); break; default: error = EINVAL; break; } break; default: error = ENOIOCTL; } return (error); } static int iodev_efivar_getvar(struct iodev_efivar_req *req) { void *data; efi_char *name; int error; if ((req->namesize & 1) != 0 || req->namesize < 4) return (EINVAL); if (req->datasize == 0) return (EINVAL); /* * Pre-zero the allocated memory and don't copy the last 2 bytes * of the name. That should be the closing nul character (ucs-2) * and if not, then we ensured a nul-terminating string. This is * to protect the firmware and thus ourselves. */ name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO); error = copyin(req->name, name, req->namesize - 2); if (error) { free(name, M_TEMP); return (error); } data = malloc(req->datasize, M_TEMP, M_WAITOK); error = efi_var_get(name, &req->vendor, &req->attrib, &req->datasize, data); if (error == EOVERFLOW || error == ENOENT) { req->result = error; error = 0; } if (!error && !req->result) error = copyout(data, req->data, req->datasize); free(data, M_TEMP); free(name, M_TEMP); return (error); } static int iodev_efivar_nextname(struct iodev_efivar_req *req) { efi_char *name; int error; /* Enforce a reasonable minimum size of the name buffer. */ if (req->namesize < 4) return (EINVAL); name = malloc(req->namesize, M_TEMP, M_WAITOK); error = copyin(req->name, name, req->namesize); if (error) { free(name, M_TEMP); return (error); } error = efi_var_nextname(&req->namesize, name, &req->vendor); if (error == EOVERFLOW || error == ENOENT) { req->result = error; error = 0; } if (!error && !req->result) error = copyout(name, req->name, req->namesize); free(name, M_TEMP); return (error); } static int iodev_efivar_setvar(struct iodev_efivar_req *req) { void *data; efi_char *name; int error; if ((req->namesize & 1) != 0 || req->namesize < 4) return (EINVAL); /* * Pre-zero the allocated memory and don't copy the last 2 bytes * of the name. That should be the closing nul character (ucs-2) * and if not, then we ensured a nul-terminating string. This is * to protect the firmware and thus ourselves. */ name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO); error = copyin(req->name, name, req->namesize - 2); if (error) { free(name, M_TEMP); return (error); } if (req->datasize) { data = malloc(req->datasize, M_TEMP, M_WAITOK); error = copyin(req->data, data, req->datasize); if (error) { free(data, M_TEMP); free(name, M_TEMP); return (error); } } else data = NULL; error = efi_var_set(name, &req->vendor, req->attrib, req->datasize, data); if (error == EAGAIN || error == ENOENT) { req->result = error; error = 0; } free(data, M_TEMP); free(name, M_TEMP); return (error); } Index: stable/10/sys/ia64/ia64/machdep.c =================================================================== --- stable/10/sys/ia64/ia64/machdep.c (revision 270295) +++ stable/10/sys/ia64/ia64/machdep.c (revision 270296) @@ -1,1540 +1,1540 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ddb.h" #include "opt_kstack_pages.h" #include "opt_sched.h" #include "opt_xtrace.h" #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #ifdef SMP #include #endif #include #include /* * For atomicity reasons, we demand that pc_curthread is the first * field in the struct pcpu. It allows us to read the pointer with * a single atomic instruction: * ld8 %curthread = [r13] * Otherwise we would first have to calculate the load address and * store the result in a temporary register and that for the load: * add %temp = %offsetof(struct pcpu), r13 * ld8 %curthread = [%temp] * A context switch inbetween the add and the ld8 could have the * thread migrate to a different core. In that case, %curthread * would be the thread running on the original core and not actually * the current thread. */ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); static SYSCTL_NODE(_hw, OID_AUTO, freq, CTLFLAG_RD, 0, ""); static SYSCTL_NODE(_machdep, OID_AUTO, cpu, CTLFLAG_RD, 0, ""); static u_int bus_freq; SYSCTL_UINT(_hw_freq, OID_AUTO, bus, CTLFLAG_RD, &bus_freq, 0, "Bus clock frequency"); static u_int cpu_freq; SYSCTL_UINT(_hw_freq, OID_AUTO, cpu, CTLFLAG_RD, &cpu_freq, 0, "CPU clock frequency"); static u_int itc_freq; SYSCTL_UINT(_hw_freq, OID_AUTO, itc, CTLFLAG_RD, &itc_freq, 0, "ITC frequency"); int cold = 1; int unmapped_buf_allowed = 0; struct bootinfo *bootinfo; struct pcpu pcpu0; extern u_int64_t kernel_text[], _end[]; extern u_int64_t ia64_gateway_page[]; extern u_int64_t break_sigtramp[]; extern u_int64_t epc_sigtramp[]; struct fpswa_iface *fpswa_iface; vm_size_t ia64_pal_size; vm_paddr_t ia64_pal_base; vm_offset_t ia64_port_base; u_int64_t ia64_lapic_addr = PAL_PIB_DEFAULT_ADDR; struct ia64_pib *ia64_pib; static int ia64_sync_icache_needed; char machine[] = MACHINE; SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, ""); static char cpu_model[64]; SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD, cpu_model, 0, "The CPU model name"); static char cpu_family[64]; SYSCTL_STRING(_hw, OID_AUTO, family, CTLFLAG_RD, cpu_family, 0, "The CPU family name"); #ifdef DDB extern vm_offset_t ksym_start, ksym_end; #endif struct msgbuf *msgbufp = NULL; /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(sbintime_t) = NULL; struct kva_md_info kmi; static void identifycpu(void) { char vendor[17]; char *family_name, *model_name; u_int64_t features, tmp; int number, revision, model, family, archrev; /* * Assumes little-endian. */ *(u_int64_t *) &vendor[0] = ia64_get_cpuid(0); *(u_int64_t *) &vendor[8] = ia64_get_cpuid(1); vendor[16] = '\0'; tmp = ia64_get_cpuid(3); number = (tmp >> 0) & 0xff; revision = (tmp >> 8) & 0xff; model = (tmp >> 16) & 0xff; family = (tmp >> 24) & 0xff; archrev = (tmp >> 32) & 0xff; family_name = model_name = "unknown"; switch (family) { case 0x07: family_name = "Itanium"; model_name = "Merced"; break; case 0x1f: family_name = "Itanium 2"; switch (model) { case 0x00: model_name = "McKinley"; break; case 0x01: /* * Deerfield is a low-voltage variant based on the * Madison core. We need circumstantial evidence * (i.e. the clock frequency) to identify those. * Allow for roughly 1% error margin. */ if (cpu_freq > 990 && cpu_freq < 1010) model_name = "Deerfield"; else model_name = "Madison"; break; case 0x02: model_name = "Madison II"; break; } break; case 0x20: ia64_sync_icache_needed = 1; family_name = "Itanium 2"; switch (model) { case 0x00: model_name = "Montecito"; break; case 0x01: model_name = "Montvale"; break; } break; } snprintf(cpu_family, sizeof(cpu_family), "%s", family_name); snprintf(cpu_model, sizeof(cpu_model), "%s", model_name); features = ia64_get_cpuid(4); printf("CPU: %s (", model_name); if (cpu_freq) printf("%u MHz ", cpu_freq); printf("%s)\n", family_name); printf(" Origin = \"%s\" Revision = %d\n", vendor, revision); printf(" Features = 0x%b\n", (u_int32_t) features, "\020" "\001LB" /* long branch (brl) instruction. */ "\002SD" /* Spontaneous deferral. */ "\003AO" /* 16-byte atomic operations (ld, st, cmpxchg). */ ); } static void cpu_startup(void *dummy) { char nodename[16]; struct pcpu *pc; struct pcpu_stats *pcs; /* * Good {morning,afternoon,evening,night}. */ identifycpu(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %ld (%ld MB)\n", ptoa(realmem), ptoa(realmem) / 1048576); vm_ksubmap_init(&kmi); printf("avail memory = %ld (%ld MB)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1048576); 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); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); /* * Traverse the MADT to discover IOSAPIC and Local SAPIC * information. */ ia64_probe_sapics(); ia64_pib = pmap_mapdev(ia64_lapic_addr, sizeof(*ia64_pib)); ia64_mca_init(); /* * Create sysctl tree for per-CPU information. */ STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { snprintf(nodename, sizeof(nodename), "%u", pc->pc_cpuid); sysctl_ctx_init(&pc->pc_md.sysctl_ctx); pc->pc_md.sysctl_tree = SYSCTL_ADD_NODE(&pc->pc_md.sysctl_ctx, SYSCTL_STATIC_CHILDREN(_machdep_cpu), OID_AUTO, nodename, CTLFLAG_RD, NULL, ""); if (pc->pc_md.sysctl_tree == NULL) continue; pcs = &pc->pc_md.stats; SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nasts", CTLFLAG_RD, &pcs->pcs_nasts, "Number of IPI_AST interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nclks", CTLFLAG_RD, &pcs->pcs_nclks, "Number of clock interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nextints", CTLFLAG_RD, &pcs->pcs_nextints, "Number of ExtINT interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nhardclocks", CTLFLAG_RD, &pcs->pcs_nhardclocks, "Number of IPI_HARDCLOCK interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nhighfps", CTLFLAG_RD, &pcs->pcs_nhighfps, "Number of IPI_HIGH_FP interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nhwints", CTLFLAG_RD, &pcs->pcs_nhwints, "Number of hardware (device) interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "npreempts", CTLFLAG_RD, &pcs->pcs_npreempts, "Number of IPI_PREEMPT interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nrdvs", CTLFLAG_RD, &pcs->pcs_nrdvs, "Number of IPI_RENDEZVOUS interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nstops", CTLFLAG_RD, &pcs->pcs_nstops, "Number of IPI_STOP interrupts"); SYSCTL_ADD_ULONG(&pc->pc_md.sysctl_ctx, SYSCTL_CHILDREN(pc->pc_md.sysctl_tree), OID_AUTO, "nstrays", CTLFLAG_RD, &pcs->pcs_nstrays, "Number of stray interrupts"); } } SYSINIT(cpu_startup, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL); void cpu_flush_dcache(void *ptr, size_t len) { vm_offset_t lim, va; va = (uintptr_t)ptr & ~31; lim = (uintptr_t)ptr + len; while (va < lim) { ia64_fc(va); va += 32; } ia64_srlz_d(); } /* Get current clock frequency for the given cpu id. */ int cpu_est_clockrate(int cpu_id, uint64_t *rate) { if (pcpu_find(cpu_id) == NULL || rate == NULL) return (EINVAL); *rate = (u_long)cpu_freq * 1000000ul; return (0); } void cpu_halt() { efi_reset_system(); } void cpu_idle(int busy) { register_t ie; sbintime_t sbt = -1; if (!busy) { critical_enter(); sbt = cpu_idleclock(); } ie = intr_disable(); KASSERT(ie != 0, ("%s called with interrupts disabled\n", __func__)); if (sched_runnable()) ia64_enable_intr(); else if (cpu_idle_hook != NULL) { (*cpu_idle_hook)(sbt); /* The hook must enable interrupts! */ } else { ia64_call_pal_static(PAL_HALT_LIGHT, 0, 0, 0); ia64_enable_intr(); } if (!busy) { cpu_activeclock(); critical_exit(); } } int cpu_idle_wakeup(int cpu) { return (0); } void cpu_reset() { efi_reset_system(); } void cpu_switch(struct thread *old, struct thread *new, struct mtx *mtx) { struct pcb *oldpcb, *newpcb; oldpcb = old->td_pcb; #ifdef COMPAT_FREEBSD32 ia32_savectx(oldpcb); #endif if (PCPU_GET(fpcurthread) == old) old->td_frame->tf_special.psr |= IA64_PSR_DFH; if (!savectx(oldpcb)) { newpcb = new->td_pcb; oldpcb->pcb_current_pmap = pmap_switch(newpcb->pcb_current_pmap); atomic_store_rel_ptr(&old->td_lock, mtx); #if defined(SCHED_ULE) && defined(SMP) while (atomic_load_acq_ptr(&new->td_lock) == &blocked_lock) cpu_spinwait(); #endif PCPU_SET(curthread, new); #ifdef COMPAT_FREEBSD32 ia32_restorectx(newpcb); #endif if (PCPU_GET(fpcurthread) == new) new->td_frame->tf_special.psr &= ~IA64_PSR_DFH; restorectx(newpcb); /* We should not get here. */ panic("cpu_switch: restorectx() returned"); /* NOTREACHED */ } } void cpu_throw(struct thread *old __unused, struct thread *new) { struct pcb *newpcb; newpcb = new->td_pcb; (void)pmap_switch(newpcb->pcb_current_pmap); #if defined(SCHED_ULE) && defined(SMP) while (atomic_load_acq_ptr(&new->td_lock) == &blocked_lock) cpu_spinwait(); #endif PCPU_SET(curthread, new); #ifdef COMPAT_FREEBSD32 ia32_restorectx(newpcb); #endif restorectx(newpcb); /* We should not get here. */ panic("cpu_throw: restorectx() returned"); /* NOTREACHED */ } void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { /* * Set pc_acpi_id to "uninitialized". * See sys/dev/acpica/acpi_cpu.c */ pcpu->pc_acpi_id = 0xffffffff; } void cpu_pcpu_setup(struct pcpu *pc, u_int acpi_id, u_int sapic_id) { pc->pc_acpi_id = acpi_id; pc->pc_md.lid = IA64_LID_SET_SAPIC_ID(sapic_id); } void spinlock_enter(void) { struct thread *td; int intr; td = curthread; if (td->td_md.md_spinlock_count == 0) { intr = intr_disable(); td->td_md.md_spinlock_count = 1; td->td_md.md_saved_intr = intr; } else td->td_md.md_spinlock_count++; critical_enter(); } void spinlock_exit(void) { struct thread *td; int intr; td = curthread; critical_exit(); intr = td->td_md.md_saved_intr; td->td_md.md_spinlock_count--; if (td->td_md.md_spinlock_count == 0) intr_restore(intr); } void kdb_cpu_trap(int vector, int code __unused) { #ifdef XTRACE ia64_xtrace_stop(); #endif __asm __volatile("flushrs;;"); /* Restart after the break instruction. */ if (vector == IA64_VEC_BREAK && kdb_frame->tf_special.ifa == IA64_FIXED_BREAK) kdb_frame->tf_special.psr += IA64_PSR_RI_1; } void map_vhpt(uintptr_t vhpt) { pt_entry_t pte; uint64_t psr; 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"(pmap_vhpt_log2size << 2)); __asm __volatile("mov %0=psr" : "=r"(psr)); __asm __volatile("rsm psr.ic|psr.i"); ia64_srlz_i(); ia64_set_ifa(vhpt); ia64_set_itir(pmap_vhpt_log2size << 2); 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; vm_offset_t va; vm_size_t sz; uint64_t psr; u_int shft; if (ia64_pal_size == 0) return; va = IA64_PHYS_TO_RR7(ia64_pal_base); sz = ia64_pal_size; shft = 0; while (sz > 1) { shft++; sz >>= 1; } 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"(va), "r"(shft<<2)); __asm __volatile("mov %0=psr" : "=r"(psr)); __asm __volatile("rsm psr.ic|psr.i"); ia64_srlz_i(); ia64_set_ifa(va); ia64_set_itir(shft << 2); ia64_srlz_d(); __asm __volatile("itr.d dtr[%0]=%1" :: "r"(4), "r"(pte)); ia64_srlz_d(); __asm __volatile("itr.i itr[%0]=%1" :: "r"(1), "r"(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 |= ia64_tpa((uint64_t)ia64_gateway_page) & PTE_PPN_MASK; __asm __volatile("ptr.d %0,%1; ptr.i %0,%1" :: "r"(VM_MAXUSER_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_MAXUSER_ADDRESS); ia64_set_itir(PAGE_SHIFT << 2); ia64_srlz_d(); __asm __volatile("itr.d dtr[%0]=%1" :: "r"(5), "r"(pte)); ia64_srlz_d(); __asm __volatile("itr.i itr[%0]=%1" :: "r"(2), "r"(pte)); __asm __volatile("mov psr.l=%0" :: "r" (psr)); ia64_srlz_i(); /* Expose the mapping to userland in ar.k5 */ ia64_set_k5(VM_MAXUSER_ADDRESS); } static u_int freq_ratio(u_long base, u_long ratio) { u_long f; f = (base * (ratio >> 32)) / (ratio & 0xfffffffful); return ((f + 500000) / 1000000); } static void calculate_frequencies(void) { struct ia64_sal_result sal; struct ia64_pal_result pal; register_t ie; ie = intr_disable(); 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); intr_restore(ie); 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)); } cpu_freq = freq_ratio(sal.sal_result[0], pal.pal_result[0]); bus_freq = freq_ratio(sal.sal_result[0], pal.pal_result[1]); itc_freq = freq_ratio(sal.sal_result[0], pal.pal_result[2]); } } struct ia64_init_return ia64_init(void) { struct ia64_init_return ret; struct efi_md *md; pt_entry_t *pbvm_pgtbl_ent, *pbvm_pgtbl_lim; char *p; vm_size_t mdlen; int metadata_missing; /* * 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(); /* Initialize/setup physical memory datastructures */ ia64_physmem_init(); /* * Process the memory map. This gives us the PAL locations, * the I/O port base address, the available memory regions * for initializing the physical memory map. */ for (md = efi_md_first(); md != NULL; md = efi_md_next(md)) { mdlen = md->md_pages * EFI_PAGE_SIZE; switch (md->md_type) { case EFI_MD_TYPE_IOPORT: ia64_port_base = pmap_mapdev_priv(md->md_phys, mdlen, VM_MEMATTR_UNCACHEABLE); break; case EFI_MD_TYPE_PALCODE: ia64_pal_base = md->md_phys; ia64_pal_size = mdlen; /*FALLTHROUGH*/ case EFI_MD_TYPE_BAD: case EFI_MD_TYPE_FIRMWARE: case EFI_MD_TYPE_RECLAIM: case EFI_MD_TYPE_RT_CODE: case EFI_MD_TYPE_RT_DATA: /* Don't use these memory regions. */ ia64_physmem_track(md->md_phys, mdlen); break; case EFI_MD_TYPE_BS_CODE: case EFI_MD_TYPE_BS_DATA: case EFI_MD_TYPE_CODE: case EFI_MD_TYPE_DATA: case EFI_MD_TYPE_FREE: /* These are ok to use. */ ia64_physmem_add(md->md_phys, mdlen); break; } } /* * Remove the PBVM and its page table from phys_avail. The loader * passes the physical address of the page table to us. The virtual * address of the page table is fixed. * Track and the PBVM limit for later use. */ ia64_physmem_delete(bootinfo->bi_pbvm_pgtbl, bootinfo->bi_pbvm_pgtblsz); pbvm_pgtbl_ent = (void *)IA64_PBVM_PGTBL; pbvm_pgtbl_lim = (void *)(IA64_PBVM_PGTBL + bootinfo->bi_pbvm_pgtblsz); while (pbvm_pgtbl_ent < pbvm_pgtbl_lim) { if ((*pbvm_pgtbl_ent & PTE_PRESENT) == 0) break; ia64_physmem_delete(*pbvm_pgtbl_ent & PTE_PPN_MASK, IA64_PBVM_PAGE_SIZE); pbvm_pgtbl_ent++; } /* Finalize physical memory datastructures */ ia64_physmem_fini(); metadata_missing = 0; if (bootinfo->bi_modulep) preload_metadata = (caddr_t)bootinfo->bi_modulep; else metadata_missing = 1; if (envmode == 0 && bootinfo->bi_envp) kern_envp = (caddr_t)bootinfo->bi_envp; else kern_envp = static_env; /* * Look at arguments passed to us and compute boothowto. */ boothowto = bootinfo->bi_boothowto; if (boothowto & RB_VERBOSE) bootverbose = 1; /* * Wire things up so we can call the firmware. */ map_pal_code(); efi_boot_minimal(bootinfo->bi_systab); ia64_xiv_init(); ia64_sal_init(); calculate_frequencies(); set_cputicker(ia64_get_itc, (u_long)itc_freq * 1000000, 0); /* * Setup the PCPU data for the bootstrap processor. It is needed * by printf(). Also, since printf() has critical sections, we * need to initialize at least pc_curthread. */ pcpup = &pcpu0; ia64_set_k4((u_int64_t)pcpup); pcpu_init(pcpup, 0, sizeof(pcpu0)); dpcpu_init(ia64_physmem_alloc(DPCPU_SIZE, PAGE_SIZE), 0); cpu_pcpu_setup(pcpup, ~0U, ia64_get_lid()); PCPU_SET(curthread, &thread0); /* * Initialize the console before we print anything out. */ cninit(); /* OUTPUT NOW ALLOWED */ if (metadata_missing) printf("WARNING: loader(8) metadata is missing!\n"); /* Get FPSWA interface */ fpswa_iface = (bootinfo->bi_fpswa == 0) ? NULL : (struct fpswa_iface *)IA64_PHYS_TO_RR7(bootinfo->bi_fpswa); /* Init basic tunables, including hz */ init_param1(); p = getenv("kernelname"); if (p != NULL) { strlcpy(kernelname, p, sizeof(kernelname)); freeenv(p); } init_param2(physmem); /* * Initialize error message buffer (at end of core). */ msgbufp = ia64_physmem_alloc(msgbufsize, PAGE_SIZE); msgbufinit(msgbufp, msgbufsize); proc_linkup0(&proc0, &thread0); /* * Init mapping for kernel stack for proc 0 */ p = ia64_physmem_alloc(KSTACK_PAGES * PAGE_SIZE, PAGE_SIZE); thread0.td_kstack = (uintptr_t)p; thread0.td_kstack_pages = KSTACK_PAGES; mutex_init(); /* * Initialize the rest of proc 0's PCB. * * Set the kernel sp, reserving space for an (empty) trapframe, * and make proc0's trapframe pointer point to it for sanity. * Initialise proc0's backing store to start after u area. */ cpu_thread_alloc(&thread0); thread0.td_frame->tf_flags = FRAME_SYSCALL; thread0.td_pcb->pcb_special.sp = (u_int64_t)thread0.td_frame - 16; thread0.td_pcb->pcb_special.bspstore = thread0.td_kstack; /* * Initialize the virtual memory system. */ pmap_bootstrap(); #ifdef XTRACE ia64_xtrace_init_bsp(); #endif /* * Initialize debuggers, and break into them if appropriate. */ #ifdef DDB ksym_start = bootinfo->bi_symtab; ksym_end = bootinfo->bi_esymtab; #endif kdb_init(); #ifdef KDB if (boothowto & RB_KDB) kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger\n"); #endif ia64_set_tpr(0); ia64_srlz_d(); ret.bspstore = thread0.td_pcb->pcb_special.bspstore; ret.sp = thread0.td_pcb->pcb_special.sp; return (ret); } uint64_t ia64_get_hcdp(void) { return (bootinfo->bi_hcdp); } void bzero(void *buf, size_t len) { caddr_t p = buf; while (((vm_offset_t) p & (sizeof(u_long) - 1)) && len) { *p++ = 0; len--; } while (len >= sizeof(u_long) * 8) { *(u_long*) p = 0; *((u_long*) p + 1) = 0; *((u_long*) p + 2) = 0; *((u_long*) p + 3) = 0; len -= sizeof(u_long) * 8; *((u_long*) p + 4) = 0; *((u_long*) p + 5) = 0; *((u_long*) p + 6) = 0; *((u_long*) p + 7) = 0; p += sizeof(u_long) * 8; } while (len >= sizeof(u_long)) { *(u_long*) p = 0; len -= sizeof(u_long); p += sizeof(u_long); } while (len) { *p++ = 0; len--; } } u_int ia64_itc_freq(void) { return (itc_freq); } void DELAY(int n) { u_int64_t start, end, now; sched_pin(); start = ia64_get_itc(); end = start + itc_freq * n; /* printf("DELAY from 0x%lx to 0x%lx\n", start, end); */ do { now = ia64_get_itc(); } while (now < end || (now > start && end < start)); sched_unpin(); } /* * Send an interrupt (signal) to a process. */ void sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct proc *p; struct thread *td; struct trapframe *tf; struct sigacts *psp; struct sigframe sf, *sfp; u_int64_t sbs, sp; int oonstack; int sig; u_long code; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); sig = ksi->ksi_signo; code = ksi->ksi_code; psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); tf = td->td_frame; sp = tf->tf_special.sp; oonstack = sigonstack(sp); sbs = 0; /* save user context */ bzero(&sf, sizeof(struct sigframe)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; /* * Allocate and validate space for the signal handler * context. Note that if the stack is in P0 space, the * call to grow() is a nop, and the useracc() check * will fail if the process has not already allocated * the space with a `brk'. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sbs = (u_int64_t)td->td_sigstk.ss_sp; sbs = (sbs + 15) & ~15; sfp = (struct sigframe *)(sbs + td->td_sigstk.ss_size); #if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)sp; sfp = (struct sigframe *)((u_int64_t)(sfp - 1) & ~15); /* Fill in the siginfo structure for POSIX handlers. */ if (SIGISMEMBER(psp->ps_siginfo, sig)) { sf.sf_si = ksi->ksi_info; sf.sf_si.si_signo = sig; /* * XXX this shouldn't be here after code in trap.c * is fixed */ sf.sf_si.si_addr = (void*)tf->tf_special.ifa; code = (u_int64_t)&sfp->sf_si; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); get_mcontext(td, &sf.sf_uc.uc_mcontext, 0); /* Copy the frame out to userland. */ if (copyout(&sf, sfp, sizeof(sf)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); sigexit(td, SIGILL); return; } if ((tf->tf_flags & FRAME_SYSCALL) == 0) { tf->tf_special.psr &= ~IA64_PSR_RI; tf->tf_special.iip = ia64_get_k5() + ((uint64_t)break_sigtramp - (uint64_t)ia64_gateway_page); } else tf->tf_special.iip = ia64_get_k5() + ((uint64_t)epc_sigtramp - (uint64_t)ia64_gateway_page); /* * Setup the trapframe to return to the signal trampoline. We pass * information to the trampoline in the following registers: * * gp new backing store or NULL * r8 signal number * r9 signal code or siginfo pointer * r10 signal handler (function descriptor) */ tf->tf_special.sp = (u_int64_t)sfp - 16; tf->tf_special.gp = sbs; tf->tf_special.bspstore = sf.sf_uc.uc_mcontext.mc_special.bspstore; tf->tf_special.ndirty = 0; tf->tf_special.rnat = sf.sf_uc.uc_mcontext.mc_special.rnat; tf->tf_scratch.gr8 = sig; tf->tf_scratch.gr9 = code; tf->tf_scratch.gr10 = (u_int64_t)catcher; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * state to gain improper privileges. * * MPSAFE */ int sys_sigreturn(struct thread *td, struct sigreturn_args /* { ucontext_t *sigcntxp; } */ *uap) { ucontext_t uc; struct trapframe *tf; struct pcb *pcb; tf = td->td_frame; pcb = td->td_pcb; /* * Fetch the entire context structure at once for speed. * We don't use a normal argument to simplify RSE handling. */ if (copyin(uap->sigcntxp, (caddr_t)&uc, sizeof(uc))) return (EFAULT); set_mcontext(td, &uc.uc_mcontext); #if defined(COMPAT_43) if (sigonstack(tf->tf_special.sp)) td->td_sigstk.ss_flags |= SS_ONSTACK; else td->td_sigstk.ss_flags &= ~SS_ONSTACK; #endif kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); return (EJUSTRETURN); } #ifdef COMPAT_FREEBSD4 int freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) { return sys_sigreturn(td, (struct sigreturn_args *)uap); } #endif /* * 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); } int ia64_flush_dirty(struct thread *td, struct _special *r) { struct iovec iov; struct uio uio; uint64_t bspst, kstk, rnat; int error, locked; if (r->ndirty == 0) return (0); kstk = td->td_kstack + (r->bspstore & 0x1ffUL); if (td == curthread) { __asm __volatile("mov ar.rsc=0;;"); __asm __volatile("mov %0=ar.bspstore" : "=r"(bspst)); /* Make sure we have all the user registers written out. */ if (bspst - kstk < r->ndirty) { __asm __volatile("flushrs;;"); __asm __volatile("mov %0=ar.bspstore" : "=r"(bspst)); } __asm __volatile("mov %0=ar.rnat;;" : "=r"(rnat)); __asm __volatile("mov ar.rsc=3"); error = copyout((void*)kstk, (void*)r->bspstore, r->ndirty); kstk += r->ndirty; r->rnat = (bspst > kstk && (bspst & 0x1ffL) < (kstk & 0x1ffL)) ? *(uint64_t*)(kstk | 0x1f8L) : rnat; } else { locked = PROC_LOCKED(td->td_proc); if (!locked) PHOLD(td->td_proc); iov.iov_base = (void*)(uintptr_t)kstk; iov.iov_len = r->ndirty; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = r->bspstore; uio.uio_resid = r->ndirty; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_WRITE; uio.uio_td = td; error = proc_rwmem(td->td_proc, &uio); /* * XXX proc_rwmem() doesn't currently return ENOSPC, * so I think it can bogusly return 0. Neither do * we allow short writes. */ if (uio.uio_resid != 0 && error == 0) error = ENOSPC; if (!locked) PRELE(td->td_proc); } r->bspstore += r->ndirty; r->ndirty = 0; return (error); } int get_mcontext(struct thread *td, mcontext_t *mc, int flags) { struct trapframe *tf; int error; tf = td->td_frame; bzero(mc, sizeof(*mc)); mc->mc_special = tf->tf_special; error = ia64_flush_dirty(td, &mc->mc_special); if (tf->tf_flags & FRAME_SYSCALL) { mc->mc_flags |= _MC_FLAGS_SYSCALL_CONTEXT; mc->mc_scratch = tf->tf_scratch; if (flags & GET_MC_CLEAR_RET) { mc->mc_scratch.gr8 = 0; mc->mc_scratch.gr9 = 0; mc->mc_scratch.gr10 = 0; mc->mc_scratch.gr11 = 0; } } else { mc->mc_flags |= _MC_FLAGS_ASYNC_CONTEXT; mc->mc_scratch = tf->tf_scratch; mc->mc_scratch_fp = tf->tf_scratch_fp; /* * XXX If the thread never used the high FP registers, we * probably shouldn't waste time saving them. */ ia64_highfp_save(td); mc->mc_flags |= _MC_FLAGS_HIGHFP_VALID; mc->mc_high_fp = td->td_pcb->pcb_high_fp; } save_callee_saved(&mc->mc_preserved); save_callee_saved_fp(&mc->mc_preserved_fp); return (error); } int set_mcontext(struct thread *td, const mcontext_t *mc) { struct _special s; struct trapframe *tf; uint64_t psrmask; tf = td->td_frame; KASSERT((tf->tf_special.ndirty & ~PAGE_MASK) == 0, ("Whoa there! We have more than 8KB of dirty registers!")); s = mc->mc_special; /* * Only copy the user mask and the restart instruction bit from * the new context. */ psrmask = IA64_PSR_BE | IA64_PSR_UP | IA64_PSR_AC | IA64_PSR_MFL | IA64_PSR_MFH | IA64_PSR_RI; s.psr = (tf->tf_special.psr & ~psrmask) | (s.psr & psrmask); /* We don't have any dirty registers of the new context. */ s.ndirty = 0; if (mc->mc_flags & _MC_FLAGS_ASYNC_CONTEXT) { /* * We can get an async context passed to us while we * entered the kernel through a syscall: sigreturn(2) * takes contexts that could previously be the result of * a trap or interrupt. * Hence, we cannot assert that the trapframe is not * a syscall frame, but we can assert that it's at * least an expected syscall. */ if (tf->tf_flags & FRAME_SYSCALL) { KASSERT(tf->tf_scratch.gr15 == SYS_sigreturn, ("foo")); tf->tf_flags &= ~FRAME_SYSCALL; } tf->tf_scratch = mc->mc_scratch; tf->tf_scratch_fp = mc->mc_scratch_fp; if (mc->mc_flags & _MC_FLAGS_HIGHFP_VALID) td->td_pcb->pcb_high_fp = mc->mc_high_fp; } else { KASSERT((tf->tf_flags & FRAME_SYSCALL) != 0, ("foo")); if ((mc->mc_flags & _MC_FLAGS_SYSCALL_CONTEXT) == 0) { s.cfm = tf->tf_special.cfm; s.iip = tf->tf_special.iip; tf->tf_scratch.gr15 = 0; /* Clear syscall nr. */ } else tf->tf_scratch = mc->mc_scratch; } tf->tf_special = s; restore_callee_saved(&mc->mc_preserved); restore_callee_saved_fp(&mc->mc_preserved_fp); return (0); } /* * Clear registers on exec. */ void exec_setregs(struct thread *td, struct image_params *imgp, u_long stack) { struct trapframe *tf; uint64_t *ksttop, *kst; tf = td->td_frame; ksttop = (uint64_t*)(td->td_kstack + tf->tf_special.ndirty + (tf->tf_special.bspstore & 0x1ffUL)); /* * 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, ("Whoa there! We have more than 8KB of dirty registers!")); bzero(&tf->tf_special, sizeof(tf->tf_special)); if ((tf->tf_flags & FRAME_SYSCALL) == 0) { /* break syscalls. */ bzero(&tf->tf_scratch, sizeof(tf->tf_scratch)); bzero(&tf->tf_scratch_fp, 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-- = 0; if (((uintptr_t)kst & 0x1ff) == 0x1f8) *kst-- = 0; *kst-- = imgp->ps_strings; if (((uintptr_t)kst & 0x1ff) == 0x1f8) *kst-- = 0; *kst = stack; 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 and out2 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. */ suword((caddr_t)tf->tf_special.bspstore - 24, stack); suword((caddr_t)tf->tf_special.bspstore - 16, imgp->ps_strings); suword((caddr_t)tf->tf_special.bspstore - 8, 0); } tf->tf_special.iip = imgp->entry_addr; 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; } int ptrace_set_pc(struct thread *td, unsigned long addr) { uint64_t slot; switch (addr & 0xFUL) { case 0: slot = IA64_PSR_RI_0; break; case 1: /* XXX we need to deal with MLX bundles here */ slot = IA64_PSR_RI_1; break; case 2: slot = IA64_PSR_RI_2; break; default: return (EINVAL); } td->td_frame->tf_special.iip = addr & ~0x0FULL; td->td_frame->tf_special.psr = (td->td_frame->tf_special.psr & ~IA64_PSR_RI) | slot; return (0); } int ptrace_single_step(struct thread *td) { struct trapframe *tf; /* * There's no way to set single stepping when we're leaving the * kernel through the EPC syscall path. The way we solve this is * by enabling the lower-privilege trap so that we re-enter the * kernel as soon as the privilege level changes. See trap.c for * how we proceed from there. */ tf = td->td_frame; if (tf->tf_flags & FRAME_SYSCALL) tf->tf_special.psr |= IA64_PSR_LP; else tf->tf_special.psr |= IA64_PSR_SS; return (0); } int ptrace_clear_single_step(struct thread *td) { struct trapframe *tf; /* * Clear any and all status bits we may use to implement single * stepping. */ tf = td->td_frame; tf->tf_special.psr &= ~IA64_PSR_SS; tf->tf_special.psr &= ~IA64_PSR_LP; tf->tf_special.psr &= ~IA64_PSR_TB; return (0); } int fill_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; regs->r_special = tf->tf_special; regs->r_scratch = tf->tf_scratch; save_callee_saved(®s->r_preserved); return (0); } int set_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; int error; tf = td->td_frame; error = ia64_flush_dirty(td, &tf->tf_special); if (!error) { tf->tf_special = regs->r_special; tf->tf_special.bspstore += tf->tf_special.ndirty; tf->tf_special.ndirty = 0; tf->tf_scratch = regs->r_scratch; restore_callee_saved(®s->r_preserved); } return (error); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { return (ENOSYS); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { return (ENOSYS); } int fill_fpregs(struct thread *td, struct fpreg *fpregs) { struct trapframe *frame = td->td_frame; struct pcb *pcb = td->td_pcb; /* Save the high FP registers. */ ia64_highfp_save(td); fpregs->fpr_scratch = frame->tf_scratch_fp; save_callee_saved_fp(&fpregs->fpr_preserved); fpregs->fpr_high = pcb->pcb_high_fp; return (0); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { struct trapframe *frame = td->td_frame; struct pcb *pcb = td->td_pcb; /* Throw away the high FP registers (should be redundant). */ ia64_highfp_drop(td); frame->tf_scratch_fp = fpregs->fpr_scratch; restore_callee_saved_fp(&fpregs->fpr_preserved); pcb->pcb_high_fp = fpregs->fpr_high; return (0); } void ia64_sync_icache(vm_offset_t va, vm_offset_t sz) { vm_offset_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(); } Index: stable/10/sys/ia64/ia64/mem.c =================================================================== --- stable/10/sys/ia64/ia64/mem.c (revision 270295) +++ stable/10/sys/ia64/ia64/mem.c (revision 270296) @@ -1,179 +1,179 @@ /*- * Copyright (c) 1988 University of Utah. * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department, and code derived from software contributed to * Berkeley by William Jolitz. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: Utah $Hdr: mem.c 1.13 89/10/08$ * from: @(#)mem.c 7.2 (Berkeley) 5/9/91 */ #include __FBSDID("$FreeBSD$"); /* * Memory special file */ #include #include +#include #include #include #include #include #include #include -#include #include #include #include #include struct mem_range_softc mem_range_softc; static int mem_phys2virt(vm_offset_t offset, int prot, void **ptr, u_long *limit) { struct efi_md *md; if (prot & ~(VM_PROT_READ | VM_PROT_WRITE)) return (EPERM); md = efi_md_find(offset); if (md == NULL) return (EFAULT); if (md->md_type == EFI_MD_TYPE_BAD) return (EIO); *ptr = (void *)((md->md_attr & EFI_MD_ATTR_WB) ? IA64_PHYS_TO_RR7(offset) : IA64_PHYS_TO_RR6(offset)); *limit = (md->md_pages * EFI_PAGE_SIZE) - (offset - md->md_phys); return (0); } /* ARGSUSED */ int memrw(struct cdev *dev, struct uio *uio, int flags) { struct iovec *iov; off_t ofs; vm_offset_t addr; void *ptr; u_long limit; int count, error, phys, rw; error = 0; rw = (uio->uio_rw == UIO_READ) ? VM_PROT_READ : VM_PROT_WRITE; while (uio->uio_resid > 0 && !error) { iov = uio->uio_iov; if (iov->iov_len == 0) { uio->uio_iov++; uio->uio_iovcnt--; if (uio->uio_iovcnt < 0) panic("memrw"); continue; } ofs = uio->uio_offset; phys = (dev2unit(dev) == CDEV_MINOR_MEM) ? 1 : 0; if (phys == 0 && ofs >= IA64_RR_BASE(6)) { ofs = IA64_RR_MASK(ofs); phys++; } if (phys) { error = mem_phys2virt(ofs, rw, &ptr, &limit); if (error) return (error); count = min(uio->uio_resid, limit); error = uiomove(ptr, count, uio); } else { ptr = (void *)ofs; count = iov->iov_len; /* * Make sure that all of the pages are currently * resident so that we don't create any zero-fill * pages. */ limit = round_page(ofs + count); addr = trunc_page(ofs); if (addr < VM_MAXUSER_ADDRESS) return (EINVAL); for (; addr < limit; addr += PAGE_SIZE) { if (pmap_kextract(addr) == 0) return (EFAULT); } if (!kernacc(ptr, count, rw)) return (EFAULT); error = uiomove(ptr, count, uio); } /* else panic! */ } return (error); } /* * allow user processes to MMAP some memory sections * instead of going through read/write */ int memmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { void *ptr; u_long limit; int error; /* * /dev/mem is the only one that makes sense through this * interface. For /dev/kmem any physaddr we return here * could be transient and hence incorrect or invalid at * a later time. */ if (dev2unit(dev) != CDEV_MINOR_MEM) return (ENXIO); error = mem_phys2virt(offset, prot, &ptr, &limit); if (error) return (error); *paddr = offset; *memattr = ((uintptr_t)ptr >= IA64_RR_BASE(7)) ? VM_MEMATTR_WRITE_BACK : VM_MEMATTR_UNCACHEABLE; return (0); } Index: stable/10/sys/ia64/ia64/nexus.c =================================================================== --- stable/10/sys/ia64/ia64/nexus.c (revision 270295) +++ stable/10/sys/ia64/ia64/nexus.c (revision 270296) @@ -1,530 +1,530 @@ /*- * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * * $FreeBSD$ */ /* * This code implements a `root nexus' for Intel Architecture * machines. The function of the root nexus is to serve as an * attachment point for both processors and buses, and to manage * resources which are common to all of them. In particular, * this code implements the core resource managers for interrupt * requests, DMA requests (which rightfully should be a part of the * ISA code but it's easier to do it here for now), I/O port addresses, * and I/O memory address space. */ #include #include #include #include +#include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include "clock_if.h" static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device"); struct nexus_device { struct resource_list nx_resources; }; #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev)) static struct rman irq_rman, port_rman, mem_rman; static int nexus_probe(device_t); static int nexus_attach(device_t); static int nexus_print_child(device_t, device_t); static device_t nexus_add_child(device_t bus, u_int order, const char *name, int unit); static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int nexus_adjust_resource(device_t, device_t, int, struct resource *, u_long, u_long); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, struct resource *); static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, driver_filter_t filter, void (*)(void *), void *, void **); static int nexus_teardown_intr(device_t, device_t, struct resource *, void *); static struct resource_list *nexus_get_reslist(device_t dev, device_t child); static int nexus_set_resource(device_t, device_t, int, int, u_long, u_long); static int nexus_get_resource(device_t, device_t, int, int, u_long *, u_long *); static void nexus_delete_resource(device_t, device_t, int, int); static int nexus_bind_intr(device_t, device_t, struct resource *, int); static int nexus_config_intr(device_t, int, enum intr_trigger, enum intr_polarity); static int nexus_gettime(device_t, struct timespec *); static int nexus_settime(device_t, struct timespec *); static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_adjust_resource, nexus_adjust_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), DEVMETHOD(bus_get_resource_list, nexus_get_reslist), DEVMETHOD(bus_set_resource, nexus_set_resource), DEVMETHOD(bus_get_resource, nexus_get_resource), DEVMETHOD(bus_delete_resource, nexus_delete_resource), DEVMETHOD(bus_bind_intr, nexus_bind_intr), DEVMETHOD(bus_config_intr, nexus_config_intr), /* Clock interface */ DEVMETHOD(clock_gettime, nexus_gettime), DEVMETHOD(clock_settime, nexus_settime), { 0, 0 } }; static driver_t nexus_driver = { "nexus", nexus_methods, 1, /* no softc */ }; static devclass_t nexus_devclass; DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); static int nexus_probe(device_t dev) { device_quiet(dev); /* suppress attach message for neatness */ irq_rman.rm_type = RMAN_ARRAY; irq_rman.rm_descr = "Interrupt request lines"; irq_rman.rm_start = 0; irq_rman.rm_end = IA64_NXIVS - 1; if (rman_init(&irq_rman) || rman_manage_region(&irq_rman, irq_rman.rm_start, irq_rman.rm_end)) panic("nexus_probe irq_rman"); port_rman.rm_start = 0; port_rman.rm_end = 0xffff; port_rman.rm_type = RMAN_ARRAY; port_rman.rm_descr = "I/O ports"; if (rman_init(&port_rman) || rman_manage_region(&port_rman, 0, 0xffff)) panic("nexus_probe port_rman"); mem_rman.rm_start = 0; mem_rman.rm_end = ~0ul; mem_rman.rm_type = RMAN_ARRAY; mem_rman.rm_descr = "I/O memory addresses"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0)) panic("nexus_probe mem_rman"); return bus_generic_probe(dev); } static int nexus_attach(device_t dev) { if (acpi_identify() == 0) BUS_ADD_CHILD(dev, 10, "acpi", 0); clock_register(dev, 1000); bus_generic_attach(dev); return 0; } static int nexus_print_child(device_t bus, device_t child) { struct nexus_device *ndev = DEVTONX(child); struct resource_list *rl = &ndev->nx_resources; int retval = 0; retval += bus_print_child_header(bus, child); retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); if (device_get_flags(child)) retval += printf(" flags %#x", device_get_flags(child)); retval += printf(" on motherboard\n"); /* XXX "motherboard", ick */ return (retval); } static device_t nexus_add_child(device_t bus, u_int order, const char *name, int unit) { device_t child; struct nexus_device *ndev; ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO); if (!ndev) return(0); resource_list_init(&ndev->nx_resources); child = device_add_child_ordered(bus, order, name, unit); /* should we free this in nexus_child_detached? */ device_set_ivars(child, ndev); return(child); } static struct rman * nexus_rman(int type) { switch (type) { case SYS_RES_IRQ: return (&irq_rman); case SYS_RES_IOPORT: return (&port_rman); case SYS_RES_MEMORY: return (&mem_rman); default: return (NULL); } } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of nexus0. * (Exceptions include npx.) */ static struct resource * nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct nexus_device *ndev = DEVTONX(child); struct resource *rv; struct resource_list_entry *rle; struct rman *rm; int needactivate = flags & RF_ACTIVE; /* * If this is an allocation of the "default" range for a given RID, and * we know what the resources for this device are (ie. they aren't maintained * by a child bus), then work out the start/end values. */ if ((start == 0UL) && (end == ~0UL) && (count == 1)) { if (ndev == NULL) return(NULL); rle = resource_list_find(&ndev->nx_resources, type, *rid); if (rle == NULL) return(NULL); start = rle->start; end = rle->end; count = rle->count; } flags &= ~RF_ACTIVE; rm = nexus_rman(type); if (rm == NULL) return (NULL); rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) return 0; rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return 0; } } return rv; } static int nexus_adjust_resource(device_t bus, device_t child, int type, struct resource *r, u_long start, u_long end) { struct rman *rm; rm = nexus_rman(type); if (rm == NULL) return (ENXIO); if (!rman_is_region_manager(r, rm)) return (EINVAL); return (rman_adjust_resource(r, start, end)); } static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { vm_paddr_t paddr; void *vaddr; paddr = rman_get_start(r); switch (type) { case SYS_RES_IOPORT: rman_set_bustag(r, IA64_BUS_SPACE_IO); rman_set_bushandle(r, paddr); break; case SYS_RES_MEMORY: vaddr = pmap_mapdev(paddr, rman_get_size(r)); rman_set_bustag(r, IA64_BUS_SPACE_MEM); rman_set_bushandle(r, (bus_space_handle_t) vaddr); rman_set_virtual(r, vaddr); break; } return (rman_activate_resource(r)); } static int nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } static int nexus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (rman_get_flags(r) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, r); if (error) return error; } return (rman_release_resource(r)); } /* * Currently this uses the really grody interface from kern/kern_intr.c * (which really doesn't belong in kern/anything.c). Eventually, all of * the code in kern_intr.c and machdep_intr.c should get moved here, since * this is going to be the official interface. */ static int nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, driver_filter_t filter, void (*ihand)(void *), void *arg, void **cookiep) { driver_t *driver; int error; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; if ((rman_get_flags(irq) & RF_SHAREABLE) == 0) flags |= INTR_EXCL; driver = device_get_driver(child); /* * We depend here on rman_activate_resource() being idempotent. */ error = rman_activate_resource(irq); if (error) return (error); error = ia64_setup_intr(device_get_nameunit(child), rman_get_start(irq), filter, ihand, arg, flags, cookiep); return (error); } static int nexus_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { return (ia64_teardown_intr(cookie)); } static struct resource_list * nexus_get_reslist(device_t dev, device_t child) { struct nexus_device *ndev = DEVTONX(child); return (&ndev->nx_resources); } static int nexus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct nexus_device *ndev = DEVTONX(child); struct resource_list *rl = &ndev->nx_resources; if (type == SYS_RES_IOPORT && start > (0x10000 - count)) { /* * Work around a firmware bug in the HP rx2660, where in ACPI * an I/O port is really a memory mapped I/O address. The bug * is in the GAS that describes the address and in particular * the SpaceId field. The field should not say the address is * an I/O port when it is in fact an I/O memory address. */ if (bootverbose) printf("%s: invalid port range (%#lx-%#lx); " "assuming I/O memory range.\n", __func__, start, start + count - 1); type = SYS_RES_MEMORY; } /* XXX this should return a success/failure indicator */ resource_list_add(rl, type, rid, start, start + count - 1, count); return(0); } static int nexus_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct nexus_device *ndev = DEVTONX(child); struct resource_list *rl = &ndev->nx_resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); device_printf(child, "type %d rid %d startp %p countp %p - got %p\n", type, rid, startp, countp, rle); if (!rle) return(ENOENT); if (startp) *startp = rle->start; if (countp) *countp = rle->count; return(0); } static void nexus_delete_resource(device_t dev, device_t child, int type, int rid) { struct nexus_device *ndev = DEVTONX(child); struct resource_list *rl = &ndev->nx_resources; resource_list_delete(rl, type, rid); } static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { return (sapic_config_intr(irq, trig, pol)); } static int nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu) { struct pcpu *pc; pc = cpuid_to_pcpu[cpu]; if (pc == NULL) return (EINVAL); return (sapic_bind_intr(rman_get_start(irq), pc)); } static int nexus_gettime(device_t dev, struct timespec *ts) { struct clocktime ct; struct efi_tm tm; efi_get_time(&tm); /* * This code was written in 2005, so logically EFI cannot return * a year smaller than that. Assume the EFI clock is out of whack * in that case and reset the EFI clock. */ if (tm.tm_year < 2005) return (EINVAL); ct.nsec = tm.tm_nsec; ct.sec = tm.tm_sec; ct.min = tm.tm_min; ct.hour = tm.tm_hour; ct.day = tm.tm_mday; ct.mon = tm.tm_mon; ct.year = tm.tm_year; ct.dow = -1; return (clock_ct_to_ts(&ct, ts)); } static int nexus_settime(device_t dev, struct timespec *ts) { struct clocktime ct; struct efi_tm tm; efi_get_time(&tm); clock_ts_to_ct(ts, &ct); tm.tm_nsec = ts->tv_nsec; tm.tm_sec = ct.sec; tm.tm_min = ct.min; tm.tm_hour = ct.hour; tm.tm_year = ct.year; tm.tm_mon = ct.mon; tm.tm_mday = ct.day; return (efi_set_time(&tm)); } Index: stable/10/sys/ia64/ia64/pmap.c =================================================================== --- stable/10/sys/ia64/ia64/pmap.c (revision 270295) +++ stable/10/sys/ia64/ia64/pmap.c (revision 270296) @@ -1,2892 +1,2892 @@ /*- * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 John S. Dyson * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * Copyright (c) 1998,2000 Doug Rabson * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department and William Jolitz of UUNET Technologies 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91 * from: i386 Id: pmap.c,v 1.193 1998/04/19 15:22:48 bde Exp * with some ideas from NetBSD's alpha pmap */ #include __FBSDID("$FreeBSD$"); #include "opt_pmap.h" #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include /* * Manages physical address maps. * * Since the information managed by this module is * also stored by the logical address mapping module, * this module may throw away valid virtual-to-physical * mappings at almost any time. However, invalidations * of virtual-to-physical mappings must be done as * requested. * * In order to cope with hardware architectures which * make virtual-to-physical map invalidates expensive, * this module may delay invalidate or reduced protection * operations until such time as they are actually * necessary. This module is given full information as * to which processors are currently using which maps, * and to when physical maps must be made correct. */ /* * Following the Linux model, region IDs are allocated in groups of * eight so that a single region ID can be used for as many RRs as we * want by encoding the RR number into the low bits of the ID. * * We reserve region ID 0 for the kernel and allocate the remaining * IDs for user pmaps. * * Region 0-3: User virtually mapped * Region 4: PBVM and special mappings * Region 5: Kernel virtual memory * Region 6: Direct-mapped uncacheable * Region 7: Direct-mapped cacheable */ /* XXX move to a header. */ extern uint64_t ia64_gateway_page[]; #if !defined(DIAGNOSTIC) #define PMAP_INLINE __inline #else #define PMAP_INLINE #endif #ifdef PV_STATS #define PV_STAT(x) do { x ; } while (0) #else #define PV_STAT(x) do { } while (0) #endif #define pmap_accessed(lpte) ((lpte)->pte & PTE_ACCESSED) #define pmap_dirty(lpte) ((lpte)->pte & PTE_DIRTY) #define pmap_exec(lpte) ((lpte)->pte & PTE_AR_RX) #define pmap_managed(lpte) ((lpte)->pte & PTE_MANAGED) #define pmap_ppn(lpte) ((lpte)->pte & PTE_PPN_MASK) #define pmap_present(lpte) ((lpte)->pte & PTE_PRESENT) #define pmap_prot(lpte) (((lpte)->pte & PTE_PROT_MASK) >> 56) #define pmap_wired(lpte) ((lpte)->pte & PTE_WIRED) #define pmap_clear_accessed(lpte) (lpte)->pte &= ~PTE_ACCESSED #define pmap_clear_dirty(lpte) (lpte)->pte &= ~PTE_DIRTY #define pmap_clear_present(lpte) (lpte)->pte &= ~PTE_PRESENT #define pmap_clear_wired(lpte) (lpte)->pte &= ~PTE_WIRED #define pmap_set_wired(lpte) (lpte)->pte |= PTE_WIRED /* * Individual PV entries are stored in per-pmap chunks. This saves * space by eliminating the need to record the pmap within every PV * entry. */ #if PAGE_SIZE == 8192 #define _NPCM 6 #define _NPCPV 337 #define _NPCS 2 #elif PAGE_SIZE == 16384 #define _NPCM 11 #define _NPCPV 677 #define _NPCS 1 #endif struct pv_chunk { pmap_t pc_pmap; TAILQ_ENTRY(pv_chunk) pc_list; u_long pc_map[_NPCM]; /* bitmap; 1 = free */ TAILQ_ENTRY(pv_chunk) pc_lru; u_long pc_spare[_NPCS]; struct pv_entry pc_pventry[_NPCPV]; }; /* * The VHPT bucket head structure. */ struct ia64_bucket { uint64_t chain; struct mtx mutex; u_int length; }; /* * Statically allocated kernel pmap */ struct pmap kernel_pmap_store; vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss) */ vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */ /* * Kernel virtual memory management. */ static int nkpt; extern struct ia64_lpte ***ia64_kptdir; #define KPTE_DIR0_INDEX(va) \ (((va) >> (3*PAGE_SHIFT-8)) & ((1<<(PAGE_SHIFT-3))-1)) #define KPTE_DIR1_INDEX(va) \ (((va) >> (2*PAGE_SHIFT-5)) & ((1<<(PAGE_SHIFT-3))-1)) #define KPTE_PTE_INDEX(va) \ (((va) >> PAGE_SHIFT) & ((1<<(PAGE_SHIFT-5))-1)) #define NKPTEPG (PAGE_SIZE / sizeof(struct ia64_lpte)) vm_offset_t kernel_vm_end; /* Defaults for ptc.e. */ static uint64_t pmap_ptc_e_base = 0; static uint32_t pmap_ptc_e_count1 = 1; static uint32_t pmap_ptc_e_count2 = 1; static uint32_t pmap_ptc_e_stride1 = 0; static uint32_t pmap_ptc_e_stride2 = 0; struct mtx pmap_ptc_mutex; /* * Data for the RID allocator */ static int pmap_ridcount; static int pmap_rididx; static int pmap_ridmapsz; static int pmap_ridmax; static uint64_t *pmap_ridmap; struct mtx pmap_ridmutex; static struct rwlock_padalign pvh_global_lock; /* * Data for the pv entry allocation mechanism */ static TAILQ_HEAD(pch, pv_chunk) pv_chunks = TAILQ_HEAD_INITIALIZER(pv_chunks); static int pv_entry_count; /* * Data for allocating PTEs for user processes. */ static uma_zone_t ptezone; /* * Virtual Hash Page Table (VHPT) data. */ /* SYSCTL_DECL(_machdep); */ static SYSCTL_NODE(_machdep, OID_AUTO, vhpt, CTLFLAG_RD, 0, ""); struct ia64_bucket *pmap_vhpt_bucket; int pmap_vhpt_nbuckets; SYSCTL_INT(_machdep_vhpt, OID_AUTO, nbuckets, CTLFLAG_RD, &pmap_vhpt_nbuckets, 0, ""); int pmap_vhpt_log2size = 0; TUNABLE_INT("machdep.vhpt.log2size", &pmap_vhpt_log2size); SYSCTL_INT(_machdep_vhpt, OID_AUTO, log2size, CTLFLAG_RD, &pmap_vhpt_log2size, 0, ""); static int pmap_vhpt_inserts; SYSCTL_INT(_machdep_vhpt, OID_AUTO, inserts, CTLFLAG_RD, &pmap_vhpt_inserts, 0, ""); static int pmap_vhpt_population(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_machdep_vhpt, OID_AUTO, population, CTLTYPE_INT | CTLFLAG_RD, NULL, 0, pmap_vhpt_population, "I", ""); static struct ia64_lpte *pmap_find_vhpt(vm_offset_t va); static void free_pv_chunk(struct pv_chunk *pc); static void free_pv_entry(pmap_t pmap, pv_entry_t pv); static pv_entry_t get_pv_entry(pmap_t pmap, boolean_t try); static vm_page_t pmap_pv_reclaim(pmap_t locked_pmap); static void pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot); static void pmap_free_pte(struct ia64_lpte *pte, vm_offset_t va); static int pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte, vm_offset_t va, pv_entry_t pv, int freepte); static int pmap_remove_vhpt(vm_offset_t va); static boolean_t pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va, vm_page_t m); static void pmap_initialize_vhpt(vm_offset_t vhpt) { struct ia64_lpte *pte; u_int i; pte = (struct ia64_lpte *)vhpt; for (i = 0; i < pmap_vhpt_nbuckets; i++) { pte[i].pte = 0; pte[i].itir = 0; pte[i].tag = 1UL << 63; /* Invalid tag */ pte[i].chain = (uintptr_t)(pmap_vhpt_bucket + i); } } #ifdef SMP vm_offset_t pmap_alloc_vhpt(void) { vm_offset_t vhpt; vm_page_t m; vm_size_t size; size = 1UL << pmap_vhpt_log2size; m = vm_page_alloc_contig(NULL, 0, VM_ALLOC_SYSTEM | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED, atop(size), 0UL, ~0UL, size, 0UL, VM_MEMATTR_DEFAULT); if (m != NULL) { vhpt = IA64_PHYS_TO_RR7(VM_PAGE_TO_PHYS(m)); pmap_initialize_vhpt(vhpt); return (vhpt); } return (0); } #endif /* * Bootstrap the system enough to run with virtual memory. */ void pmap_bootstrap() { struct ia64_pal_result res; vm_offset_t base; size_t size; int i, ridbits; /* * Query the PAL Code to find the loop parameters for the * ptc.e instruction. */ res = ia64_call_pal_static(PAL_PTCE_INFO, 0, 0, 0); if (res.pal_status != 0) panic("Can't configure ptc.e parameters"); pmap_ptc_e_base = res.pal_result[0]; pmap_ptc_e_count1 = res.pal_result[1] >> 32; pmap_ptc_e_count2 = res.pal_result[1]; pmap_ptc_e_stride1 = res.pal_result[2] >> 32; pmap_ptc_e_stride2 = res.pal_result[2]; if (bootverbose) printf("ptc.e base=0x%lx, count1=%u, count2=%u, " "stride1=0x%x, stride2=0x%x\n", pmap_ptc_e_base, pmap_ptc_e_count1, pmap_ptc_e_count2, pmap_ptc_e_stride1, pmap_ptc_e_stride2); mtx_init(&pmap_ptc_mutex, "PTC.G mutex", NULL, MTX_SPIN); /* * Setup RIDs. RIDs 0..7 are reserved for the kernel. * * We currently need at least 19 bits in the RID because PID_MAX * can only be encoded in 17 bits and we need RIDs for 4 regions * per process. With PID_MAX equalling 99999 this means that we * need to be able to encode 399996 (=4*PID_MAX). * The Itanium processor only has 18 bits and the architected * minimum is exactly that. So, we cannot use a PID based scheme * in those cases. Enter pmap_ridmap... * We should avoid the map when running on a processor that has * implemented enough bits. This means that we should pass the * process/thread ID to pmap. This we currently don't do, so we * use the map anyway. However, we don't want to allocate a map * that is large enough to cover the range dictated by the number * of bits in the RID, because that may result in a RID map of * 2MB in size for a 24-bit RID. A 64KB map is enough. * The bottomline: we create a 32KB map when the processor only * implements 18 bits (or when we can't figure it out). Otherwise * we create a 64KB map. */ res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0); if (res.pal_status != 0) { if (bootverbose) printf("Can't read VM Summary - assuming 18 Region ID bits\n"); ridbits = 18; /* guaranteed minimum */ } else { ridbits = (res.pal_result[1] >> 8) & 0xff; if (bootverbose) printf("Processor supports %d Region ID bits\n", ridbits); } if (ridbits > 19) ridbits = 19; pmap_ridmax = (1 << ridbits); pmap_ridmapsz = pmap_ridmax / 64; pmap_ridmap = ia64_physmem_alloc(pmap_ridmax / 8, PAGE_SIZE); pmap_ridmap[0] |= 0xff; pmap_rididx = 0; pmap_ridcount = 8; mtx_init(&pmap_ridmutex, "RID allocator lock", NULL, MTX_DEF); /* * Allocate some memory for initial kernel 'page tables'. */ ia64_kptdir = ia64_physmem_alloc(PAGE_SIZE, PAGE_SIZE); nkpt = 0; kernel_vm_end = VM_INIT_KERNEL_ADDRESS; /* * Determine a valid (mappable) VHPT size. */ TUNABLE_INT_FETCH("machdep.vhpt.log2size", &pmap_vhpt_log2size); if (pmap_vhpt_log2size == 0) pmap_vhpt_log2size = 20; else if (pmap_vhpt_log2size < 16) pmap_vhpt_log2size = 16; else if (pmap_vhpt_log2size > 28) pmap_vhpt_log2size = 28; if (pmap_vhpt_log2size & 1) pmap_vhpt_log2size--; size = 1UL << pmap_vhpt_log2size; base = (uintptr_t)ia64_physmem_alloc(size, size); if (base == 0) panic("Unable to allocate VHPT"); PCPU_SET(md.vhpt, base); if (bootverbose) printf("VHPT: address=%#lx, size=%#lx\n", base, size); pmap_vhpt_nbuckets = size / sizeof(struct ia64_lpte); pmap_vhpt_bucket = ia64_physmem_alloc(pmap_vhpt_nbuckets * sizeof(struct ia64_bucket), PAGE_SIZE); for (i = 0; i < pmap_vhpt_nbuckets; i++) { /* Stolen memory is zeroed. */ mtx_init(&pmap_vhpt_bucket[i].mutex, "VHPT bucket lock", NULL, MTX_NOWITNESS | MTX_SPIN); } pmap_initialize_vhpt(base); map_vhpt(base); ia64_set_pta(base + (1 << 8) + (pmap_vhpt_log2size << 2) + 1); ia64_srlz_i(); virtual_avail = VM_INIT_KERNEL_ADDRESS; virtual_end = VM_MAX_KERNEL_ADDRESS; /* * Initialize the kernel pmap (which is statically allocated). */ PMAP_LOCK_INIT(kernel_pmap); for (i = 0; i < IA64_VM_MINKERN_REGION; i++) kernel_pmap->pm_rid[i] = 0; TAILQ_INIT(&kernel_pmap->pm_pvchunk); PCPU_SET(md.current_pmap, kernel_pmap); /* * Initialize the global pv list lock. */ rw_init(&pvh_global_lock, "pmap pv global"); /* Region 5 is mapped via the VHPT. */ ia64_set_rr(IA64_RR_BASE(5), (5 << 8) | (PAGE_SHIFT << 2) | 1); /* * Clear out any random TLB entries left over from booting. */ pmap_invalidate_all(); map_gateway_page(); } static int pmap_vhpt_population(SYSCTL_HANDLER_ARGS) { int count, error, i; count = 0; for (i = 0; i < pmap_vhpt_nbuckets; i++) count += pmap_vhpt_bucket[i].length; error = SYSCTL_OUT(req, &count, sizeof(count)); return (error); } vm_offset_t pmap_page_to_va(vm_page_t m) { vm_paddr_t pa; vm_offset_t va; pa = VM_PAGE_TO_PHYS(m); va = (m->md.memattr == VM_MEMATTR_UNCACHEABLE) ? IA64_PHYS_TO_RR6(pa) : IA64_PHYS_TO_RR7(pa); return (va); } /* * Initialize a vm_page's machine-dependent fields. */ void pmap_page_init(vm_page_t m) { CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); TAILQ_INIT(&m->md.pv_list); m->md.memattr = VM_MEMATTR_DEFAULT; } /* * Initialize the pmap module. * Called by vm_init, to initialize any structures that the pmap * system needs to map virtual memory. */ void pmap_init(void) { CTR1(KTR_PMAP, "%s()", __func__); ptezone = uma_zcreate("PT ENTRY", sizeof (struct ia64_lpte), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM|UMA_ZONE_NOFREE); } /*************************************************** * Manipulate TLBs for a pmap ***************************************************/ static void pmap_invalidate_page(vm_offset_t va) { struct ia64_lpte *pte; struct pcpu *pc; uint64_t tag; u_int vhpt_ofs; critical_enter(); vhpt_ofs = ia64_thash(va) - PCPU_GET(md.vhpt); tag = ia64_ttag(va); STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { pte = (struct ia64_lpte *)(pc->pc_md.vhpt + vhpt_ofs); atomic_cmpset_64(&pte->tag, tag, 1UL << 63); } mtx_lock_spin(&pmap_ptc_mutex); ia64_ptc_ga(va, PAGE_SHIFT << 2); ia64_mf(); ia64_srlz_i(); mtx_unlock_spin(&pmap_ptc_mutex); ia64_invala(); critical_exit(); } void pmap_invalidate_all(void) { uint64_t addr; int i, j; addr = pmap_ptc_e_base; for (i = 0; i < pmap_ptc_e_count1; i++) { for (j = 0; j < pmap_ptc_e_count2; j++) { ia64_ptc_e(addr); addr += pmap_ptc_e_stride2; } addr += pmap_ptc_e_stride1; } ia64_srlz_i(); } static uint32_t pmap_allocate_rid(void) { uint64_t bit, bits; int rid; mtx_lock(&pmap_ridmutex); if (pmap_ridcount == pmap_ridmax) panic("pmap_allocate_rid: All Region IDs used"); /* Find an index with a free bit. */ while ((bits = pmap_ridmap[pmap_rididx]) == ~0UL) { pmap_rididx++; if (pmap_rididx == pmap_ridmapsz) pmap_rididx = 0; } rid = pmap_rididx * 64; /* Find a free bit. */ bit = 1UL; while (bits & bit) { rid++; bit <<= 1; } pmap_ridmap[pmap_rididx] |= bit; pmap_ridcount++; mtx_unlock(&pmap_ridmutex); return rid; } static void pmap_free_rid(uint32_t rid) { uint64_t bit; int idx; idx = rid / 64; bit = ~(1UL << (rid & 63)); mtx_lock(&pmap_ridmutex); pmap_ridmap[idx] &= bit; pmap_ridcount--; mtx_unlock(&pmap_ridmutex); } /*************************************************** * Page table page management routines..... ***************************************************/ static void pmap_pinit_common(pmap_t pmap) { int i; for (i = 0; i < IA64_VM_MINKERN_REGION; i++) pmap->pm_rid[i] = pmap_allocate_rid(); TAILQ_INIT(&pmap->pm_pvchunk); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); } void pmap_pinit0(pmap_t pmap) { CTR2(KTR_PMAP, "%s(pm=%p)", __func__, pmap); PMAP_LOCK_INIT(pmap); pmap_pinit_common(pmap); } /* * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ int pmap_pinit(pmap_t pmap) { CTR2(KTR_PMAP, "%s(pm=%p)", __func__, pmap); pmap_pinit_common(pmap); return (1); } /*************************************************** * Pmap allocation/deallocation routines. ***************************************************/ /* * Release any resources held by the given physical map. * Called when a pmap initialized by pmap_pinit is being released. * Should only be called if the map contains no valid mappings. */ void pmap_release(pmap_t pmap) { int i; CTR2(KTR_PMAP, "%s(pm=%p)", __func__, pmap); for (i = 0; i < IA64_VM_MINKERN_REGION; i++) if (pmap->pm_rid[i]) pmap_free_rid(pmap->pm_rid[i]); } /* * grow the number of kernel page table entries, if needed */ void pmap_growkernel(vm_offset_t addr) { struct ia64_lpte **dir1; struct ia64_lpte *leaf; vm_page_t nkpg; CTR2(KTR_PMAP, "%s(va=%#lx)", __func__, addr); while (kernel_vm_end <= addr) { if (nkpt == PAGE_SIZE/8 + PAGE_SIZE*PAGE_SIZE/64) panic("%s: out of kernel address space", __func__); dir1 = ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)]; if (dir1 == NULL) { nkpg = vm_page_alloc(NULL, nkpt++, VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED); if (!nkpg) panic("%s: cannot add dir. page", __func__); dir1 = (struct ia64_lpte **)pmap_page_to_va(nkpg); bzero(dir1, PAGE_SIZE); ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)] = dir1; } nkpg = vm_page_alloc(NULL, nkpt++, VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED); if (!nkpg) panic("%s: cannot add PTE page", __func__); leaf = (struct ia64_lpte *)pmap_page_to_va(nkpg); bzero(leaf, PAGE_SIZE); dir1[KPTE_DIR1_INDEX(kernel_vm_end)] = leaf; kernel_vm_end += PAGE_SIZE * NKPTEPG; } } /*************************************************** * page management routines. ***************************************************/ CTASSERT(sizeof(struct pv_chunk) == PAGE_SIZE); static __inline struct pv_chunk * pv_to_chunk(pv_entry_t pv) { return ((struct pv_chunk *)((uintptr_t)pv & ~(uintptr_t)PAGE_MASK)); } #define PV_PMAP(pv) (pv_to_chunk(pv)->pc_pmap) #define PC_FREE_FULL 0xfffffffffffffffful #define PC_FREE_PARTIAL \ ((1UL << (_NPCPV - sizeof(u_long) * 8 * (_NPCM - 1))) - 1) #if PAGE_SIZE == 8192 static const u_long pc_freemask[_NPCM] = { PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_PARTIAL }; #elif PAGE_SIZE == 16384 static const u_long pc_freemask[_NPCM] = { PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL, PC_FREE_PARTIAL }; #endif static SYSCTL_NODE(_vm, OID_AUTO, pmap, CTLFLAG_RD, 0, "VM/pmap parameters"); SYSCTL_INT(_vm_pmap, OID_AUTO, pv_entry_count, CTLFLAG_RD, &pv_entry_count, 0, "Current number of pv entries"); #ifdef PV_STATS static int pc_chunk_count, pc_chunk_allocs, pc_chunk_frees, pc_chunk_tryfail; SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_count, CTLFLAG_RD, &pc_chunk_count, 0, "Current number of pv entry chunks"); SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_allocs, CTLFLAG_RD, &pc_chunk_allocs, 0, "Current number of pv entry chunks allocated"); SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_frees, CTLFLAG_RD, &pc_chunk_frees, 0, "Current number of pv entry chunks frees"); SYSCTL_INT(_vm_pmap, OID_AUTO, pc_chunk_tryfail, CTLFLAG_RD, &pc_chunk_tryfail, 0, "Number of times tried to get a chunk page but failed."); static long pv_entry_frees, pv_entry_allocs; static int pv_entry_spare; SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_frees, CTLFLAG_RD, &pv_entry_frees, 0, "Current number of pv entry frees"); SYSCTL_LONG(_vm_pmap, OID_AUTO, pv_entry_allocs, CTLFLAG_RD, &pv_entry_allocs, 0, "Current number of pv entry allocs"); SYSCTL_INT(_vm_pmap, OID_AUTO, pv_entry_spare, CTLFLAG_RD, &pv_entry_spare, 0, "Current number of spare pv entries"); #endif /* * We are in a serious low memory condition. Resort to * drastic measures to free some pages so we can allocate * another pv entry chunk. */ static vm_page_t pmap_pv_reclaim(pmap_t locked_pmap) { struct pch newtail; struct pv_chunk *pc; struct ia64_lpte *pte; pmap_t pmap; pv_entry_t pv; vm_offset_t va; vm_page_t m, m_pc; u_long inuse; int bit, field, freed, idx; PMAP_LOCK_ASSERT(locked_pmap, MA_OWNED); pmap = NULL; m_pc = NULL; TAILQ_INIT(&newtail); while ((pc = TAILQ_FIRST(&pv_chunks)) != NULL) { TAILQ_REMOVE(&pv_chunks, pc, pc_lru); if (pmap != pc->pc_pmap) { if (pmap != NULL) { if (pmap != locked_pmap) { pmap_switch(locked_pmap); PMAP_UNLOCK(pmap); } } pmap = pc->pc_pmap; /* Avoid deadlock and lock recursion. */ if (pmap > locked_pmap) PMAP_LOCK(pmap); else if (pmap != locked_pmap && !PMAP_TRYLOCK(pmap)) { pmap = NULL; TAILQ_INSERT_TAIL(&newtail, pc, pc_lru); continue; } pmap_switch(pmap); } /* * Destroy every non-wired, 8 KB page mapping in the chunk. */ freed = 0; for (field = 0; field < _NPCM; field++) { for (inuse = ~pc->pc_map[field] & pc_freemask[field]; inuse != 0; inuse &= ~(1UL << bit)) { bit = ffsl(inuse) - 1; idx = field * sizeof(inuse) * NBBY + bit; pv = &pc->pc_pventry[idx]; va = pv->pv_va; pte = pmap_find_vhpt(va); KASSERT(pte != NULL, ("pte")); if (pmap_wired(pte)) continue; pmap_remove_vhpt(va); pmap_invalidate_page(va); m = PHYS_TO_VM_PAGE(pmap_ppn(pte)); if (pmap_accessed(pte)) vm_page_aflag_set(m, PGA_REFERENCED); if (pmap_dirty(pte)) vm_page_dirty(m); pmap_free_pte(pte, va); TAILQ_REMOVE(&m->md.pv_list, pv, pv_list); if (TAILQ_EMPTY(&m->md.pv_list)) vm_page_aflag_clear(m, PGA_WRITEABLE); pc->pc_map[field] |= 1UL << bit; freed++; } } if (freed == 0) { TAILQ_INSERT_TAIL(&newtail, pc, pc_lru); continue; } /* Every freed mapping is for a 8 KB page. */ pmap->pm_stats.resident_count -= freed; PV_STAT(pv_entry_frees += freed); PV_STAT(pv_entry_spare += freed); pv_entry_count -= freed; TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); for (field = 0; field < _NPCM; field++) if (pc->pc_map[field] != pc_freemask[field]) { TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list); TAILQ_INSERT_TAIL(&newtail, pc, pc_lru); /* * One freed pv entry in locked_pmap is * sufficient. */ if (pmap == locked_pmap) goto out; break; } if (field == _NPCM) { PV_STAT(pv_entry_spare -= _NPCPV); PV_STAT(pc_chunk_count--); PV_STAT(pc_chunk_frees++); /* Entire chunk is free; return it. */ m_pc = PHYS_TO_VM_PAGE(IA64_RR_MASK((vm_offset_t)pc)); break; } } out: TAILQ_CONCAT(&pv_chunks, &newtail, pc_lru); if (pmap != NULL) { if (pmap != locked_pmap) { pmap_switch(locked_pmap); PMAP_UNLOCK(pmap); } } return (m_pc); } /* * free the pv_entry back to the free list */ static void free_pv_entry(pmap_t pmap, pv_entry_t pv) { struct pv_chunk *pc; int bit, field, idx; rw_assert(&pvh_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); PV_STAT(pv_entry_frees++); PV_STAT(pv_entry_spare++); pv_entry_count--; pc = pv_to_chunk(pv); idx = pv - &pc->pc_pventry[0]; field = idx / (sizeof(u_long) * NBBY); bit = idx % (sizeof(u_long) * NBBY); pc->pc_map[field] |= 1ul << bit; for (idx = 0; idx < _NPCM; idx++) if (pc->pc_map[idx] != pc_freemask[idx]) { /* * 98% of the time, pc is already at the head of the * list. If it isn't already, move it to the head. */ if (__predict_false(TAILQ_FIRST(&pmap->pm_pvchunk) != pc)) { TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list); } return; } TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); free_pv_chunk(pc); } static void free_pv_chunk(struct pv_chunk *pc) { vm_page_t m; TAILQ_REMOVE(&pv_chunks, pc, pc_lru); PV_STAT(pv_entry_spare -= _NPCPV); PV_STAT(pc_chunk_count--); PV_STAT(pc_chunk_frees++); /* entire chunk is free, return it */ m = PHYS_TO_VM_PAGE(IA64_RR_MASK((vm_offset_t)pc)); vm_page_unwire(m, 0); vm_page_free(m); } /* * get a new pv_entry, allocating a block from the system * when needed. */ static pv_entry_t get_pv_entry(pmap_t pmap, boolean_t try) { struct pv_chunk *pc; pv_entry_t pv; vm_page_t m; int bit, field, idx; rw_assert(&pvh_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); PV_STAT(pv_entry_allocs++); pv_entry_count++; retry: pc = TAILQ_FIRST(&pmap->pm_pvchunk); if (pc != NULL) { for (field = 0; field < _NPCM; field++) { if (pc->pc_map[field]) { bit = ffsl(pc->pc_map[field]) - 1; break; } } if (field < _NPCM) { idx = field * sizeof(pc->pc_map[field]) * NBBY + bit; pv = &pc->pc_pventry[idx]; pc->pc_map[field] &= ~(1ul << bit); /* If this was the last item, move it to tail */ for (field = 0; field < _NPCM; field++) if (pc->pc_map[field] != 0) { PV_STAT(pv_entry_spare--); return (pv); /* not full, return */ } TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); TAILQ_INSERT_TAIL(&pmap->pm_pvchunk, pc, pc_list); PV_STAT(pv_entry_spare--); return (pv); } } /* No free items, allocate another chunk */ m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED); if (m == NULL) { if (try) { pv_entry_count--; PV_STAT(pc_chunk_tryfail++); return (NULL); } m = pmap_pv_reclaim(pmap); if (m == NULL) goto retry; } PV_STAT(pc_chunk_count++); PV_STAT(pc_chunk_allocs++); pc = (struct pv_chunk *)IA64_PHYS_TO_RR7(VM_PAGE_TO_PHYS(m)); pc->pc_pmap = pmap; pc->pc_map[0] = pc_freemask[0] & ~1ul; /* preallocated bit 0 */ for (field = 1; field < _NPCM; field++) pc->pc_map[field] = pc_freemask[field]; TAILQ_INSERT_TAIL(&pv_chunks, pc, pc_lru); pv = &pc->pc_pventry[0]; TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list); PV_STAT(pv_entry_spare += _NPCPV - 1); return (pv); } /* * Conditionally create a pv entry. */ static boolean_t pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va, vm_page_t m) { pv_entry_t pv; PMAP_LOCK_ASSERT(pmap, MA_OWNED); rw_assert(&pvh_global_lock, RA_WLOCKED); if ((pv = get_pv_entry(pmap, TRUE)) != NULL) { pv->pv_va = va; TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list); return (TRUE); } else return (FALSE); } /* * Add an ia64_lpte to the VHPT. */ static void pmap_enter_vhpt(struct ia64_lpte *pte, vm_offset_t va) { struct ia64_bucket *bckt; struct ia64_lpte *vhpte; uint64_t pte_pa; /* Can fault, so get it out of the way. */ pte_pa = ia64_tpa((vm_offset_t)pte); vhpte = (struct ia64_lpte *)ia64_thash(va); bckt = (struct ia64_bucket *)vhpte->chain; mtx_lock_spin(&bckt->mutex); pte->chain = bckt->chain; ia64_mf(); bckt->chain = pte_pa; pmap_vhpt_inserts++; bckt->length++; mtx_unlock_spin(&bckt->mutex); } /* * Remove the ia64_lpte matching va from the VHPT. Return zero if it * worked or an appropriate error code otherwise. */ static int pmap_remove_vhpt(vm_offset_t va) { struct ia64_bucket *bckt; struct ia64_lpte *pte; struct ia64_lpte *lpte; struct ia64_lpte *vhpte; uint64_t chain, tag; tag = ia64_ttag(va); vhpte = (struct ia64_lpte *)ia64_thash(va); bckt = (struct ia64_bucket *)vhpte->chain; lpte = NULL; mtx_lock_spin(&bckt->mutex); chain = bckt->chain; pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain); while (chain != 0 && pte->tag != tag) { lpte = pte; chain = pte->chain; pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain); } if (chain == 0) { mtx_unlock_spin(&bckt->mutex); return (ENOENT); } /* Snip this pv_entry out of the collision chain. */ if (lpte == NULL) bckt->chain = pte->chain; else lpte->chain = pte->chain; ia64_mf(); bckt->length--; mtx_unlock_spin(&bckt->mutex); return (0); } /* * Find the ia64_lpte for the given va, if any. */ static struct ia64_lpte * pmap_find_vhpt(vm_offset_t va) { struct ia64_bucket *bckt; struct ia64_lpte *pte; uint64_t chain, tag; tag = ia64_ttag(va); pte = (struct ia64_lpte *)ia64_thash(va); bckt = (struct ia64_bucket *)pte->chain; mtx_lock_spin(&bckt->mutex); chain = bckt->chain; pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain); while (chain != 0 && pte->tag != tag) { chain = pte->chain; pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain); } mtx_unlock_spin(&bckt->mutex); return ((chain != 0) ? pte : NULL); } /* * Remove an entry from the list of managed mappings. */ static int pmap_remove_entry(pmap_t pmap, vm_page_t m, vm_offset_t va, pv_entry_t pv) { rw_assert(&pvh_global_lock, RA_WLOCKED); if (!pv) { TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { if (pmap == PV_PMAP(pv) && va == pv->pv_va) break; } } if (pv) { TAILQ_REMOVE(&m->md.pv_list, pv, pv_list); if (TAILQ_FIRST(&m->md.pv_list) == NULL) vm_page_aflag_clear(m, PGA_WRITEABLE); free_pv_entry(pmap, pv); return 0; } else { return ENOENT; } } /* * Create a pv entry for page at pa for * (pmap, va). */ static void pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t m) { pv_entry_t pv; rw_assert(&pvh_global_lock, RA_WLOCKED); pv = get_pv_entry(pmap, FALSE); pv->pv_va = va; TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list); } /* * Routine: pmap_extract * Function: * Extract the physical page address associated * with the given map/virtual_address pair. */ vm_paddr_t pmap_extract(pmap_t pmap, vm_offset_t va) { struct ia64_lpte *pte; pmap_t oldpmap; vm_paddr_t pa; CTR3(KTR_PMAP, "%s(pm=%p, va=%#lx)", __func__, pmap, va); pa = 0; PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(va); if (pte != NULL && pmap_present(pte)) pa = pmap_ppn(pte); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); return (pa); } /* * Routine: pmap_extract_and_hold * Function: * Atomically extract and hold the physical page * with the given pmap and virtual address pair * if that mapping permits the given protection. */ vm_page_t pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot) { struct ia64_lpte *pte; pmap_t oldpmap; vm_page_t m; vm_paddr_t pa; CTR4(KTR_PMAP, "%s(pm=%p, va=%#lx, prot=%#x)", __func__, pmap, va, prot); pa = 0; m = NULL; PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); retry: pte = pmap_find_vhpt(va); if (pte != NULL && pmap_present(pte) && (pmap_prot(pte) & prot) == prot) { m = PHYS_TO_VM_PAGE(pmap_ppn(pte)); if (vm_page_pa_tryrelock(pmap, pmap_ppn(pte), &pa)) goto retry; vm_page_hold(m); } PA_UNLOCK_COND(pa); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); return (m); } /*************************************************** * Low level mapping routines..... ***************************************************/ /* * Find the kernel lpte for mapping the given virtual address, which * must be in the part of region 5 which we can cover with our kernel * 'page tables'. */ static struct ia64_lpte * pmap_find_kpte(vm_offset_t va) { struct ia64_lpte **dir1; struct ia64_lpte *leaf; KASSERT((va >> 61) == 5, ("kernel mapping 0x%lx not in region 5", va)); KASSERT(va < kernel_vm_end, ("kernel mapping 0x%lx out of range", va)); dir1 = ia64_kptdir[KPTE_DIR0_INDEX(va)]; leaf = dir1[KPTE_DIR1_INDEX(va)]; return (&leaf[KPTE_PTE_INDEX(va)]); } /* * Find a pte suitable for mapping a user-space address. If one exists * in the VHPT, that one will be returned, otherwise a new pte is * allocated. */ static struct ia64_lpte * pmap_find_pte(vm_offset_t va) { struct ia64_lpte *pte; if (va >= VM_MAXUSER_ADDRESS) return pmap_find_kpte(va); pte = pmap_find_vhpt(va); if (pte == NULL) { pte = uma_zalloc(ptezone, M_NOWAIT | M_ZERO); pte->tag = 1UL << 63; } return (pte); } /* * Free a pte which is now unused. This simply returns it to the zone * allocator if it is a user mapping. For kernel mappings, clear the * valid bit to make it clear that the mapping is not currently used. */ static void pmap_free_pte(struct ia64_lpte *pte, vm_offset_t va) { if (va < VM_MAXUSER_ADDRESS) uma_zfree(ptezone, pte); else pmap_clear_present(pte); } static PMAP_INLINE void pmap_pte_prot(pmap_t pm, struct ia64_lpte *pte, vm_prot_t prot) { static long prot2ar[4] = { PTE_AR_R, /* VM_PROT_NONE */ PTE_AR_RW, /* VM_PROT_WRITE */ PTE_AR_RX|PTE_ED, /* VM_PROT_EXECUTE */ PTE_AR_RWX|PTE_ED /* VM_PROT_WRITE|VM_PROT_EXECUTE */ }; pte->pte &= ~(PTE_PROT_MASK | PTE_PL_MASK | PTE_AR_MASK | PTE_ED); pte->pte |= (uint64_t)(prot & VM_PROT_ALL) << 56; pte->pte |= (prot == VM_PROT_NONE || pm == kernel_pmap) ? PTE_PL_KERN : PTE_PL_USER; pte->pte |= prot2ar[(prot & VM_PROT_ALL) >> 1]; } static PMAP_INLINE void pmap_pte_attr(struct ia64_lpte *pte, vm_memattr_t ma) { pte->pte &= ~PTE_MA_MASK; pte->pte |= (ma & PTE_MA_MASK); } /* * Set a pte to contain a valid mapping and enter it in the VHPT. If * the pte was orginally valid, then its assumed to already be in the * VHPT. * This functions does not set the protection bits. It's expected * that those have been set correctly prior to calling this function. */ static void pmap_set_pte(struct ia64_lpte *pte, vm_offset_t va, vm_offset_t pa, boolean_t wired, boolean_t managed) { pte->pte &= PTE_PROT_MASK | PTE_MA_MASK | PTE_PL_MASK | PTE_AR_MASK | PTE_ED; pte->pte |= PTE_PRESENT; pte->pte |= (managed) ? PTE_MANAGED : (PTE_DIRTY | PTE_ACCESSED); pte->pte |= (wired) ? PTE_WIRED : 0; pte->pte |= pa & PTE_PPN_MASK; pte->itir = PAGE_SHIFT << 2; ia64_mf(); pte->tag = ia64_ttag(va); } /* * Remove the (possibly managed) mapping represented by pte from the * given pmap. */ static int pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte, vm_offset_t va, pv_entry_t pv, int freepte) { int error; vm_page_t m; /* * First remove from the VHPT. */ error = pmap_remove_vhpt(va); KASSERT(error == 0, ("%s: pmap_remove_vhpt returned %d", __func__, error)); pmap_invalidate_page(va); if (pmap_wired(pte)) pmap->pm_stats.wired_count -= 1; pmap->pm_stats.resident_count -= 1; if (pmap_managed(pte)) { m = PHYS_TO_VM_PAGE(pmap_ppn(pte)); if (pmap_dirty(pte)) vm_page_dirty(m); if (pmap_accessed(pte)) vm_page_aflag_set(m, PGA_REFERENCED); error = pmap_remove_entry(pmap, m, va, pv); } if (freepte) pmap_free_pte(pte, va); return (error); } /* * Extract the physical page address associated with a kernel * virtual address. */ vm_paddr_t pmap_kextract(vm_offset_t va) { struct ia64_lpte *pte; uint64_t *pbvm_pgtbl; vm_paddr_t pa; u_int idx; CTR2(KTR_PMAP, "%s(va=%#lx)", __func__, va); KASSERT(va >= VM_MAXUSER_ADDRESS, ("Must be kernel VA")); /* Regions 6 and 7 are direct mapped. */ if (va >= IA64_RR_BASE(6)) { pa = IA64_RR_MASK(va); goto out; } /* Region 5 is our KVA. Bail out if the VA is beyond our limits. */ if (va >= kernel_vm_end) goto err_out; if (va >= VM_INIT_KERNEL_ADDRESS) { pte = pmap_find_kpte(va); pa = pmap_present(pte) ? pmap_ppn(pte) | (va & PAGE_MASK) : 0; goto out; } /* The PBVM page table. */ if (va >= IA64_PBVM_PGTBL + bootinfo->bi_pbvm_pgtblsz) goto err_out; if (va >= IA64_PBVM_PGTBL) { pa = (va - IA64_PBVM_PGTBL) + bootinfo->bi_pbvm_pgtbl; goto out; } /* The PBVM itself. */ if (va >= IA64_PBVM_BASE) { pbvm_pgtbl = (void *)IA64_PBVM_PGTBL; idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT; if (idx >= (bootinfo->bi_pbvm_pgtblsz >> 3)) goto err_out; if ((pbvm_pgtbl[idx] & PTE_PRESENT) == 0) goto err_out; pa = (pbvm_pgtbl[idx] & PTE_PPN_MASK) + (va & IA64_PBVM_PAGE_MASK); goto out; } err_out: printf("XXX: %s: va=%#lx is invalid\n", __func__, va); pa = 0; /* FALLTHROUGH */ out: return (pa); } /* * Add a list of wired pages to the kva this routine is only used for * temporary kernel mappings that do not need to have page modification * or references recorded. Note that old mappings are simply written * over. The page is effectively wired, but it's customary to not have * the PTE reflect that, nor update statistics. */ void pmap_qenter(vm_offset_t va, vm_page_t *m, int count) { struct ia64_lpte *pte; int i; CTR4(KTR_PMAP, "%s(va=%#lx, m_p=%p, cnt=%d)", __func__, va, m, count); for (i = 0; i < count; i++) { pte = pmap_find_kpte(va); if (pmap_present(pte)) pmap_invalidate_page(va); else pmap_enter_vhpt(pte, va); pmap_pte_prot(kernel_pmap, pte, VM_PROT_ALL); pmap_pte_attr(pte, m[i]->md.memattr); pmap_set_pte(pte, va, VM_PAGE_TO_PHYS(m[i]), FALSE, FALSE); va += PAGE_SIZE; } } /* * this routine jerks page mappings from the * kernel -- it is meant only for temporary mappings. */ void pmap_qremove(vm_offset_t va, int count) { struct ia64_lpte *pte; int i; CTR3(KTR_PMAP, "%s(va=%#lx, cnt=%d)", __func__, va, count); for (i = 0; i < count; i++) { pte = pmap_find_kpte(va); if (pmap_present(pte)) { pmap_remove_vhpt(va); pmap_invalidate_page(va); pmap_clear_present(pte); } va += PAGE_SIZE; } } /* * Add a wired page to the kva. As for pmap_qenter(), it's customary * to not have the PTE reflect that, nor update statistics. */ void pmap_kenter(vm_offset_t va, vm_paddr_t pa) { struct ia64_lpte *pte; CTR3(KTR_PMAP, "%s(va=%#lx, pa=%#lx)", __func__, va, pa); pte = pmap_find_kpte(va); if (pmap_present(pte)) pmap_invalidate_page(va); else pmap_enter_vhpt(pte, va); pmap_pte_prot(kernel_pmap, pte, VM_PROT_ALL); pmap_pte_attr(pte, VM_MEMATTR_DEFAULT); pmap_set_pte(pte, va, pa, FALSE, FALSE); } /* * Remove a page from the kva */ void pmap_kremove(vm_offset_t va) { struct ia64_lpte *pte; CTR2(KTR_PMAP, "%s(va=%#lx)", __func__, va); pte = pmap_find_kpte(va); if (pmap_present(pte)) { pmap_remove_vhpt(va); pmap_invalidate_page(va); pmap_clear_present(pte); } } /* * Used to map a range of physical addresses into kernel * virtual address space. * * The value passed in '*virt' is a suggested virtual address for * the mapping. Architectures which can support a direct-mapped * physical to virtual region can return the appropriate address * within that region, leaving '*virt' unchanged. Other * architectures should map the pages starting at '*virt' and * update '*virt' with the first usable address after the mapped * region. */ vm_offset_t pmap_map(vm_offset_t *virt, vm_offset_t start, vm_offset_t end, int prot) { CTR5(KTR_PMAP, "%s(va_p=%p, sva=%#lx, eva=%#lx, prot=%#x)", __func__, virt, start, end, prot); return IA64_PHYS_TO_RR7(start); } /* * Remove the given range of addresses from the specified map. * * It is assumed that the start and end are properly * rounded to the page size. * * Sparsely used ranges are inefficiently removed. The VHPT is * probed for every page within the range. XXX */ void pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { pmap_t oldpmap; vm_offset_t va; struct ia64_lpte *pte; CTR4(KTR_PMAP, "%s(pm=%p, sva=%#lx, eva=%#lx)", __func__, pmap, sva, eva); /* * Perform an unsynchronized read. This is, however, safe. */ if (pmap->pm_stats.resident_count == 0) return; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); for (va = sva; va < eva; va += PAGE_SIZE) { pte = pmap_find_vhpt(va); if (pte != NULL) pmap_remove_pte(pmap, pte, va, 0, 1); } rw_wunlock(&pvh_global_lock); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } /* * Routine: pmap_remove_all * Function: * Removes this physical page from * all physical maps in which it resides. * Reflects back modify bits to the pager. * * Notes: * Original versions of this routine were very * inefficient because they iteratively called * pmap_remove (slow...) */ void pmap_remove_all(vm_page_t m) { pmap_t oldpmap; pv_entry_t pv; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_all: page %p is not managed", m)); rw_wlock(&pvh_global_lock); while ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) { struct ia64_lpte *pte; pmap_t pmap = PV_PMAP(pv); vm_offset_t va = pv->pv_va; PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(va); KASSERT(pte != NULL, ("pte")); if (pmap_ppn(pte) != VM_PAGE_TO_PHYS(m)) panic("pmap_remove_all: pv_table for %lx is inconsistent", VM_PAGE_TO_PHYS(m)); pmap_remove_pte(pmap, pte, va, pv, 1); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); } /* * Set the physical protection on the * specified range of this map as requested. */ void pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { pmap_t oldpmap; struct ia64_lpte *pte; CTR5(KTR_PMAP, "%s(pm=%p, sva=%#lx, eva=%#lx, prot=%#x)", __func__, pmap, sva, eva, prot); if ((prot & VM_PROT_READ) == VM_PROT_NONE) { pmap_remove(pmap, sva, eva); return; } if ((prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) == (VM_PROT_WRITE|VM_PROT_EXECUTE)) return; if ((sva & PAGE_MASK) || (eva & PAGE_MASK)) panic("pmap_protect: unaligned addresses"); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); for ( ; sva < eva; sva += PAGE_SIZE) { /* If page is invalid, skip this page */ pte = pmap_find_vhpt(sva); if (pte == NULL) continue; /* If there's no change, skip it too */ if (pmap_prot(pte) == prot) continue; if ((prot & VM_PROT_WRITE) == 0 && pmap_managed(pte) && pmap_dirty(pte)) { vm_paddr_t pa = pmap_ppn(pte); vm_page_t m = PHYS_TO_VM_PAGE(pa); vm_page_dirty(m); pmap_clear_dirty(pte); } if (prot & VM_PROT_EXECUTE) ia64_sync_icache(sva, PAGE_SIZE); pmap_pte_prot(pmap, pte, prot); pmap_invalidate_page(sva); } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } /* * Insert the given physical page (p) at * the specified virtual address (v) in the * target physical map with the protection requested. * * If specified, the page will be wired down, meaning * that the related pte can not be reclaimed. * * NB: This is the only routine which MAY NOT lazy-evaluate * or lose information. That is, this routine must actually * insert this page into the given map NOW. */ void pmap_enter(pmap_t pmap, vm_offset_t va, vm_prot_t access, vm_page_t m, vm_prot_t prot, boolean_t wired) { pmap_t oldpmap; vm_offset_t pa; vm_offset_t opa; struct ia64_lpte origpte; struct ia64_lpte *pte; boolean_t icache_inval, managed; CTR6(KTR_PMAP, "pmap_enter(pm=%p, va=%#lx, acc=%#x, m=%p, prot=%#x, " "wired=%u)", pmap, va, access, m, prot, wired); rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); va &= ~PAGE_MASK; KASSERT(va <= VM_MAX_KERNEL_ADDRESS, ("pmap_enter: toobig")); KASSERT((m->oflags & VPO_UNMANAGED) != 0 || vm_page_xbusied(m), ("pmap_enter: page %p is not busy", m)); /* * Find (or create) a pte for the given mapping. */ while ((pte = pmap_find_pte(va)) == NULL) { pmap_switch(oldpmap); PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); VM_WAIT; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); } origpte = *pte; if (!pmap_present(pte)) { opa = ~0UL; pmap_enter_vhpt(pte, va); } else opa = pmap_ppn(pte); managed = FALSE; pa = VM_PAGE_TO_PHYS(m); icache_inval = (prot & VM_PROT_EXECUTE) ? TRUE : FALSE; /* * Mapping has not changed, must be protection or wiring change. */ if (opa == pa) { /* * Wiring change, just update stats. We don't worry about * wiring PT pages as they remain resident as long as there * are valid mappings in them. Hence, if a user page is wired, * the PT page will be also. */ if (wired && !pmap_wired(&origpte)) pmap->pm_stats.wired_count++; else if (!wired && pmap_wired(&origpte)) pmap->pm_stats.wired_count--; managed = (pmap_managed(&origpte)) ? TRUE : FALSE; /* * We might be turning off write access to the page, * so we go ahead and sense modify status. Otherwise, * we can avoid I-cache invalidation if the page * already allowed execution. */ if (managed && pmap_dirty(&origpte)) vm_page_dirty(m); else if (pmap_exec(&origpte)) icache_inval = FALSE; pmap_invalidate_page(va); goto validate; } /* * Mapping has changed, invalidate old range and fall * through to handle validating new mapping. */ if (opa != ~0UL) { pmap_remove_pte(pmap, pte, va, 0, 0); pmap_enter_vhpt(pte, va); } /* * Enter on the PV list if part of our managed memory. */ if ((m->oflags & VPO_UNMANAGED) == 0) { KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva, ("pmap_enter: managed mapping within the clean submap")); pmap_insert_entry(pmap, va, m); managed = TRUE; } /* * Increment counters */ pmap->pm_stats.resident_count++; if (wired) pmap->pm_stats.wired_count++; validate: /* * Now validate mapping with desired protection/wiring. This * adds the pte to the VHPT if necessary. */ pmap_pte_prot(pmap, pte, prot); pmap_pte_attr(pte, m->md.memattr); pmap_set_pte(pte, va, pa, wired, managed); /* Invalidate the I-cache when needed. */ if (icache_inval) ia64_sync_icache(va, PAGE_SIZE); if ((prot & VM_PROT_WRITE) != 0 && managed) vm_page_aflag_set(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { pmap_t oldpmap; vm_page_t m; vm_pindex_t diff, psize; CTR6(KTR_PMAP, "%s(pm=%p, sva=%#lx, eva=%#lx, m=%p, prot=%#x)", __func__, pmap, start, end, m_start, prot); VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); m = m_start; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { pmap_enter_quick_locked(pmap, start + ptoa(diff), m, prot); m = TAILQ_NEXT(m, listq); } rw_wunlock(&pvh_global_lock); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } /* * this code makes some *MAJOR* assumptions: * 1. Current pmap & pmap exists. * 2. Not wired. * 3. Read access. * 4. No page table pages. * but is *MUCH* faster than pmap_enter... */ void pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) { pmap_t oldpmap; CTR5(KTR_PMAP, "%s(pm=%p, va=%#lx, m=%p, prot=%#x)", __func__, pmap, va, m, prot); rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pmap_enter_quick_locked(pmap, va, m, prot); rw_wunlock(&pvh_global_lock); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } static void pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) { struct ia64_lpte *pte; boolean_t managed; KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva || (m->oflags & VPO_UNMANAGED) != 0, ("pmap_enter_quick_locked: managed mapping within the clean submap")); rw_assert(&pvh_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); if ((pte = pmap_find_pte(va)) == NULL) return; if (!pmap_present(pte)) { /* Enter on the PV list if the page is managed. */ if ((m->oflags & VPO_UNMANAGED) == 0) { if (!pmap_try_insert_pv_entry(pmap, va, m)) { pmap_free_pte(pte, va); return; } managed = TRUE; } else managed = FALSE; /* Increment counters. */ pmap->pm_stats.resident_count++; /* Initialise with R/O protection and enter into VHPT. */ pmap_enter_vhpt(pte, va); pmap_pte_prot(pmap, pte, prot & (VM_PROT_READ | VM_PROT_EXECUTE)); pmap_pte_attr(pte, m->md.memattr); pmap_set_pte(pte, va, VM_PAGE_TO_PHYS(m), FALSE, managed); if (prot & VM_PROT_EXECUTE) ia64_sync_icache(va, PAGE_SIZE); } } /* * pmap_object_init_pt preloads the ptes for a given object * into the specified pmap. This eliminates the blast of soft * faults on process startup and immediately after an mmap. */ void pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size) { CTR6(KTR_PMAP, "%s(pm=%p, va=%#lx, obj=%p, idx=%lu, sz=%#lx)", __func__, pmap, addr, object, pindex, size); VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(object->type == OBJT_DEVICE || object->type == OBJT_SG, ("pmap_object_init_pt: non-device object")); } /* * Routine: pmap_change_wiring * Function: Change the wiring attribute for a map/virtual-address * pair. * In/out conditions: * The mapping must already exist in the pmap. */ void pmap_change_wiring(pmap_t pmap, vm_offset_t va, boolean_t wired) { pmap_t oldpmap; struct ia64_lpte *pte; CTR4(KTR_PMAP, "%s(pm=%p, va=%#lx, wired=%u)", __func__, pmap, va, wired); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(va); KASSERT(pte != NULL, ("pte")); if (wired && !pmap_wired(pte)) { pmap->pm_stats.wired_count++; pmap_set_wired(pte); } else if (!wired && pmap_wired(pte)) { pmap->pm_stats.wired_count--; pmap_clear_wired(pte); } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } /* * Copy the range specified by src_addr/len * from the source map to the range dst_addr/len * in the destination map. * * This routine is only advisory and need not do anything. */ void pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_va, vm_size_t len, vm_offset_t src_va) { CTR6(KTR_PMAP, "%s(dpm=%p, spm=%p, dva=%#lx, sz=%#lx, sva=%#lx)", __func__, dst_pmap, src_pmap, dst_va, len, src_va); } /* * pmap_zero_page zeros the specified hardware page by * mapping it into virtual memory and using bzero to clear * its contents. */ void pmap_zero_page(vm_page_t m) { void *p; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); p = (void *)pmap_page_to_va(m); bzero(p, PAGE_SIZE); } /* * pmap_zero_page_area zeros the specified hardware page by * mapping it into virtual memory and using bzero to clear * its contents. * * off and size must reside within a single page. */ void pmap_zero_page_area(vm_page_t m, int off, int size) { char *p; CTR4(KTR_PMAP, "%s(m=%p, ofs=%d, len=%d)", __func__, m, off, size); p = (void *)pmap_page_to_va(m); bzero(p + off, size); } /* * pmap_zero_page_idle zeros the specified hardware page by * mapping it into virtual memory and using bzero to clear * its contents. This is for the vm_idlezero process. */ void pmap_zero_page_idle(vm_page_t m) { void *p; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); p = (void *)pmap_page_to_va(m); bzero(p, PAGE_SIZE); } /* * pmap_copy_page copies the specified (machine independent) * page by mapping the page into virtual memory and using * bcopy to copy the page, one machine dependent page at a * time. */ void pmap_copy_page(vm_page_t msrc, vm_page_t mdst) { void *dst, *src; CTR3(KTR_PMAP, "%s(sm=%p, dm=%p)", __func__, msrc, mdst); src = (void *)pmap_page_to_va(msrc); dst = (void *)pmap_page_to_va(mdst); bcopy(src, dst, PAGE_SIZE); } void pmap_copy_pages(vm_page_t ma[], vm_offset_t a_offset, vm_page_t mb[], vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; CTR6(KTR_PMAP, "%s(m0=%p, va0=%#lx, m1=%p, va1=%#lx, sz=%#x)", __func__, ma, a_offset, mb, b_offset, xfersize); while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); a_cp = (char *)pmap_page_to_va(ma[a_offset >> PAGE_SHIFT]) + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); b_cp = (char *)pmap_page_to_va(mb[b_offset >> PAGE_SHIFT]) + b_pg_offset; bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t pmap_page_exists_quick(pmap_t pmap, vm_page_t m) { pv_entry_t pv; int loops = 0; boolean_t rv; CTR3(KTR_PMAP, "%s(pm=%p, m=%p)", __func__, pmap, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_page_exists_quick: page %p is not managed", m)); rv = FALSE; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { if (PV_PMAP(pv) == pmap) { rv = TRUE; break; } loops++; if (loops >= 16) break; } rw_wunlock(&pvh_global_lock); return (rv); } /* * pmap_page_wired_mappings: * * Return the number of managed mappings to the given physical page * that are wired. */ int pmap_page_wired_mappings(vm_page_t m) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; int count; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); count = 0; if ((m->oflags & VPO_UNMANAGED) != 0) return (count); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); KASSERT(pte != NULL, ("pte")); if (pmap_wired(pte)) count++; pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } rw_wunlock(&pvh_global_lock); return (count); } /* * Remove all pages from specified address space * this aids process exit speeds. Also, this code * is special cased for current process only, but * can have the more generic (and slightly slower) * mode enabled. This is much faster than pmap_remove * in the case of running down an entire address space. */ void pmap_remove_pages(pmap_t pmap) { struct pv_chunk *pc, *npc; struct ia64_lpte *pte; pmap_t oldpmap; pv_entry_t pv; vm_offset_t va; vm_page_t m; u_long inuse, bitmask; int allfree, bit, field, idx; CTR2(KTR_PMAP, "%s(pm=%p)", __func__, pmap); rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); TAILQ_FOREACH_SAFE(pc, &pmap->pm_pvchunk, pc_list, npc) { allfree = 1; for (field = 0; field < _NPCM; field++) { inuse = ~pc->pc_map[field] & pc_freemask[field]; while (inuse != 0) { bit = ffsl(inuse) - 1; bitmask = 1UL << bit; idx = field * sizeof(inuse) * NBBY + bit; pv = &pc->pc_pventry[idx]; inuse &= ~bitmask; va = pv->pv_va; pte = pmap_find_vhpt(va); KASSERT(pte != NULL, ("pte")); if (pmap_wired(pte)) { allfree = 0; continue; } pmap_remove_vhpt(va); pmap_invalidate_page(va); m = PHYS_TO_VM_PAGE(pmap_ppn(pte)); if (pmap_dirty(pte)) vm_page_dirty(m); pmap_free_pte(pte, va); /* Mark free */ PV_STAT(pv_entry_frees++); PV_STAT(pv_entry_spare++); pv_entry_count--; pc->pc_map[field] |= bitmask; pmap->pm_stats.resident_count--; TAILQ_REMOVE(&m->md.pv_list, pv, pv_list); if (TAILQ_EMPTY(&m->md.pv_list)) vm_page_aflag_clear(m, PGA_WRITEABLE); } } if (allfree) { TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list); free_pv_chunk(pc); } } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); } /* * pmap_ts_referenced: * * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * XXX: The exact number of bits to check and clear is a matter that * should be tested and standardized at some point in the future for * optimal aging of shared pages. */ int pmap_ts_referenced(vm_page_t m) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; int count = 0; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_ts_referenced: page %p is not managed", m)); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); KASSERT(pte != NULL, ("pte")); if (pmap_accessed(pte)) { count++; pmap_clear_accessed(pte); pmap_invalidate_page(pv->pv_va); } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } rw_wunlock(&pvh_global_lock); return (count); } /* * pmap_is_modified: * * Return whether or not the specified physical page was modified * in any physical maps. */ boolean_t pmap_is_modified(vm_page_t m) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; boolean_t rv; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_is_modified: page %p is not managed", m)); rv = FALSE; /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can be dirty. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); pmap_switch(oldpmap); KASSERT(pte != NULL, ("pte")); rv = pmap_dirty(pte) ? TRUE : FALSE; PMAP_UNLOCK(pmap); if (rv) break; } rw_wunlock(&pvh_global_lock); return (rv); } /* * pmap_is_prefaultable: * * Return whether or not the specified virtual address is elgible * for prefault. */ boolean_t pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr) { struct ia64_lpte *pte; CTR3(KTR_PMAP, "%s(pm=%p, va=%#lx)", __func__, pmap, addr); pte = pmap_find_vhpt(addr); if (pte != NULL && pmap_present(pte)) return (FALSE); return (TRUE); } /* * pmap_is_referenced: * * Return whether or not the specified physical page was referenced * in any physical maps. */ boolean_t pmap_is_referenced(vm_page_t m) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; boolean_t rv; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_is_referenced: page %p is not managed", m)); rv = FALSE; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); pmap_switch(oldpmap); KASSERT(pte != NULL, ("pte")); rv = pmap_accessed(pte) ? TRUE : FALSE; PMAP_UNLOCK(pmap); if (rv) break; } rw_wunlock(&pvh_global_lock); return (rv); } /* * Apply the given advice to the specified range of addresses within the * given pmap. Depending on the advice, clear the referenced and/or * modified flags in each mapping and set the mapped page's dirty field. */ void pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) { struct ia64_lpte *pte; pmap_t oldpmap; vm_page_t m; CTR5(KTR_PMAP, "%s(pm=%p, sva=%#lx, eva=%#lx, adv=%d)", __func__, pmap, sva, eva, advice); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); for (; sva < eva; sva += PAGE_SIZE) { /* If page is invalid, skip this page. */ pte = pmap_find_vhpt(sva); if (pte == NULL) continue; /* If it isn't managed, skip it too. */ if (!pmap_managed(pte)) continue; /* Clear its modified and referenced bits. */ if (pmap_dirty(pte)) { if (advice == MADV_DONTNEED) { /* * Future calls to pmap_is_modified() can be * avoided by making the page dirty now. */ m = PHYS_TO_VM_PAGE(pmap_ppn(pte)); vm_page_dirty(m); } pmap_clear_dirty(pte); } else if (!pmap_accessed(pte)) continue; pmap_clear_accessed(pte); pmap_invalidate_page(sva); } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } /* * Clear the modify bits on the specified physical page. */ void pmap_clear_modify(vm_page_t m) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can be modified. * If the object containing the page is locked and the page is not * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); KASSERT(pte != NULL, ("pte")); if (pmap_dirty(pte)) { pmap_clear_dirty(pte); pmap_invalidate_page(pv->pv_va); } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } rw_wunlock(&pvh_global_lock); } /* * Clear the write and modified bits in each of the given page's mappings. */ void pmap_remove_write(vm_page_t m) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; vm_prot_t prot; CTR2(KTR_PMAP, "%s(m=%p)", __func__, m); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); KASSERT(pte != NULL, ("pte")); prot = pmap_prot(pte); if ((prot & VM_PROT_WRITE) != 0) { if (pmap_dirty(pte)) { vm_page_dirty(m); pmap_clear_dirty(pte); } prot &= ~VM_PROT_WRITE; pmap_pte_prot(pmap, pte, prot); pmap_pte_attr(pte, m->md.memattr); pmap_invalidate_page(pv->pv_va); } pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); } vm_offset_t pmap_mapdev_priv(vm_paddr_t pa, vm_size_t sz, vm_memattr_t attr) { static vm_offset_t last_va = 0; static vm_paddr_t last_pa = ~0UL; static vm_size_t last_sz = 0; struct efi_md *md; if (pa == last_pa && sz == last_sz) return (last_va); md = efi_md_find(pa); if (md == NULL) { printf("%s: [%#lx..%#lx] not covered by memory descriptor\n", __func__, pa, pa + sz - 1); return (IA64_PHYS_TO_RR6(pa)); } if (md->md_type == EFI_MD_TYPE_FREE) { printf("%s: [%#lx..%#lx] is in DRAM\n", __func__, pa, pa + sz - 1); return (0); } last_va = (md->md_attr & EFI_MD_ATTR_WB) ? IA64_PHYS_TO_RR7(pa) : IA64_PHYS_TO_RR6(pa); last_pa = pa; last_sz = sz; return (last_va); } /* * Map a set of physical memory pages into the kernel virtual * address space. Return a pointer to where it is mapped. This * routine is intended to be used for mapping device memory, * NOT real memory. */ void * pmap_mapdev_attr(vm_paddr_t pa, vm_size_t sz, vm_memattr_t attr) { vm_offset_t va; CTR4(KTR_PMAP, "%s(pa=%#lx, sz=%#lx, attr=%#x)", __func__, pa, sz, attr); va = pmap_mapdev_priv(pa, sz, attr); return ((void *)(uintptr_t)va); } /* * 'Unmap' a range mapped by pmap_mapdev_attr(). */ void pmap_unmapdev(vm_offset_t va, vm_size_t size) { CTR3(KTR_PMAP, "%s(va=%#lx, sz=%#lx)", __func__, va, size); } /* * Sets the memory attribute for the specified page. */ static void pmap_page_set_memattr_1(void *arg) { struct ia64_pal_result res; register_t is; uintptr_t pp = (uintptr_t)arg; is = intr_disable(); res = ia64_call_pal_static(pp, 0, 0, 0); intr_restore(is); } void pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma) { struct ia64_lpte *pte; pmap_t oldpmap, pmap; pv_entry_t pv; void *va; CTR3(KTR_PMAP, "%s(m=%p, attr=%#x)", __func__, m, ma); rw_wlock(&pvh_global_lock); m->md.memattr = ma; TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(pv->pv_va); KASSERT(pte != NULL, ("pte")); pmap_pte_attr(pte, ma); pmap_invalidate_page(pv->pv_va); pmap_switch(oldpmap); PMAP_UNLOCK(pmap); } rw_wunlock(&pvh_global_lock); if (ma == VM_MEMATTR_UNCACHEABLE) { #ifdef SMP smp_rendezvous(NULL, pmap_page_set_memattr_1, NULL, (void *)PAL_PREFETCH_VISIBILITY); #else pmap_page_set_memattr_1((void *)PAL_PREFETCH_VISIBILITY); #endif va = (void *)pmap_page_to_va(m); critical_enter(); cpu_flush_dcache(va, PAGE_SIZE); critical_exit(); #ifdef SMP smp_rendezvous(NULL, pmap_page_set_memattr_1, NULL, (void *)PAL_MC_DRAIN); #else pmap_page_set_memattr_1((void *)PAL_MC_DRAIN); #endif } } /* * perform the pmap work for mincore */ int pmap_mincore(pmap_t pmap, vm_offset_t addr, vm_paddr_t *locked_pa) { pmap_t oldpmap; struct ia64_lpte *pte, tpte; vm_paddr_t pa; int val; CTR4(KTR_PMAP, "%s(pm=%p, va=%#lx, pa_p=%p)", __func__, pmap, addr, locked_pa); PMAP_LOCK(pmap); retry: oldpmap = pmap_switch(pmap); pte = pmap_find_vhpt(addr); if (pte != NULL) { tpte = *pte; pte = &tpte; } pmap_switch(oldpmap); if (pte == NULL || !pmap_present(pte)) { val = 0; goto out; } val = MINCORE_INCORE; if (pmap_dirty(pte)) val |= MINCORE_MODIFIED | MINCORE_MODIFIED_OTHER; if (pmap_accessed(pte)) val |= MINCORE_REFERENCED | MINCORE_REFERENCED_OTHER; if ((val & (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER)) != (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER) && pmap_managed(pte)) { pa = pmap_ppn(pte); /* Ensure that "PHYS_TO_VM_PAGE(pa)->object" doesn't change. */ if (vm_page_pa_tryrelock(pmap, pa, locked_pa)) goto retry; } else out: PA_UNLOCK_COND(*locked_pa); PMAP_UNLOCK(pmap); return (val); } /* * */ void pmap_activate(struct thread *td) { CTR2(KTR_PMAP, "%s(td=%p)", __func__, td); pmap_switch(vmspace_pmap(td->td_proc->p_vmspace)); } pmap_t pmap_switch(pmap_t pm) { pmap_t prevpm; int i; critical_enter(); prevpm = PCPU_GET(md.current_pmap); if (prevpm == pm) goto out; if (pm == NULL) { for (i = 0; i < IA64_VM_MINKERN_REGION; i++) { ia64_set_rr(IA64_RR_BASE(i), (i << 8)|(PAGE_SHIFT << 2)|1); } } else { for (i = 0; i < IA64_VM_MINKERN_REGION; i++) { ia64_set_rr(IA64_RR_BASE(i), (pm->pm_rid[i] << 8)|(PAGE_SHIFT << 2)|1); } } PCPU_SET(md.current_pmap, pm); ia64_srlz_d(); out: critical_exit(); return (prevpm); } /* * */ void pmap_sync_icache(pmap_t pm, vm_offset_t va, vm_size_t sz) { pmap_t oldpm; struct ia64_lpte *pte; vm_offset_t lim; vm_size_t len; CTR4(KTR_PMAP, "%s(pm=%p, va=%#lx, sz=%#lx)", __func__, pm, va, sz); sz += va & 31; va &= ~31; sz = (sz + 31) & ~31; PMAP_LOCK(pm); oldpm = pmap_switch(pm); while (sz > 0) { lim = round_page(va); len = MIN(lim - va, sz); pte = pmap_find_vhpt(va); if (pte != NULL && pmap_present(pte)) ia64_sync_icache(va, len); va += len; sz -= len; } pmap_switch(oldpm); PMAP_UNLOCK(pm); } /* * Increase the starting virtual address of the given mapping if a * different alignment might result in more superpage mappings. */ void pmap_align_superpage(vm_object_t object, vm_ooffset_t offset, vm_offset_t *addr, vm_size_t size) { CTR5(KTR_PMAP, "%s(obj=%p, ofs=%#lx, va_p=%p, sz=%#lx)", __func__, object, offset, addr, size); } #include "opt_ddb.h" #ifdef DDB #include 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 void 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 *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) { db_printf("Can't get VM summary\n"); return; } if (type == 0) maxtr = (res.pal_result[0] >> 40) & 0xff; else maxtr = (res.pal_result[0] >> 32) & 0xff; db_printf("V RID Virtual Page Physical Page PgSz ED AR PL D A MA P KEY\n"); for (i = 0; i <= maxtr; i++) { bzero(&buf, sizeof(buf)); res = ia64_pal_physical(PAL_VM_TR_READ, i, type, ia64_tpa((uint64_t)&buf)); 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)) pmap_clear_dirty(&buf); if (!(res.pal_result[0] & 8)) buf.pte &= ~PTE_MA_MASK; db_printf("%d %06x %013lx %013lx %4s %d %d %d %d %d %-3s " "%d %06x\n", (int)buf.ifa & 1, 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, (pmap_dirty(&buf)) ? 1 : 0, (pmap_accessed(&buf)) ? 1 : 0, manames[(buf.pte & PTE_MA_MASK) >> 2], (pmap_present(&buf)) ? 1 : 0, (int)((buf.itir & ITIR_KEY_MASK) >> 8)); } } DB_COMMAND(itr, db_itr) { print_trs(0); } DB_COMMAND(dtr, db_dtr) { print_trs(1); } DB_COMMAND(rr, db_rr) { int i; uint64_t t; struct ia64_rr rr; printf("RR RID PgSz VE\n"); for (i = 0; i < 8; i++) { __asm __volatile ("mov %0=rr[%1]" : "=r"(t) : "r"(IA64_RR_BASE(i))); *(uint64_t *) &rr = t; printf("%d %06x %4s %d\n", i, rr.rr_rid, psnames[rr.rr_ps], rr.rr_ve); } } DB_COMMAND(thash, db_thash) { if (!have_addr) return; db_printf("%p\n", (void *) ia64_thash(addr)); } DB_COMMAND(ttag, db_ttag) { if (!have_addr) return; db_printf("0x%lx\n", ia64_ttag(addr)); } DB_COMMAND(kpte, db_kpte) { struct ia64_lpte *pte; if (!have_addr) { db_printf("usage: kpte \n"); return; } if (addr < VM_INIT_KERNEL_ADDRESS) { db_printf("kpte: error: invalid \n"); return; } pte = pmap_find_kpte(addr); db_printf("kpte at %p:\n", pte); db_printf(" pte =%016lx\n", pte->pte); db_printf(" itir =%016lx\n", pte->itir); db_printf(" tag =%016lx\n", pte->tag); db_printf(" chain=%016lx\n", pte->chain); } #endif Index: stable/10/sys/ia64/ia64/sal.c =================================================================== --- stable/10/sys/ia64/ia64/sal.c (revision 270295) +++ stable/10/sys/ia64/ia64/sal.c (revision 270296) @@ -1,130 +1,130 @@ /*- * Copyright (c) 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include -#include #include #include #include #include int ia64_ipi_wakeup; static struct ia64_fdesc sal_fdesc; static sal_entry_t fake_sal; extern u_int64_t ia64_pal_entry; sal_entry_t *ia64_sal_entry = fake_sal; static struct uuid sal_table = EFI_TABLE_SAL; static struct sal_system_table *sal_systbl; static struct ia64_sal_result fake_sal(u_int64_t a1, u_int64_t a2, u_int64_t a3, u_int64_t a4, u_int64_t a5, u_int64_t a6, u_int64_t a7, u_int64_t a8) { struct ia64_sal_result res; res.sal_status = -3; res.sal_result[0] = 0; res.sal_result[1] = 0; res.sal_result[2] = 0; return res; } void ia64_sal_init(void) { static int sizes[6] = { 48, 32, 16, 32, 16, 16 }; u_int8_t *p; int error, i; sal_systbl = efi_get_table(&sal_table); if (sal_systbl == NULL) return; if (bcmp(sal_systbl->sal_signature, SAL_SIGNATURE, 4)) { printf("Bad signature for SAL System Table\n"); return; } p = (u_int8_t *) (sal_systbl + 1); for (i = 0; i < sal_systbl->sal_entry_count; i++) { switch (*p) { case 0: { struct sal_entrypoint_descriptor *dp; dp = (struct sal_entrypoint_descriptor*)p; ia64_pal_entry = IA64_PHYS_TO_RR7(dp->sale_pal_proc); if (bootverbose) printf("PAL Proc at 0x%lx\n", ia64_pal_entry); sal_fdesc.func = IA64_PHYS_TO_RR7(dp->sale_sal_proc); sal_fdesc.gp = IA64_PHYS_TO_RR7(dp->sale_sal_gp); if (bootverbose) printf("SAL Proc at 0x%lx, GP at 0x%lx\n", sal_fdesc.func, sal_fdesc.gp); ia64_sal_entry = (sal_entry_t *) &sal_fdesc; break; } case 5: { struct sal_ap_wakeup_descriptor *dp; dp = (struct sal_ap_wakeup_descriptor*)p; if (dp->sale_mechanism != 0) { printf("SAL: unsupported AP wake-up mechanism " "(%d)\n", dp->sale_mechanism); break; } /* Reserve the XIV so that we won't use it. */ error = ia64_xiv_reserve(dp->sale_vector, IA64_XIV_PLAT, NULL); if (error) { printf("SAL: invalid AP wake-up XIV (%#lx)\n", dp->sale_vector); break; } ia64_ipi_wakeup = dp->sale_vector; if (bootverbose) printf("SAL: AP wake-up XIV: %#x\n", ia64_ipi_wakeup); break; } } p += sizes[*p]; } } Index: stable/10/sys/ia64/ia64/trap.c =================================================================== --- stable/10/sys/ia64/ia64/trap.c (revision 270295) +++ stable/10/sys/ia64/ia64/trap.c (revision 270296) @@ -1,962 +1,962 @@ /*- * Copyright (c) 2005 Marcel Moolenaar * 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 ``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 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 __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #ifdef SMP #include #endif #include #include static int print_usertrap = 0; SYSCTL_INT(_machdep, OID_AUTO, print_usertrap, CTLFLAG_RW, &print_usertrap, 0, ""); static void break_syscall(struct trapframe *tf); /* * EFI-Provided FPSWA interface (Floating Point SoftWare Assist) */ extern struct fpswa_iface *fpswa_iface; static const char *ia64_vector_names[] = { "VHPT Translation", /* 0 */ "Instruction TLB", /* 1 */ "Data TLB", /* 2 */ "Alternate Instruction TLB", /* 3 */ "Alternate Data TLB", /* 4 */ "Data Nested TLB", /* 5 */ "Instruction Key Miss", /* 6 */ "Data Key Miss", /* 7 */ "Dirty-Bit", /* 8 */ "Instruction Access-Bit", /* 9 */ "Data Access-Bit", /* 10 */ "Break Instruction", /* 11 */ "External Interrupt", /* 12 */ "Reserved 13", /* 13 */ "Reserved 14", /* 14 */ "Reserved 15", /* 15 */ "Reserved 16", /* 16 */ "Reserved 17", /* 17 */ "Reserved 18", /* 18 */ "Reserved 19", /* 19 */ "Page Not Present", /* 20 */ "Key Permission", /* 21 */ "Instruction Access Rights", /* 22 */ "Data Access Rights", /* 23 */ "General Exception", /* 24 */ "Disabled FP-Register", /* 25 */ "NaT Consumption", /* 26 */ "Speculation", /* 27 */ "Reserved 28", /* 28 */ "Debug", /* 29 */ "Unaligned Reference", /* 30 */ "Unsupported Data Reference", /* 31 */ "Floating-point Fault", /* 32 */ "Floating-point Trap", /* 33 */ "Lower-Privilege Transfer Trap", /* 34 */ "Taken Branch Trap", /* 35 */ "Single Step Trap", /* 36 */ "Reserved 37", /* 37 */ "Reserved 38", /* 38 */ "Reserved 39", /* 39 */ "Reserved 40", /* 40 */ "Reserved 41", /* 41 */ "Reserved 42", /* 42 */ "Reserved 43", /* 43 */ "Reserved 44", /* 44 */ "IA-32 Exception", /* 45 */ "IA-32 Intercept", /* 46 */ "IA-32 Interrupt", /* 47 */ "Reserved 48", /* 48 */ "Reserved 49", /* 49 */ "Reserved 50", /* 50 */ "Reserved 51", /* 51 */ "Reserved 52", /* 52 */ "Reserved 53", /* 53 */ "Reserved 54", /* 54 */ "Reserved 55", /* 55 */ "Reserved 56", /* 56 */ "Reserved 57", /* 57 */ "Reserved 58", /* 58 */ "Reserved 59", /* 59 */ "Reserved 60", /* 60 */ "Reserved 61", /* 61 */ "Reserved 62", /* 62 */ "Reserved 63", /* 63 */ "Reserved 64", /* 64 */ "Reserved 65", /* 65 */ "Reserved 66", /* 66 */ "Reserved 67", /* 67 */ }; struct bitname { uint64_t mask; const char* name; }; static void printbits(uint64_t mask, struct bitname *bn, int count) { int i, first = 1; uint64_t bit; for (i = 0; i < count; i++) { /* * Handle fields wider than one bit. */ bit = bn[i].mask & ~(bn[i].mask - 1); if (bn[i].mask > bit) { if (first) first = 0; else printf(","); printf("%s=%ld", bn[i].name, (mask & bn[i].mask) / bit); } else if (mask & bit) { if (first) first = 0; else printf(","); printf("%s", bn[i].name); } } } struct bitname psr_bits[] = { {IA64_PSR_BE, "be"}, {IA64_PSR_UP, "up"}, {IA64_PSR_AC, "ac"}, {IA64_PSR_MFL, "mfl"}, {IA64_PSR_MFH, "mfh"}, {IA64_PSR_IC, "ic"}, {IA64_PSR_I, "i"}, {IA64_PSR_PK, "pk"}, {IA64_PSR_DT, "dt"}, {IA64_PSR_DFL, "dfl"}, {IA64_PSR_DFH, "dfh"}, {IA64_PSR_SP, "sp"}, {IA64_PSR_PP, "pp"}, {IA64_PSR_DI, "di"}, {IA64_PSR_SI, "si"}, {IA64_PSR_DB, "db"}, {IA64_PSR_LP, "lp"}, {IA64_PSR_TB, "tb"}, {IA64_PSR_RT, "rt"}, {IA64_PSR_CPL, "cpl"}, {IA64_PSR_IS, "is"}, {IA64_PSR_MC, "mc"}, {IA64_PSR_IT, "it"}, {IA64_PSR_ID, "id"}, {IA64_PSR_DA, "da"}, {IA64_PSR_DD, "dd"}, {IA64_PSR_SS, "ss"}, {IA64_PSR_RI, "ri"}, {IA64_PSR_ED, "ed"}, {IA64_PSR_BN, "bn"}, {IA64_PSR_IA, "ia"}, }; static void printpsr(uint64_t psr) { printbits(psr, psr_bits, sizeof(psr_bits)/sizeof(psr_bits[0])); } struct bitname isr_bits[] = { {IA64_ISR_CODE, "code"}, {IA64_ISR_VECTOR, "vector"}, {IA64_ISR_X, "x"}, {IA64_ISR_W, "w"}, {IA64_ISR_R, "r"}, {IA64_ISR_NA, "na"}, {IA64_ISR_SP, "sp"}, {IA64_ISR_RS, "rs"}, {IA64_ISR_IR, "ir"}, {IA64_ISR_NI, "ni"}, {IA64_ISR_SO, "so"}, {IA64_ISR_EI, "ei"}, {IA64_ISR_ED, "ed"}, }; static void printisr(uint64_t isr) { printbits(isr, isr_bits, sizeof(isr_bits)/sizeof(isr_bits[0])); } static void printtrap(int vector, struct trapframe *tf, int isfatal, int user) { printf("\n"); printf("%s %s trap (cpu %d):\n", isfatal? "fatal" : "handled", user ? "user" : "kernel", PCPU_GET(cpuid)); printf("\n"); printf(" trap vector = 0x%x (%s)\n", vector, ia64_vector_names[vector]); printf(" cr.iip = 0x%lx\n", tf->tf_special.iip); printf(" cr.ipsr = 0x%lx (", tf->tf_special.psr); printpsr(tf->tf_special.psr); printf(")\n"); printf(" cr.isr = 0x%lx (", tf->tf_special.isr); printisr(tf->tf_special.isr); printf(")\n"); printf(" cr.ifa = 0x%lx\n", tf->tf_special.ifa); if (tf->tf_special.psr & IA64_PSR_IS) { printf(" ar.cflg = 0x%lx\n", ia64_get_cflg()); printf(" ar.csd = 0x%lx\n", ia64_get_csd()); printf(" ar.ssd = 0x%lx\n", ia64_get_ssd()); } printf(" curthread = %p\n", curthread); if (curthread != NULL) printf(" pid = %d, comm = %s\n", curthread->td_proc->p_pid, curthread->td_name); printf("\n"); } /* * We got a trap caused by a break instruction and the immediate was 0. * This indicates that we may have a break.b with some non-zero immediate. * The break.b doesn't cause the immediate to be put in cr.iim. Hence, * we need to disassemble the bundle and return the immediate found there. * This may be a 0 value anyway. Return 0 for any error condition. This * will result in a SIGILL, which is pretty much the best thing to do. */ static uint64_t trap_decode_break(struct trapframe *tf) { struct asm_bundle bundle; struct asm_inst *inst; int slot; if (!asm_decode(tf->tf_special.iip, &bundle)) return (0); slot = ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_0) ? 0 : ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_1) ? 1 : 2; inst = bundle.b_inst + slot; /* * Sanity checking: It must be a break instruction and the operand * that has the break value must be an immediate. */ if (inst->i_op != ASM_OP_BREAK || inst->i_oper[1].o_type != ASM_OPER_IMM) return (0); return (inst->i_oper[1].o_value); } void trap_panic(int vector, struct trapframe *tf) { printtrap(vector, tf, 1, TRAPF_USERMODE(tf)); #ifdef KDB kdb_trap(vector, 0, tf); #endif panic("trap"); } /* * */ int do_ast(struct trapframe *tf) { ia64_disable_intr(); while (curthread->td_flags & (TDF_ASTPENDING|TDF_NEEDRESCHED)) { ia64_enable_intr(); ast(tf); ia64_disable_intr(); } /* * Keep interrupts disabled. We return r10 as a favor to the EPC * syscall code so that it can quicky determine if the syscall * needs to be restarted or not. */ return (tf->tf_scratch.gr10); } /* * Trap is called from exception.s to handle most types of processor traps. */ /*ARGSUSED*/ void trap(int vector, struct trapframe *tf) { struct proc *p; struct thread *td; uint64_t ucode; int error, sig, user; ksiginfo_t ksi; user = TRAPF_USERMODE(tf) ? 1 : 0; if (user) ia64_set_fpsr(IA64_FPSR_DEFAULT); #ifdef XTRACE ia64_xtrace_save(); #endif PCPU_INC(cnt.v_trap); td = curthread; p = td->td_proc; ucode = 0; if (user) { td->td_pticks = 0; td->td_frame = tf; if (td->td_ucred != p->p_ucred) cred_update_thread(td); } else { KASSERT(cold || td->td_ucred != NULL, ("kernel trap doesn't have ucred")); #ifdef KDB if (kdb_active) kdb_reenter(); #endif } sig = 0; switch (vector) { case IA64_VEC_VHPT: /* * This one is tricky. We should hardwire the VHPT, but * don't at this time. I think we're mostly lucky that * the VHPT is mapped. */ trap_panic(vector, tf); break; case IA64_VEC_ITLB: case IA64_VEC_DTLB: case IA64_VEC_EXT_INTR: /* We never call trap() with these vectors. */ trap_panic(vector, tf); break; case IA64_VEC_ALT_ITLB: case IA64_VEC_ALT_DTLB: /* * These should never happen, because regions 0-4 use the * VHPT. If we get one of these it means we didn't program * the region registers correctly. */ trap_panic(vector, tf); break; case IA64_VEC_NESTED_DTLB: /* * When the nested TLB handler encounters an unexpected * condition, it'll switch to the backup stack and transfer * here. All we need to do is panic. */ trap_panic(vector, tf); break; case IA64_VEC_IKEY_MISS: case IA64_VEC_DKEY_MISS: case IA64_VEC_KEY_PERMISSION: /* * We don't use protection keys, so we should never get * these faults. */ trap_panic(vector, tf); break; case IA64_VEC_DIRTY_BIT: case IA64_VEC_INST_ACCESS: case IA64_VEC_DATA_ACCESS: /* * We get here if we read or write to a page of which the * PTE does not have the access bit or dirty bit set and * we can not find the PTE in our datastructures. This * either means we have a stale PTE in the TLB, or we lost * the PTE in our datastructures. */ trap_panic(vector, tf); break; case IA64_VEC_BREAK: if (user) { ucode = (int)tf->tf_special.ifa & 0x1FFFFF; if (ucode == 0) { /* * A break.b doesn't cause the immediate to be * stored in cr.iim (and saved in the TF in * tf_special.ifa). We need to decode the * instruction to find out what the immediate * was. Note that if the break instruction * didn't happen to be a break.b, but any * other break with an immediate of 0, we * will do unnecessary work to get the value * we already had. Not an issue, because a * break 0 is invalid. */ ucode = trap_decode_break(tf); } if (ucode < 0x80000) { /* Software interrupts. */ switch (ucode) { case 0: /* Unknown error. */ sig = SIGILL; break; case 1: /* Integer divide by zero. */ sig = SIGFPE; ucode = FPE_INTDIV; break; case 2: /* Integer overflow. */ sig = SIGFPE; ucode = FPE_INTOVF; break; case 3: /* Range check/bounds check. */ sig = SIGFPE; ucode = FPE_FLTSUB; break; case 6: /* Decimal overflow. */ case 7: /* Decimal divide by zero. */ case 8: /* Packed decimal error. */ case 9: /* Invalid ASCII digit. */ case 10: /* Invalid decimal digit. */ sig = SIGFPE; ucode = FPE_FLTINV; break; case 4: /* Null pointer dereference. */ case 5: /* Misaligned data. */ case 11: /* Paragraph stack overflow. */ sig = SIGSEGV; break; default: sig = SIGILL; break; } } else if (ucode < 0x100000) { /* Debugger breakpoint. */ tf->tf_special.psr &= ~IA64_PSR_SS; sig = SIGTRAP; } else if (ucode == 0x100000) { break_syscall(tf); return; /* do_ast() already called. */ } else if (ucode == 0x180000) { mcontext_t mc; error = copyin((void*)tf->tf_scratch.gr8, &mc, sizeof(mc)); if (!error) { set_mcontext(td, &mc); return; /* Don't call do_ast()!!! */ } sig = SIGSEGV; ucode = tf->tf_scratch.gr8; } else sig = SIGILL; } else { #ifdef KDB if (kdb_trap(vector, 0, tf)) return; panic("trap"); #else trap_panic(vector, tf); #endif } break; case IA64_VEC_PAGE_NOT_PRESENT: case IA64_VEC_INST_ACCESS_RIGHTS: case IA64_VEC_DATA_ACCESS_RIGHTS: { vm_offset_t va; struct vmspace *vm; vm_map_t map; vm_prot_t ftype; int rv; rv = 0; va = trunc_page(tf->tf_special.ifa); if (va >= VM_MAXUSER_ADDRESS) { /* * Don't allow user-mode faults for kernel virtual * addresses, including the gateway page. */ if (user) goto no_fault_in; map = kernel_map; } else { vm = (p != NULL) ? p->p_vmspace : NULL; if (vm == NULL) goto no_fault_in; map = &vm->vm_map; } if (tf->tf_special.isr & IA64_ISR_X) ftype = VM_PROT_EXECUTE; else if (tf->tf_special.isr & IA64_ISR_W) ftype = VM_PROT_WRITE; else ftype = VM_PROT_READ; if (map != kernel_map) { /* * Keep swapout from messing with us during this * critical time. */ PROC_LOCK(p); ++p->p_lock; PROC_UNLOCK(p); /* Fault in the user page: */ rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL); PROC_LOCK(p); --p->p_lock; PROC_UNLOCK(p); } else { /* * Don't have to worry about process locking or * stacks in the kernel. */ rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL); } if (rv == KERN_SUCCESS) goto out; no_fault_in: if (!user) { /* Check for copyin/copyout fault. */ if (td != NULL && td->td_pcb->pcb_onfault != 0) { tf->tf_special.iip = td->td_pcb->pcb_onfault; tf->tf_special.psr &= ~IA64_PSR_RI; td->td_pcb->pcb_onfault = 0; goto out; } trap_panic(vector, tf); } ucode = va; sig = (rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV; break; } case IA64_VEC_GENERAL_EXCEPTION: { int code; if (!user) trap_panic(vector, tf); code = tf->tf_special.isr & (IA64_ISR_CODE & 0xf0ull); switch (code) { case 0x0: /* Illegal Operation Fault. */ sig = ia64_emulate(tf, td); break; default: sig = SIGILL; break; } if (sig == 0) goto out; ucode = vector; break; } case IA64_VEC_SPECULATION: /* * The branching behaviour of the chk instruction is not * implemented by the processor. All we need to do is * compute the target address of the branch and make sure * that control is transfered to that address. * We should do this in the IVT table and not by entring * the kernel... */ tf->tf_special.iip += tf->tf_special.ifa << 4; tf->tf_special.psr &= ~IA64_PSR_RI; goto out; case IA64_VEC_NAT_CONSUMPTION: case IA64_VEC_UNSUPP_DATA_REFERENCE: if (user) { ucode = vector; sig = SIGILL; } else trap_panic(vector, tf); break; case IA64_VEC_DISABLED_FP: { if (user) ia64_highfp_enable(td, tf); else trap_panic(vector, tf); goto out; } case IA64_VEC_DEBUG: case IA64_VEC_SINGLE_STEP_TRAP: tf->tf_special.psr &= ~IA64_PSR_SS; if (!user) { #ifdef KDB if (kdb_trap(vector, 0, tf)) return; panic("trap"); #else trap_panic(vector, tf); #endif } sig = SIGTRAP; break; case IA64_VEC_UNALIGNED_REFERENCE: /* * If user-land, do whatever fixups, printing, and * signalling is appropriate (based on system-wide * and per-process unaligned-access-handling flags). */ if (user) { sig = unaligned_fixup(tf, td); if (sig == 0) goto out; ucode = tf->tf_special.ifa; /* VA */ } else { /* Check for copyin/copyout fault. */ if (td != NULL && td->td_pcb->pcb_onfault != 0) { tf->tf_special.iip = td->td_pcb->pcb_onfault; tf->tf_special.psr &= ~IA64_PSR_RI; td->td_pcb->pcb_onfault = 0; goto out; } trap_panic(vector, tf); } break; case IA64_VEC_FLOATING_POINT_FAULT: case IA64_VEC_FLOATING_POINT_TRAP: { struct fpswa_bundle bundle; struct fpswa_fpctx fpctx; struct fpswa_ret ret; char *ip; u_long fault; /* Always fatal in kernel. Should never happen. */ if (!user) trap_panic(vector, tf); if (fpswa_iface == NULL) { sig = SIGFPE; ucode = 0; break; } ip = (char *)tf->tf_special.iip; if (vector == IA64_VEC_FLOATING_POINT_TRAP && (tf->tf_special.psr & IA64_PSR_RI) == 0) ip -= 16; error = copyin(ip, &bundle, sizeof(bundle)); if (error) { sig = SIGBUS; /* EFAULT, basically */ ucode = 0; /* exception summary */ break; } /* f6-f15 are saved in exception_save */ fpctx.mask_low = 0xffc0; /* bits 6 - 15 */ fpctx.mask_high = 0; fpctx.fp_low_preserved = NULL; fpctx.fp_low_volatile = &tf->tf_scratch_fp.fr6; fpctx.fp_high_preserved = NULL; fpctx.fp_high_volatile = NULL; fault = (vector == IA64_VEC_FLOATING_POINT_FAULT) ? 1 : 0; /* * We have the high FP registers disabled while in the * kernel. Enable them for the FPSWA handler only. */ ia64_enable_highfp(); /* The docs are unclear. Is Fpswa reentrant? */ ret = fpswa_iface->if_fpswa(fault, &bundle, &tf->tf_special.psr, &tf->tf_special.fpsr, &tf->tf_special.isr, &tf->tf_special.pr, &tf->tf_special.cfm, &fpctx); ia64_disable_highfp(); /* * Update ipsr and iip to next instruction. We only * have to do that for faults. */ if (fault && (ret.status == 0 || (ret.status & 2))) { int ei; ei = (tf->tf_special.isr >> 41) & 0x03; if (ei == 0) { /* no template for this case */ tf->tf_special.psr &= ~IA64_ISR_EI; tf->tf_special.psr |= IA64_ISR_EI_1; } else if (ei == 1) { /* MFI or MFB */ tf->tf_special.psr &= ~IA64_ISR_EI; tf->tf_special.psr |= IA64_ISR_EI_2; } else if (ei == 2) { /* MMF */ tf->tf_special.psr &= ~IA64_ISR_EI; tf->tf_special.iip += 0x10; } } if (ret.status == 0) { goto out; } else if (ret.status == -1) { printf("FATAL: FPSWA err1 %lx, err2 %lx, err3 %lx\n", ret.err1, ret.err2, ret.err3); panic("fpswa fatal error on fp fault"); } else { sig = SIGFPE; ucode = 0; /* XXX exception summary */ break; } } case IA64_VEC_LOWER_PRIVILEGE_TRANSFER: /* * The lower-privilege transfer trap is used by the EPC * syscall code to trigger re-entry into the kernel when the * process should be single stepped. The problem is that * there's no way to set single stepping directly without * using the rfi instruction. So instead we enable the * lower-privilege transfer trap and when we get here we * know that the process is about to enter userland (and * has already lowered its privilege). * However, there's another gotcha. When the process has * lowered it's privilege it's still running in the gateway * page. If we enable single stepping, we'll be stepping * the code in the gateway page. In and by itself this is * not a problem, but it's an address debuggers won't know * anything about. Hence, it can only cause confusion. * We know that we need to branch to get out of the gateway * page, so what we do here is enable the taken branch * trap and just let the process continue. When we branch * out of the gateway page we'll get back into the kernel * and then we enable single stepping. * Since this a rather round-about way of enabling single * stepping, don't make things even more complicated by * calling userret() and do_ast(). We do that later... */ tf->tf_special.psr &= ~IA64_PSR_LP; tf->tf_special.psr |= IA64_PSR_TB; return; case IA64_VEC_TAKEN_BRANCH_TRAP: /* * Don't assume there aren't any branches other than the * branch that takes us out of the gateway page. Check the * iip and enable single stepping only when it's an user * address. */ if (tf->tf_special.iip >= VM_MAXUSER_ADDRESS) return; tf->tf_special.psr &= ~IA64_PSR_TB; tf->tf_special.psr |= IA64_PSR_SS; return; case IA64_VEC_IA32_EXCEPTION: case IA64_VEC_IA32_INTERCEPT: case IA64_VEC_IA32_INTERRUPT: sig = SIGEMT; ucode = tf->tf_special.iip; break; default: /* Reserved vectors get here. Should never happen of course. */ trap_panic(vector, tf); break; } KASSERT(sig != 0, ("foo")); if (print_usertrap) printtrap(vector, tf, 1, user); ksiginfo_init(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = ucode; trapsignal(td, &ksi); out: if (user) { userret(td, tf); do_ast(tf); } return; } /* * Handle break instruction based system calls. */ void break_syscall(struct trapframe *tf) { uint64_t *bsp, *tfp; uint64_t iip, psr; int error, nargs; /* Save address of break instruction. */ iip = tf->tf_special.iip; psr = tf->tf_special.psr; /* Advance to the next instruction. */ tf->tf_special.psr += IA64_PSR_RI_1; if ((tf->tf_special.psr & IA64_PSR_RI) > IA64_PSR_RI_2) { tf->tf_special.iip += 16; tf->tf_special.psr &= ~IA64_PSR_RI; } /* * Copy the arguments on the register stack into the trapframe * to avoid having interleaved NaT collections. */ tfp = &tf->tf_scratch.gr16; nargs = tf->tf_special.cfm & 0x7f; bsp = (uint64_t*)(curthread->td_kstack + tf->tf_special.ndirty + (tf->tf_special.bspstore & 0x1ffUL)); bsp -= (((uintptr_t)bsp & 0x1ff) < (nargs << 3)) ? (nargs + 1): nargs; while (nargs--) { *tfp++ = *bsp++; if (((uintptr_t)bsp & 0x1ff) == 0x1f8) bsp++; } error = syscall(tf); if (error == ERESTART) { tf->tf_special.iip = iip; tf->tf_special.psr = psr; } do_ast(tf); } int cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa) { struct proc *p; struct trapframe *tf; p = td->td_proc; tf = td->td_frame; sa->code = tf->tf_scratch.gr15; sa->args = &tf->tf_scratch.gr16; /* * syscall() and __syscall() are handled the same on * the ia64, as everything is 64-bit aligned, anyway. */ if (sa->code == SYS_syscall || sa->code == SYS___syscall) { /* * Code is first argument, followed by actual args. */ sa->code = sa->args[0]; sa->args++; } if (p->p_sysent->sv_mask) sa->code &= p->p_sysent->sv_mask; if (sa->code >= p->p_sysent->sv_size) sa->callp = &p->p_sysent->sv_table[0]; else sa->callp = &p->p_sysent->sv_table[sa->code]; sa->narg = sa->callp->sy_narg; td->td_retval[0] = 0; td->td_retval[1] = 0; return (0); } #include "../../kern/subr_syscall.c" /* * Process a system call. * * See syscall.s for details as to how we get here. In order to support * the ERESTART case, we return the error to our caller. They deal with * the hairy details. */ int syscall(struct trapframe *tf) { struct syscall_args sa; struct thread *td; int error; td = curthread; td->td_frame = tf; ia64_set_fpsr(IA64_FPSR_DEFAULT); tf->tf_scratch.gr10 = EJUSTRETURN; error = syscallenter(td, &sa); syscallret(td, error, &sa); return (error); } Index: stable/10/sys/ia64/include/efi.h =================================================================== --- stable/10/sys/ia64/include/efi.h (revision 270295) +++ stable/10/sys/ia64/include/efi.h (nonexistent) @@ -1,177 +0,0 @@ -/*- - * Copyright (c) 2004 Marcel Moolenaar - * 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 ``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 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. - * - * $FreeBSD$ - */ - -#ifndef _MACHINE_EFI_H_ -#define _MACHINE_EFI_H_ - -#include - -#define EFI_PAGE_SHIFT 12 -#define EFI_PAGE_SIZE (1 << EFI_PAGE_SHIFT) -#define EFI_PAGE_MASK (EFI_PAGE_SIZE - 1) - -#define EFI_TABLE_ACPI20 \ - {0x8868e871,0xe4f1,0x11d3,0xbc,0x22,{0x00,0x80,0xc7,0x3c,0x88,0x81}} -#define EFI_TABLE_SAL \ - {0xeb9d2d32,0x2d88,0x11d3,0x9a,0x16,{0x00,0x90,0x27,0x3f,0xc1,0x4d}} - -enum efi_reset { - EFI_RESET_COLD, - EFI_RESET_WARM -}; - -typedef uint16_t efi_char; -typedef unsigned long efi_status; - -struct efi_cfgtbl { - struct uuid ct_uuid; - uint64_t ct_data; -}; - -struct efi_md { - uint32_t md_type; -#define EFI_MD_TYPE_NULL 0 -#define EFI_MD_TYPE_CODE 1 /* Loader text. */ -#define EFI_MD_TYPE_DATA 2 /* Loader data. */ -#define EFI_MD_TYPE_BS_CODE 3 /* Boot services text. */ -#define EFI_MD_TYPE_BS_DATA 4 /* Boot services data. */ -#define EFI_MD_TYPE_RT_CODE 5 /* Runtime services text. */ -#define EFI_MD_TYPE_RT_DATA 6 /* Runtime services data. */ -#define EFI_MD_TYPE_FREE 7 /* Unused/free memory. */ -#define EFI_MD_TYPE_BAD 8 /* Bad memory */ -#define EFI_MD_TYPE_RECLAIM 9 /* ACPI reclaimable memory. */ -#define EFI_MD_TYPE_FIRMWARE 10 /* ACPI NV memory */ -#define EFI_MD_TYPE_IOMEM 11 /* Memory-mapped I/O. */ -#define EFI_MD_TYPE_IOPORT 12 /* I/O port space. */ -#define EFI_MD_TYPE_PALCODE 13 /* PAL */ - uint32_t __pad; - uint64_t md_phys; - void *md_virt; - uint64_t md_pages; - uint64_t md_attr; -#define EFI_MD_ATTR_UC 0x0000000000000001UL -#define EFI_MD_ATTR_WC 0x0000000000000002UL -#define EFI_MD_ATTR_WT 0x0000000000000004UL -#define EFI_MD_ATTR_WB 0x0000000000000008UL -#define EFI_MD_ATTR_UCE 0x0000000000000010UL -#define EFI_MD_ATTR_WP 0x0000000000001000UL -#define EFI_MD_ATTR_RP 0x0000000000002000UL -#define EFI_MD_ATTR_XP 0x0000000000004000UL -#define EFI_MD_ATTR_RT 0x8000000000000000UL -}; - -struct efi_tm { - uint16_t tm_year; /* 1998 - 20XX */ - uint8_t tm_mon; /* 1 - 12 */ - uint8_t tm_mday; /* 1 - 31 */ - uint8_t tm_hour; /* 0 - 23 */ - uint8_t tm_min; /* 0 - 59 */ - uint8_t tm_sec; /* 0 - 59 */ - uint8_t __pad1; - uint32_t tm_nsec; /* 0 - 999,999,999 */ - int16_t tm_tz; /* -1440 to 1440 or 2047 */ - uint8_t tm_dst; - uint8_t __pad2; -}; - -struct efi_tmcap { - uint32_t tc_res; /* 1e-6 parts per million */ - uint32_t tc_prec; /* hertz */ - uint8_t tc_stz; /* Set clears sub-second time */ -}; - -struct efi_tblhdr { - uint64_t th_sig; - uint32_t th_rev; - uint32_t th_hdrsz; - uint32_t th_crc32; - uint32_t __res; -}; - -struct efi_rt { - struct efi_tblhdr rt_hdr; - efi_status (*rt_gettime)(struct efi_tm *, struct efi_tmcap *); - efi_status (*rt_settime)(struct efi_tm *); - efi_status (*rt_getwaketime)(uint8_t *, uint8_t *, - struct efi_tm *); - efi_status (*rt_setwaketime)(uint8_t, struct efi_tm *); - efi_status (*rt_setvirtual)(u_long, u_long, uint32_t, - struct efi_md *); - efi_status (*rt_cvtptr)(u_long, void **); - efi_status (*rt_getvar)(efi_char *, struct uuid *, uint32_t *, - u_long *, void *); - efi_status (*rt_scanvar)(u_long *, efi_char *, struct uuid *); - efi_status (*rt_setvar)(efi_char *, struct uuid *, uint32_t, - u_long, void *); - efi_status (*rt_gethicnt)(uint32_t *); - efi_status (*rt_reset)(enum efi_reset, efi_status, u_long, - efi_char *); -}; - -struct efi_systbl { - struct efi_tblhdr st_hdr; -#define EFI_SYSTBL_SIG 0x5453595320494249UL - efi_char *st_fwvendor; - uint32_t st_fwrev; - uint32_t __pad; - void *st_cin; - void *st_cinif; - void *st_cout; - void *st_coutif; - void *st_cerr; - void *st_cerrif; - uint64_t st_rt; - void *st_bs; - u_long st_entries; - uint64_t st_cfgtbl; -}; - -#ifdef _KERNEL - -typedef u_long (*ia64_efi_f)(u_long, u_long, u_long, u_long); - -u_long ia64_efi_physical(ia64_efi_f, u_long, u_long, u_long, u_long); - -void efi_boot_finish(void); -int efi_boot_minimal(uint64_t); -void *efi_get_table(struct uuid *); -void efi_get_time(struct efi_tm *); -struct efi_md *efi_md_find(vm_paddr_t); -struct efi_md *efi_md_first(void); -struct efi_md *efi_md_last(void); -struct efi_md *efi_md_next(struct efi_md *); -struct efi_md *efi_md_prev(struct efi_md *); -void efi_reset_system(void); -int efi_set_time(struct efi_tm *); -int efi_var_get(efi_char *, struct uuid *, uint32_t *, size_t *, void *); -int efi_var_nextname(size_t *, efi_char *, struct uuid *); -int efi_var_set(efi_char *, struct uuid *, uint32_t, size_t, void *); - -#endif /* _KERNEL */ - -#endif /* _MACHINE_EFI_H_ */ Property changes on: stable/10/sys/ia64/include/efi.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: stable/10/sys/sys/efi.h =================================================================== --- stable/10/sys/sys/efi.h (nonexistent) +++ stable/10/sys/sys/efi.h (revision 270296) @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 2004 Marcel Moolenaar + * 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 ``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 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_EFI_H_ +#define _SYS_EFI_H_ + +#include + +#define EFI_PAGE_SHIFT 12 +#define EFI_PAGE_SIZE (1 << EFI_PAGE_SHIFT) +#define EFI_PAGE_MASK (EFI_PAGE_SIZE - 1) + +#define EFI_TABLE_ACPI20 \ + {0x8868e871,0xe4f1,0x11d3,0xbc,0x22,{0x00,0x80,0xc7,0x3c,0x88,0x81}} +#define EFI_TABLE_SAL \ + {0xeb9d2d32,0x2d88,0x11d3,0x9a,0x16,{0x00,0x90,0x27,0x3f,0xc1,0x4d}} + +enum efi_reset { + EFI_RESET_COLD, + EFI_RESET_WARM +}; + +typedef uint16_t efi_char; +typedef unsigned long efi_status; + +struct efi_cfgtbl { + struct uuid ct_uuid; + uint64_t ct_data; +}; + +struct efi_md { + uint32_t md_type; +#define EFI_MD_TYPE_NULL 0 +#define EFI_MD_TYPE_CODE 1 /* Loader text. */ +#define EFI_MD_TYPE_DATA 2 /* Loader data. */ +#define EFI_MD_TYPE_BS_CODE 3 /* Boot services text. */ +#define EFI_MD_TYPE_BS_DATA 4 /* Boot services data. */ +#define EFI_MD_TYPE_RT_CODE 5 /* Runtime services text. */ +#define EFI_MD_TYPE_RT_DATA 6 /* Runtime services data. */ +#define EFI_MD_TYPE_FREE 7 /* Unused/free memory. */ +#define EFI_MD_TYPE_BAD 8 /* Bad memory */ +#define EFI_MD_TYPE_RECLAIM 9 /* ACPI reclaimable memory. */ +#define EFI_MD_TYPE_FIRMWARE 10 /* ACPI NV memory */ +#define EFI_MD_TYPE_IOMEM 11 /* Memory-mapped I/O. */ +#define EFI_MD_TYPE_IOPORT 12 /* I/O port space. */ +#define EFI_MD_TYPE_PALCODE 13 /* PAL */ + uint32_t __pad; + uint64_t md_phys; + void *md_virt; + uint64_t md_pages; + uint64_t md_attr; +#define EFI_MD_ATTR_UC 0x0000000000000001UL +#define EFI_MD_ATTR_WC 0x0000000000000002UL +#define EFI_MD_ATTR_WT 0x0000000000000004UL +#define EFI_MD_ATTR_WB 0x0000000000000008UL +#define EFI_MD_ATTR_UCE 0x0000000000000010UL +#define EFI_MD_ATTR_WP 0x0000000000001000UL +#define EFI_MD_ATTR_RP 0x0000000000002000UL +#define EFI_MD_ATTR_XP 0x0000000000004000UL +#define EFI_MD_ATTR_RT 0x8000000000000000UL +}; + +struct efi_tm { + uint16_t tm_year; /* 1998 - 20XX */ + uint8_t tm_mon; /* 1 - 12 */ + uint8_t tm_mday; /* 1 - 31 */ + uint8_t tm_hour; /* 0 - 23 */ + uint8_t tm_min; /* 0 - 59 */ + uint8_t tm_sec; /* 0 - 59 */ + uint8_t __pad1; + uint32_t tm_nsec; /* 0 - 999,999,999 */ + int16_t tm_tz; /* -1440 to 1440 or 2047 */ + uint8_t tm_dst; + uint8_t __pad2; +}; + +struct efi_tmcap { + uint32_t tc_res; /* 1e-6 parts per million */ + uint32_t tc_prec; /* hertz */ + uint8_t tc_stz; /* Set clears sub-second time */ +}; + +struct efi_tblhdr { + uint64_t th_sig; + uint32_t th_rev; + uint32_t th_hdrsz; + uint32_t th_crc32; + uint32_t __res; +}; + +struct efi_rt { + struct efi_tblhdr rt_hdr; + efi_status (*rt_gettime)(struct efi_tm *, struct efi_tmcap *); + efi_status (*rt_settime)(struct efi_tm *); + efi_status (*rt_getwaketime)(uint8_t *, uint8_t *, + struct efi_tm *); + efi_status (*rt_setwaketime)(uint8_t, struct efi_tm *); + efi_status (*rt_setvirtual)(u_long, u_long, uint32_t, + struct efi_md *); + efi_status (*rt_cvtptr)(u_long, void **); + efi_status (*rt_getvar)(efi_char *, struct uuid *, uint32_t *, + u_long *, void *); + efi_status (*rt_scanvar)(u_long *, efi_char *, struct uuid *); + efi_status (*rt_setvar)(efi_char *, struct uuid *, uint32_t, + u_long, void *); + efi_status (*rt_gethicnt)(uint32_t *); + efi_status (*rt_reset)(enum efi_reset, efi_status, u_long, + efi_char *); +}; + +struct efi_systbl { + struct efi_tblhdr st_hdr; +#define EFI_SYSTBL_SIG 0x5453595320494249UL + efi_char *st_fwvendor; + uint32_t st_fwrev; + uint32_t __pad; + void *st_cin; + void *st_cinif; + void *st_cout; + void *st_coutif; + void *st_cerr; + void *st_cerrif; + uint64_t st_rt; + void *st_bs; + u_long st_entries; + uint64_t st_cfgtbl; +}; + +#if defined(_KERNEL) && defined(__ia64__) + +typedef u_long (*ia64_efi_f)(u_long, u_long, u_long, u_long); + +u_long ia64_efi_physical(ia64_efi_f, u_long, u_long, u_long, u_long); + +void efi_boot_finish(void); +int efi_boot_minimal(uint64_t); +void *efi_get_table(struct uuid *); +void efi_get_time(struct efi_tm *); +struct efi_md *efi_md_find(vm_paddr_t); +struct efi_md *efi_md_first(void); +struct efi_md *efi_md_last(void); +struct efi_md *efi_md_next(struct efi_md *); +struct efi_md *efi_md_prev(struct efi_md *); +void efi_reset_system(void); +int efi_set_time(struct efi_tm *); +int efi_var_get(efi_char *, struct uuid *, uint32_t *, size_t *, void *); +int efi_var_nextname(size_t *, efi_char *, struct uuid *); +int efi_var_set(efi_char *, struct uuid *, uint32_t, size_t, void *); + +#endif /* _KERNEL && __ia64__ */ + +#endif /* _SYS_EFI_H_ */ Property changes on: stable/10/sys/sys/efi.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10 =================================================================== --- stable/10 (revision 270295) +++ stable/10 (revision 270296) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r263815,263872