#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.123 2023/10/06 11:53:26 skrll Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/core.h>
#include <sys/exec.h>
#include <uvm/uvm.h>
#include <machine/cpu.h>
#include <machine/alpha.h>
#include <machine/pmap.h>
#include <machine/reg.h>
void
cpu_lwp_free(struct lwp *l, int proc)
{
(void) l;
}
void
cpu_lwp_free2(struct lwp *l)
{
(void) l;
}
void
alpha_kthread_backstop(void)
{
struct lwp * const l = curlwp;
panic("kthread lwp %p (%s) hit the backstop", l, l->l_name);
}
void
cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize,
void (*func)(void *), void *arg)
{
struct pcb *pcb1, *pcb2;
extern void lwp_trampoline(void);
pcb1 = lwp_getpcb(l1);
pcb2 = lwp_getpcb(l2);
l2->l_md.md_tf = l1->l_md.md_tf;
l2->l_md.md_flags = l1->l_md.md_flags & MDLWP_FP_C;
KASSERT(l2->l_md.md_astpending == 0);
l2->l_md.md_pcbpaddr = (void *)vtophys((vaddr_t)pcb2);
*pcb2 = *pcb1;
if (stack != NULL) {
pcb2->pcb_hw.apcb_usp =
((u_long)stack + stacksize) & ~((u_long)STACK_ALIGNBYTES);
} else {
pcb2->pcb_hw.apcb_usp = alpha_pal_rdusp();
}
pcb2->pcb_hw.apcb_ptbr =
ALPHA_K0SEG_TO_PHYS((vaddr_t)kernel_lev1map) >> PGSHIFT;
pcb2->pcb_hw.apcb_asn = PMAP_ASN_KERNEL;
#ifdef DIAGNOSTIC
if (l1 != curlwp && l1 != &lwp0)
panic("cpu_lwp_fork: curlwp");
#endif
{
struct trapframe *l2tf;
uint64_t call_root;
l2tf = l2->l_md.md_tf = (struct trapframe *)
(uvm_lwp_getuarea(l2) + USPACE - sizeof(struct trapframe));
memcpy(l2->l_md.md_tf, l1->l_md.md_tf,
sizeof(struct trapframe));
l2tf->tf_regs[FRAME_V0] = l1->l_proc->p_pid;
l2tf->tf_regs[FRAME_A3] = 0;
l2tf->tf_regs[FRAME_A4] = 1;
if (__predict_true(l2->l_proc != &proc0))
call_root = (uint64_t)exception_return;
else
call_root = (uint64_t)alpha_kthread_backstop;
pcb2->pcb_hw.apcb_ksp =
(uint64_t)l2->l_md.md_tf;
pcb2->pcb_context[0] =
(uint64_t)func;
pcb2->pcb_context[1] =
call_root;
pcb2->pcb_context[2] =
(uint64_t)arg;
pcb2->pcb_context[3] =
(uint64_t)l2;
pcb2->pcb_context[7] =
(uint64_t)lwp_trampoline;
}
}
int
vmapbuf(struct buf *bp, vsize_t len)
{
vaddr_t faddr, taddr, off;
paddr_t pa;
struct proc *p;
if ((bp->b_flags & B_PHYS) == 0)
panic("vmapbuf");
p = bp->b_proc;
bp->b_saveaddr = bp->b_data;
faddr = trunc_page((vaddr_t)bp->b_data);
off = (vaddr_t)bp->b_data - faddr;
len = round_page(off + len);
taddr = uvm_km_alloc(phys_map, len, 0, UVM_KMF_VAONLY|UVM_KMF_WAITVA);
bp->b_data = (void *)(taddr + off);
len = atop(len);
while (len--) {
if (pmap_extract(vm_map_pmap(&p->p_vmspace->vm_map), faddr,
&pa) == false)
panic("vmapbuf: null page frame");
pmap_enter(vm_map_pmap(phys_map), taddr, trunc_page(pa),
VM_PROT_READ|VM_PROT_WRITE, PMAP_WIRED);
faddr += PAGE_SIZE;
taddr += PAGE_SIZE;
}
pmap_update(vm_map_pmap(phys_map));
return 0;
}
void
vunmapbuf(struct buf *bp, vsize_t len)
{
vaddr_t addr, off;
if ((bp->b_flags & B_PHYS) == 0)
panic("vunmapbuf");
addr = trunc_page((vaddr_t)bp->b_data);
off = (vaddr_t)bp->b_data - addr;
len = round_page(off + len);
pmap_remove(vm_map_pmap(phys_map), addr, addr + len);
pmap_update(vm_map_pmap(phys_map));
uvm_km_free(phys_map, addr, len, UVM_KMF_VAONLY);
bp->b_data = bp->b_saveaddr;
bp->b_saveaddr = NULL;
}
#ifdef __HAVE_CPU_UAREA_ROUTINES
static struct evcnt uarea_direct_success =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "uarea direct", "success");
static struct evcnt uarea_direct_failure =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "uarea direct", "failure");
EVCNT_ATTACH_STATIC(uarea_direct_success);
EVCNT_ATTACH_STATIC(uarea_direct_failure);
void *
cpu_uarea_alloc(bool system)
{
struct pglist pglist;
int error;
error = uvm_pglistalloc(USPACE, 0, ptoa(physmem), 0, 0, &pglist, 1, 1);
if (error) {
atomic_inc_ulong(&uarea_direct_failure.ev_count);
return NULL;
}
atomic_inc_ulong(&uarea_direct_success.ev_count);
const struct vm_page * const pg = TAILQ_FIRST(&pglist);
KASSERT(pg != NULL);
const paddr_t pa = VM_PAGE_TO_PHYS(pg);
return (void *)PMAP_MAP_POOLPAGE(pa);
}
bool
cpu_uarea_free(void *vva)
{
vaddr_t va = (vaddr_t) vva;
if (va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS)
return false;
struct vm_page *pg = PHYS_TO_VM_PAGE(PMAP_UNMAP_POOLPAGE(va));
KASSERT(pg != NULL);
for (size_t i = 0; i < UPAGES; i++, pg++) {
uvm_pagefree(pg);
}
return true;
}
#endif