Coherent4.2.10/coh.386/null.c
/* (lgl-
* The information contained herein is a trade secret of Mark Williams
* Company, and is confidential information. It is provided under a
* license agreement, and may be copied or disclosed only under the
* terms of that agreement. Any reproduction or disclosure of this
* material without the express written authorization of Mark Williams
* Company or persuant to the license agreement is unlawful.
*
* COHERENT Version 2.6
* Copyright (c) 1982, 1994.
* An unpublished work by Mark Williams Company, Chicago.
* All rights reserved.
-lgl) */
/*
* Null and memory driver.
* Minor device 0 is /dev/null
* Minor device 1 is /dev/mem, physical memory
* Minor device 2 is /dev/kmem, kernel data
* Minor device 3 is /dev/cmos
* Minor device 4 is /dev/boot_gift
* Minor device 5 is /dev/clock
* Minor device 6 is /dev/ps
* Minor device 7 is /dev/kmemhi, virtual memory 0x8000_0000-0xFFFF_FFFF
* Minor device 11 is /dev/idle
* Minor device 12 is /dev/freemem
*
* Revision 2.7 94/03/08 20:35:15 udo
* Cleaned up and /dev/freemem added
*
* $Log: null.c,v $
* Revision 2.6 93/10/29 00:55:25 nigel
* R98 (aka 4.2 Beta) prior to removing System Global memory
*
* Revision 2.5 93/09/13 07:58:47 nigel
* Updated to reflect the fact that most driver entry points are 'void' again.
*
* Revision 2.4 93/08/19 03:26:39 nigel
* Nigel's r83 (Stylistic cleanup)
*
* Revision 2.2 93/07/26 14:28:57 nigel
* Nigel's R80
*
* Revision 1.7 93/04/14 10:06:37 root
* r75
*
* Revision 1.10 93/03/02 08:16:25 bin
* kernel 73 update
*
* Revision 1.6 92/11/09 17:10:54 root
* Just before adding vio segs.
*
* Revision 1.2 92/01/06 11:59:49 hal
* Compile with cc.mwc.
*
* Revision 1.1 88/03/24 16:14:04 src
* Initial revision
*/
#include <kernel/ddi_cpu.h>
#include <kernel/proc_lib.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/cred.h>
#include <sys/types.h>
#include <stddef.h>
#define _KERNEL 1
#include <kernel/typed.h>
#include <sys/con.h>
#include <sys/inode.h>
#include <sys/seg.h>
#include <sys/coh_ps.h>
#include <sys/io.h>
#include <sys/proc.h>
#include <sys/uproc.h>
#include <sys/mmu.h>
#include <sys/null.h>
/*
* The symbol "DANGEROUS" should be undefined for a production system.
*/
#ifdef TRACER
#define KMEM_IOCTL /* Allow ioctl()s for /dev/kmem. */
#define DANGEROUS /* Allow dangerous ioctl()s for /dev/null. */
#endif
unsigned char read_cmos __PROTO ((unsigned offset));
void write_cmos __PROTO ((unsigned offset,
unsigned char value));
/* These are minor numbers. */
#define DEV_NULL 0 /* /dev/null */
#define DEV_MEM 1 /* /dev/mem */
#define DEV_KMEM 2 /* /dev/kmem */
#define DEV_CMOS 3 /* /dev/cmos */
#define DEV_BOOTGIFT 4 /* /dev/bootgift*/
#define DEV_CLOCK 5 /* /dev/clock */
#define DEV_PS 6 /* /dev/ps */
#define DEV_KMEMHI 7 /* /dev/kmemhi */
#define DEV_IDLE 11 /* /dev/idle */
#define DEV_FMEM 12 /* /dev/freemem */
#define KMEMHI_BASE 0x80000000
#define PXCOPY_LIM 4096
/*
* CMOS devices are limited by an 8 bit address.
*/
#define MAX_CMOS 255
#define CMOS_LEN 256
/*
* The first 14 bytes of the CMOS are the clock.
*/
#define MAX_CLOCK 13
#define CLOCK_LEN 14
/*
* These are definitions for mucking with the CMOS clock.
*/
#define SRA 10 /* Status Register A */
#define SRB 11 /* Status Register B */
#define SRC 12 /* Status Register C */
#define SRD 13 /* Status Register D */
#define UIP 0x80 /* Update In Progress bit of SRA. */
#define NO_UPD 0x80 /* No Update bit of SRB. */
/*
* int lock_clock() -- Stop the update cycle on the CMOS RT clock and
* wait for it to settle. Returns 0 if the clock would not settle
* in time.
*/
static int
lock_clock()
{
register int i;
/*
* Wait for the clock to settle. If it does not settle in
* a reasonable amount of time, give up.
*/
i = 65536; /* Loop for a longish time. */
while (-- i > 0) {
if (0 == (UIP & read_cmos (SRA))) {
break; /* Break if there is no update in progress. */
}
}
if (0 == i) {
/* The clock would not settle. */
return 0;
}
/*
* There is a tiny race here--an interrupt could conceivably
* come here, thus allowing enough delay for another update to
* begin. But if we take interrupts that take a full second
* to process, other things are going to break horribly.
*/
/*
* Lock out updates.
* We set the No Updates bit in Clock Status Register B.
*/
write_cmos (SRB, read_cmos (SRB) | NO_UPD);
return 1;
}
/*
* void unlock_clock() -- Restart the update cycle on the CMOS RT clock.
*/
static void
unlock_clock()
{
/*
* We clear the No Updates bit in Clock Status Register B.
*/
write_cmos (SRB, read_cmos(SRB) & ~ NO_UPD);
}
/*
* Null/memory open routine.
*/
static void
nlopen(dev, mode)
dev_t dev;
int mode;
{
switch (minor(dev)) {
case DEV_PS:
/* /dev/ps is read only */
if (IPR != (IPR & mode))
set_user_error (EACCES);
break;
default:
/*
* For other minor devices there is
* usually no action for open().
*/
break;
}
return;
}
/*
* Null/memory close routine.
*/
static void
nlclose(dev, mode)
dev_t dev;
int mode;
{
}
/*
* Null/memory read routine.
*/
static void
nlread (dev, iop)
dev_t dev;
IO *iop;
{
register unsigned bytesRead;
register PROC *pp1; /* */
char psBuf[ARGSZ]; /* buffer for command line
* arguments for ps. */
stMonitor psData; /* All process data for */
unsigned int seek;
extern typed_space boot_gift;
switch (minor (dev)) {
case DEV_NULL:
/*
* Read nothing.
* Do NOT update iop->io_ioc.
* This way, caller knows 0 bytes were read.
*/
break;
case DEV_MEM: {
int src = iop->io_seek;
int dest = iop->io.pbase;
while (iop->io_ioc) {
int numBytes = PXCOPY_LIM;
if (numBytes > iop->io_ioc)
numBytes = iop->io_ioc;
bytesRead = pxcopy (src, dest, numBytes, SEL_386_UD);
src += bytesRead;
dest += bytesRead;
iop->io_ioc -= bytesRead;
if (get_user_error () == EFAULT) {
set_user_error (0);
break;
}
}
break;
}
case DEV_KMEM:
iowrite (iop, (caddr_t) iop->io_seek, iop->io_ioc);
if (get_user_error () == EFAULT)
set_user_error (0);
break;
case DEV_CLOCK:
/*
* Don't go past the end of the CLOCK.
*/
if (iop->io_seek >= CLOCK_LEN)
break;
/*
* Lock the clock before any reading.
*/
if (lock_clock () == 0) {
set_user_error (EIO);
break;
}
/*
* Read the requested data out of the CMOS.
*/
for (seek = iop->io_seek; seek < CLOCK_LEN ; seek++) {
if (ioputc (read_cmos (seek), iop) == -1)
break;
}
/*
* Now that we are done reading the CMOS, let
* the clock loose.
*/
unlock_clock ();
break;
case DEV_CMOS:
/*
* Don't go past the end of the CMOS.
*/
if (iop->io_seek >= CMOS_LEN)
break;
/*
* Read the requested data out of the CMOS.
*/
for (seek = iop->io_seek; seek < CMOS_LEN; seek ++) {
if (ioputc (read_cmos (seek), iop) == -1)
break;
}
break;
case DEV_BOOTGIFT:
/*
* Reads all from the data structure boot_gift.
*/
if (iop->io_seek < BG_LEN) {
bytesRead = iop->io_ioc;
/*
* Copy no more than to the end of boot_gift.
*/
if (iop->io_seek + bytesRead > BG_LEN)
bytesRead = BG_LEN - iop->io_seek;
iowrite (iop, (char *) & boot_gift + iop->io_seek,
bytesRead);
}
break;
case DEV_PS:
__GLOBAL_LOCK_PROCESS_TABLE ("nlread ()");
/* Main driver loop. Go through all processes. Fill struct PS
* and send put to user buffer.
*/
for (pp1 = & procq; (pp1 = pp1->p_nforw) != & procq; ) {
int i; /* loop index */
unsigned uLen; /* Process size */
unsigned uLenR; /* Real process size */
/* Check if driver can send next proc data */
if (iop->io_ioc < sizeof (stMonitor))
break;
/* Calculate the size of process. */
uLen = uLenR = 0;
for (i = 0 ; i < NUSEG ; i++) {
SEG * sp;
if ((sp = pp1->p_segl [i].sr_segp) == NULL)
continue;
uLenR += sp->s_size;
if (i == SIUSERP /* || i == SIAUXIL */)
continue;
uLen += sp->s_size;
}
memcpy (psData.u_comm, pp1->p_comm, ARGSZ);
if (pp1->p_sleep == NULL)
psData.u_sleep [0] = 0;
else
strncpy (psData.u_sleep, pp1->p_sleep,
sizeof (psData.u_sleep));
/* fill up stMonitor */
psData.p_pid = pp1->p_pid;
psData.p_ppid = pp1->p_ppid;
psData.p_uid = pp1->p_credp->cr_uid;
psData.p_ruid = pp1->p_credp->cr_ruid;
#if 1
psData.p_rgid = pp1->p_group;
#else
psData.p_rgid = pp1->p_credp->cr_rgid;
#endif
psData.p_state = pp1->p_state;
psData.p_flags = pp1->p_flags;
psData.rrun = (char *) pp1 != pp1->p_event;
psData.p_event = pp1->p_event;
psData.p_ttdev = pp1->p_ttdev;
psData.p_nice = pp1->p_nice;
psData.size = (short) (uLen >> 10);
psData.rsize = (short) (uLenR >> 10);
psData.p_schedPri = pp1->p_schedPri;
psData.p_utime = pp1->p_utime;
psData.p_stime = pp1->p_stime;
memcpy (psData.pr_argv, psBuf, ARGSZ);
/* send data to user */
iowrite (iop, (char *) & psData, sizeof (stMonitor));
}
__GLOBAL_UNLOCK_PROCESS_TABLE ();
break;
case DEV_KMEMHI:
iowrite (iop, (caddr_t) iop->io_seek - KMEMHI_BASE,
iop->io_ioc);
if (get_user_error () == EFAULT)
set_user_error (0);
break;
default:
set_user_error (ENXIO);
}
}
/*
* Null/memory write routine.
*/
static void
nlwrite(dev, iop)
dev_t dev;
IO *iop;
{
unsigned bytesWrit;
unsigned seek;
int ch;
switch (minor (dev)) {
case DEV_NULL:
/*
* Tell caller all bytes were written.
*/
iop->io_ioc = 0;
break;
case DEV_MEM:
while (iop->io_ioc) {
int src = iop->io.pbase;
int dest = iop->io_seek;
int numBytes = PXCOPY_LIM;
if (numBytes > iop->io_ioc)
numBytes = iop->io_ioc;
bytesWrit = xpcopy (src, dest, numBytes, SEL_386_UD);
src += bytesWrit;
dest += bytesWrit;
iop->io_ioc -= bytesWrit;
if (get_user_error () == EFAULT) {
set_user_error (0);
break;
}
}
break;
case DEV_KMEM:
ioread (iop, (caddr_t) iop->io_seek, iop->io_ioc);
break;
case DEV_CLOCK:
/*
* Don't go past the end of the CLOCK.
*/
if (iop->io_seek >= CLOCK_LEN)
break;
/*
* Lock the clock before any writing.
*/
if (lock_clock () == 0) {
set_user_error (EIO);
break;
}
/*
* Write the requested data into the CMOS.
*/
for (seek = iop->io_seek ; seek < CLOCK_LEN ; seek ++) {
if ((ch = iogetc (iop)) == -1)
break;
write_cmos (seek, ch);
}
/*
* Now that we are done writing the CMOS, let
* the clock loose.
*/
unlock_clock ();
break;
case DEV_CMOS:
/*
* Don't go past the end of the CMOS.
*/
if (iop->io_seek >= CMOS_LEN)
break;
/*
* Write the requested data into the CMOS.
*/
for (seek = iop->io_seek ; seek < CMOS_LEN ; seek ++) {
if ((ch = iogetc (iop)) == -1)
break;
write_cmos (seek, ch);
}
break;
case DEV_KMEMHI:
ioread (iop, (caddr_t) iop->io_seek - KMEMHI_BASE, iop->io_ioc);
break;
default:
set_user_error (ENXIO);
}
}
#ifdef DANGEROUS /* Includes all of docall(). */
/*
* MASSIVE SECURITY HOLE! This should NOT be included in a distribution
* system. Among other problems, it becomes possible to do "setuid(0)".
*
* Call a function with arguments.
*
* Takes an array of unsigned ints. The first element is the length of
* the whole array, the second element is a pointer to the function to
* call, all other elements are arguments. At most 5 arguments may be
* passed.
*
* Returns the return value of the called fuction in uvec[0].
*/
static int
docall(uvec)
unsigned uvec [];
{
int (* func)();
unsigned kvec[7];
int retval;
printf("NLCALL security hole.\n");
/* Fetch the first element of vec. */
ukcopy (uvec, kvec, sizeof (unsigned));
if (kvec [0] < 2 || kvec[0] > 7) {
/* Invalid number of elements in uvec. */
set_user_error (EINVAL);
return -1;
}
/* Fetch the whole vector. */
ukcopy (uvec, kvec, kvec [0] * sizeof (unsigned));
/* Extract the function. */
func = (int (*)()) kvec [1];
/* Call the function with all arguments. */
retval = (* func) (kvec [2], kvec [3], kvec [4], kvec [5], kvec [6]);
kucopy (& retval, uvec, sizeof (unsigned));
return retval;
}
#endif /* DANGEROUS */
/*
* Do an ioctl call for /dev/null.
*/
static void
nlioctl(dev, cmd, vec, mode, credp, rvalp)
dev_t dev;
int cmd;
char * vec;
int mode;
cred_t * credp;
int * rvalp;
{
switch (minor (dev)) {
#ifdef KMEM_IOCTL
case DEV_KMEM:
switch (cmd) {
#ifdef DANGEROUS
case NLCALL: /* Call a function. */
* rvalp = docall (vec);
break;
#endif /* DANGEROUS */
default:
set_user_error (EINVAL);
break;
}
#endif /* KMEM_IOCTL */
case DEV_IDLE: {
/*
* Write into a two-word user struct at "vec" -
*
* Low address gets number of ticks for which system was
* idle, since boot.
*
* High address gets total number of ticks since boot.
*
* By looking at time differences of these, we can estimate
* CPU load factor - this is how xload does it.
*/
IDLESYS *mem = (IDLESYS *) vec;
if (cmd != NLIDLE) {
set_user_error (EINVAL);
break;
}
putuwd ((char *)&mem->idle_ticks, ddi_cpu_data()->dc_idle_ticks);
putuwd ((char *)&mem->total_ticks, lbolt);
break;
}
case DEV_FMEM: {
/*
* Compute amount of memory left for user processes
* and amount of free memory and return them in a
* two-word structure. Both values are kbytes.
*/
FREEMEM *mem = (FREEMEM *) vec;
if (cmd != NLFREE) {
set_user_error (EINVAL);
break;
}
putuwd ((char *)&mem->avail_mem, (sysmem.efree - sysmem.tfree) << 2);
putuwd ((char *)&mem->free_mem, (sysmem.pfree - sysmem.tfree) << 2);
break;
}
default:
set_user_error (EINVAL);
break;
}
}
/*
* Configuration table.
*/
CON nlcon ={
DFCHR, /* Flags */
0, /* Major index */
nlopen, /* Open */
nlclose, /* Close */
NULL, /* Block */
nlread, /* Read */
nlwrite, /* Write */
nlioctl, /* Ioctl */
NULL, /* Powerfail */
NULL, /* Timeout */
NULL, /* Load */
NULL /* Unload */
};