#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.79 2024/01/15 20:35:22 andvar Exp $");
#include "opt_armfpe.h"
#include "opt_cputypes.h"
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/cpu.h>
#include <sys/exec.h>
#include <sys/proc.h>
#ifdef STACKCHECKS
#include <sys/syslog.h>
#endif
#include <sys/systm.h>
#include <sys/vnode.h>
#include <uvm/uvm_extern.h>
#include <arm/vfpreg.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/reg.h>
#include <machine/vmparam.h>
void lwp_trampoline(void);
void
cpu_proc_fork(struct proc *p1, struct proc *p2)
{
memcpy(p2->p_md.md_march, p1->p_md.md_march, sizeof(p2->p_md.md_march));
}
void
cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize,
void (*func)(void *), void *arg)
{
struct switchframe *sf;
vaddr_t uv;
const struct pcb * const pcb1 = lwp_getpcb(l1);
struct pcb * const pcb2 = lwp_getpcb(l2);
#ifdef XXXDEBUG
printf("cpu_lwp_fork: %p %p %p %p\n", l1, l2, curlwp, &lwp0);
#endif
*pcb2 = *pcb1;
#ifdef FPU_VFP
pcb2->pcb_vfp.vfp_fpexc &= ~VFP_FPEXC_EN;
#endif
uv = uvm_lwp_getuarea(l2);
pcb2->pcb_ksp = uv + USPACE_SVC_STACK_TOP;
#ifdef STACKCHECKS
memset((void *)(uv + USPACE_SVC_STACK_BOTTOM), 0xdd,
(USPACE_SVC_STACK_TOP - USPACE_SVC_STACK_BOTTOM));
#endif
#ifdef XXXDEBUG
printf("l1: pcb=%p pid=%d pmap=%p\n",
pcb1, l1->l_lid, l1->l_proc->p_vmspace->vm_map.pmap);
printf("l2: pcb=%p pid=%d pmap=%p\n",
pcb2, l2->l_lid, l2->l_proc->p_vmspace->vm_map.pmap);
#endif
struct trapframe *tf = (struct trapframe *)pcb2->pcb_ksp - 1;
lwp_settrapframe(l2, tf);
*tf = *lwp_trapframe(l1);
if (stack != NULL)
tf->tf_usr_sp = ((vaddr_t)(stack) + stacksize) & -8;
sf = (struct switchframe *)tf - 1;
sf->sf_r4 = (u_int)func;
sf->sf_r5 = (u_int)arg;
sf->sf_r7 = PSR_USR32_MODE;
sf->sf_sp = (u_int)tf;
sf->sf_pc = (u_int)lwp_trampoline;
pcb2->pcb_ksp = (u_int)sf;
}
void
cpu_lwp_free(struct lwp *l, int proc)
{
#ifdef STACKCHECKS
struct pcb * const pcb = lwp_getpcb(l);
u_char *ptr;
u_int loop;
ptr = (u_char *)pcb + USPACE_SVC_STACK_BOTTOM;
for (loop = 0; loop < (USPACE_SVC_STACK_TOP - USPACE_SVC_STACK_BOTTOM)
&& *ptr == 0xdd; ++loop, ++ptr) ;
log(LOG_INFO, "%u bytes of svc stack fill pattern\n", loop);
#endif
}
void
cpu_lwp_free2(struct lwp *l)
{
}
int
vmapbuf(struct buf *bp, vsize_t len)
{
struct pmap * const pm = vm_map_pmap(&bp->b_proc->p_vmspace->vm_map);
vaddr_t faddr, taddr, off;
paddr_t fpa;
KASSERT(pm != pmap_kernel());
#ifdef XXXDEBUG
printf("vmapbuf: bp=%08x buf=%08x len=%08x\n", (u_int)bp,
(u_int)bp->b_data, (u_int)len);
#endif
if ((bp->b_flags & B_PHYS) == 0)
panic("vmapbuf");
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, atop(faddr) & uvmexp.colormask,
UVM_KMF_VAONLY | UVM_KMF_WAITVA | UVM_KMF_COLORMATCH);
bp->b_data = (void *)(taddr + off);
while (len) {
(void) pmap_extract(pm, faddr, &fpa);
pmap_enter(pmap_kernel(), taddr, fpa, VM_PROT_READ|VM_PROT_WRITE,
VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED);
faddr += PAGE_SIZE;
taddr += PAGE_SIZE;
len -= PAGE_SIZE;
}
pmap_update(pmap_kernel());
return 0;
}
void
vunmapbuf(struct buf *bp, vsize_t len)
{
vaddr_t addr, off;
#ifdef XXXDEBUG
printf("vunmapbuf: bp=%08x buf=%08x len=%08x\n",
(u_int)bp, (u_int)bp->b_data, (u_int)len);
#endif
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(pmap_kernel(), addr, addr + len);
pmap_update(pmap_kernel());
uvm_km_free(phys_map, addr, len, UVM_KMF_VAONLY);
bp->b_data = bp->b_saveaddr;
bp->b_saveaddr = 0;
}
void
ktext_write(void *to, const void *from, size_t size)
{
struct pmap *pmap = pmap_kernel();
pd_entry_t *pde, oldpde, tmppde;
pt_entry_t *pte, oldpte, tmppte;
vaddr_t pgva;
size_t limit, savesize;
const char *src;
char *dst;
oldpte = 0;
if ((savesize = size) == 0)
return;
dst = (char *) to;
src = (const char *) from;
do {
if (pmap_get_pde_pte(pmap, (vaddr_t) dst, &pde, &pte) == false)
goto no_mapping;
switch ((oldpde = *pde) & L1_TYPE_MASK) {
case L1_TYPE_S:
pgva = (vaddr_t)dst & L1_S_FRAME;
limit = L1_S_SIZE - ((vaddr_t)dst & L1_S_OFFSET);
tmppde = l1pte_set_writable(oldpde);
*pde = tmppde;
PTE_SYNC(pde);
break;
case L1_TYPE_C:
pgva = (vaddr_t)dst & L2_S_FRAME;
limit = L2_S_SIZE - ((vaddr_t)dst & L2_S_OFFSET);
if (pte == NULL)
goto no_mapping;
oldpte = *pte;
tmppte = l2pte_set_writable(oldpte);
*pte = tmppte;
PTE_SYNC(pte);
break;
default:
no_mapping:
printf(" address 0x%08lx not a valid page\n",
(vaddr_t) dst);
return;
}
cpu_tlb_flushD_SE(pgva);
cpu_cpwait();
if (limit > size)
limit = size;
size -= limit;
for (; limit > 0; limit--)
*dst++ = *src++;
switch (oldpde & L1_TYPE_MASK) {
case L1_TYPE_S:
*pde = oldpde;
PTE_SYNC(pde);
break;
case L1_TYPE_C:
*pte = oldpte;
PTE_SYNC(pte);
break;
}
cpu_tlb_flushD_SE(pgva);
cpu_cpwait();
} while (size != 0);
cpu_icache_sync_range((vaddr_t)to, savesize);
}