NetBSD/sys/arch/vax/vax/vm_machdep.c

412 lines
11 KiB
C

/* $NetBSD: vm_machdep.c,v 1.38 1998/03/02 17:00:01 ragge Exp $ */
/*
* Copyright (c) 1994 Ludd, University of Lule}, Sweden.
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed at Ludd, University of Lule}.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* 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 <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/exec.h>
#include <sys/vnode.h>
#include <sys/core.h>
#include <sys/mount.h>
#include <sys/device.h>
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
#include <machine/vmparam.h>
#include <machine/mtpr.h>
#include <machine/pmap.h>
#include <machine/pte.h>
#include <machine/macros.h>
#include <machine/trap.h>
#include <machine/pcb.h>
#include <machine/frame.h>
#include <machine/cpu.h>
#include <machine/sid.h>
#include <sys/syscallargs.h>
volatile int whichqs;
/*
* pagemove - moves pages at virtual address from to virtual address to,
* block moved of size size. Using fast insn bcopy for pte move.
*/
void
pagemove(from, to, size)
caddr_t from, to;
size_t size;
{
pt_entry_t *fpte, *tpte;
int stor;
fpte = kvtopte(from);
tpte = kvtopte(to);
stor = (size >> PGSHIFT) * sizeof(struct pte);
bcopy(fpte, tpte, stor);
bzero(fpte, stor);
mtpr(0, PR_TBIA);
}
/*
* cpu_fork() copies parent process trapframe directly into child PCB
* so that when we swtch() to the child process it will go directly
* back to user mode without any need to jump back through kernel.
* We also take away mapping for the second page after pcb, so that
* we get something like a "red zone".
* No need for either double-map kernel stack or relocate it when
* forking.
*/
void
cpu_fork(p1, p2)
struct proc *p1, *p2;
{
struct pte *pt;
struct pcb *nyproc;
struct trapframe *tf;
struct pmap *pmap, *opmap;
nyproc = &p2->p_addr->u_pcb;
tf = p1->p_addr->u_pcb.framep;
opmap = p1->p_vmspace->vm_map.pmap;
pmap = p2->p_vmspace->vm_map.pmap;
/* Mark page invalid */
pt = kvtopte((u_int)p2->p_addr + NBPG);
pt->pg_v = 0;
/*
* Activate address space for the new process. The PTEs have
* already been allocated by way of pmap_create().
*/
pmap_activate(p2);
/* Set up internal defs in PCB. */
nyproc->iftrap = NULL;
nyproc->KSP = (u_int)p2->p_addr + USPACE;
/* General registers as taken from userspace */
/* trapframe should be synced with pcb */
bcopy(&tf->r2,&nyproc->R[2],10*sizeof(int));
nyproc->AP = tf->ap;
nyproc->FP = tf->fp;
nyproc->USP = tf->sp;
nyproc->PC = tf->pc;
nyproc->PSL = tf->psl & ~PSL_C;
nyproc->R[0] = p1->p_pid; /* parent pid. (shouldn't be needed) */
nyproc->R[1] = 1;
return; /* Child is ready. Parent, return! */
}
/*
* cpu_set_kpc() sets up pcb for the new kernel process so that it will
* start at the procedure pointed to by pc next time swtch() is called.
* When that procedure returns, it will pop off everything from the
* faked calls frame on the kernel stack, do an REI and go down to
* user mode.
*/
void
cpu_set_kpc(p, pc)
struct proc *p;
void (*pc) __P((struct proc *));
{
struct pcb *nyproc;
struct {
struct callsframe cf;
struct trapframe tf;
} *kc;
extern int sret, boothowto;
nyproc = &p->p_addr->u_pcb;
(unsigned)kc = nyproc->FP = nyproc->KSP =
(unsigned)p->p_addr + USPACE - sizeof(*kc);
kc->cf.ca_cond = 0;
kc->cf.ca_maskpsw = 0x20000000;
kc->cf.ca_pc = (unsigned)&sret;
kc->cf.ca_argno = 1;
kc->cf.ca_arg1 = (unsigned)p;
kc->tf.r11 = boothowto; /* If we have old init */
kc->tf.psl = 0x3c00000;
nyproc->framep = (void *)&kc->tf;
nyproc->AP = (unsigned)&kc->cf.ca_argno;
nyproc->FP = nyproc->KSP = (unsigned)kc;
nyproc->PC = (unsigned)pc + 2;
}
int reno_zmagic __P((struct proc *, struct exec_package *));
int
cpu_exec_aout_makecmds(p, epp)
struct proc *p;
struct exec_package *epp;
{
int error;
struct exec *ep;
/*
* Compatibility with reno programs.
*/
ep=epp->ep_hdr;
switch (ep->a_midmag) {
case 0x10b: /* ZMAGIC in 4.3BSD Reno programs */
error = reno_zmagic(p, epp);
break;
case 0x108:
printf("Warning: reno_nmagic\n");
error = exec_aout_prep_nmagic(p, epp);
break;
case 0x107:
printf("Warning: reno_omagic\n");
error = exec_aout_prep_omagic(p, epp);
break;
default:
error = ENOEXEC;
}
return(error);
}
int
sys_sysarch(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
return (ENOSYS);
};
#ifdef COMPAT_ULTRIX
extern struct emul emul_ultrix;
#endif
/*
* 4.3BSD Reno programs have an 1K header first in the executable
* file, containing a.out header. Otherwise programs are identical.
*
* from: exec_aout.c,v 1.9 1994/01/28 23:46:59 jtc Exp $
*/
int
reno_zmagic(p, epp)
struct proc *p;
struct exec_package *epp;
{
struct exec *execp = epp->ep_hdr;
epp->ep_taddr = 0;
epp->ep_tsize = execp->a_text;
epp->ep_daddr = epp->ep_taddr + execp->a_text;
epp->ep_dsize = execp->a_data + execp->a_bss;
epp->ep_entry = execp->a_entry;
#ifdef COMPAT_ULTRIX
epp->ep_emul = &emul_ultrix;
#endif
/*
* check if vnode is in open for writing, because we want to
* demand-page out of it. if it is, don't do it, for various
* reasons
*/
if ((execp->a_text != 0 || execp->a_data != 0) &&
epp->ep_vp->v_writecount != 0) {
return ETXTBSY;
}
epp->ep_vp->v_flag |= VTEXT;
/* set up command for text segment */
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_pagedvn, execp->a_text,
epp->ep_taddr, epp->ep_vp, 0x400, VM_PROT_READ|VM_PROT_EXECUTE);
/* set up command for data segment */
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_pagedvn, execp->a_data,
epp->ep_daddr, epp->ep_vp, execp->a_text+0x400,
VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
/* set up command for bss segment */
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, execp->a_bss,
epp->ep_daddr + execp->a_data, NULLVP, 0,
VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
return exec_aout_setup_stack(p, epp);
}
/*
* Dump the machine specific header information at the start of a core dump.
* First put all regs in PCB for debugging purposes. This is not an good
* way to do this, but good for my purposes so far.
*/
int
cpu_coredump(p, vp, cred, chdr)
struct proc *p;
struct vnode *vp;
struct ucred *cred;
struct core *chdr;
{
struct trapframe *tf;
struct md_coredump state;
struct coreseg cseg;
int error;
tf = p->p_addr->u_pcb.framep;
CORE_SETMAGIC(*chdr, COREMAGIC, MID_VAX, 0);
chdr->c_hdrsize = sizeof(struct core);
chdr->c_seghdrsize = sizeof(struct coreseg);
chdr->c_cpusize = sizeof(struct md_coredump);
bcopy(tf, &state, sizeof(struct md_coredump));
CORE_SETMAGIC(cseg, CORESEGMAGIC, MID_VAX, CORE_CPU);
cseg.c_addr = 0;
cseg.c_size = chdr->c_cpusize;
error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&cseg, chdr->c_seghdrsize,
(off_t)chdr->c_hdrsize, UIO_SYSSPACE,
IO_NODELOCKED|IO_UNIT, cred, (int *)NULL, p);
if (error)
return error;
error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&state, sizeof(state),
(off_t)(chdr->c_hdrsize + chdr->c_seghdrsize), UIO_SYSSPACE,
IO_NODELOCKED|IO_UNIT, cred, (int *)NULL, p);
if (!error)
chdr->c_nseg++;
return error;
}
/*
* cpu_swapin() is called just before a process shall be swapped in.
* Kernel stack and pcb must be mapped when we swtch() to this new
* process, to guarantee that we frob all pages here to ensure that
* they actually are in-core. Kernel stack red zone is also updated
* here.
*/
void
cpu_swapin(p)
struct proc *p;
{
struct pte *pt;
u_int uarea, i, *j, rv;
uarea = (u_int)p->p_addr;
for (i = uarea;i < uarea + USPACE;i += PAGE_SIZE) {
j = (u_int *)kvtopte(i);
if ((*j & PG_V) == 0) {
#if defined(UVM)
rv = uvm_fault(kernel_map, i, 0,
VM_PROT_WRITE|VM_PROT_READ);
#else
rv = vm_fault(kernel_map, i,
VM_PROT_WRITE|VM_PROT_READ, FALSE);
#endif
if (rv != KERN_SUCCESS)
panic("cpu_swapin: rv %d",rv);
}
}
pt = kvtopte(uarea + NBPG);
pt->pg_v = 0; /* Set kernel stack red zone */
}
#if VAX410 || VAX43
/*
* vmapbuf()/vunmapbuf() only used on some vaxstations without
* any busadapter with MMU.
* XXX - This must be reworked to be effective.
*/
void
vmapbuf(bp, len)
struct buf *bp;
vm_size_t len;
{
vm_offset_t faddr, taddr, off, pa;
pmap_t fmap, tmap;
if ((vax_boardtype != VAX_BTYP_43) && (vax_boardtype != VAX_BTYP_410))
return;
faddr = trunc_page(bp->b_saveaddr = bp->b_data);
off = (vm_offset_t)bp->b_data - faddr;
len = round_page(off + len);
#if defined(UVM)
taddr = uvm_km_valloc_wait(phys_map, len);
#else
taddr = kmem_alloc_wait(phys_map, len);
#endif
bp->b_data = (caddr_t)(taddr + off);
fmap = vm_map_pmap(&bp->b_proc->p_vmspace->vm_map);
tmap = vm_map_pmap(phys_map);
len = len >> PGSHIFT;
while (len--) {
pa = pmap_extract(fmap, faddr);
if (pa == 0)
panic("vmapbuf: null page frame for %x", (u_int)faddr);
pmap_enter(tmap, taddr, pa & ~(NBPG - 1),
VM_PROT_READ|VM_PROT_WRITE, TRUE);
faddr += NBPG;
taddr += NBPG;
}
}
/*
* Free the io map PTEs associated with this IO operation.
* We also invalidate the TLB entries and restore the original b_addr.
*/
void
vunmapbuf(bp, len)
struct buf *bp;
vm_size_t len;
{
vm_offset_t addr, off;
if ((vax_boardtype != VAX_BTYP_43) && (vax_boardtype != VAX_BTYP_410))
return;
addr = trunc_page(bp->b_data);
off = (vm_offset_t)bp->b_data - addr;
len = round_page(off + len);
#if defined(UVM)
uvm_km_free_wakeup(phys_map, addr, len);
#else
kmem_free_wakeup(phys_map, addr, len);
#endif
bp->b_data = bp->b_saveaddr;
bp->b_saveaddr = 0;
}
#endif