#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.309 2026/04/08 04:06:41 thorpej Exp $");
#include "opt_ddb.h"
#include "opt_multiprocessor.h"
#include "opt_modular.h"
#include "opt_compat_netbsd.h"
#include "opt_compat_sunos.h"
#include <sys/param.h>
#include <sys/extent.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ras.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/syscallargs.h>
#include <sys/exec.h>
#include <sys/ucontext.h>
#include <sys/cpu.h>
#include <sys/module.h>
#include <sys/ksyms.h>
#include <sys/pserialize.h>
#include <sys/exec_aout.h>
#include <ddb/db_active.h>
#include <dev/mm.h>
#include <uvm/uvm.h>
#include <sys/sysctl.h>
#ifndef ELFSIZE
#ifdef __arch64__
#define ELFSIZE 64
#else
#define ELFSIZE 32
#endif
#endif
#include <sys/exec_elf.h>
#define _SPARC_BUS_DMA_PRIVATE
#include <machine/autoconf.h>
#include <sys/bus.h>
#include <sys/kprintf.h>
#include <machine/frame.h>
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/openfirm.h>
#include <machine/sparc64.h>
#include <sparc64/sparc64/cache.h>
#include "ksyms.h"
int bus_space_debug = 0;
#ifdef DEBUG
#define DPRINTF(l, s) do { if (bus_space_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif
#if defined(COMPAT_16) || defined(COMPAT_SUNOS)
#ifdef DEBUG
int sigdebug = 0x0;
int sigpid = 0;
#endif
#endif
extern vaddr_t avail_end;
#ifdef MODULAR
vaddr_t module_start, module_end;
static struct vm_map module_map_store;
#endif
#ifndef MAX_DMA_SEGS
#define MAX_DMA_SEGS 20
#endif
void dumpsys(void);
void stackdump(void);
void
cpu_startup(void)
{
#ifdef DEBUG
extern int pmapdebug;
int opmapdebug = pmapdebug;
#endif
char pbuf[9];
#ifdef DEBUG
pmapdebug = 0;
#endif
printf("%s%s", copyright, version);
format_bytes(pbuf, sizeof(pbuf), ctob((uint64_t)physmem));
printf("total memory = %s\n", pbuf);
#ifdef DEBUG
pmapdebug = opmapdebug;
#endif
format_bytes(pbuf, sizeof(pbuf), ptoa(uvm_availmem(false)));
printf("avail memory = %s\n", pbuf);
#if 0
pmap_redzone();
#endif
#ifdef MODULAR
uvm_map_setup(&module_map_store, module_start, module_end, 0);
module_map_store.pmap = pmap_kernel();
module_map = &module_map_store;
#endif
}
#ifdef __arch64__
#define STACK_OFFSET BIAS
#undef CCFSZ
#define CCFSZ CC64FSZ
#else
#define STACK_OFFSET 0
#endif
void
setregs(struct lwp *l, struct exec_package *pack, vaddr_t stack)
{
struct trapframe64 *tf = l->l_md.md_tf;
struct fpstate64 *fs;
int64_t tstate;
int pstate = PSTATE_USER;
#ifdef __arch64__
Elf_Ehdr *eh = pack->ep_hdr;
#endif
l->l_proc->p_flag &= ~PK_32;
l->l_proc->p_md.md_flags &= ~MDP_FIXALIGN;
#ifdef __arch64__
switch ((eh->e_flags & EF_SPARCV9_MM)) {
default:
printf("Unknown memory model %d\n",
(eh->e_flags & EF_SPARCV9_MM));
case EF_SPARCV9_TSO:
pstate = PSTATE_MM_TSO|PSTATE_IE;
break;
case EF_SPARCV9_PSO:
pstate = PSTATE_MM_PSO|PSTATE_IE;
break;
case EF_SPARCV9_RMO:
pstate = PSTATE_MM_RMO|PSTATE_IE;
break;
}
#endif
tstate = ((int64_t)ASI_PRIMARY_NO_FAULT << TSTATE_ASI_SHIFT) |
(pstate << TSTATE_PSTATE_SHIFT) | (tf->tf_tstate & TSTATE_CWP);
if ((fs = l->l_md.md_fpstate) != NULL) {
fpusave_lwp(l, false);
pool_cache_put(fpstate_cache, fs);
l->l_md.md_fpstate = NULL;
}
memset(tf, 0, sizeof *tf);
tf->tf_tstate = tstate;
tf->tf_global[1] = l->l_proc->p_psstrp;
tf->tf_global[4] = 0;
tf->tf_pc = pack->ep_entry & ~3;
tf->tf_npc = tf->tf_pc + 4;
stack -= sizeof(struct rwindow);
tf->tf_out[6] = stack - STACK_OFFSET;
tf->tf_out[7] = 0UL;
#ifdef NOTDEF_DEBUG
printf("setregs: setting tf %p sp %p pc %p\n", (long)tf,
(long)tf->tf_out[6], (long)tf->tf_pc);
#ifdef DDB
Debugger();
#endif
#endif
}
static int
sysctl_machdep_boot(SYSCTLFN_ARGS)
{
struct sysctlnode node = *rnode;
char bootpath[256];
const char *cp = NULL;
extern char ofbootpath[], *ofbootpartition, *ofbootfile, *ofbootflags;
switch (node.sysctl_num) {
case CPU_BOOTED_KERNEL:
cp = ofbootfile;
if (cp == NULL || cp[0] == '\0')
cp = "netbsd";
break;
case CPU_BOOT_ARGS:
cp = ofbootflags;
break;
case CPU_BOOTED_DEVICE:
if (ofbootpartition) {
snprintf(bootpath, sizeof(bootpath), "%s:%s",
ofbootpath, ofbootpartition);
cp = bootpath;
} else {
cp = ofbootpath;
}
break;
}
if (cp == NULL || cp[0] == '\0')
return (ENOENT);
node.sysctl_data = __UNCONST(cp);
node.sysctl_size = strlen(cp) + 1;
return (sysctl_lookup(SYSCTLFN_CALL(&node)));
}
static int
get_vis(void)
{
int vis = 0;
if ( CPU_ISSUN4V ) {
vis = 2;
} else {
if (GETVER_CPU_MANUF() == MANUF_FUJITSU) {
if (GETVER_CPU_IMPL() >= IMPL_SPARC64_III) {
vis = 1;
}
} else {
vis = 1;
if (CPU_IS_USIII_UP()) {
vis = 2;
}
}
}
return vis;
}
SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
{
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_NODE, "machdep", NULL,
NULL, 0, NULL, 0,
CTL_MACHDEP, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRING, "booted_kernel", NULL,
sysctl_machdep_boot, 0, NULL, 0,
CTL_MACHDEP, CPU_BOOTED_KERNEL, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRING, "boot_args", NULL,
sysctl_machdep_boot, 0, NULL, 0,
CTL_MACHDEP, CPU_BOOT_ARGS, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRING, "booted_device", NULL,
sysctl_machdep_boot, 0, NULL, 0,
CTL_MACHDEP, CPU_BOOTED_DEVICE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
CTLTYPE_INT, "cpu_arch", NULL,
NULL, 9, NULL, 0,
CTL_MACHDEP, CPU_ARCH, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
CTLTYPE_INT, "vis",
SYSCTL_DESCR("supported version of VIS instruction set"),
NULL, get_vis(), NULL, 0,
CTL_MACHDEP, CPU_VIS, CTL_EOL);
}
void *
getframe(struct lwp *l, int sig, int *onstack)
{
struct proc *p = l->l_proc;
struct trapframe64 *tf = l->l_md.md_tf;
*onstack = (l->l_sigstk.ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0
&& (SIGACTION(p, sig).sa_flags & SA_ONSTACK) != 0;
if (*onstack)
return ((char *)l->l_sigstk.ss_sp + l->l_sigstk.ss_size);
else
return (void *)((uintptr_t)tf->tf_out[6] + STACK_OFFSET);
}
struct sigframe_siginfo {
siginfo_t sf_si;
ucontext_t sf_uc;
};
void
sendsig_siginfo(const ksiginfo_t *ksi, const sigset_t *mask)
{
struct lwp *l = curlwp;
struct proc *p = l->l_proc;
struct sigacts *ps = p->p_sigacts;
int onstack, error;
int sig = ksi->ksi_signo;
ucontext_t uc;
long ucsz;
struct sigframe_siginfo *fp = getframe(l, sig, &onstack);
sig_t catcher = SIGACTION(p, sig).sa_handler;
struct trapframe64 *tf = l->l_md.md_tf;
struct rwindow *newsp;
register_t sp;
fp = (void *)((u_long)(fp - 1) & ~0x0f);
memset(&uc, 0, sizeof(uc));
uc.uc_flags = _UC_SIGMASK |
((l->l_sigstk.ss_flags & SS_ONSTACK)
? _UC_SETSTACK : _UC_CLRSTACK);
uc.uc_sigmask = *mask;
uc.uc_link = l->l_ctxlink;
sendsig_reset(l, sig);
mutex_exit(p->p_lock);
cpu_getmcontext(l, &uc.uc_mcontext, &uc.uc_flags);
ucsz = (char *)&uc.__uc_pad - (char *)&uc;
newsp = (struct rwindow *)((u_long)fp - CCFSZ);
sp = (register_t)(uintptr_t)tf->tf_out[6];
error = (copyout(&ksi->ksi_info, &fp->sf_si,
sizeof(ksi->ksi_info)) != 0 ||
copyout(&uc, &fp->sf_uc, ucsz) != 0 ||
copyout(&sp, &newsp->rw_in[6], sizeof(sp)) != 0);
mutex_enter(p->p_lock);
if (error) {
sigexit(l, SIGILL);
}
tf->tf_pc = (const vaddr_t)catcher;
tf->tf_npc = (const vaddr_t)catcher + 4;
tf->tf_out[0] = sig;
tf->tf_out[1] = (vaddr_t)&fp->sf_si;
tf->tf_out[2] = (vaddr_t)&fp->sf_uc;
tf->tf_out[6] = (vaddr_t)newsp - STACK_OFFSET;
tf->tf_out[7] = (vaddr_t)ps->sa_sigdesc[sig].sd_tramp - 8;
if (onstack)
l->l_sigstk.ss_flags |= SS_ONSTACK;
}
struct pcb dumppcb;
static void
maybe_dump(int howto)
{
int s;
s = splhigh();
if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP)
dumpsys();
splx(s);
}
void
cpu_reboot(int howto, char *user_boot_string)
{
static bool syncdone = false;
int i;
static char str[128];
struct lwp *l;
l = (curlwp == NULL) ? &lwp0 : curlwp;
if (cold) {
howto |= RB_HALT;
goto haltsys;
}
#if NFB > 0
fb_unblank();
#endif
boothowto = howto;
maybe_dump(howto);
if ((howto & RB_NOSYNC) == 0 && panicstr == NULL) {
if (!syncdone) {
syncdone = true;
vfs_sync_all(l);
}
while (vfs_unmountall1(l, false, false) ||
config_detach_all(boothowto) ||
vfs_unmount_forceone(l))
;
} else {
if (!db_active)
suspendsched();
}
pmf_system_shutdown(boothowto);
splhigh();
haltsys:
doshutdownhooks();
#ifdef MULTIPROCESSOR
mp_halt_cpus();
#endif
if ((howto & RB_POWERDOWN) == RB_POWERDOWN) {
#ifdef MULTIPROCESSOR
printf("cpu%d: powered down\n\n", cpu_number());
#else
printf("powered down\n\n");
#endif
OF_poweroff();
printf("WARNING: powerdown failed!\n");
}
if (howto & RB_HALT) {
#ifdef MULTIPROCESSOR
printf("cpu%d: halted\n\n", cpu_number());
#else
printf("halted\n\n");
#endif
OF_exit();
panic("PROM exit failed");
}
#ifdef MULTIPROCESSOR
printf("cpu%d: rebooting\n\n", cpu_number());
#else
printf("rebooting\n\n");
#endif
if (user_boot_string && *user_boot_string) {
i = strlen(user_boot_string);
if (i > sizeof(str))
OF_boot(user_boot_string);
memcpy(str, user_boot_string, i);
} else {
i = 1;
str[0] = '\0';
}
if (howto & RB_SINGLE)
str[i++] = 's';
if (howto & RB_KDB)
str[i++] = 'd';
if (i > 1) {
if (str[0] == '\0')
str[0] = '-';
str[i] = 0;
} else
str[0] = 0;
OF_boot(str);
panic("cpu_reboot -- failed");
}
uint32_t dumpmag = 0x8fca0101;
int dumpsize = 0;
long dumplo = 0;
void
cpu_dumpconf(void)
{
int nblks, dumpblks;
if (dumpdev == NODEV)
return;
nblks = bdev_size(dumpdev);
dumpblks = ctod(physmem) + pmap_dumpsize();
if (dumpblks > (nblks - ctod(1)))
return;
dumplo = nblks - dumpblks;
dumpsize = physmem;
}
#define BYTES_PER_DUMP MAXPHYS
static vaddr_t dumpspace;
void *
reserve_dumppages(void *p)
{
dumpspace = (vaddr_t)p;
return (char *)p + BYTES_PER_DUMP;
}
void
dumpsys(void)
{
const struct bdevsw *bdev;
int psize;
daddr_t blkno;
int (*dump)(dev_t, daddr_t, void *, size_t);
int j, error = 0;
uint64_t todo;
struct mem_region *mp;
memset(&dumppcb, 0, sizeof(struct pcb));
snapshot(&dumppcb);
stackdump();
if (dumpdev == NODEV)
return;
bdev = bdevsw_lookup(dumpdev);
if (bdev == NULL || bdev->d_psize == NULL)
return;
if (dumpsize == 0)
cpu_dumpconf();
if (!dumpspace) {
printf("\nno address space available, dump not possible\n");
return;
}
if (dumplo <= 0) {
printf("\ndump to dev %" PRId32 ",%" PRId32 " not possible ("
"partition too small?)\n", major(dumpdev), minor(dumpdev));
return;
}
printf("\ndumping to dev %" PRId32 ",%" PRId32 " offset %ld\n",
major(dumpdev), minor(dumpdev), dumplo);
psize = bdev_size(dumpdev);
if (psize == -1) {
printf("dump area unavailable\n");
return;
}
blkno = dumplo;
dump = bdev->d_dump;
error = pmap_dumpmmu(dump, blkno);
blkno += pmap_dumpsize();
for (todo = 0, j = 0; j < phys_installed_size; j++)
todo += phys_installed[j].size;
for (mp = &phys_installed[0], j = 0; j < phys_installed_size;
j++, mp = &phys_installed[j]) {
uint64_t i = 0, n, off;
paddr_t maddr = mp->start;
for (; i < mp->size; i += n) {
n = mp->size - i;
if (n > BYTES_PER_DUMP)
n = BYTES_PER_DUMP;
if ((todo % (1024*1024)) == 0)
printf_flags(TOCONS|NOTSTAMP,
"\r%6" PRIu64 " M ",
todo / (1024*1024));
for (off = 0; off < n; off += PAGE_SIZE)
pmap_kenter_pa(dumpspace+off, maddr+off,
VM_PROT_READ, 0);
error = (*dump)(dumpdev, blkno,
(void *)dumpspace, (size_t)n);
pmap_kremove(dumpspace, n);
if (error)
break;
maddr += n;
todo -= n;
blkno += btodb(n);
}
}
switch (error) {
case ENXIO:
printf("- device bad\n");
break;
case EFAULT:
printf("- device not ready\n");
break;
case EINVAL:
printf("- area improper\n");
break;
case EIO:
printf("- i/o error\n");
break;
case 0:
printf_flags(TOCONS|NOTSTAMP, "\r ");
printf("\ndump succeeded\n");
break;
default:
printf("- error %d\n", error);
break;
}
}
void trapdump(struct trapframe64*);
void
trapdump(struct trapframe64* tf)
{
printf("TRAPFRAME: tstate=%llx pc=%llx npc=%llx y=%x\n",
(unsigned long long)tf->tf_tstate, (unsigned long long)tf->tf_pc,
(unsigned long long)tf->tf_npc, (unsigned)tf->tf_y);
printf("%%g1-7: %llx %llx %llx %llx %llx %llx %llx\n",
(unsigned long long)tf->tf_global[1],
(unsigned long long)tf->tf_global[2],
(unsigned long long)tf->tf_global[3],
(unsigned long long)tf->tf_global[4],
(unsigned long long)tf->tf_global[5],
(unsigned long long)tf->tf_global[6],
(unsigned long long)tf->tf_global[7]);
printf("%%o0-7: %llx %llx %llx %llx\n %llx %llx %llx %llx\n",
(unsigned long long)tf->tf_out[0],
(unsigned long long)tf->tf_out[1],
(unsigned long long)tf->tf_out[2],
(unsigned long long)tf->tf_out[3],
(unsigned long long)tf->tf_out[4],
(unsigned long long)tf->tf_out[5],
(unsigned long long)tf->tf_out[6],
(unsigned long long)tf->tf_out[7]);
}
static void
get_symbol_and_offset(const char **mod, const char **sym, vaddr_t *offset, vaddr_t pc)
{
static char symbuf[256];
unsigned long symaddr;
int s, error;
#if NKSYMS || defined(DDB) || defined(MODULAR)
s = pserialize_read_enter();
if (ksyms_getname(mod, sym, pc,
KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY) == 0) {
error = ksyms_getval(*mod, *sym, &symaddr,
KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY);
pserialize_read_exit(s);
if (error)
goto failed;
*offset = (vaddr_t)(pc - symaddr);
return;
}
pserialize_read_exit(s);
#endif
failed:
snprintf(symbuf, sizeof symbuf, "%llx", (unsigned long long)pc);
*mod = "netbsd";
*sym = symbuf;
*offset = 0;
}
void
stackdump(void)
{
struct frame32 *fp = (struct frame32 *)getfp(), *sfp;
struct frame64 *fp64;
const char *mod, *sym;
vaddr_t offset;
sfp = fp;
printf("Frame pointer is at %p\n", fp);
printf("Call traceback:\n");
while (fp && ((u_long)fp >> PGSHIFT) == ((u_long)sfp >> PGSHIFT)) {
if( ((long)fp) & 1 ) {
fp64 = (struct frame64*)(((char*)fp)+BIAS);
get_symbol_and_offset(&mod, &sym, &offset, fp64->fr_pc);
printf(" %s:%s+%#llx(%llx, %llx, %llx, %llx, %llx, %llx) fp = %llx\n",
mod, sym,
(unsigned long long)offset,
(unsigned long long)fp64->fr_arg[0],
(unsigned long long)fp64->fr_arg[1],
(unsigned long long)fp64->fr_arg[2],
(unsigned long long)fp64->fr_arg[3],
(unsigned long long)fp64->fr_arg[4],
(unsigned long long)fp64->fr_arg[5],
(unsigned long long)fp64->fr_fp);
fp = (struct frame32 *)(u_long)fp64->fr_fp;
} else {
get_symbol_and_offset(&mod, &sym, &offset, fp->fr_pc);
printf(" %s:%s+%#lx(%x, %x, %x, %x, %x, %x) fp = %x\n",
mod, sym,
(unsigned long)offset,
fp->fr_arg[0],
fp->fr_arg[1],
fp->fr_arg[2],
fp->fr_arg[3],
fp->fr_arg[4],
fp->fr_arg[5],
fp->fr_fp);
fp = (struct frame32*)(u_long)fp->fr_fp;
}
}
}
int
cpu_exec_aout_makecmds(struct lwp *l, struct exec_package *epp)
{
return (ENOEXEC);
}
static size_t
_bus_dmamap_mapsize(int const nsegments)
{
KASSERT(nsegments > 0);
return sizeof(struct sparc_bus_dmamap) +
(sizeof(bus_dma_segment_t) * (nsegments - 1));
}
int
_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
bus_size_t maxsegsz, bus_size_t boundary, int flags,
bus_dmamap_t *dmamp)
{
struct sparc_bus_dmamap *map;
void *mapstore;
if ((mapstore = kmem_zalloc(_bus_dmamap_mapsize(nsegments),
(flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP)) == NULL)
return (ENOMEM);
map = (struct sparc_bus_dmamap *)mapstore;
map->_dm_size = size;
map->_dm_segcnt = nsegments;
map->_dm_maxmaxsegsz = maxsegsz;
map->_dm_boundary = boundary;
map->_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT|BUS_DMA_COHERENT|
BUS_DMA_NOWRITE|BUS_DMA_NOCACHE);
map->dm_maxsegsz = maxsegsz;
map->dm_mapsize = 0;
map->dm_nsegs = 0;
*dmamp = map;
return (0);
}
void
_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
{
if (map->dm_nsegs)
bus_dmamap_unload(t, map);
kmem_free(map, _bus_dmamap_mapsize(map->_dm_segcnt));
}
int
_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *sbuf,
bus_size_t buflen, struct proc *p, int flags)
{
bus_size_t sgsize;
vaddr_t vaddr = (vaddr_t)sbuf;
long incr;
int i;
map->dm_nsegs = 0;
KASSERT(map->dm_maxsegsz <= map->_dm_maxmaxsegsz);
if (buflen > map->_dm_size)
{
#ifdef DEBUG
printf("_bus_dmamap_load(): error %lu > %lu -- map size exceeded!\n",
(unsigned long)buflen, (unsigned long)map->_dm_size);
#ifdef DDB
Debugger();
#endif
#endif
return (EINVAL);
}
sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
i = 0;
map->dm_segs[i].ds_addr = 0UL;
map->dm_segs[i].ds_len = 0;
incr = PAGE_SIZE - (vaddr & PGOFSET);
while (sgsize > 0) {
paddr_t pa;
incr = uimin(sgsize, incr);
(void) pmap_extract(pmap_kernel(), vaddr, &pa);
if (map->dm_segs[i].ds_len == 0)
map->dm_segs[i].ds_addr = pa;
if (pa == (map->dm_segs[i].ds_addr + map->dm_segs[i].ds_len)
&& ((map->dm_segs[i].ds_len + incr) <= map->dm_maxsegsz)) {
map->dm_segs[i].ds_len += incr;
} else {
if (++i >= map->_dm_segcnt)
return (EFBIG);
map->dm_segs[i].ds_addr = pa;
map->dm_segs[i].ds_len = incr;
}
sgsize -= incr;
vaddr += incr;
incr = PAGE_SIZE;
}
map->dm_nsegs = i + 1;
map->dm_mapsize = buflen;
return (0);
}
int
_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m,
int flags)
{
bus_dma_segment_t segs[MAX_DMA_SEGS];
int i;
size_t len;
map->dm_nsegs = 0;
KASSERT(map->dm_maxsegsz <= map->_dm_maxmaxsegsz);
if (m->m_pkthdr.len > map->_dm_size)
return EINVAL;
map->_dm_type = _DM_TYPE_MBUF;
map->_dm_source = (void *)m;
i = 0;
len = 0;
while (m) {
vaddr_t vaddr = mtod(m, vaddr_t);
long buflen = (long)m->m_len;
len += buflen;
while (buflen > 0 && i < MAX_DMA_SEGS) {
paddr_t pa;
long incr;
incr = PAGE_SIZE - (vaddr & PGOFSET);
incr = uimin(buflen, incr);
if (pmap_extract(pmap_kernel(), vaddr, &pa) == FALSE) {
#ifdef DIAGNOSTIC
printf("_bus_dmamap_load_mbuf: pmap_extract failed %lx\n",
vaddr);
#endif
map->_dm_type = 0;
map->_dm_source = NULL;
return EINVAL;
}
buflen -= incr;
vaddr += incr;
if (i > 0 &&
pa == (segs[i-1].ds_addr + segs[i-1].ds_len) &&
((segs[i-1].ds_len + incr) <=
map->dm_maxsegsz)) {
segs[i-1].ds_len += incr;
continue;
}
segs[i].ds_addr = pa;
segs[i].ds_len = incr;
segs[i]._ds_boundary = 0;
segs[i]._ds_align = 0;
segs[i]._ds_mlist = NULL;
i++;
}
m = m->m_next;
if (m && i >= MAX_DMA_SEGS) {
map->_dm_type = 0;
map->_dm_source = NULL;
return EFBIG;
}
}
#ifdef DEBUG
{
size_t mbuflen, sglen;
int j;
int retval;
mbuflen = 0;
for (m = (struct mbuf *)map->_dm_source; m; m = m->m_next)
mbuflen += (long)m->m_len;
sglen = 0;
for (j = 0; j < i; j++)
sglen += segs[j].ds_len;
if (sglen != mbuflen)
panic("load_mbuf: sglen %ld != mbuflen %lx\n",
sglen, mbuflen);
if (sglen != len)
panic("load_mbuf: sglen %ld != len %lx\n",
sglen, len);
retval = bus_dmamap_load_raw(t, map, segs, i,
(bus_size_t)len, flags);
if (retval == 0) {
if (map->dm_mapsize != len)
panic("load_mbuf: mapsize %ld != len %lx\n",
(long)map->dm_mapsize, len);
sglen = 0;
for (j = 0; j < map->dm_nsegs; j++)
sglen += map->dm_segs[j].ds_len;
if (sglen != len)
panic("load_mbuf: dmamap sglen %ld != len %lx\n",
sglen, len);
}
return (retval);
}
#endif
return (bus_dmamap_load_raw(t, map, segs, i, (bus_size_t)len, flags));
}
int
_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio,
int flags)
{
#if 0
bus_dma_segment_t segs[MAX_DMA_SEGS];
int i, j;
size_t len;
struct proc *p = uio->uio_lwp->l_proc;
struct pmap *pm;
map->dm_nsegs = 0;
KASSERT(map->dm_maxsegsz <= map->_dm_maxmaxsegsz);
if (uio->uio_segflg == UIO_USERSPACE) {
pm = p->p_vmspace->vm_map.pmap;
} else
pm = pmap_kernel();
i = 0;
len = 0;
for (j = 0; j < uio->uio_iovcnt; j++) {
struct iovec *iov = &uio->uio_iov[j];
vaddr_t vaddr = (vaddr_t)iov->iov_base;
bus_size_t buflen = iov->iov_len;
if (__predict_false(uvm_vslock(p->p_vmspace, vaddr, buflen,
(uio->uio_rw == UIO_WRITE) ?
VM_PROT_WRITE : VM_PROT_READ) != 0)) {
goto after_vsunlock;
}
len += buflen;
while (buflen > 0 && i < MAX_DMA_SEGS) {
paddr_t pa;
long incr;
incr = uimin(buflen, PAGE_SIZE);
(void) pmap_extract(pm, vaddr, &pa);
buflen -= incr;
vaddr += incr;
if (segs[i].ds_len == 0)
segs[i].ds_addr = pa;
if (i > 0 && pa == (segs[i-1].ds_addr + segs[i-1].ds_len)
&& ((segs[i-1].ds_len + incr) <= map->dm_maxsegsz)) {
segs[i-1].ds_len += incr;
continue;
}
segs[i].ds_addr = pa;
segs[i].ds_len = incr;
segs[i]._ds_boundary = 0;
segs[i]._ds_align = 0;
segs[i]._ds_mlist = NULL;
i++;
}
uvm_vsunlock(p->p_vmspace, bp->b_data, todo);
if (buflen > 0 && i >= MAX_DMA_SEGS)
return EFBIG;
}
map->_dm_type = DM_TYPE_UIO;
map->_dm_source = (void *)uio;
return (bus_dmamap_load_raw(t, map, segs, i,
(bus_size_t)len, flags));
#endif
return 0;
}
int
_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs,
int nsegs, bus_size_t size, int flags)
{
panic("_bus_dmamap_load_raw: not implemented");
}
void
_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
int i;
struct vm_page *pg;
struct pglist *pglist;
paddr_t pa;
for (i = 0; i < map->dm_nsegs; i++) {
if ((pglist = map->dm_segs[i]._ds_mlist) == NULL) {
blast_dcache();
break;
}
TAILQ_FOREACH(pg, pglist, pageq.queue) {
pa = VM_PAGE_TO_PHYS(pg);
dcache_flush_page_all(pa);
}
}
map->dm_maxsegsz = map->_dm_maxmaxsegsz;
map->dm_mapsize = 0;
map->dm_nsegs = 0;
}
void
_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
bus_size_t len, int ops)
{
int i;
struct vm_page *pg;
struct pglist *pglist;
if ((ops & BUS_DMASYNC_PREREAD) || (ops & BUS_DMASYNC_PREWRITE)) {
membar_Sync();
}
if (ops & BUS_DMASYNC_POSTREAD) {
for (i = 0; i < map->dm_nsegs; i++) {
if ((pglist = map->dm_segs[i]._ds_mlist) == NULL)
continue;
TAILQ_FOREACH(pg, pglist, pageq.queue) {
paddr_t start;
psize_t size = PAGE_SIZE;
if (offset < PAGE_SIZE) {
start = VM_PAGE_TO_PHYS(pg) + offset;
size -= offset;
if (size > len)
size = len;
cache_flush_phys(start, size, 0);
len -= size;
if (len == 0)
goto done;
offset = 0;
continue;
}
offset -= size;
}
}
}
done:
if (ops & BUS_DMASYNC_POSTWRITE) {
}
}
extern paddr_t vm_first_phys, vm_num_phys;
int
_bus_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
int flags)
{
vaddr_t low, high;
struct pglist *pglist;
int error;
size = round_page(size);
low = vm_first_phys;
high = vm_first_phys + vm_num_phys - PAGE_SIZE;
if ((pglist = kmem_alloc(sizeof(*pglist),
(flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP)) == NULL)
return (ENOMEM);
segs[0]._ds_boundary = boundary;
segs[0]._ds_align = alignment;
if (flags & BUS_DMA_DVMA) {
boundary = 0;
alignment = 0;
}
error = uvm_pglistalloc(size, low, high,
alignment, boundary, pglist, nsegs, (flags & BUS_DMA_NOWAIT) == 0);
if (error) {
kmem_free(pglist, sizeof(*pglist));
return (error);
}
segs[0].ds_addr = 0UL;
segs[0].ds_len = size;
*rsegs = 1;
segs[0]._ds_mlist = pglist;
return (0);
}
void
_bus_dmamem_free(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs)
{
struct pglist *pglist = segs[0]._ds_mlist;
if (nsegs != 1)
panic("bus_dmamem_free: nsegs = %d", nsegs);
uvm_pglistfree(pglist);
kmem_free(pglist, sizeof(*pglist));
}
int
_bus_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
size_t size, void **kvap, int flags)
{
vaddr_t va, sva;
int r;
size_t oversize;
u_long align;
if (nsegs != 1)
panic("_bus_dmamem_map: nsegs = %d", nsegs);
align = PAGE_SIZE;
size = round_page(size);
oversize = size + align - PAGE_SIZE;
r = uvm_map(kernel_map, &sva, oversize, NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE,
UVM_ADV_NORMAL, 0));
if (r != 0)
return (ENOMEM);
va = sva;
va += ((segs[0].ds_addr & (align - 1)) + align - va) & (align - 1);
if (va != sva)
uvm_unmap(kernel_map, sva, va);
if (va + size != sva + oversize)
uvm_unmap(kernel_map, va + size, sva + oversize);
*kvap = (void *)va;
return (0);
}
void
_bus_dmamem_unmap(bus_dma_tag_t t, void *kva, size_t size)
{
#ifdef DIAGNOSTIC
if ((u_long)kva & PGOFSET)
panic("_bus_dmamem_unmap");
#endif
size = round_page(size);
uvm_unmap(kernel_map, (vaddr_t)kva, (vaddr_t)kva + size);
}
paddr_t
_bus_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, off_t off,
int prot, int flags)
{
int i;
for (i = 0; i < nsegs; i++) {
#ifdef DIAGNOSTIC
if (off & PGOFSET)
panic("_bus_dmamem_mmap: offset unaligned");
if (segs[i].ds_addr & PGOFSET)
panic("_bus_dmamem_mmap: segment unaligned");
if (segs[i].ds_len & PGOFSET)
panic("_bus_dmamem_mmap: segment size not multiple"
" of page size");
#endif
if (off >= segs[i].ds_len) {
off -= segs[i].ds_len;
continue;
}
return (atop(segs[i].ds_addr + off));
}
return (-1);
}
struct sparc_bus_dma_tag mainbus_dma_tag = {
NULL,
NULL,
_bus_dmamap_create,
_bus_dmamap_destroy,
_bus_dmamap_load,
_bus_dmamap_load_mbuf,
_bus_dmamap_load_uio,
_bus_dmamap_load_raw,
_bus_dmamap_unload,
_bus_dmamap_sync,
_bus_dmamem_alloc,
_bus_dmamem_free,
_bus_dmamem_map,
_bus_dmamem_unmap,
_bus_dmamem_mmap
};
static int sparc_bus_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
vaddr_t, bus_space_handle_t *);
static int sparc_bus_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
static int sparc_bus_subregion(bus_space_tag_t, bus_space_handle_t, bus_size_t,
bus_size_t, bus_space_handle_t *);
static paddr_t sparc_bus_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int);
static void *sparc_mainbus_intr_establish(bus_space_tag_t, int, int,
int (*)(void *), void *, void (*)(void));
static int sparc_bus_alloc(bus_space_tag_t, bus_addr_t, bus_addr_t, bus_size_t,
bus_size_t, bus_size_t, int, bus_addr_t *, bus_space_handle_t *);
static void sparc_bus_free(bus_space_tag_t, bus_space_handle_t, bus_size_t);
struct extent *io_space = NULL;
int
bus_space_alloc(bus_space_tag_t t, bus_addr_t rs, bus_addr_t re, bus_size_t s,
bus_size_t a, bus_size_t b, int f, bus_addr_t *ap,
bus_space_handle_t *hp)
{
_BS_CALL(t, sparc_bus_alloc)(t, rs, re, s, a, b, f, ap, hp);
}
void
bus_space_free(bus_space_tag_t t, bus_space_handle_t h, bus_size_t s)
{
_BS_CALL(t, sparc_bus_free)(t, h, s);
}
int
bus_space_map(bus_space_tag_t t, bus_addr_t a, bus_size_t s, int f,
bus_space_handle_t *hp)
{
_BS_CALL(t, sparc_bus_map)(t, a, s, f, 0, hp);
}
void
bus_space_unmap(bus_space_tag_t t, bus_space_handle_t h, bus_size_t s)
{
_BS_VOID_CALL(t, sparc_bus_unmap)(t, h, s);
}
int
bus_space_subregion(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
bus_size_t s, bus_space_handle_t *hp)
{
_BS_CALL(t, sparc_bus_subregion)(t, h, o, s, hp);
}
paddr_t
bus_space_mmap(bus_space_tag_t t, bus_addr_t a, off_t o, int p, int f)
{
_BS_CALL(t, sparc_bus_mmap)(t, a, o, p, f);
}
void
bus_space_read_multi_1(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint8_t * a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_1(t, h, o);
}
void
bus_space_read_multi_2(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint16_t * a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_2(t, h, o);
}
void
bus_space_read_multi_4(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint32_t * a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_4(t, h, o);
}
void
bus_space_read_multi_8(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint64_t * a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_8(t, h, o);
}
void
bus_space_write_multi_1(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint8_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_1(t, h, o, *a++);
}
void
bus_space_write_multi_2(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint16_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_2(t, h, o, *a++);
}
void
bus_space_write_multi_4(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint32_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_4(t, h, o, *a++);
}
void
bus_space_write_multi_8(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint64_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_8(t, h, o, *a++);
}
void
bus_space_set_multi_stream_1(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint8_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_1(t, h, o, v);
}
void
bus_space_set_multi_stream_2(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint16_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_2(t, h, o, v);
}
void
bus_space_set_multi_stream_4(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint32_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_4(t, h, o, v);
}
void
bus_space_set_multi_stream_8(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint64_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_8(t, h, o, v);
}
void
bus_space_copy_region_stream_1(bus_space_tag_t t, bus_space_handle_t h1,
bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1++, o2++)
bus_space_write_stream_1(t, h1, o1, bus_space_read_stream_1(t, h2, o2));
}
void
bus_space_copy_region_stream_2(bus_space_tag_t t, bus_space_handle_t h1,
bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1+=2, o2+=2)
bus_space_write_stream_2(t, h1, o1, bus_space_read_stream_2(t, h2, o2));
}
void
bus_space_copy_region_stream_4(bus_space_tag_t t, bus_space_handle_t h1,
bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1+=4, o2+=4)
bus_space_write_stream_4(t, h1, o1, bus_space_read_stream_4(t, h2, o2));
}
void
bus_space_copy_region_stream_8(bus_space_tag_t t, bus_space_handle_t h1,
bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1+=8, o2+=8)
bus_space_write_stream_8(t, h1, o1, bus_space_read_8(t, h2, o2));
}
void
bus_space_set_region_stream_1(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint8_t v, bus_size_t c)
{
for (; c; c--, o++)
bus_space_write_stream_1(t, h, o, v);
}
void
bus_space_set_region_stream_2(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint16_t v, bus_size_t c)
{
for (; c; c--, o+=2)
bus_space_write_stream_2(t, h, o, v);
}
void
bus_space_set_region_stream_4(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint32_t v, bus_size_t c)
{
for (; c; c--, o+=4)
bus_space_write_stream_4(t, h, o, v);
}
void
bus_space_set_region_stream_8(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint64_t v, bus_size_t c)
{
for (; c; c--, o+=8)
bus_space_write_stream_8(t, h, o, v);
}
void
bus_space_read_multi_stream_1(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
uint8_t *a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_stream_1(t, h, o);
}
void
bus_space_read_multi_stream_2(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
uint16_t *a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_stream_2(t, h, o);
}
void
bus_space_read_multi_stream_4(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
uint32_t *a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_stream_4(t, h, o);
}
void
bus_space_read_multi_stream_8(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
uint64_t *a, bus_size_t c)
{
while (c-- > 0)
*a++ = bus_space_read_stream_8(t, h, o);
}
void
bus_space_read_region_stream_1(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint8_t *a, bus_size_t c)
{
for (; c; a++, c--, o++)
*a = bus_space_read_stream_1(t, h, o);
}
void
bus_space_read_region_stream_2(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint16_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=2)
*a = bus_space_read_stream_2(t, h, o);
}
void
bus_space_read_region_stream_4(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint32_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=4)
*a = bus_space_read_stream_4(t, h, o);
}
void
bus_space_read_region_stream_8(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, uint64_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=8)
*a = bus_space_read_stream_8(t, h, o);
}
void
bus_space_write_multi_stream_1(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint8_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_1(t, h, o, *a++);
}
void
bus_space_write_multi_stream_2(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint16_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_2(t, h, o, *a++);
}
void
bus_space_write_multi_stream_4(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint32_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_4(t, h, o, *a++);
}
void
bus_space_write_multi_stream_8(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o,
const uint64_t *a, bus_size_t c)
{
while (c-- > 0)
bus_space_write_stream_8(t, h, o, *a++);
}
void
bus_space_copy_region_1(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1++, o2++)
bus_space_write_1(t, h1, o1, bus_space_read_1(t, h2, o2));
}
void
bus_space_copy_region_2(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1+=2, o2+=2)
bus_space_write_2(t, h1, o1, bus_space_read_2(t, h2, o2));
}
void
bus_space_copy_region_4(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1+=4, o2+=4)
bus_space_write_4(t, h1, o1, bus_space_read_4(t, h2, o2));
}
void
bus_space_copy_region_8(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
for (; c; c--, o1+=8, o2+=8)
bus_space_write_8(t, h1, o1, bus_space_read_8(t, h2, o2));
}
void
bus_space_set_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint8_t v, bus_size_t c)
{
for (; c; c--, o++)
bus_space_write_1(t, h, o, v);
}
void
bus_space_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint16_t v, bus_size_t c)
{
for (; c; c--, o+=2)
bus_space_write_2(t, h, o, v);
}
void
bus_space_set_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint32_t v, bus_size_t c)
{
for (; c; c--, o+=4)
bus_space_write_4(t, h, o, v);
}
void
bus_space_set_region_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint64_t v, bus_size_t c)
{
for (; c; c--, o+=8)
bus_space_write_8(t, h, o, v);
}
void
bus_space_set_multi_1(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint8_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_1(t, h, o, v);
}
void
bus_space_set_multi_2(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint16_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_2(t, h, o, v);
}
void
bus_space_set_multi_4(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint32_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_4(t, h, o, v);
}
void
bus_space_set_multi_8(bus_space_tag_t t,
bus_space_handle_t h, bus_size_t o, uint64_t v,
bus_size_t c)
{
while (c-- > 0)
bus_space_write_8(t, h, o, v);
}
void
bus_space_write_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint8_t *a, bus_size_t c)
{
for (; c; a++, c--, o++)
bus_space_write_1(t, h, o, *a);
}
void
bus_space_write_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint16_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=2)
bus_space_write_2(t, h, o, *a);
}
void
bus_space_write_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint32_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=4)
bus_space_write_4(t, h, o, *a);
}
void
bus_space_write_region_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
const uint64_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=8)
bus_space_write_8(t, h, o, *a);
}
void
bus_space_read_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
uint8_t *a, bus_size_t c)
{
for (; c; a++, c--, o++)
*a = bus_space_read_1(t, h, o);
}
void
bus_space_read_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
uint16_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=2)
*a = bus_space_read_2(t, h, o);
}
void
bus_space_read_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
uint32_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=4)
*a = bus_space_read_4(t, h, o);
}
void
bus_space_read_region_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
uint64_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=8)
*a = bus_space_read_8(t, h, o);
}
void
bus_space_write_region_stream_1(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint8_t *a, bus_size_t c)
{
for (; c; a++, c--, o++)
bus_space_write_stream_1(t, h, o, *a);
}
void
bus_space_write_region_stream_2(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint16_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=2)
bus_space_write_stream_2(t, h, o, *a);
}
void
bus_space_write_region_stream_4(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint32_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=4)
bus_space_write_stream_4(t, h, o, *a);
}
void
bus_space_write_region_stream_8(bus_space_tag_t t, bus_space_handle_t h,
bus_size_t o, const uint64_t *a, bus_size_t c)
{
for (; c; a++, c--, o+=8)
bus_space_write_stream_8(t, h, o, *a);
}
bus_space_tag_t
bus_space_tag_alloc(bus_space_tag_t parent, void *cookie)
{
struct sparc_bus_space_tag *sbt;
sbt = kmem_zalloc(sizeof(*sbt), KM_SLEEP);
if (parent) {
memcpy(sbt, parent, sizeof(*sbt));
sbt->parent = parent;
sbt->ranges = NULL;
sbt->nranges = 0;
}
sbt->cookie = cookie;
return (sbt);
}
int
bus_space_translate_address_generic(struct openprom_range *ranges, int nranges,
bus_addr_t *bap)
{
int i, space = BUS_ADDR_IOSPACE(*bap);
for (i = 0; i < nranges; i++) {
struct openprom_range *rp = &ranges[i];
if (rp->or_child_space != space)
continue;
*bap = BUS_ADDR(rp->or_parent_space,
rp->or_parent_base + BUS_ADDR_PADDR(*bap));
return (0);
}
return (EINVAL);
}
int
sparc_bus_map(bus_space_tag_t t, bus_addr_t addr, bus_size_t size,
int flags, vaddr_t unused, bus_space_handle_t *hp)
{
vaddr_t v;
uint64_t pa;
paddr_t pm_flags = 0;
vm_prot_t pm_prot = VM_PROT_READ;
int err, map_little = 0;
if (io_space == NULL)
io_space = extent_create("IOSPACE",
(u_long)IODEV_BASE, (u_long)IODEV_END,
0, 0, EX_NOWAIT);
size = round_page(size);
if (size == 0) {
printf("sparc_bus_map: zero size\n");
return (EINVAL);
}
switch (t->type) {
case PCI_CONFIG_BUS_SPACE:
if (flags & BUS_SPACE_MAP_LINEAR)
return (-1);
hp->_ptr = addr;
hp->_asi = ASI_PHYS_NON_CACHED_LITTLE;
hp->_sasi = ASI_PHYS_NON_CACHED;
DPRINTF(BSDB_MAP, ("\n%s: config type %x flags %x "
"addr %016llx size %016llx virt %llx\n", __func__,
(int)t->type, (int) flags, (unsigned long long)addr,
(unsigned long long)size,
(unsigned long long)hp->_ptr));
return (0);
case PCI_IO_BUS_SPACE:
map_little = 1;
break;
case PCI_MEMORY_BUS_SPACE:
map_little = 1;
break;
default:
map_little = 0;
break;
}
#ifdef _LP64
if (!CPU_ISSUN4V) {
if ((flags & BUS_SPACE_MAP_LINEAR) == 0) {
hp->_ptr = addr;
if (map_little)
hp->_asi = ASI_PHYS_NON_CACHED_LITTLE;
else
hp->_asi = ASI_PHYS_NON_CACHED;
hp->_sasi = ASI_PHYS_NON_CACHED;
return (0);
}
}
#endif
if (!(flags & BUS_SPACE_MAP_CACHEABLE))
pm_flags |= PMAP_NC;
if ((flags & BUS_SPACE_MAP_PREFETCHABLE))
pm_flags |= PMAP_WC;
if ((err = extent_alloc(io_space, size, PAGE_SIZE,
0, EX_NOWAIT|EX_BOUNDZERO, (u_long *)&v)))
panic("sparc_bus_map: cannot allocate io_space: %d", err);
hp->_ptr = (v | ((u_long)addr & PGOFSET));
hp->_sasi = ASI_PRIMARY;
if (map_little)
hp->_asi = ASI_PRIMARY_LITTLE;
else
hp->_asi = ASI_PRIMARY;
pa = trunc_page(addr);
if (!(flags&BUS_SPACE_MAP_READONLY))
pm_prot |= VM_PROT_WRITE;
DPRINTF(BSDB_MAP, ("\n%s: type %x flags %x addr %016llx prot %02x "
"pm_flags %x size %016llx virt %llx paddr %016llx\n", __func__,
(int)t->type, (int)flags, (unsigned long long)addr, pm_prot,
(int)pm_flags, (unsigned long long)size,
(unsigned long long)hp->_ptr, (unsigned long long)pa));
do {
DPRINTF(BSDB_MAP, ("%s: phys %llx virt %p hp %llx\n",
__func__,
(unsigned long long)pa, (char *)v,
(unsigned long long)hp->_ptr));
pmap_kenter_pa(v, pa | pm_flags, pm_prot, 0);
v += PAGE_SIZE;
pa += PAGE_SIZE;
} while ((size -= PAGE_SIZE) > 0);
return (0);
}
int
sparc_bus_subregion(bus_space_tag_t tag, bus_space_handle_t handle,
bus_size_t offset, bus_size_t size, bus_space_handle_t *nhandlep)
{
nhandlep->_ptr = handle._ptr + offset;
nhandlep->_asi = handle._asi;
nhandlep->_sasi = handle._sasi;
return (0);
}
int
sparc_bus_unmap(bus_space_tag_t t, bus_space_handle_t bh, bus_size_t size)
{
vaddr_t va = trunc_page((vaddr_t)bh._ptr);
vaddr_t endva = va + round_page(size);
int error = 0;
if (PHYS_ASI(bh._asi)) return (0);
error = extent_free(io_space, va, size, EX_NOWAIT);
if (error) printf("sparc_bus_unmap: extent_free returned %d\n", error);
pmap_remove(pmap_kernel(), va, endva);
return (0);
}
paddr_t
sparc_bus_mmap(bus_space_tag_t t, bus_addr_t paddr, off_t off, int prot,
int flags)
{
paddr_t pa;
pa = (paddr + off) | PMAP_NC;
if (flags & BUS_SPACE_MAP_LITTLE)
pa |= PMAP_LITTLE;
if (flags & BUS_SPACE_MAP_PREFETCHABLE)
pa |= PMAP_WC;
return pa;
}
void *
sparc_mainbus_intr_establish(bus_space_tag_t t, int pil, int level,
int (*handler)(void *), void *arg, void (*fastvec)(void) )
{
struct intrhand *ih;
ih = intrhand_alloc();
ih->ih_fun = handler;
ih->ih_arg = arg;
intr_establish(pil, level != IPL_VM, ih);
return (ih);
}
int
sparc_bus_alloc(bus_space_tag_t t, bus_addr_t rs, bus_addr_t re, bus_size_t s,
bus_size_t a, bus_size_t b, int f, bus_addr_t *ap, bus_space_handle_t *hp)
{
return (ENOTTY);
}
void
sparc_bus_free(bus_space_tag_t t, bus_space_handle_t h, bus_size_t s)
{
return;
}
struct sparc_bus_space_tag mainbus_space_tag = {
NULL,
NULL,
NULL,
0,
UPA_BUS_SPACE,
sparc_bus_alloc,
sparc_bus_free,
sparc_bus_map,
sparc_bus_unmap,
sparc_bus_subregion,
sparc_bus_mmap,
sparc_mainbus_intr_establish
};
void
cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags)
{
__greg_t *gr = mcp->__gregs;
__greg_t ras_pc;
const struct trapframe64 *tf = l->l_md.md_tf;
write_user_windows();
if (rwindow_save(l)) {
mutex_enter(l->l_proc->p_lock);
sigexit(l, SIGILL);
}
(void)memset(mcp, 0, sizeof (*mcp));
#ifdef __arch64__
gr[_REG_CCR] = (tf->tf_tstate & TSTATE_CCR) >> TSTATE_CCR_SHIFT;
#else
gr[_REG_PSR] = TSTATECCR_TO_PSR(tf->tf_tstate);
#endif
gr[_REG_PC] = tf->tf_pc;
gr[_REG_nPC] = tf->tf_npc;
gr[_REG_Y] = tf->tf_y;
gr[_REG_G1] = tf->tf_global[1];
gr[_REG_G2] = tf->tf_global[2];
gr[_REG_G3] = tf->tf_global[3];
gr[_REG_G4] = tf->tf_global[4];
gr[_REG_G5] = tf->tf_global[5];
gr[_REG_G6] = tf->tf_global[6];
gr[_REG_G7] = tf->tf_global[7];
gr[_REG_O0] = tf->tf_out[0];
gr[_REG_O1] = tf->tf_out[1];
gr[_REG_O2] = tf->tf_out[2];
gr[_REG_O3] = tf->tf_out[3];
gr[_REG_O4] = tf->tf_out[4];
gr[_REG_O5] = tf->tf_out[5];
gr[_REG_O6] = tf->tf_out[6];
gr[_REG_O7] = tf->tf_out[7];
#ifdef __arch64__
gr[_REG_ASI] = (tf->tf_tstate & TSTATE_ASI) >> TSTATE_ASI_SHIFT;
#if 0
gr[_REG_FPRS] = ;
#endif
#endif
if ((ras_pc = (__greg_t)ras_lookup(l->l_proc,
(void *) gr[_REG_PC])) != -1) {
gr[_REG_PC] = ras_pc;
gr[_REG_nPC] = ras_pc + 4;
}
*flags |= (_UC_CPU|_UC_TLSBASE);
mcp->__gwins = NULL;
if (l->l_md.md_fpstate != NULL) {
struct fpstate64 *fsp;
__fpregset_t *fpr = &mcp->__fpregs;
fpusave_lwp(l, true);
fsp = l->l_md.md_fpstate;
memcpy(&fpr->__fpu_fr, fsp->fs_regs, sizeof (fpr->__fpu_fr));
mcp->__fpregs.__fpu_q = NULL;
mcp->__fpregs.__fpu_fsr = fsp->fs_fsr;
mcp->__fpregs.__fpu_qcnt = 0 ;
mcp->__fpregs.__fpu_q_entrysize =
(unsigned char) sizeof (*mcp->__fpregs.__fpu_q);
mcp->__fpregs.__fpu_en = 1;
*flags |= _UC_FPU;
} else {
mcp->__fpregs.__fpu_en = 0;
}
mcp->__xrs.__xrs_id = 0;
}
int
cpu_mcontext_validate(struct lwp *l, const mcontext_t *mc)
{
const __greg_t *gr = mc->__gregs;
if (((gr[_REG_PC] | gr[_REG_nPC]) & 3) != 0 ||
gr[_REG_PC] == 0 || gr[_REG_nPC] == 0)
return EINVAL;
return 0;
}
int
cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
{
const __greg_t *gr = mcp->__gregs;
struct trapframe64 *tf = l->l_md.md_tf;
struct proc *p = l->l_proc;
int error;
write_user_windows();
if (rwindow_save(l)) {
mutex_enter(p->p_lock);
sigexit(l, SIGILL);
}
if ((flags & _UC_CPU) != 0) {
error = cpu_mcontext_validate(l, mcp);
if (error)
return error;
#ifdef __arch64__
tf->tf_tstate = (tf->tf_tstate & ~(TSTATE_CCR | TSTATE_ASI)) |
((gr[_REG_CCR] << TSTATE_CCR_SHIFT) & TSTATE_CCR) |
((gr[_REG_ASI] << TSTATE_ASI_SHIFT) & TSTATE_ASI);
#else
tf->tf_tstate = (tf->tf_tstate & ~TSTATE_CCR) |
PSRCC_TO_TSTATE(gr[_REG_PSR]);
#endif
tf->tf_pc = (uint64_t)gr[_REG_PC];
tf->tf_npc = (uint64_t)gr[_REG_nPC];
tf->tf_y = (uint64_t)gr[_REG_Y];
tf->tf_global[1] = (uint64_t)gr[_REG_G1];
tf->tf_global[2] = (uint64_t)gr[_REG_G2];
tf->tf_global[3] = (uint64_t)gr[_REG_G3];
tf->tf_global[4] = (uint64_t)gr[_REG_G4];
tf->tf_global[5] = (uint64_t)gr[_REG_G5];
tf->tf_global[6] = (uint64_t)gr[_REG_G6];
tf->tf_out[0] = (uint64_t)gr[_REG_O0];
tf->tf_out[1] = (uint64_t)gr[_REG_O1];
tf->tf_out[2] = (uint64_t)gr[_REG_O2];
tf->tf_out[3] = (uint64_t)gr[_REG_O3];
tf->tf_out[4] = (uint64_t)gr[_REG_O4];
tf->tf_out[5] = (uint64_t)gr[_REG_O5];
tf->tf_out[6] = (uint64_t)gr[_REG_O6];
tf->tf_out[7] = (uint64_t)gr[_REG_O7];
if (flags & _UC_TLSBASE)
lwp_setprivate(l, (void *)(uintptr_t)gr[_REG_G7]);
}
if ((flags & _UC_FPU) != 0 && mcp->__fpregs.__fpu_en != 0) {
struct fpstate64 *fsp;
const __fpregset_t *fpr = &mcp->__fpregs;
if ((fsp = l->l_md.md_fpstate) == NULL) {
fsp = pool_cache_get(fpstate_cache, PR_WAITOK);
l->l_md.md_fpstate = fsp;
} else {
fpusave_lwp(l, false);
}
memcpy(fsp->fs_regs, &fpr->__fpu_fr, sizeof (fpr->__fpu_fr));
fsp->fs_fsr = mcp->__fpregs.__fpu_fsr;
fsp->fs_qsize = 0;
#if 0
mcp->__fpregs.__fpu_q = NULL;
mcp->__fpregs.__fpu_qcnt = 0 ;
#endif
}
mutex_enter(p->p_lock);
if (flags & _UC_SETSTACK)
l->l_sigstk.ss_flags |= SS_ONSTACK;
if (flags & _UC_CLRSTACK)
l->l_sigstk.ss_flags &= ~SS_ONSTACK;
mutex_exit(p->p_lock);
return 0;
}
void
cpu_need_resched(struct cpu_info *ci, struct lwp *l, int flags)
{
ci->ci_want_ast = 1;
#ifdef MULTIPROCESSOR
if ((flags & RESCHED_REMOTE) != 0) {
sparc64_send_ipi(ci->ci_cpuid, sparc64_ipi_nop, 0, 0);
}
#endif
}
void
cpu_signotify(struct lwp *l)
{
struct cpu_info *ci = l->l_cpu;
ci->ci_want_ast = 1;
#ifdef MULTIPROCESSOR
if (ci != curcpu()) {
sparc64_send_ipi(ci->ci_cpuid, sparc64_ipi_nop, 0, 0);
}
#endif
}
bool
cpu_intr_p(void)
{
int idepth;
long pctr;
lwp_t *l;
l = curlwp;
do {
pctr = lwp_pctr();
idepth = l->l_cpu->ci_idepth;
} while (__predict_false(pctr != lwp_pctr()));
return idepth >= 0;
}
int
mm_md_physacc(paddr_t pa, vm_prot_t prot)
{
return pmap_pa_exists(pa) ? 0 : EFAULT;
}
int
mm_md_kernacc(void *ptr, vm_prot_t prot, bool *handled)
{
const vaddr_t prom_vstart = 0xf000000, prom_vend = 0xf0100000;
const vaddr_t msgbufpv = (vaddr_t)msgbufp, v = (vaddr_t)ptr;
const size_t msgbufsz = msgbufp->msg_bufs +
offsetof(struct kern_msgbuf, msg_bufc);
*handled = (v >= msgbufpv && v < msgbufpv + msgbufsz) ||
(v >= prom_vstart && v < prom_vend && (prot & VM_PROT_WRITE) == 0);
return 0;
}
int
mm_md_readwrite(dev_t dev, struct uio *uio)
{
return ENXIO;
}
#ifdef __arch64__
void
sparc64_elf_mcmodel_check(struct exec_package *epp, const char *model,
size_t len)
{
if (epp->ep_flags & EXEC_32)
return;
#ifdef __USE_TOPDOWN_VM
if (strncmp(model, "medany", len) == 0 ||
strncmp(model, "medmid", len) == 0)
epp->ep_flags |= EXEC_TOPDOWN_VM;
#endif
}
#endif