* [9fans] Apm Enhancement + some bonuses
@ 2003-02-22 20:19 Maksim Radziwill
2003-02-22 21:51 ` Russ Cox
2003-02-23 2:13 ` Skip Tavakkolian
0 siblings, 2 replies; 4+ messages in thread
From: Maksim Radziwill @ 2003-02-22 20:19 UTC (permalink / raw)
To: 9fans
[-- Attachment #1.1: Type: text/plain, Size: 2420 bytes --]
Hi all,
I wrote a (ugly) enhancement to the plan9 kernel that allows users using
workstations
To press ^P and power off the machine, this is especially useful on
(old) notebooks
That has a broken power switch.
I have added too some extra feature ^T^T 5 will turn off the screen
^T^T 6 will put it into a ready state
^T^T f confirms apm is present in the kernel
^T^T g is equivalent to ^P on workstations
There was too an options to poweroff disks, but it may hang the whole
system.
Normally aux/apm disallows you to power off because you won't be able to
sync the fs
And if you halt the fs you won't be able to power off using aux/apm . ;)
(Of course if you uncomment those two lines that allows suspending the
sys in the aux/apm code)
What is the solutions to this ? Place the power off sequence in the
kernel as a key shortcut
We will use ^P because using it will power off workstations and reboot
cpu's , a nice behavior
(since we won't need to power off cpu server)
VERY IMPORTANT !!! This thing won't work if aux/apm isn't working in the
background.
And if boot.ini don't have the apm0=
line added.
So how to install the code?
Copy the attached lib.h to /sys/src/9/port/lib.h
The devcons.c to /sys/src/9/port/devcons.c
The apm.c to /sys/src/9/pc/apm.c
And recompile the kernel and then install it.
This is a *extremely* dirty (and ugly) hack, don't blame me for it
please.
If you ever want to add some shorcuts for other devices (like net cards)
Then you'll have to know that the poweroff_call functions
Takes too argument
- Which device
- What to do with it
So as an example poweroff_call(ALL_DEVICES,POWEROFF);
Will effectively power off all devices
The defines for the Devices are self explanatory:
#define BIOS 0x00
#define ALL_DEVICES 0x01
#define DISPLAY 0x1ff
#define SEC_STORAGE 0x2ff
#define PARALE_PORT 0x3ff
#define SERIAL_PORT 0x4ff
#define NET_ADAPTER 0x5ff
#define PCMCIA 0x6ff
#define BATTERY 0x80ff
Consider BIOS equivalent to ALL_DEVICES
The same for the action taken:
#define POWEROFF 0x03
#define SLEEP_DEEP 0x02
#define SLEEP_LIGHT 0x01
#define READY 0x00
Anyway HAVE FUN,
Maks
P.S: If you got some troubles just mail me
I'm planning too to clean that stuff one of those days.
[-- Attachment #1.2: Type: text/html, Size: 19095 bytes --]
[-- Attachment #2: apm.c --]
[-- Type: application/octet-stream, Size: 4073 bytes --]
/*
* Interface to Advanced Power Management 1.2 BIOS
*
* This is, in many ways, a giant hack, and when things settle down
* a bit and standardize, hopefully we can write a driver that deals
* more directly with the hardware and thus might be a bit cleaner.
*
* ACPI might be the answer, but at the moment this is simpler
* and more widespread.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
extern int apmfarcall(ushort, ulong, Ureg*); /* apmjump.s */
static int
getreg(ulong *reg, ISAConf *isa, char *name)
{
int i;
int nl;
nl = strlen(name);
for(i=0; i<isa->nopt; i++){
if(cistrncmp(isa->opt[i], name, nl)==0 && isa->opt[i][nl] == '='){
*reg = strtoul(isa->opt[i]+nl+1, nil, 16);
return 0;
}
}
return -1;
}
/*
* Segment descriptors look like this.
*
* d1: [base 31:24] [gran] [is32bit] [0] [unused] [limit 19:16]
[present] [privlev] [type 3:0] [base 23:16]
* d0: [base 15:00] [limit 15:00]
*
* gran is 0 for 1-byte granularity, 1 for 4k granularity
* type is 0 for system segment, 1 for code/data.
*
* clearly we know way too much about the memory unit.
* however, knowing this much about the memory unit
* means that the memory unit need not know anything
* about us.
*
* what a crock.
*/
static void
setgdt(int sel, ulong base, ulong limit, int flag)
{
if(sel < 0 || sel >= NGDT)
panic("setgdt");
base = (ulong)KADDR(base);
m->gdt[sel].d0 = (base<<16) | (limit&0xFFFF);
m->gdt[sel].d1 = (base&0xFF000000) | (limit&0x000F0000) |
((base>>16)&0xFF) | SEGP | SEGPL(0) | flag;
}
static ulong ax, cx, dx, di, ebx, esi;
static Ureg apmu;
static long
apmread(Chan*, void *a, long n, vlong off)
{
if(off < 0)
error("badarg");
if(n+off > sizeof apmu)
n = sizeof apmu - off;
if(n <= 0)
return 0;
memmove(a, (char*)&apmu+off, n);
return n;
}
void
poweroff_call(int what,int how){
int sec;
Ureg happyregs;
happyregs.ax = 0x5307;
happyregs.cx = how;
happyregs.bx = what;
sec = splhi();
apmfarcall(APMCSEL, ebx, &happyregs);
splx(sec);
return;
}
static long
apmwrite(Chan*, void *a, long n, vlong off)
{
int s;
if(off || n != sizeof apmu)
error("write a Ureg");
memmove(&apmu, a, sizeof apmu);
s = splhi();
apmfarcall(APMCSEL, ebx, &apmu);
splx(s);
return n;
}
void
apmlink(void)
{
ISAConf isa;
char *s;
if(isaconfig("apm", 0, &isa) == 0)
return;
/*
* APM info passed from boot loader.
* Now we need to set up the GDT entries for APM.
*
* AX = 32-bit code segment base address
* EBX = 32-bit code segment offset
* CX = 16-bit code segment base address
* DX = 32-bit data segment base address
* ESI = <16-bit code segment length> <32-bit code segment length> (hi then lo)
* DI = 32-bit data segment length
*/
if(getreg(&ax, &isa, s="ax") < 0
|| getreg(&ebx, &isa, s="ebx") < 0
|| getreg(&cx, &isa, s="cx") < 0
|| getreg(&dx, &isa, s="dx") < 0
|| getreg(&esi, &isa, s="esi") < 0
|| getreg(&di, &isa, s="di") < 0){
print("apm: missing register %s\n", s);
return;
}
/*
* The NEC Versa SX bios does not report the correct 16-bit code
* segment length when loaded directly from mbr -> 9load (as compared
* with going through ld.com). We'll make both code segments 64k-1 bytes.
*/
esi = 0xFFFFFFFF;
/*
* We are required by the BIOS to set up three consecutive segments,
* one for the APM 32-bit code, one for the APM 16-bit code, and
* one for the APM data. The BIOS handler uses the code segment it
* get called with to determine the other two segment selector.
*/
setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD);
setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR);
setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD);
addarchfile("apm", 0660, apmread, apmwrite);
print("apm0: configured cbase %.8lux off %.8lux\n", ax<<4, ebx);
return;
}
[-- Attachment #3: devcons.c --]
[-- Type: application/octet-stream, Size: 22248 bytes --]
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include <authsrv.h>
void (*consdebug)(void) = nil;
void (*screenputs)(char*, int) = nil;
Queue* kbdq; /* unprocessed console input */
Queue* lineq; /* processed console input */
Queue* serialoq; /* serial console output */
Queue* kprintoq; /* console output, for /dev/kprint */
ulong kprintinuse; /* test and set whether /dev/kprint is open */
int iprintscreenputs = 1;
static struct
{
QLock;
int raw; /* true if we shouldn't process input */
int ctl; /* number of opens to the control file */
int x; /* index into line */
char line[1024]; /* current input line */
int count;
int ctlpoff;
/* a place to save up characters at interrupt time before dumping them in the queue */
Lock lockputc;
char istage[512];
char *iw;
char *ir;
char *ie;
} kbd = {
.iw = kbd.istage,
.ir = kbd.istage,
.ie = kbd.istage + sizeof(kbd.istage),
};
char *sysname;
vlong fasthz;
static void seedrand(void);
static int readtime(ulong, char*, int);
static int readbintime(char*, int);
static int writetime(char*, int);
static int writebintime(char*, int);
enum
{
CMreboot,
CMpanic,
};
Cmdtab rebootmsg[] =
{
CMreboot, "reboot", 0,
CMpanic, "panic", 0,
};
void
printinit(void)
{
lineq = qopen(2*1024, 0, 0, 0);
if(lineq == nil)
panic("printinit");
qnoblock(lineq, 1);
}
int
consactive(void)
{
if(serialoq)
return qlen(serialoq) > 0;
return 0;
}
void
prflush(void)
{
ulong now;
now = m->ticks;
while(consactive())
if(m->ticks - now >= HZ)
break;
}
/*
* Print a string on the console. Convert \n to \r\n for serial
* line consoles. Locking of the queues is left up to the screen
* or uart code. Multi-line messages to serial consoles may get
* interspersed with other messages.
*/
static void
putstrn0(char *str, int n, int usewrite)
{
int m;
char *t;
/*
* if someone is reading /dev/kprint,
* put the message there.
* if not and there's an attached bit mapped display,
* put the message there.
*
* if there's a serial line being used as a console,
* put the message there.
*/
if(kprintoq != nil && !qisclosed(kprintoq)){
if(usewrite)
qwrite(kprintoq, str, n);
else
qiwrite(kprintoq, str, n);
}else if(screenputs != nil)
screenputs(str, n);
if(serialoq == nil){
uartputs(str, n);
return;
}
while(n > 0) {
t = memchr(str, '\n', n);
if(t && !kbd.raw) {
m = t-str;
if(usewrite){
qwrite(serialoq, str, m);
qwrite(serialoq, "\r\n", 2);
} else {
qiwrite(serialoq, str, m);
qiwrite(serialoq, "\r\n", 2);
}
n -= m+1;
str = t+1;
} else {
if(usewrite)
qwrite(serialoq, str, n);
else
qiwrite(serialoq, str, n);
break;
}
}
}
void
putstrn(char *str, int n)
{
putstrn0(str, n, 0);
}
int noprint;
int
print(char *fmt, ...)
{
int n;
va_list arg;
char buf[PRINTSIZE];
if(noprint)
return -1;
va_start(arg, fmt);
n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
va_end(arg);
putstrn(buf, n);
return n;
}
int
iprint(char *fmt, ...)
{
int n, s;
va_list arg;
char buf[PRINTSIZE];
s = splhi();
va_start(arg, fmt);
n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
va_end(arg);
if(screenputs != nil && iprintscreenputs)
screenputs(buf, n);
uartputs(buf, n);
splx(s);
return n;
}
void
panic(char *fmt, ...)
{
int n;
va_list arg;
char buf[PRINTSIZE];
static int panicking;
kprintoq = nil; /* don't try to write to /dev/kprint */
if(panicking)
for(;;);
panicking = 1;
splhi();
strcpy(buf, "panic: ");
va_start(arg, fmt);
n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf;
va_end(arg);
buf[n] = '\n';
uartputs(buf, n+1);
if(consdebug)
(*consdebug)();
spllo();
prflush();
putstrn(buf, n+1);
dumpstack();
exit(1);
}
void
_assert(char *fmt)
{
panic("assert failed: %s", fmt);
}
int
pprint(char *fmt, ...)
{
int n;
Chan *c;
va_list arg;
char buf[2*PRINTSIZE];
if(up == nil || up->fgrp == nil)
return 0;
c = up->fgrp->fd[2];
if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR))
return 0;
n = sprint(buf, "%s %lud: ", up->text, up->pid);
va_start(arg, fmt);
n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
va_end(arg);
if(waserror())
return 0;
devtab[c->type]->write(c, buf, n, c->offset);
poperror();
lock(c);
c->offset += n;
unlock(c);
return n;
}
static void
echoscreen(char *buf, int n)
{
char *e, *p;
char ebuf[128];
int x;
p = ebuf;
e = ebuf + sizeof(ebuf) - 4;
while(n-- > 0){
if(p >= e){
screenputs(ebuf, p - ebuf);
p = ebuf;
}
x = *buf++;
if(x == 0x15){
*p++ = '^';
*p++ = 'U';
*p++ = '\n';
} else
*p++ = x;
}
if(p != ebuf)
screenputs(ebuf, p - ebuf);
}
static void
echoserialoq(char *buf, int n)
{
char *e, *p;
char ebuf[128];
int x;
p = ebuf;
e = ebuf + sizeof(ebuf) - 4;
while(n-- > 0){
if(p >= e){
qiwrite(serialoq, ebuf, p - ebuf);
p = ebuf;
}
x = *buf++;
if(x == '\n'){
*p++ = '\r';
*p++ = '\n';
} else if(x == 0x15){
*p++ = '^';
*p++ = 'U';
*p++ = '\n';
} else
*p++ = x;
}
if(p != ebuf)
qiwrite(serialoq, ebuf, p - ebuf);
}
void
echo(char *buf, int n)
{
static int ctrlt, pid;
extern ulong etext;
int x;
char *e, *p;
e = buf+n;
for(p = buf; p < e; p++){
switch(*p){
case 0x10: /* ^P */
if(cpuserver && !kbd.ctlpoff){
active.exiting = 1;
return;
}else{
// Poweroff code...
poweroff_call(1,3); // All,Poweroff
print("APM failed.\n");
}
break;
case 0x14: /* ^T */
ctrlt++;
if(ctrlt > 2)
ctrlt = 2;
continue;
}
if(ctrlt != 2)
continue;
/* ^T escapes */
ctrlt = 0;
switch(*p){
case '5': // Disp. poweroff
poweroff_call(0x1ff,3);
return;
case '6': // Disp. ready
poweroff_call(0x1ff,0);
return;
case 'f': // Check if present
print("APM functions present\n");
return;
case 'S':
x = splhi();
dumpstack();
procdump();
splx(x);
return;
case 's':
dumpstack();
return;
case 'x':
xsummary();
ixsummary();
mallocsummary();
pagersummary();
return;
case 'd':
if(consdebug == nil)
consdebug = rdb;
else
consdebug = nil;
print("consdebug now 0x%p\n", consdebug);
return;
case 'D':
if(consdebug == nil)
consdebug = rdb;
consdebug();
return;
case 'p':
x = spllo();
procdump();
splx(x);
return;
case 'q':
scheddump();
return;
case 'k':
if(!cpuserver)
killbig();
return;
case 'r':
exit(0);
return;
case 'g': // Poweroff all
x = splhi();
poweroff_call(1,3);
print("APM Failed.");
splx(x);
return;
}
}
qproduce(kbdq, buf, n);
if(kbd.raw)
return;
if(screenputs != nil)
echoscreen(buf, n);
if(serialoq)
echoserialoq(buf, n);
}
/*
* Called by a uart interrupt for console input.
*
* turn '\r' into '\n' before putting it into the queue.
*/
int
kbdcr2nl(Queue*, int ch)
{
char *next;
ilock(&kbd.lockputc); /* just a mutex */
if(ch == '\r' && !kbd.raw)
ch = '\n';
next = kbd.iw+1;
if(next >= kbd.ie)
next = kbd.istage;
if(next != kbd.ir){
*kbd.iw = ch;
kbd.iw = next;
}
iunlock(&kbd.lockputc);
return 0;
}
/*
* Put character, possibly a rune, into read queue at interrupt time.
* Called at interrupt time to process a character.
*/
int
kbdputc(Queue*, int ch)
{
int i, n;
char buf[3];
Rune r;
char *next;
if(kbd.ir == nil)
return 0; /* in case we're not inited yet */
ilock(&kbd.lockputc); /* just a mutex */
r = ch;
n = runetochar(buf, &r);
for(i = 0; i < n; i++){
next = kbd.iw+1;
if(next >= kbd.ie)
next = kbd.istage;
if(next == kbd.ir)
break;
*kbd.iw = buf[i];
kbd.iw = next;
}
iunlock(&kbd.lockputc);
return 0;
}
/*
* we save up input characters till clock time to reduce
* per character interrupt overhead.
*/
static void
kbdputcclock(void)
{
char *iw;
/* this amortizes cost of qproduce */
if(kbd.iw != kbd.ir){
iw = kbd.iw;
if(iw < kbd.ir){
echo(kbd.ir, kbd.ie-kbd.ir);
kbd.ir = kbd.istage;
}
echo(kbd.ir, iw-kbd.ir);
kbd.ir = iw;
}
}
enum{
Qdir,
Qbintime,
Qcons,
Qconsctl,
Qcputime,
Qdrivers,
Qkprint,
Qhostdomain,
Qhostowner,
Qnull,
Qosversion,
Qpgrpid,
Qpid,
Qppid,
Qrandom,
Qreboot,
Qswap,
Qsysname,
Qsysstat,
Qtime,
Quser,
Qzero,
};
enum
{
VLNUMSIZE= 22,
};
static Dirtab consdir[]={
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
"bintime", {Qbintime}, 24, 0664,
"cons", {Qcons}, 0, 0660,
"consctl", {Qconsctl}, 0, 0220,
"cputime", {Qcputime}, 6*NUMSIZE, 0444,
"drivers", {Qdrivers}, 0, 0444,
"hostdomain", {Qhostdomain}, DOMLEN, 0664,
"hostowner", {Qhostowner}, 0, 0664,
"kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440,
"null", {Qnull}, 0, 0666,
"osversion", {Qosversion}, 0, 0444,
"pgrpid", {Qpgrpid}, NUMSIZE, 0444,
"pid", {Qpid}, NUMSIZE, 0444,
"ppid", {Qppid}, NUMSIZE, 0444,
"random", {Qrandom}, 0, 0444,
"reboot", {Qreboot}, 0, 0664,
"swap", {Qswap}, 0, 0664,
"sysname", {Qsysname}, 0, 0664,
"sysstat", {Qsysstat}, 0, 0666,
"time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664,
"user", {Quser}, 0, 0666,
"zero", {Qzero}, 0, 0444,
};
int
readnum(ulong off, char *buf, ulong n, ulong val, int size)
{
char tmp[64];
snprint(tmp, sizeof(tmp), "%*.0lud", size-1, val);
tmp[size-1] = ' ';
if(off >= size)
return 0;
if(off+n > size)
n = size-off;
memmove(buf, tmp+off, n);
return n;
}
int
readstr(ulong off, char *buf, ulong n, char *str)
{
int size;
size = strlen(str);
if(off >= size)
return 0;
if(off+n > size)
n = size-off;
memmove(buf, str+off, n);
return n;
}
static void
consinit(void)
{
todinit();
randominit();
addclock0link(kbdputcclock);
}
static Chan*
consattach(char *spec)
{
return devattach('c', spec);
}
static Walkqid*
conswalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
}
static int
consstat(Chan *c, uchar *dp, int n)
{
return devstat(c, dp, n, consdir, nelem(consdir), devgen);
}
static Chan*
consopen(Chan *c, int omode)
{
c->aux = nil;
c = devopen(c, omode, consdir, nelem(consdir), devgen);
switch((ulong)c->qid.path){
case Qconsctl:
qlock(&kbd);
kbd.ctl++;
qunlock(&kbd);
break;
case Qkprint:
if(tas(&kprintinuse) != 0){
c->flag &= ~COPEN;
error(Einuse);
}
if(kprintoq == nil){
kprintoq = qopen(8*1024, -1, 0, 0);
if(kprintoq == nil){
c->flag &= ~COPEN;
error(Enomem);
}
qnoblock(kprintoq, 1);
}else
qreopen(kprintoq);
c->iounit = qiomaxatomic;
break;
}
return c;
}
static void
consclose(Chan *c)
{
switch((ulong)c->qid.path){
/* last close of control file turns off raw */
case Qconsctl:
if(c->flag&COPEN){
qlock(&kbd);
if(--kbd.ctl == 0)
kbd.raw = 0;
qunlock(&kbd);
}
break;
/* close of kprint allows other opens */
case Qkprint:
if(c->flag & COPEN){
kprintinuse = 0;
qhangup(kprintoq, nil);
}
break;
}
}
static long
consread(Chan *c, void *buf, long n, vlong off)
{
ulong l;
Mach *mp;
char *b, *bp;
char tmp[128]; /* must be >= 6*NUMSIZE */
char *cbuf = buf;
int ch, i, k, id, eol;
vlong offset = off;
if(n <= 0)
return n;
switch((ulong)c->qid.path){
case Qdir:
return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
case Qcons:
qlock(&kbd);
if(waserror()) {
qunlock(&kbd);
nexterror();
}
if(kbd.raw) {
if(qcanread(lineq))
n = qread(lineq, buf, n);
else {
/* read as much as possible */
do {
i = qread(kbdq, cbuf, n);
cbuf += i;
n -= i;
} while (n>0 && qcanread(kbdq));
n = cbuf - (char*)buf;
}
} else {
while(!qcanread(lineq)) {
qread(kbdq, &kbd.line[kbd.x], 1);
ch = kbd.line[kbd.x];
eol = 0;
switch(ch){
case '\b':
if(kbd.x)
kbd.x--;
break;
case 0x15:
kbd.x = 0;
break;
case '\n':
case 0x04:
eol = 1;
default:
kbd.line[kbd.x++] = ch;
break;
}
if(kbd.x == sizeof(kbd.line) || eol){
if(ch == 0x04)
kbd.x--;
qwrite(lineq, kbd.line, kbd.x);
kbd.x = 0;
}
}
n = qread(lineq, buf, n);
}
qunlock(&kbd);
poperror();
return n;
case Qcputime:
k = offset;
if(k >= 6*NUMSIZE)
return 0;
if(k+n > 6*NUMSIZE)
n = 6*NUMSIZE - k;
/* easiest to format in a separate buffer and copy out */
for(i=0; i<6 && NUMSIZE*i<k+n; i++){
l = up->time[i];
if(i == TReal)
l = MACHP(0)->ticks - l;
l = TK2MS(l);
readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
}
memmove(buf, tmp+k, n);
return n;
case Qkprint:
return qread(kprintoq, buf, n);
case Qpgrpid:
return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE);
case Qpid:
return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);
case Qppid:
return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);
case Qtime:
return readtime((ulong)offset, buf, n);
case Qbintime:
return readbintime(buf, n);
case Qhostowner:
return readstr((ulong)offset, buf, n, eve);
case Qhostdomain:
return readstr((ulong)offset, buf, n, hostdomain);
case Quser:
return readstr((ulong)offset, buf, n, up->user);
case Qnull:
return 0;
case Qsysstat:
b = smalloc(conf.nmach*(NUMSIZE*8+1) + 1); /* +1 for NUL */
bp = b;
for(id = 0; id < 32; id++) {
if(active.machs & (1<<id)) {
mp = MACHP(id);
readnum(0, bp, NUMSIZE, id, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
bp += NUMSIZE;
readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
bp += NUMSIZE;
*bp++ = '\n';
}
}
if(waserror()){
free(b);
nexterror();
}
n = readstr((ulong)offset, buf, n, b);
free(b);
poperror();
return n;
case Qswap:
sprint(tmp, "%lud/%lud memory %lud/%lud swap\n",
palloc.user-palloc.freecount,
palloc.user, conf.nswap-swapalloc.free, conf.nswap);
return readstr((ulong)offset, buf, n, tmp);
case Qsysname:
if(sysname == nil)
return 0;
return readstr((ulong)offset, buf, n, sysname);
case Qrandom:
return randomread(buf, n);
case Qdrivers:
b = malloc(READSTR);
if(b == nil)
error(Enomem);
n = 0;
for(i = 0; devtab[i] != nil; i++)
n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name);
if(waserror()){
free(b);
nexterror();
}
n = readstr((ulong)offset, buf, n, b);
free(b);
poperror();
return n;
case Qzero:
memset(buf, 0, n);
return n;
case Qosversion:
snprint(tmp, sizeof tmp, "2000");
n = readstr((ulong)offset, buf, n, tmp);
return n;
default:
print("consread 0x%llux\n", c->qid.path);
error(Egreg);
}
return -1; /* never reached */
}
static long
conswrite(Chan *c, void *va, long n, vlong off)
{
char buf[256];
long l, bp;
char *a = va;
Mach *mp;
int id, fd;
Chan *swc;
ulong offset = off;
Cmdbuf *cb;
Cmdtab *ct;
switch((ulong)c->qid.path){
case Qcons:
/*
* Can't page fault in putstrn, so copy the data locally.
*/
l = n;
while(l > 0){
bp = l;
if(bp > sizeof buf)
bp = sizeof buf;
memmove(buf, a, bp);
putstrn0(buf, bp, 1);
a += bp;
l -= bp;
}
break;
case Qconsctl:
if(n >= sizeof(buf))
n = sizeof(buf)-1;
strncpy(buf, a, n);
buf[n] = 0;
for(a = buf; a;){
if(strncmp(a, "rawon", 5) == 0){
qlock(&kbd);
if(kbd.x){
qwrite(kbdq, kbd.line, kbd.x);
kbd.x = 0;
}
kbd.raw = 1;
qunlock(&kbd);
} else if(strncmp(a, "rawoff", 6) == 0){
qlock(&kbd);
kbd.raw = 0;
kbd.x = 0;
qunlock(&kbd);
} else if(strncmp(a, "ctlpon", 6) == 0){
kbd.ctlpoff = 0;
} else if(strncmp(a, "ctlpoff", 7) == 0){
kbd.ctlpoff = 1;
}
if(a = strchr(a, ' '))
a++;
}
break;
case Qtime:
if(!iseve())
error(Eperm);
return writetime(a, n);
case Qbintime:
if(!iseve())
error(Eperm);
return writebintime(a, n);
case Qhostowner:
return hostownerwrite(a, n);
case Qhostdomain:
return hostdomainwrite(a, n);
case Quser:
return userwrite(a, n);
case Qnull:
break;
case Qreboot:
if(!iseve())
error(Eperm);
cb = parsecmd(a, n);
if(waserror()) {
free(cb);
nexterror();
}
ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
switch(ct->index) {
case CMreboot:
rebootcmd(cb->nf-1, cb->f+1);
break;
case CMpanic:
panic("/dev/reboot");
}
poperror();
free(cb);
break;
case Qsysstat:
for(id = 0; id < 32; id++) {
if(active.machs & (1<<id)) {
mp = MACHP(id);
mp->cs = 0;
mp->intr = 0;
mp->syscall = 0;
mp->pfault = 0;
mp->tlbfault = 0;
mp->tlbpurge = 0;
}
}
break;
case Qswap:
if(n >= sizeof buf)
error(Egreg);
memmove(buf, va, n); /* so we can NUL-terminate */
buf[n] = 0;
/* start a pager if not already started */
if(strncmp(buf, "start", 5) == 0){
kickpager();
break;
}
if(cpuserver && !iseve())
error(Eperm);
if(buf[0]<'0' || '9'<buf[0])
error(Ebadarg);
fd = strtoul(buf, 0, 0);
swc = fdtochan(fd, -1, 1, 1);
setswapchan(swc);
break;
case Qsysname:
if(offset != 0)
error(Ebadarg);
if(n <= 0 || n >= sizeof buf)
error(Ebadarg);
strncpy(buf, a, n);
buf[n] = 0;
if(buf[n-1] == '\n')
buf[n-1] = 0;
kstrdup(&sysname, buf);
break;
default:
print("conswrite: 0x%llux\n", c->qid.path);
error(Egreg);
}
return n;
}
Dev consdevtab = {
'c',
"cons",
devreset,
consinit,
devshutdown,
consattach,
conswalk,
consstat,
consopen,
devcreate,
consclose,
consread,
devbread,
conswrite,
devbwrite,
devremove,
devwstat,
};
static ulong randn;
static void
seedrand(void)
{
randomread((void*)&randn, sizeof(randn));
}
int
nrand(int n)
{
if(randn == 0)
seedrand();
randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
return (randn>>16) % n;
}
int
rand(void)
{
nrand(1);
return randn;
}
static uvlong uvorder = 0x0001020304050607ULL;
static uchar*
le2vlong(vlong *to, uchar *f)
{
uchar *t, *o;
int i;
t = (uchar*)to;
o = (uchar*)&uvorder;
for(i = 0; i < sizeof(vlong); i++)
t[o[i]] = f[i];
return f+sizeof(vlong);
}
static uchar*
vlong2le(uchar *t, vlong from)
{
uchar *f, *o;
int i;
f = (uchar*)&from;
o = (uchar*)&uvorder;
for(i = 0; i < sizeof(vlong); i++)
t[i] = f[o[i]];
return t+sizeof(vlong);
}
static long order = 0x00010203;
static uchar*
le2long(long *to, uchar *f)
{
uchar *t, *o;
int i;
t = (uchar*)to;
o = (uchar*)ℴ
for(i = 0; i < sizeof(long); i++)
t[o[i]] = f[i];
return f+sizeof(long);
}
static uchar*
long2le(uchar *t, long from)
{
uchar *f, *o;
int i;
f = (uchar*)&from;
o = (uchar*)ℴ
for(i = 0; i < sizeof(long); i++)
t[i] = f[o[i]];
return t+sizeof(long);
}
char *Ebadtimectl = "bad time control";
/*
* like the old #c/time but with added info. Return
*
* secs nanosecs fastticks fasthz
*/
static int
readtime(ulong off, char *buf, int n)
{
vlong nsec, ticks;
long sec;
char str[7*NUMSIZE];
nsec = todget(&ticks);
if(fasthz == 0LL)
fastticks((uvlong*)&fasthz);
sec = nsec/1000000000ULL;
snprint(str, sizeof(str), "%*.0lud %*.0llud %*.0llud %*.0llud ",
NUMSIZE-1, sec,
VLNUMSIZE-1, nsec,
VLNUMSIZE-1, ticks,
VLNUMSIZE-1, fasthz);
return readstr(off, buf, n, str);
}
/*
* set the time in seconds
*/
static int
writetime(char *buf, int n)
{
char b[13];
long i;
vlong now;
if(n >= sizeof(b))
error(Ebadtimectl);
strncpy(b, buf, n);
b[n] = 0;
i = strtol(b, 0, 0);
if(i <= 0)
error(Ebadtimectl);
now = i*1000000000LL;
todset(now, 0, 0);
return n;
}
/*
* read binary time info. all numbers are little endian.
* ticks and nsec are syncronized.
*/
static int
readbintime(char *buf, int n)
{
int i;
vlong nsec, ticks;
uchar *b = (uchar*)buf;
i = 0;
if(fasthz == 0LL)
fastticks((uvlong*)&fasthz);
nsec = todget(&ticks);
if(n >= 3*sizeof(uvlong)){
vlong2le(b+2*sizeof(uvlong), fasthz);
i += sizeof(uvlong);
}
if(n >= 2*sizeof(uvlong)){
vlong2le(b+sizeof(uvlong), ticks);
i += sizeof(uvlong);
}
if(n >= 8){
vlong2le(b, nsec);
i += sizeof(vlong);
}
return i;
}
/*
* set any of the following
* - time in nsec
* - nsec trim applied over some seconds
* - clock frequency
*/
static int
writebintime(char *buf, int n)
{
uchar *p;
vlong delta;
long period;
n--;
p = (uchar*)buf + 1;
switch(*buf){
case 'n':
if(n < sizeof(vlong))
error(Ebadtimectl);
le2vlong(&delta, p);
todset(delta, 0, 0);
break;
case 'd':
if(n < sizeof(vlong)+sizeof(long))
error(Ebadtimectl);
p = le2vlong(&delta, p);
le2long(&period, p);
todset(-1, delta, period);
break;
case 'f':
if(n < sizeof(uvlong))
error(Ebadtimectl);
le2vlong(&fasthz, p);
todsetfreq(fasthz);
break;
}
return n;
}
[-- Attachment #4: lib.h --]
[-- Type: application/octet-stream, Size: 6379 bytes --]
/*
* functions (possibly) linked in, complete, from libc.
*/
/*
* APM routines and defines
*/
#define POWEROFF 0x03
#define SLEEP_DEEP 0x02
#define SLEEP_LIGHT 0x01
#define READY 0x00
#define BIOS 0x00
#define ALL_DEVICES 0x01
#define DISPLAY 0x1ff
#define SEC_STORAGE 0x2ff
#define PARALE_PORT 0x3ff
#define SERIAL_PORT 0x4ff
#define NET_ADAPTER 0x5ff
#define PCMCIA 0x6ff
#define BATTERY 0x80ff
void poweroff_call(int,int);
/*
* mem routines
*/
extern void* memccpy(void*, void*, int, ulong);
extern void* memset(void*, int, ulong);
extern int memcmp(void*, void*, ulong);
extern void* memmove(void*, void*, ulong);
extern void* memchr(void*, int, ulong);
int nbattery;
int batterystatus;
int percent;
int time;
/*
* string routines
*/
extern char* strcat(char*, char*);
extern char* strchr(char*, char);
extern char* strrchr(char*, char);
extern int strcmp(char*, char*);
extern char* strcpy(char*, char*);
extern char* strecpy(char*, char*, char*);
extern char* strncat(char*, char*, long);
extern char* strncpy(char*, char*, long);
extern int strncmp(char*, char*, long);
extern long strlen(char*);
extern char* strstr(char*, char*);
extern int atoi(char*);
extern int fullrune(char*, int);
enum
{
UTFmax = 3, /* maximum bytes per rune */
Runesync = 0x80, /* cannot represent part of a UTF sequence */
Runeself = 0x80, /* rune and UTF sequences are the same (<) */
Runeerror = 0x80, /* decoding error in UTF */
};
/*
* rune routines
*/
extern int runetochar(char*, Rune*);
extern int chartorune(Rune*, char*);
extern char* utfrune(char*, long);
extern int utflen(char*);
extern int runelen(long);
extern int abs(int);
/*
* print routines
*/
typedef struct Fmt Fmt;
typedef int (*Fmts)(Fmt*);
struct Fmt{
uchar runes; /* output buffer is runes or chars? */
void *start; /* of buffer */
void *to; /* current place in the buffer */
void *stop; /* end of the buffer; overwritten if flush fails */
int (*flush)(Fmt *); /* called when to == stop */
void *farg; /* to make flush a closure */
int nfmt; /* num chars formatted so far */
va_list args; /* args passed to dofmt */
int r; /* % format Rune */
int width;
int prec;
ulong flags;
};
extern int print(char*, ...);
extern char* seprint(char*, char*, char*, ...);
extern char* vseprint(char*, char*, char*, va_list);
extern int snprint(char*, int, char*, ...);
extern int vsnprint(char*, int, char*, va_list);
extern int sprint(char*, char*, ...);
extern int fmtinstall(int, int (*)(Fmt*));
extern int quotefmtinstall(void);
extern int fmtprint(Fmt*, char*, ...);
extern int fmtstrcpy(Fmt*, char*);
#pragma varargck argpos fmtprint 2
#pragma varargck argpos print 1
#pragma varargck argpos seprint 3
#pragma varargck argpos snprint 3
#pragma varargck argpos sprint 2
/*
* one-of-a-kind
*/
extern char* cleanname(char*);
extern ulong getcallerpc(void*);
extern long strtol(char*, char**, int);
extern ulong strtoul(char*, char**, int);
extern vlong strtoll(char*, char**, int);
extern uvlong strtoull(char*, char**, int);
extern char etext[];
extern char edata[];
extern char end[];
extern int getfields(char*, char**, int, int, char*);
extern int tokenize(char*, char**, int);
extern int dec64(uchar*, int, char*, int);
/*
* Syscall data structures
*/
#define MORDER 0x0003 /* mask for bits defining order of mounting */
#define MREPL 0x0000 /* mount replaces object */
#define MBEFORE 0x0001 /* mount goes before others in union directory */
#define MAFTER 0x0002 /* mount goes after others in union directory */
#define MCREATE 0x0004 /* permit creation in mounted directory */
#define MCACHE 0x0010 /* cache some data */
#define MMASK 0x0017 /* all bits on */
#define OREAD 0 /* open for read */
#define OWRITE 1 /* write */
#define ORDWR 2 /* read and write */
#define OEXEC 3 /* execute, == read but check execute permission */
#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
#define OCEXEC 32 /* or'ed in, close on exec */
#define ORCLOSE 64 /* or'ed in, remove on close */
#define OEXCL 0x1000 /* or'ed in, exclusive create */
#define NCONT 0 /* continue after note */
#define NDFLT 1 /* terminate after note */
#define NSAVE 2 /* clear note but hold state */
#define NRSTR 3 /* restore saved state */
typedef struct Qid Qid;
typedef struct Dir Dir;
typedef struct OWaitmsg OWaitmsg;
typedef struct Waitmsg Waitmsg;
#define ERRMAX 128 /* max length of error string */
#define KNAMELEN 28 /* max length of name held in kernel */
/* bits in Qid.type */
#define QTDIR 0x80 /* type bit for directories */
#define QTAPPEND 0x40 /* type bit for append only files */
#define QTEXCL 0x20 /* type bit for exclusive use files */
#define QTMOUNT 0x10 /* type bit for mounted channel */
#define QTAUTH 0x08 /* type bit for authentication file */
#define QTFILE 0x00 /* plain file */
/* bits in Dir.mode */
#define DMDIR 0x80000000 /* mode bit for directories */
#define DMAPPEND 0x40000000 /* mode bit for append only files */
#define DMEXCL 0x20000000 /* mode bit for exclusive use files */
#define DMMOUNT 0x10000000 /* mode bit for mounted channel */
#define DMREAD 0x4 /* mode bit for read permission */
#define DMWRITE 0x2 /* mode bit for write permission */
#define DMEXEC 0x1 /* mode bit for execute permission */
struct Qid
{
vlong path;
ulong vers;
uchar type;
};
struct Dir {
/* system-modified data */
ushort type; /* server type */
uint dev; /* server subtype */
/* file data */
Qid qid; /* unique id from server */
ulong mode; /* permissions */
ulong atime; /* last read time */
ulong mtime; /* last write time */
vlong length; /* file length: see <u.h> */
char *name; /* last element of path */
char *uid; /* owner name */
char *gid; /* group name */
char *muid; /* last modifier name */
};
struct OWaitmsg
{
char pid[12]; /* of loved one */
char time[3*12]; /* of loved one and descendants */
char msg[64]; /* compatibility BUG */
};
struct Waitmsg
{
int pid; /* of loved one */
ulong time[3]; /* of loved one and descendants */
char msg[ERRMAX]; /* actually variable-size in user mode */
};
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [9fans] Apm Enhancement + some bonuses
2003-02-22 20:19 [9fans] Apm Enhancement + some bonuses Maksim Radziwill
@ 2003-02-22 21:51 ` Russ Cox
2003-02-23 2:13 ` Skip Tavakkolian
1 sibling, 0 replies; 4+ messages in thread
From: Russ Cox @ 2003-02-22 21:51 UTC (permalink / raw)
To: 9fans
^t^t is an ugly hack -- it doesn't need more letters.
there are ways to provide everything you added
without going into the kernel.
because ^p currently does nothing on terminals,
we can sit at terminals, connect to the consoles of
cpu servers, and reboot them by typing ^p.
changing ^p to have a magic meaning on terminals
will break that behavior, hence it won't happen.
i'm still sorry that ^p kills drawterm.
as you noted, you can get aux/apm to let you turn
things off by uncommenting
/*
else if(strcmp(p, "off")==0)
respondx(r, apmsetpowerstate(&apm, dev, PowerOff));
*/
in /sys/src/cmd/aux/apm.c.
once you've done that, you don't need ^t^t 5 or ^t^t 6
to turn the screen on and off.
you don't need ^t^t g: ^t^t r already reboots both
cpu servers and terminals.
you are right that you can't run
disk/kfscmd halt
echo off >/mnt/apm/ctl
but you could easily write a program to
halt the file system and then turn off the machine:
#!/bin/python
import plan9
# open both
kfd = plan9.open("/srv/kfs.cmd", plan9.ORDWR)
cfd = plan9.open("/mnt/apm/ctl", plan9.OWRITE)
# halt kfs
plan9.write(kfd, "halt")
while 1:
s = plan9.read(kfd)
if s == "done" || s == "success":
break
plan9.write(1, s)
# turn off system
plan9.write(cfd, "off")
russ
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [9fans] Apm Enhancement + some bonuses
2003-02-22 20:19 [9fans] Apm Enhancement + some bonuses Maksim Radziwill
2003-02-22 21:51 ` Russ Cox
@ 2003-02-23 2:13 ` Skip Tavakkolian
2003-02-23 4:59 ` Dan Cross
1 sibling, 1 reply; 4+ messages in thread
From: Skip Tavakkolian @ 2003-02-23 2:13 UTC (permalink / raw)
To: 9fans
> I wrote a (ugly) enhancement to the plan9 kernel that allows users using
> workstations
I know of nothing that is enhanced by ugliness, except maybe coyote-ugly.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [9fans] Apm Enhancement + some bonuses
2003-02-23 2:13 ` Skip Tavakkolian
@ 2003-02-23 4:59 ` Dan Cross
0 siblings, 0 replies; 4+ messages in thread
From: Dan Cross @ 2003-02-23 4:59 UTC (permalink / raw)
To: 9fans
> I know of nothing that is enhanced by ugliness, except maybe coyote-ugly.
No. That place is a disgusting tourist trap that tries too hard.
- Dan C.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2003-02-23 4:59 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-02-22 20:19 [9fans] Apm Enhancement + some bonuses Maksim Radziwill
2003-02-22 21:51 ` Russ Cox
2003-02-23 2:13 ` Skip Tavakkolian
2003-02-23 4:59 ` Dan Cross
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).