NetBSD-5.0.2/sys/arch/powerpc/powerpc/powerpc_machdep.c
/* $NetBSD: powerpc_machdep.c,v 1.39 2008/10/15 06:51:18 wrstuden Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
* Copyright (C) 1995, 1996 TooLs GmbH.
* 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 by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: powerpc_machdep.c,v 1.39 2008/10/15 06:51:18 wrstuden Exp $");
#include "opt_altivec.h"
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/disklabel.h>
#include <sys/exec.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/sa.h>
#include <sys/savar.h>
#include <sys/signal.h>
#include <sys/sysctl.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <sys/cpu.h>
int cpu_timebase;
int cpu_printfataltraps;
#if !defined(PPC_IBM4XX)
extern int powersave;
#endif
/* exported variable to be filled in by the bootloaders */
char *booted_kernel;
/*
* Set set up registers on exec.
*/
void
setregs(struct lwp *l, struct exec_package *pack, u_long stack)
{
struct proc *p = l->l_proc;
struct trapframe *tf = trapframe(l);
struct ps_strings arginfo;
memset(tf, 0, sizeof *tf);
tf->fixreg[1] = -roundup(-stack + 8, 16);
/*
* XXX Machine-independent code has already copied arguments and
* XXX environment to userland. Get them back here.
*/
(void)copyin((char *)p->p_psstr, &arginfo, sizeof (arginfo));
/*
* Set up arguments for _start():
* _start(argc, argv, envp, obj, cleanup, ps_strings);
*
* Notes:
* - obj and cleanup are the auxiliary and termination
* vectors. They are fixed up by ld.elf_so.
* - ps_strings is a NetBSD extension, and will be
* ignored by executables which are strictly
* compliant with the SVR4 ABI.
*
* XXX We have to set both regs and retval here due to different
* XXX calling convention in trap.c and init_main.c.
*/
tf->fixreg[3] = arginfo.ps_nargvstr;
tf->fixreg[4] = (register_t)arginfo.ps_argvstr;
tf->fixreg[5] = (register_t)arginfo.ps_envstr;
tf->fixreg[6] = 0; /* auxillary vector */
tf->fixreg[7] = 0; /* termination vector */
tf->fixreg[8] = (register_t)p->p_psstr; /* NetBSD extension */
tf->srr0 = pack->ep_entry;
tf->srr1 = PSL_MBO | PSL_USERSET;
#ifdef ALTIVEC
tf->tf_xtra[TF_VRSAVE] = 0;
#endif
l->l_addr->u_pcb.pcb_flags = PSL_FE_DFLT;
}
/*
* Machine dependent system variables.
*/
static int
sysctl_machdep_cacheinfo(SYSCTLFN_ARGS)
{
struct sysctlnode node = *rnode;
node.sysctl_data = &curcpu()->ci_ci;
node.sysctl_size = sizeof(curcpu()->ci_ci);
return (sysctl_lookup(SYSCTLFN_CALL(&node)));
}
#if !defined (PPC_IBM4XX)
static int
sysctl_machdep_powersave(SYSCTLFN_ARGS)
{
struct sysctlnode node = *rnode;
if (powersave < 0)
node.sysctl_flags |= ~CTLFLAG_READWRITE;
return (sysctl_lookup(SYSCTLFN_CALL(&node)));
}
#endif
static int
sysctl_machdep_booted_device(SYSCTLFN_ARGS)
{
struct sysctlnode node;
if (booted_device == NULL)
return (EOPNOTSUPP);
node = *rnode;
node.sysctl_data = booted_device->dv_xname;
node.sysctl_size = strlen(booted_device->dv_xname) + 1;
return (sysctl_lookup(SYSCTLFN_CALL(&node)));
}
static int
sysctl_machdep_booted_kernel(SYSCTLFN_ARGS)
{
struct sysctlnode node;
if (booted_kernel == NULL || booted_kernel[0] == '\0')
return (EOPNOTSUPP);
node = *rnode;
node.sysctl_data = booted_kernel;
node.sysctl_size = strlen(booted_kernel) + 1;
return (sysctl_lookup(SYSCTLFN_CALL(&node)));
}
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);
/* Deprecated */
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
CTLTYPE_INT, "cachelinesize", NULL,
NULL, curcpu()->ci_ci.dcache_line_size, NULL, 0,
CTL_MACHDEP, CPU_CACHELINE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_INT, "timebase", NULL,
NULL, 0, &cpu_timebase, 0,
CTL_MACHDEP, CPU_TIMEBASE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
CTLTYPE_INT, "printfataltraps", NULL,
NULL, 0, &cpu_printfataltraps, 0,
CTL_MACHDEP, CPU_PRINTFATALTRAPS, CTL_EOL);
/* Use this instead of CPU_CACHELINE */
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRUCT, "cacheinfo", NULL,
sysctl_machdep_cacheinfo, 0, NULL, 0,
CTL_MACHDEP, CPU_CACHEINFO, CTL_EOL);
#if !defined (PPC_IBM4XX)
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
CTLTYPE_INT, "powersave", NULL,
sysctl_machdep_powersave, 0, &powersave, 0,
CTL_MACHDEP, CPU_POWERSAVE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
CTLTYPE_INT, "altivec", NULL,
NULL, cpu_altivec, NULL, 0,
CTL_MACHDEP, CPU_ALTIVEC, CTL_EOL);
#else
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
CTLTYPE_INT, "altivec", NULL,
NULL, 0, NULL, 0,
CTL_MACHDEP, CPU_ALTIVEC, CTL_EOL);
#endif
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRING, "model", NULL,
NULL, 0, cpu_model, 0,
CTL_MACHDEP, CPU_MODEL, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRING, "booted_device", NULL,
sysctl_machdep_booted_device, 0, NULL, 0,
CTL_MACHDEP, CPU_BOOTED_DEVICE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRING, "booted_kernel", NULL,
sysctl_machdep_booted_kernel, 0, NULL, 0,
CTL_MACHDEP, CPU_BOOTED_KERNEL, CTL_EOL);
}
/*
* Crash dump handling.
*/
u_int32_t dumpmag = 0x8fca0101; /* magic number */
int dumpsize = 0; /* size of dump in pages */
long dumplo = -1; /* blocks */
/*
* This is called by main to set dumplo and dumpsize.
*/
void
cpu_dumpconf(void)
{
const struct bdevsw *bdev;
int nblks; /* size of dump device */
int skip;
if (dumpdev == NODEV)
return;
bdev = bdevsw_lookup(dumpdev);
if (bdev == NULL) {
dumpdev = NODEV;
return;
}
if (bdev->d_psize == NULL)
return;
nblks = (*bdev->d_psize)(dumpdev);
if (nblks <= ctod(1))
return;
dumpsize = physmem;
/* Skip enough blocks at start of disk to preserve an eventual disklabel. */
skip = LABELSECTOR + 1;
skip += ctod(1) - 1;
skip = ctod(dtoc(skip));
if (dumplo < skip)
dumplo = skip;
/* Put dump at end of partition */
if (dumpsize > dtoc(nblks - dumplo))
dumpsize = dtoc(nblks - dumplo);
if (dumplo < nblks - ctod(dumpsize))
dumplo = nblks - ctod(dumpsize);
}
void
cpu_upcall(struct lwp *l, int type, int nevents, int ninterrupted,
void *sas, void *ap, void *sp, sa_upcall_t upcall)
{
struct trapframe *tf;
tf = trapframe(l);
/*
* Build context to run handler in.
*/
tf->fixreg[1] = (register_t)((struct saframe *)sp - 1);
tf->lr = 0;
tf->fixreg[3] = (register_t)type;
tf->fixreg[4] = (register_t)sas;
tf->fixreg[5] = (register_t)nevents;
tf->fixreg[6] = (register_t)ninterrupted;
tf->fixreg[7] = (register_t)ap;
tf->srr0 = (register_t)upcall;
tf->srr1 &= ~PSL_SE;
}
bool
cpu_intr_p(void)
{
return curcpu()->ci_idepth != 0;
}