kernel: deep refactoring and cleanup
This large commit address several issues - removed 386 directory: Jehanne is 64bit only - simplified kernel options management - rewritten boot process - ported memory related stuff from 9front's 9/pc64 - removed devacpi - removed old code - deep refactor of awake syscall - removed MCACHE support for mount - fix libc's setjmp/longjmp
This commit is contained in:
parent
1bc08b7631
commit
93dde48355
@ -159,7 +159,7 @@ extern void memarc(Memimage*, Point, int, int, int, Memimage*, Point, int, int,
|
||||
extern Rectangle memlinebbox(Point, Point, int, int, int);
|
||||
extern int memlineendsize(int);
|
||||
extern void _memmkcmap(void);
|
||||
extern void memimageinit(void);
|
||||
extern int memimageinit(void);
|
||||
|
||||
/*
|
||||
* Subfont management
|
||||
|
@ -9,40 +9,39 @@
|
||||
|
||||
typedef struct Pool Pool;
|
||||
struct Pool {
|
||||
char* name;
|
||||
uint32_t maxsize;
|
||||
char* name;
|
||||
uintptr_t maxsize;
|
||||
|
||||
uint32_t cursize;
|
||||
uint32_t curfree;
|
||||
uint32_t curalloc;
|
||||
uintptr_t cursize;
|
||||
uintptr_t curfree;
|
||||
uintptr_t curalloc;
|
||||
|
||||
uint32_t minarena; /* smallest size of new arena */
|
||||
uint32_t quantum; /* allocated blocks should be multiple of */
|
||||
uint32_t minblock; /* smallest newly allocated block */
|
||||
|
||||
void* freeroot; /* actually Free* */
|
||||
void* arenalist; /* actually Arena* */
|
||||
int flags;
|
||||
int nfree;
|
||||
int lastcompact;
|
||||
|
||||
void* (*alloc)(uint32_t);
|
||||
int (*merge)(void*, void*);
|
||||
void (*move)(void* from, void* to);
|
||||
void* freeroot; /* actually Free* */
|
||||
void* arenalist; /* actually Arena* */
|
||||
|
||||
int flags;
|
||||
int nfree;
|
||||
int lastcompact;
|
||||
void* (*alloc)(uint32_t);
|
||||
int (*merge)(void*, void*);
|
||||
void (*move)(void* from, void* to);
|
||||
|
||||
void (*lock)(Pool*);
|
||||
void (*unlock)(Pool*);
|
||||
void (*print)(Pool*, char*, ...);
|
||||
void (*panic)(Pool*, char*, ...);
|
||||
void (*logstack)(Pool*);
|
||||
void (*lock)(Pool*);
|
||||
void (*unlock)(Pool*);
|
||||
void (*print)(Pool*, char*, ...);
|
||||
void (*panic)(Pool*, char*, ...);
|
||||
void (*logstack)(Pool*);
|
||||
|
||||
void* private;
|
||||
void* private;
|
||||
};
|
||||
|
||||
extern void* poolalloc(Pool*, uint32_t);
|
||||
extern void* poolallocalign(Pool*, uint32_t, uint32_t, int32_t,
|
||||
uint32_t);
|
||||
extern void* poolallocalign(Pool*, uint32_t, uint32_t, int32_t, uint32_t);
|
||||
extern void poolfree(Pool*, void*);
|
||||
extern uint32_t poolmsize(Pool*, void*);
|
||||
extern void* poolrealloc(Pool*, void*, uint32_t);
|
||||
|
@ -57,8 +57,8 @@ main(int argc, char *argv[])
|
||||
srv = "screenconsole";
|
||||
|
||||
/* first try in /dev so that binding can work */
|
||||
if((fd = open("/dev/ps2keyb", OREAD)) <= 0)
|
||||
if((fd = open("#P/ps2keyb", OREAD)) <= 0)
|
||||
if((fd = open("/dev/scancode", OREAD)) <= 0)
|
||||
if((fd = open("#b/scancode", OREAD)) <= 0)
|
||||
sysfatal("open keyboard: %r");
|
||||
dup(fd, 0);
|
||||
close(fd);
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"realemu": {
|
||||
"Cflags": [
|
||||
"-I", "/sys/src/kern/386/"
|
||||
"-I", "/sys/src/kern/$ARCH/"
|
||||
],
|
||||
"Include": [
|
||||
"/sys/src/cmd/cmd.json"
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"vga": {
|
||||
"Cflags": [
|
||||
"-I", "/sys/src/kern/386/"
|
||||
"-I", "/sys/src/kern/$ARCH/"
|
||||
],
|
||||
"Include": [
|
||||
"/sys/src/cmd/cmd.json"
|
||||
|
@ -120,9 +120,6 @@ main(int argc, char **argv)
|
||||
case 'c':
|
||||
mntflags |= MCREATE;
|
||||
break;
|
||||
case 'C':
|
||||
mntflags |= MCACHE;
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
|
@ -71,9 +71,6 @@ main(int argc, char *argv[])
|
||||
case 'd':
|
||||
setmntdevice(EARGF(usage()));
|
||||
break;
|
||||
case 'C':
|
||||
flag |= MCACHE;
|
||||
break;
|
||||
case 'k':
|
||||
keyspec = EARGF(usage());
|
||||
break;
|
||||
|
@ -103,11 +103,6 @@ main(int argc, char *argv[])
|
||||
domount = 1;
|
||||
reallymount = 1;
|
||||
break;
|
||||
case 'C':
|
||||
mountflag |= MCACHE;
|
||||
domount = 1;
|
||||
reallymount = 1;
|
||||
break;
|
||||
case 'e':
|
||||
doexec = 1;
|
||||
break;
|
||||
|
@ -1,328 +0,0 @@
|
||||
/*
|
||||
* This file is part of the UCB release of Plan 9. It is subject to the license
|
||||
* terms in the LICENSE file found in the top-level directory of this
|
||||
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
||||
* part of the UCB release of Plan 9, including this file, may be copied,
|
||||
* modified, propagated, or distributed except according to the terms contained
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../port/error.h"
|
||||
|
||||
#include "io.h"
|
||||
|
||||
enum {
|
||||
Data= 0x60, /* data port */
|
||||
|
||||
Status= 0x64, /* status port */
|
||||
Inready= 0x01, /* input character ready */
|
||||
Outbusy= 0x02, /* output busy */
|
||||
Sysflag= 0x04, /* system flag */
|
||||
Cmddata= 0x08, /* cmd==0, data==1 */
|
||||
Inhibit= 0x10, /* keyboard/mouse inhibited */
|
||||
Minready= 0x20, /* mouse character ready */
|
||||
Rtimeout= 0x40, /* general timeout */
|
||||
Parity= 0x80,
|
||||
|
||||
Cmd= 0x64, /* command port (write only) */
|
||||
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
/* controller command byte */
|
||||
Cscs1= (1<<6), /* scan code set 1 */
|
||||
Cauxdis= (1<<5), /* mouse disable */
|
||||
Ckeybdis= (1<<4), /* keyb disable */
|
||||
Csf= (1<<2), /* system flag */
|
||||
Cauxint= (1<<1), /* mouse interrupt enable */
|
||||
Ckeybint= (1<<0), /* keyb interrupt enable */
|
||||
};
|
||||
|
||||
static Queue *keybq;
|
||||
static Queue *mouseq;
|
||||
|
||||
|
||||
static int nokeyb = 1;
|
||||
|
||||
static Lock i8042lock;
|
||||
static uint8_t ccc;
|
||||
|
||||
/*
|
||||
* wait for output no longer busy
|
||||
*/
|
||||
static int
|
||||
outready(void)
|
||||
{
|
||||
int tries;
|
||||
|
||||
for(tries = 0; (inb(Status) & Outbusy); tries++){
|
||||
if(tries > 500)
|
||||
return -1;
|
||||
delay(2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for input
|
||||
*/
|
||||
static int
|
||||
inready(void)
|
||||
{
|
||||
int tries;
|
||||
|
||||
for(tries = 0; !(inb(Status) & Inready); tries++){
|
||||
if(tries > 500)
|
||||
return -1;
|
||||
delay(2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
i8042systemreset(void)
|
||||
{
|
||||
uint16_t *s = KADDR(0x472);
|
||||
int i, x;
|
||||
|
||||
if(nokeyb)
|
||||
return;
|
||||
|
||||
*s = 0x1234; /* BIOS warm-boot flag */
|
||||
|
||||
/* newer reset the machine command */
|
||||
outready();
|
||||
outb(Cmd, 0xFE);
|
||||
outready();
|
||||
|
||||
/* Pulse it by hand (old somewhat reliable) */
|
||||
x = 0xDF;
|
||||
for(i = 0; i < 5; i++){
|
||||
x ^= 1;
|
||||
outready();
|
||||
outb(Cmd, 0xD1);
|
||||
outready();
|
||||
outb(Data, x); /* toggle reset */
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mousecmd(int cmd)
|
||||
{
|
||||
unsigned int c;
|
||||
int tries;
|
||||
static int badkbd;
|
||||
|
||||
if(badkbd)
|
||||
return -1;
|
||||
c = 0;
|
||||
tries = 0;
|
||||
|
||||
ilock(&i8042lock);
|
||||
do{
|
||||
if(tries++ > 2)
|
||||
break;
|
||||
if(outready() < 0)
|
||||
break;
|
||||
outb(Cmd, 0xD4);
|
||||
if(outready() < 0)
|
||||
break;
|
||||
outb(Data, cmd);
|
||||
if(outready() < 0)
|
||||
break;
|
||||
if(inready() < 0)
|
||||
break;
|
||||
c = inb(Data);
|
||||
} while(c == 0xFE || c == 0);
|
||||
iunlock(&i8042lock);
|
||||
|
||||
if(c != 0xFA){
|
||||
jehanne_print("mousecmd: %2.2ux returned to the %2.2ux command\n", c, cmd);
|
||||
badkbd = 1; /* don't keep trying; there might not be one */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mousecmds(uint8_t *cmd, int ncmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
ilock(&i8042lock);
|
||||
for(i=0; i<ncmd; i++){
|
||||
if(outready() == -1)
|
||||
break;
|
||||
outb(Cmd, 0xD4);
|
||||
if(outready() == -1)
|
||||
break;
|
||||
outb(Data, cmd[i]);
|
||||
}
|
||||
iunlock(&i8042lock);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void
|
||||
i8042intr(Ureg* u, void* v)
|
||||
{
|
||||
uint8_t stat, data;
|
||||
|
||||
ilock(&i8042lock);
|
||||
stat = inb(Status);
|
||||
if((stat&Inready) == 0){
|
||||
iunlock(&i8042lock);
|
||||
return;
|
||||
}
|
||||
data = inb(Data);
|
||||
iunlock(&i8042lock);
|
||||
|
||||
if(stat & Minready){
|
||||
if(mouseq != nil)
|
||||
qiwrite(mouseq, &data, 1);
|
||||
} else {
|
||||
if(keybq != nil)
|
||||
qiwrite(keybq, &data, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
outbyte(int port, int c)
|
||||
{
|
||||
if(outready() == -1) {
|
||||
return -1;
|
||||
}
|
||||
outb(port, c);
|
||||
if(outready() == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
mouserwrite(Chan* c, void *vbuf, long len, int64_t off64)
|
||||
{
|
||||
return mousecmds(vbuf, len);
|
||||
}
|
||||
|
||||
static long
|
||||
mouseread(Chan* c, void *vbuf, long len, int64_t off64)
|
||||
{
|
||||
return qread(mouseq, vbuf, len);
|
||||
}
|
||||
|
||||
void
|
||||
mouseenable(void)
|
||||
{
|
||||
mouseq = qopen(32, 0, 0, 0);
|
||||
if(mouseq == nil)
|
||||
panic("mouseenable");
|
||||
qnoblock(mouseq, 1);
|
||||
|
||||
ccc &= ~Cauxdis;
|
||||
ccc |= Cauxint;
|
||||
|
||||
ilock(&i8042lock);
|
||||
if(outready() == -1)
|
||||
iprint("mouseenable: failed 0\n");
|
||||
outb(Cmd, 0x60); /* write control register */
|
||||
if(outready() == -1)
|
||||
iprint("mouseenable: failed 1\n");
|
||||
outb(Data, ccc);
|
||||
if(outready() == -1)
|
||||
iprint("mouseenable: failed 2\n");
|
||||
outb(Cmd, 0xA8); /* auxilliary device enable */
|
||||
if(outready() == -1){
|
||||
iprint("mouseenable: failed 3\n");
|
||||
iunlock(&i8042lock);
|
||||
return;
|
||||
}
|
||||
iunlock(&i8042lock);
|
||||
|
||||
intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "mouse");
|
||||
addarchfile("ps2mouse", 0666, mouseread, mouserwrite);
|
||||
}
|
||||
|
||||
void
|
||||
keybinit(void)
|
||||
{
|
||||
int c, try;
|
||||
|
||||
/* wait for a quiescent controller */
|
||||
ilock(&i8042lock);
|
||||
|
||||
try = 1000;
|
||||
while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
|
||||
if(c & Inready)
|
||||
inb(Data);
|
||||
delay(1);
|
||||
}
|
||||
if (try <= 0) {
|
||||
iunlock(&i8042lock);
|
||||
jehanne_print("keybinit failed 0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* get current controller command byte */
|
||||
outb(Cmd, 0x20);
|
||||
if(inready() == -1){
|
||||
iunlock(&i8042lock);
|
||||
jehanne_print("keybinit failed 1\n");
|
||||
ccc = 0;
|
||||
} else
|
||||
ccc = inb(Data);
|
||||
|
||||
/* enable keyb xfers and interrupts */
|
||||
ccc &= ~(Ckeybdis);
|
||||
ccc |= Csf | Ckeybint | Cscs1;
|
||||
if(outready() == -1) {
|
||||
iunlock(&i8042lock);
|
||||
jehanne_print("keybinit failed 2\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (outbyte(Cmd, 0x60) == -1){
|
||||
iunlock(&i8042lock);
|
||||
jehanne_print("keybinit failed 3\n");
|
||||
return;
|
||||
}
|
||||
if (outbyte(Data, ccc) == -1){
|
||||
iunlock(&i8042lock);
|
||||
jehanne_print("keybinit failed 4\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nokeyb = 0;
|
||||
|
||||
iunlock(&i8042lock);
|
||||
}
|
||||
|
||||
static long
|
||||
keybread(Chan* c, void *vbuf, long len, int64_t off64)
|
||||
{
|
||||
return qread(keybq, vbuf, len);
|
||||
}
|
||||
|
||||
void
|
||||
keybenable(void)
|
||||
{
|
||||
keybq = qopen(32, 0, 0, 0);
|
||||
if(keybq == nil)
|
||||
panic("keybinit");
|
||||
qnoblock(keybq, 1);
|
||||
|
||||
ioalloc(Data, 1, 0, "keyb");
|
||||
ioalloc(Cmd, 1, 0, "keyb");
|
||||
|
||||
intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "keyb");
|
||||
|
||||
addarchfile("ps2keyb", 0666, keybread, nil);
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"386": {
|
||||
"SourceFiles": [
|
||||
"../386/bios32.c",
|
||||
"../386/devether.c",
|
||||
"../386/devrtc.c",
|
||||
"../386/ether8139.c",
|
||||
"../386/ether8169.c",
|
||||
"../386/ether82557.c",
|
||||
"../386/ether82563.c",
|
||||
"../386/etherigbe.c",
|
||||
"../386/ethermii.c",
|
||||
"../386/etherm10g.c",
|
||||
"../386/i8042.c",
|
||||
"../386/pci.c",
|
||||
"../386/sdiahci.c",
|
||||
"../386/sdscsi.c",
|
||||
"../386/uarti8250.c",
|
||||
"../386/uartpci.c"
|
||||
]
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -20,8 +20,6 @@ procrestore(Proc *p)
|
||||
return;
|
||||
cycles(&t);
|
||||
p->pcycles -= t;
|
||||
|
||||
fpuprocrestore(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -35,11 +33,35 @@ procsave(Proc *p)
|
||||
cycles(&t);
|
||||
p->pcycles += t;
|
||||
|
||||
fpuprocsave(p);
|
||||
if(p->fpstate == FPactive){
|
||||
if(p->state == Moribund)
|
||||
fpclear();
|
||||
else{
|
||||
/*
|
||||
* Fpsave() stores without handling pending
|
||||
* unmasked exeptions. Postnote() can't be called
|
||||
* here as sleep() already has up->rlock, so
|
||||
* the handling of pending exceptions is delayed
|
||||
* until the process runs again and generates an
|
||||
* emulation fault to activate the FPU.
|
||||
*/
|
||||
fpsave(&p->fpsave);
|
||||
}
|
||||
p->fpstate = FPinactive;
|
||||
}
|
||||
|
||||
/*
|
||||
* While this processor is in the scheduler, the process could run
|
||||
* on another processor and exit, returning the page tables to
|
||||
* the free list where they could be reallocated and overwritten.
|
||||
* When this processor eventually has to get an entry from the
|
||||
* trashed page tables it will crash.
|
||||
*
|
||||
* If there's only one processor, this can't happen.
|
||||
* You might think it would be a win not to do this in that case,
|
||||
* especially on VMware, but it turns out not to matter.
|
||||
*/
|
||||
mmuflushtlb(m->pml4->pa);
|
||||
mmuflushtlb();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -76,12 +98,21 @@ onIdleSpin(void)
|
||||
/*
|
||||
* put the processor in the halt state if we've no processes to run.
|
||||
* an interrupt will get us going again.
|
||||
*
|
||||
* halting in an smp system can result in a startup latency for
|
||||
* processes that become ready.
|
||||
* if idle_spin is zero, we care more about saving energy
|
||||
* than reducing this latency.
|
||||
*
|
||||
* the performance loss with idle_spin == 0 seems to be slight
|
||||
* and it reduces lock contention (thus system time and real time)
|
||||
* on many-core systems with large values of NPROC.
|
||||
*/
|
||||
void
|
||||
idlehands(void)
|
||||
{
|
||||
extern int nrdy;
|
||||
if(sys->nonline == 1)
|
||||
if(sys->nmach == 1)
|
||||
halt();
|
||||
else if (SUPPORT_MWAIT)
|
||||
mwait32(&nrdy, 0);
|
||||
|
@ -1,406 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
static int
|
||||
cpuidinit(void)
|
||||
{
|
||||
uint32_t eax, info[4];
|
||||
|
||||
/*
|
||||
* Standard CPUID functions.
|
||||
* Functions 0 and 1 will be needed multiple times
|
||||
* so cache the info now.
|
||||
*/
|
||||
if((m->ncpuinfos = cpuid(0, 0, m->cpuinfo[0])) == 0)
|
||||
return 0;
|
||||
m->ncpuinfos++;
|
||||
|
||||
if(jehanne_memcmp(&m->cpuinfo[0][1], "GenuntelineI", 12) == 0)
|
||||
m->isintelcpu = 1;
|
||||
cpuid(1, 0, m->cpuinfo[1]);
|
||||
|
||||
/*
|
||||
* Extended CPUID functions.
|
||||
*/
|
||||
if((eax = cpuid(0x80000000, 0, info)) >= 0x80000000)
|
||||
m->ncpuinfoe = (eax & ~0x80000000) + 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cpuidinfo(uint32_t eax, uint32_t ecx, uint32_t info[4])
|
||||
{
|
||||
if(m->ncpuinfos == 0 && cpuidinit() == 0)
|
||||
return 0;
|
||||
|
||||
if(!(eax & 0x80000000)){
|
||||
if(eax >= m->ncpuinfos)
|
||||
return 0;
|
||||
}
|
||||
else if(eax >= (0x80000000|m->ncpuinfoe))
|
||||
return 0;
|
||||
|
||||
cpuid(eax, ecx, info);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
cpuidhz(uint32_t info[2][4])
|
||||
{
|
||||
int f, r;
|
||||
int64_t hz;
|
||||
uint64_t msr;
|
||||
|
||||
if(jehanne_memcmp(&info[0][1], "GenuntelineI", 12) == 0){
|
||||
switch(info[1][0] & 0x0fff3ff0){
|
||||
default:
|
||||
return 0;
|
||||
case 0x00000f30: /* Xeon (MP), Pentium [4D] */
|
||||
case 0x00000f40: /* Xeon (MP), Pentium [4D] */
|
||||
case 0x00000f60: /* Xeon 7100, 5000 or above */
|
||||
msr = rdmsr(0x2c);
|
||||
r = (msr>>16) & 0x07;
|
||||
switch(r){
|
||||
default:
|
||||
return 0;
|
||||
case 0:
|
||||
hz = 266666666666ll;
|
||||
break;
|
||||
case 1:
|
||||
hz = 133333333333ll;
|
||||
break;
|
||||
case 2:
|
||||
hz = 200000000000ll;
|
||||
break;
|
||||
case 3:
|
||||
hz = 166666666666ll;
|
||||
break;
|
||||
case 4:
|
||||
hz = 333333333333ll;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hz is *1000 at this point.
|
||||
* Do the scaling then round it.
|
||||
* The manual is conflicting about
|
||||
* the size of the msr field.
|
||||
*/
|
||||
hz = (((hz*(msr>>24))/100)+5)/10;
|
||||
break;
|
||||
case 0x00000690: /* Pentium M, Celeron M */
|
||||
case 0x000006d0: /* Pentium M, Celeron M */
|
||||
hz = ((rdmsr(0x2a)>>22) & 0x1f)*100 * 1000000ll;
|
||||
break;
|
||||
case 0x000006e0: /* Core Duo */
|
||||
case 0x000006f0: /* Core 2 Duo/Quad/Extreme */
|
||||
case 0x00000660: /* kvm over i5 */
|
||||
case 0x00000670: /* Core 2 Extreme */
|
||||
case 0x00000650: /* i5 6xx, i3 5xx */
|
||||
case 0x000006c0: /* i5 4xxx */
|
||||
case 0x000006a0: /* i7 paurea... */
|
||||
/*
|
||||
* Get the FSB frequemcy.
|
||||
* If processor has Enhanced Intel Speedstep Technology
|
||||
* then non-integer bus frequency ratios are possible.
|
||||
*/
|
||||
if(info[1][2] & 0x00000080){
|
||||
msr = rdmsr(0x198);
|
||||
r = (msr>>40) & 0x1f;
|
||||
}
|
||||
else{
|
||||
msr = 0;
|
||||
r = rdmsr(0x2a) & 0x1f;
|
||||
}
|
||||
f = rdmsr(0xcd) & 0x07;
|
||||
switch(f){
|
||||
default:
|
||||
return 0;
|
||||
case 5:
|
||||
hz = 100000000000ll;
|
||||
break;
|
||||
case 1:
|
||||
hz = 133333333333ll;
|
||||
break;
|
||||
case 3:
|
||||
hz = 166666666666ll;
|
||||
break;
|
||||
case 2:
|
||||
hz = 200000000000ll;
|
||||
break;
|
||||
case 0:
|
||||
hz = 266666666666ll;
|
||||
break;
|
||||
case 4:
|
||||
hz = 333333333333ll;
|
||||
break;
|
||||
case 6:
|
||||
hz = 400000000000ll;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hz is *1000 at this point.
|
||||
* Do the scaling then round it.
|
||||
*/
|
||||
if(msr & 0x0000400000000000ll)
|
||||
hz = hz*(r+10) + hz/2;
|
||||
else
|
||||
hz = hz*(r+10);
|
||||
hz = ((hz/100)+5)/10;
|
||||
break;
|
||||
}
|
||||
DBG("cpuidhz: 0x2a: %#llux hz %lld\n", rdmsr(0x2a), hz);
|
||||
}
|
||||
else if(jehanne_memcmp(&info[0][1], "AuthcAMDenti", 12) == 0){
|
||||
switch(info[1][0] & 0x0fff0ff0){
|
||||
default:
|
||||
return 0;
|
||||
case 0x00050ff0: /* K8 Athlon Venice 64 / Qemu64 */
|
||||
case 0x00020fc0: /* K8 Athlon Lima 64 */
|
||||
case 0x00000f50: /* K8 Opteron 2xxx */
|
||||
msr = rdmsr(0xc0010042);
|
||||
r = (msr>>16) & 0x3f;
|
||||
hz = 200000000ULL*(4 * 2 + r)/2;
|
||||
break;
|
||||
case 0x00100f60: /* K8 Athlon II */
|
||||
case 0x00100f40: /* Phenom II X2 */
|
||||
case 0x00100f20: /* Phenom II X4 */
|
||||
case 0x00100fa0: /* Phenom II X6 */
|
||||
msr = rdmsr(0xc0010042);
|
||||
r = msr & 0x1f;
|
||||
hz = ((r+0x10)*100000000ll)/(1<<(msr>>6 & 0x07));
|
||||
break;
|
||||
case 0x00100f90: /* K10 Opteron 61xx */
|
||||
case 0x00600f00: /* K10 Opteron 62xx */
|
||||
case 0x00600f10: /* K10 Opteron 6272, FX 6xxx/4xxx */
|
||||
case 0x00600f20: /* K10 Opteron 63xx, FX 3xxx/8xxx/9xxx */
|
||||
msr = rdmsr(0xc0010064);
|
||||
r = msr & 0x1f;
|
||||
hz = ((r+0x10)*100000000ll)/(1<<(msr>>6 & 0x07));
|
||||
break;
|
||||
case 0x00000620: /* QEMU64 / Athlon MP/XP */
|
||||
msr = rdmsr(0xc0010064);
|
||||
r = (msr>>6) & 0x07;
|
||||
hz = (((msr & 0x3f)+0x10)*100000000ll)/(1<<r);
|
||||
break;
|
||||
}
|
||||
DBG("cpuidhz: %#llux hz %lld\n", msr, hz);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
void
|
||||
cpuiddump(void)
|
||||
{
|
||||
int i;
|
||||
uint32_t info[4];
|
||||
|
||||
if(!DBGFLG)
|
||||
return;
|
||||
|
||||
if(m->ncpuinfos == 0 && cpuidinit() == 0)
|
||||
return;
|
||||
|
||||
for(i = 0; i < m->ncpuinfos; i++){
|
||||
cpuid(i, 0, info);
|
||||
DBG("eax = %#8.8ux: %8.8ux %8.8ux %8.8ux %8.8ux\n",
|
||||
i, info[0], info[1], info[2], info[3]);
|
||||
}
|
||||
for(i = 0; i < m->ncpuinfoe; i++){
|
||||
cpuid(0x80000000|i, 0, info);
|
||||
DBG("eax = %#8.8ux: %8.8ux %8.8ux %8.8ux %8.8ux\n",
|
||||
0x80000000|i, info[0], info[1], info[2], info[3]);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t
|
||||
archhz(void)
|
||||
{
|
||||
int64_t hz;
|
||||
uint32_t info[2][4];
|
||||
|
||||
if(DBGFLG && m->machno == 0)
|
||||
cpuiddump();
|
||||
if(!cpuidinfo(0, 0, info[0]) || !cpuidinfo(1, 0, info[1]))
|
||||
return 0;
|
||||
|
||||
hz = cpuidhz(info);
|
||||
if(hz != 0)
|
||||
return hz;
|
||||
else if(m->machno != 0)
|
||||
return sys->machptr[0]->cpuhz;
|
||||
|
||||
return i8254hz(info);
|
||||
}
|
||||
|
||||
void
|
||||
archenable(void)
|
||||
{
|
||||
// here used to be code to enable MONITOR/WAIT
|
||||
// writing 0x1a0 MSR; however such register is
|
||||
// supported on i386 only (MISC_ENABLE), not on x86_64.
|
||||
}
|
||||
|
||||
int
|
||||
archmmu(void)
|
||||
{
|
||||
uint32_t info[4];
|
||||
|
||||
/*
|
||||
* Should the check for m->machno != 0 be here
|
||||
* or in the caller (mmuinit)?
|
||||
*
|
||||
* To do here:
|
||||
* check and enable Pse;
|
||||
* Pge; Nxe.
|
||||
*/
|
||||
|
||||
/*
|
||||
* How many page sizes are there?
|
||||
* Always have 4*KiB, but need to check
|
||||
* configured correctly.
|
||||
*/
|
||||
assert(PGSZ == 4*KiB);
|
||||
|
||||
m->pgszlg2[0] = 12;
|
||||
m->pgszmask[0] = (1<<12)-1;
|
||||
m->npgsz = 1;
|
||||
if(m->ncpuinfos == 0 && cpuidinit() == 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Check the Pse bit in function 1 DX for 2*MiB support;
|
||||
* if false, only 4*KiB is available.
|
||||
*/
|
||||
if(!(m->cpuinfo[1][3] & 0x00000008))
|
||||
return 1;
|
||||
m->pgszlg2[1] = 21;
|
||||
m->pgszmask[1] = (1<<21)-1;
|
||||
m->npgsz = 2;
|
||||
|
||||
/*
|
||||
* Check the Page1GB bit in function 0x80000001 DX for 1*GiB support.
|
||||
*/
|
||||
if(cpuidinfo(0x80000001, 0, info) && (info[3] & 0x04000000)){
|
||||
m->pgszlg2[2] = 30;
|
||||
m->pgszmask[2] = (1<<30)-1;
|
||||
m->npgsz = 3;
|
||||
}
|
||||
|
||||
return m->npgsz;
|
||||
}
|
||||
|
||||
static int
|
||||
fmtP(Fmt* f)
|
||||
{
|
||||
uintmem pa;
|
||||
|
||||
pa = va_arg(f->args, uintmem);
|
||||
|
||||
if(f->flags & FmtSharp)
|
||||
return jehanne_fmtprint(f, "%#16.16llux", pa);
|
||||
|
||||
return jehanne_fmtprint(f, "%llud", pa);
|
||||
}
|
||||
|
||||
static int
|
||||
fmtL(Fmt* f)
|
||||
{
|
||||
Mpl pl;
|
||||
|
||||
pl = va_arg(f->args, Mpl);
|
||||
|
||||
return jehanne_fmtprint(f, "%#16.16llux", pl);
|
||||
}
|
||||
|
||||
static int
|
||||
fmtR(Fmt* f)
|
||||
{
|
||||
uint64_t r;
|
||||
|
||||
r = va_arg(f->args, uint64_t);
|
||||
|
||||
return jehanne_fmtprint(f, "%#16.16llux", r);
|
||||
}
|
||||
|
||||
/* virtual address fmt */
|
||||
static int
|
||||
fmtW(Fmt *f)
|
||||
{
|
||||
uint64_t va;
|
||||
|
||||
va = va_arg(f->args, uint64_t);
|
||||
return jehanne_fmtprint(f, "%#ullx=0x[%ullx][%ullx][%ullx][%ullx][%ullx]", va,
|
||||
PTLX(va, 3), PTLX(va, 2), PTLX(va, 1), PTLX(va, 0),
|
||||
va & ((1<<PGSHFT)-1));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
archfmtinstall(void)
|
||||
{
|
||||
/*
|
||||
* Architecture-specific formatting. Not as neat as they
|
||||
* could be (e.g. there's no defined type for a 'register':
|
||||
* L - Mpl, mach priority level
|
||||
* P - uintmem, physical address
|
||||
* R - register
|
||||
* W - virtual address
|
||||
* With a little effort these routines could be written
|
||||
* in a fairly architecturally-independent manner, relying
|
||||
* on the compiler to optimise-away impossible conditions,
|
||||
* and/or by exploiting the innards of the fmt library.
|
||||
*/
|
||||
jehanne_fmtinstall('P', fmtP);
|
||||
jehanne_fmtinstall('L', fmtL);
|
||||
jehanne_fmtinstall('R', fmtR);
|
||||
jehanne_fmtinstall('W', fmtW);
|
||||
}
|
||||
|
||||
void
|
||||
archidle(void)
|
||||
{
|
||||
halt();
|
||||
}
|
||||
|
||||
void
|
||||
microdelay(int microsecs)
|
||||
{
|
||||
uint64_t r, t;
|
||||
|
||||
r = rdtsc();
|
||||
for(t = r + m->cpumhz*microsecs; r < t; r = rdtsc())
|
||||
pause();
|
||||
}
|
||||
|
||||
void
|
||||
millidelay(int millisecs)
|
||||
{
|
||||
uint64_t r, t;
|
||||
|
||||
r = rdtsc();
|
||||
for(t = r + m->cpumhz*1000ull*millisecs; r < t; r = rdtsc())
|
||||
pause();
|
||||
}
|
||||
|
||||
int
|
||||
isdmaok(void *a, usize len, int range)
|
||||
{
|
||||
uintmem pa;
|
||||
|
||||
if(!iskaddr(a) || (char*)a < etext)
|
||||
return 0;
|
||||
pa = mmuphysaddr(PTR2UINT(a));
|
||||
if(pa == 0 || pa == ~(uintmem)0)
|
||||
return 0;
|
||||
return range > 32 || pa+len <= 0xFFFFFFFFULL;
|
||||
}
|
429
sys/src/kern/amd64/archmp.c
Normal file
429
sys/src/kern/amd64/archmp.c
Normal file
@ -0,0 +1,429 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
|
||||
#include "mp.h"
|
||||
|
||||
static PCMP *pcmp;
|
||||
|
||||
static char* buses[] = {
|
||||
"CBUSI ",
|
||||
"CBUSII",
|
||||
"EISA ",
|
||||
"FUTURE",
|
||||
"INTERN",
|
||||
"ISA ",
|
||||
"MBI ",
|
||||
"MBII ",
|
||||
"MCA ",
|
||||
"MPI ",
|
||||
"MPSA ",
|
||||
"NUBUS ",
|
||||
"PCI ",
|
||||
"PCMCIA",
|
||||
"TC ",
|
||||
"VL ",
|
||||
"VME ",
|
||||
"XPRESS",
|
||||
0,
|
||||
};
|
||||
|
||||
static Bus*
|
||||
mpgetbus(int busno)
|
||||
{
|
||||
Bus *bus;
|
||||
|
||||
for(bus = mpbus; bus; bus = bus->next)
|
||||
if(bus->busno == busno)
|
||||
return bus;
|
||||
|
||||
print("mpgetbus: can't find bus %d\n", busno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Apic*
|
||||
mkprocessor(PCMPprocessor* p)
|
||||
{
|
||||
static int machno = 1;
|
||||
int apicno;
|
||||
Apic *apic;
|
||||
|
||||
apicno = p->apicno;
|
||||
if(!(p->flags & PcmpEN) || apicno > MaxAPICNO || mpapic[apicno] != nil)
|
||||
return 0;
|
||||
|
||||
if((apic = xalloc(sizeof(Apic))) == nil)
|
||||
panic("mkprocessor: no memory for Apic");
|
||||
apic->type = PcmpPROCESSOR;
|
||||
apic->apicno = apicno;
|
||||
apic->flags = p->flags;
|
||||
apic->lintr[0] = ApicIMASK;
|
||||
apic->lintr[1] = ApicIMASK;
|
||||
if(p->flags & PcmpBP)
|
||||
apic->machno = 0;
|
||||
else
|
||||
apic->machno = machno++;
|
||||
mpapic[apicno] = apic;
|
||||
|
||||
return apic;
|
||||
}
|
||||
|
||||
static Bus*
|
||||
mkbus(PCMPbus* p)
|
||||
{
|
||||
Bus *bus;
|
||||
int i;
|
||||
|
||||
for(i = 0; buses[i]; i++)
|
||||
if(strncmp(buses[i], p->string, sizeof(p->string)) == 0)
|
||||
break;
|
||||
if(buses[i] == 0)
|
||||
return 0;
|
||||
|
||||
if((bus = xalloc(sizeof(Bus))) == nil)
|
||||
panic("mkbus: no memory for Bus");
|
||||
if(mpbus)
|
||||
mpbuslast->next = bus;
|
||||
else
|
||||
mpbus = bus;
|
||||
mpbuslast = bus;
|
||||
|
||||
bus->type = i;
|
||||
bus->busno = p->busno;
|
||||
if(bus->type == BusEISA){
|
||||
bus->po = PcmpLOW;
|
||||
bus->el = PcmpLEVEL;
|
||||
if(mpeisabus != -1)
|
||||
print("mkbus: more than one EISA bus\n");
|
||||
mpeisabus = bus->busno;
|
||||
}
|
||||
else if(bus->type == BusPCI){
|
||||
bus->po = PcmpLOW;
|
||||
bus->el = PcmpLEVEL;
|
||||
}
|
||||
else if(bus->type == BusISA){
|
||||
bus->po = PcmpHIGH;
|
||||
bus->el = PcmpEDGE;
|
||||
if(mpisabus != -1)
|
||||
print("mkbus: more than one ISA bus\n");
|
||||
mpisabus = bus->busno;
|
||||
}
|
||||
else{
|
||||
bus->po = PcmpHIGH;
|
||||
bus->el = PcmpEDGE;
|
||||
}
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
static Apic*
|
||||
mkioapic(PCMPioapic* p)
|
||||
{
|
||||
void *va;
|
||||
int apicno;
|
||||
Apic *apic;
|
||||
|
||||
apicno = p->apicno;
|
||||
if(!(p->flags & PcmpEN) || apicno > MaxAPICNO || mpioapic[apicno] != nil)
|
||||
return 0;
|
||||
/*
|
||||
* Map the I/O APIC.
|
||||
*/
|
||||
if((va = vmap(p->addr, 1024)) == nil)
|
||||
return 0;
|
||||
if((apic = xalloc(sizeof(Apic))) == nil)
|
||||
panic("mkioapic: no memory for Apic");
|
||||
apic->type = PcmpIOAPIC;
|
||||
apic->apicno = apicno;
|
||||
apic->addr = va;
|
||||
apic->paddr = p->addr;
|
||||
apic->flags = p->flags;
|
||||
mpioapic[apicno] = apic;
|
||||
|
||||
return apic;
|
||||
}
|
||||
|
||||
static Aintr*
|
||||
mkiointr(PCMPintr* p)
|
||||
{
|
||||
Bus *bus;
|
||||
Aintr *aintr;
|
||||
PCMPintr* pcmpintr;
|
||||
|
||||
/*
|
||||
* According to the MultiProcessor Specification, a destination
|
||||
* I/O APIC of 0xFF means the signal is routed to all I/O APICs.
|
||||
* It's unclear how that can possibly be correct so treat it as
|
||||
* an error for now.
|
||||
*/
|
||||
if(p->apicno > MaxAPICNO || mpioapic[p->apicno] == nil)
|
||||
return 0;
|
||||
|
||||
if((bus = mpgetbus(p->busno)) == 0)
|
||||
return 0;
|
||||
|
||||
if((aintr = xalloc(sizeof(Aintr))) == nil)
|
||||
panic("mkiointr: no memory for Aintr");
|
||||
aintr->intr = p;
|
||||
|
||||
if(0)
|
||||
print("mkiointr: type %d intr type %d flags %#o "
|
||||
"bus %d irq %d apicno %d intin %d\n",
|
||||
p->type, p->intr, p->flags,
|
||||
p->busno, p->irq, p->apicno, p->intin);
|
||||
/*
|
||||
* Hack for Intel SR1520ML motherboard, which BIOS describes
|
||||
* the i82575 dual ethernet controllers incorrectly.
|
||||
*/
|
||||
if(memcmp(pcmp->product, "INTEL X38MLST ", 20) == 0){
|
||||
if(p->busno == 1 && p->intin == 16 && p->irq == 1){
|
||||
if((pcmpintr = xalloc(sizeof(PCMPintr))) == nil)
|
||||
panic("iointr: no memory for PCMPintr");
|
||||
memmove(pcmpintr, p, sizeof(PCMPintr));
|
||||
print("mkiointr: %20.20s bus %d intin %d irq %d\n",
|
||||
(char*)pcmp->product,
|
||||
pcmpintr->busno, pcmpintr->intin,
|
||||
pcmpintr->irq);
|
||||
pcmpintr->intin = 17;
|
||||
aintr->intr = pcmpintr;
|
||||
}
|
||||
}
|
||||
aintr->apic = mpioapic[p->apicno];
|
||||
aintr->next = bus->aintr;
|
||||
bus->aintr = aintr;
|
||||
|
||||
return aintr;
|
||||
}
|
||||
|
||||
static int
|
||||
mklintr(PCMPintr* p)
|
||||
{
|
||||
Apic *apic;
|
||||
Bus *bus;
|
||||
int i, intin, v;
|
||||
|
||||
/*
|
||||
* The offsets of vectors for LINT[01] are known to be
|
||||
* 0 and 1 from the local APIC vector space at VectorLAPIC.
|
||||
*/
|
||||
if((bus = mpgetbus(p->busno)) == 0)
|
||||
return 0;
|
||||
intin = p->intin;
|
||||
|
||||
/*
|
||||
* Pentium Pros have problems if LINT[01] are set to ExtINT
|
||||
* so just bag it, SMP mode shouldn't need ExtINT anyway.
|
||||
*/
|
||||
if(p->intr == PcmpExtINT || p->intr == PcmpNMI)
|
||||
v = ApicIMASK;
|
||||
else
|
||||
v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq);
|
||||
|
||||
if(p->apicno == 0xFF){
|
||||
for(i=0; i<=MaxAPICNO; i++){
|
||||
if((apic = mpapic[i]) == nil)
|
||||
continue;
|
||||
if(apic->flags & PcmpEN)
|
||||
apic->lintr[intin] = v;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(apic = mpapic[p->apicno])
|
||||
if(apic->flags & PcmpEN)
|
||||
apic->lintr[intin] = v;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
dumpmp(uint8_t *p, uint8_t *e)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; p < e; p++) {
|
||||
if((i % 16) == 0) print("*mp%d=", i/16);
|
||||
print("%.2x ", *p);
|
||||
if((++i % 16) == 0) print("\n");
|
||||
}
|
||||
if((i % 16) != 0) print("\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mpoverride(uint8_t** newp, uint8_t** e)
|
||||
{
|
||||
int size, i, j;
|
||||
char buf[20];
|
||||
uint8_t* p;
|
||||
char* s;
|
||||
|
||||
size = strtol(getconf("*mp"), 0, 0);
|
||||
if(size <= 0) panic("mpoverride: invalid size in *mp");
|
||||
*newp = p = xalloc(size);
|
||||
if(p == nil) panic("mpoverride: can't allocate memory");
|
||||
*e = p + size;
|
||||
for(i = 0; ; i++){
|
||||
snprint(buf, sizeof buf, "*mp%d", i);
|
||||
s = getconf(buf);
|
||||
if(s == nil) break;
|
||||
while(*s){
|
||||
j = strtol(s, &s, 16);
|
||||
if(*s && *s != ' ' || j < 0 || j > 0xff) panic("mpoverride: invalid entry in %s", buf);
|
||||
if(p >= *e) panic("mpoverride: overflow in %s", buf);
|
||||
*p++ = j;
|
||||
}
|
||||
}
|
||||
if(p != *e) panic("mpoverride: size doesn't match");
|
||||
}
|
||||
|
||||
static void
|
||||
pcmpinit(void)
|
||||
{
|
||||
uint8_t *p, *e;
|
||||
Apic *apic;
|
||||
void *va;
|
||||
|
||||
/*
|
||||
* Map the local APIC.
|
||||
*/
|
||||
va = vmap(pcmp->lapicbase, 1024);
|
||||
|
||||
print("LAPIC: %.8lux %#p\n", pcmp->lapicbase, va);
|
||||
if(va == nil)
|
||||
panic("pcmpinit: cannot map lapic %.8lux", pcmp->lapicbase);
|
||||
|
||||
p = ((uint8_t*)pcmp)+PCMPsz;
|
||||
e = ((uint8_t*)pcmp)+pcmp->length;
|
||||
if(getconf("*dumpmp") != nil)
|
||||
dumpmp(p, e);
|
||||
if(getconf("*mp") != nil)
|
||||
mpoverride(&p, &e);
|
||||
|
||||
/*
|
||||
* Run through the table saving information needed for starting
|
||||
* application processors and initialising any I/O APICs. The table
|
||||
* is guaranteed to be in order such that only one pass is necessary.
|
||||
*/
|
||||
while(p < e) switch(*p){
|
||||
default:
|
||||
print("pcmpinit: unknown PCMP type 0x%uX (e-p 0x%zuX)\n",
|
||||
*p, e-p);
|
||||
while(p < e){
|
||||
print("%uX ", *p);
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
|
||||
case PcmpPROCESSOR:
|
||||
if(apic = mkprocessor((PCMPprocessor*)p)){
|
||||
apic->addr = va;
|
||||
apic->paddr = pcmp->lapicbase;
|
||||
}
|
||||
p += PCMPprocessorsz;
|
||||
continue;
|
||||
|
||||
case PcmpBUS:
|
||||
mkbus((PCMPbus*)p);
|
||||
p += PCMPbussz;
|
||||
continue;
|
||||
|
||||
case PcmpIOAPIC:
|
||||
if(apic = mkioapic((PCMPioapic*)p))
|
||||
ioapicinit(apic, apic->apicno);
|
||||
p += PCMPioapicsz;
|
||||
continue;
|
||||
|
||||
case PcmpIOINTR:
|
||||
mkiointr((PCMPintr*)p);
|
||||
p += PCMPintrsz;
|
||||
continue;
|
||||
|
||||
case PcmpLINTR:
|
||||
mklintr((PCMPintr*)p);
|
||||
p += PCMPintrsz;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ininitalize local APIC and start application processors.
|
||||
*/
|
||||
mpinit();
|
||||
}
|
||||
|
||||
static void
|
||||
mpreset(void)
|
||||
{
|
||||
/* stop application processors */
|
||||
mpshutdown();
|
||||
|
||||
/* do generic reset */
|
||||
archreset();
|
||||
}
|
||||
|
||||
static int identify(void);
|
||||
|
||||
PCArch archmp = {
|
||||
.id= "_MP_",
|
||||
.ident= identify,
|
||||
.reset= mpreset,
|
||||
.intrinit= pcmpinit,
|
||||
.intrenable= mpintrenable,
|
||||
.intron= lapicintron,
|
||||
.introff= lapicintroff,
|
||||
.fastclock= i8253read,
|
||||
.timerset= lapictimerset,
|
||||
};
|
||||
|
||||
static int
|
||||
identify(void)
|
||||
{
|
||||
char *cp;
|
||||
_MP_ *_mp_;
|
||||
uint32_t len;
|
||||
|
||||
if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Search for an MP configuration table. For now,
|
||||
* don't accept the default configurations (physaddr == 0).
|
||||
* Check for correct signature, calculate the checksum and,
|
||||
* if correct, check the version.
|
||||
* To do: check extended table checksum.
|
||||
*/
|
||||
if((_mp_ = sigsearch("_MP_")) == nil || checksum(_mp_, _MP_sz) != 0 || _mp_->physaddr == 0)
|
||||
return 1;
|
||||
|
||||
len = PCMPsz;
|
||||
if(_mp_->physaddr < MemMin)
|
||||
pcmp = KADDR(_mp_->physaddr);
|
||||
else if((pcmp = vmap(_mp_->physaddr, len)) == nil)
|
||||
return 1;
|
||||
if(pcmp->length < len
|
||||
|| memcmp(pcmp, "PCMP", 4) != 0
|
||||
|| (pcmp->version != 1 && pcmp->version != 4)){
|
||||
Bad:
|
||||
if((uintptr)pcmp < KZERO)
|
||||
vunmap(pcmp, len);
|
||||
pcmp = nil;
|
||||
return 1;
|
||||
}
|
||||
len = pcmp->length;
|
||||
if((uintptr)pcmp < KZERO)
|
||||
vunmap(pcmp, PCMPsz);
|
||||
if(_mp_->physaddr < MemMin)
|
||||
pcmp = KADDR(_mp_->physaddr);
|
||||
else if((pcmp = vmap(_mp_->physaddr, len)) == nil)
|
||||
return 1;
|
||||
if(checksum(pcmp, len) != 0)
|
||||
goto Bad;
|
||||
|
||||
if(m->havetsc && getconf("*notsc") == nil)
|
||||
archmp.fastclock = tscticks;
|
||||
|
||||
return 0;
|
||||
}
|
@ -6,7 +6,6 @@
|
||||
"Include": [
|
||||
"core.json",
|
||||
"devdraw.json",
|
||||
"../386/include.json",
|
||||
"../ip/include.json",
|
||||
"../port/include.json"
|
||||
],
|
||||
@ -18,7 +17,6 @@
|
||||
"int printallsyscalls;"
|
||||
],
|
||||
"Dev": [
|
||||
"acpi",
|
||||
"arch",
|
||||
"bridge",
|
||||
"cap",
|
||||
@ -29,6 +27,7 @@
|
||||
"ether",
|
||||
"ip",
|
||||
"kprof",
|
||||
"kbd",
|
||||
"ninep",
|
||||
"pci",
|
||||
"pipe",
|
||||
@ -87,7 +86,6 @@
|
||||
"autogenerated.c",
|
||||
"sdata.c",
|
||||
"cga.c",
|
||||
"devacpi.c",
|
||||
"usbehcipc.c",
|
||||
"usbohci.c",
|
||||
"usbuhci.c"
|
||||
|
@ -179,7 +179,7 @@ cgaprinthex(uintptr_t x)
|
||||
cgaputc('\n');
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
cgaconsputs(char* s, int n)
|
||||
{
|
||||
ilock(&cgalock);
|
||||
@ -235,7 +235,7 @@ cgawrite(Chan* c, void *vbuf, long len, int64_t off)
|
||||
}
|
||||
|
||||
void
|
||||
cgainit(void)
|
||||
screen_init(void)
|
||||
{
|
||||
ilock(&cgalock);
|
||||
|
||||
@ -245,5 +245,6 @@ cgainit(void)
|
||||
cgablinkoff();
|
||||
cgainitdone = 1;
|
||||
iunlock(&cgalock);
|
||||
screenputs = cgaconsputs;
|
||||
addarchfile("cgamem", 0666, cgaread, cgawrite);
|
||||
}
|
||||
|
@ -62,7 +62,24 @@
|
||||
"inith.json"
|
||||
],
|
||||
"SourceFiles": [
|
||||
"bios32.c",
|
||||
"devether.c",
|
||||
"devrtc.c",
|
||||
"devkbd.c",
|
||||
"ether8139.c",
|
||||
"ether8169.c",
|
||||
"ether82557.c",
|
||||
"ether82563.c",
|
||||
"etherigbe.c",
|
||||
"ethermii.c",
|
||||
"etherm10g.c",
|
||||
"pci.c",
|
||||
"sdiahci.c",
|
||||
"sdscsi.c",
|
||||
"uarti8250.c",
|
||||
"uartpci.c",
|
||||
"entry.S",
|
||||
"ec.c",
|
||||
"l64v.S",
|
||||
"l64fpu.S",
|
||||
"cpuidamd64.S",
|
||||
@ -70,26 +87,24 @@
|
||||
"l64vsyscall.S",
|
||||
"acpi.c",
|
||||
"arch.c",
|
||||
"archamd64.c",
|
||||
"archmp.c",
|
||||
"devarch.c",
|
||||
"fpu.c",
|
||||
"hpet.c",
|
||||
"i8254.c",
|
||||
"i8253.c",
|
||||
"i8259.c",
|
||||
"ioapic.c",
|
||||
"lapic.c",
|
||||
"main.c",
|
||||
"map.c",
|
||||
"memory.c",
|
||||
"mmu.c",
|
||||
"mp.c",
|
||||
"msi.c",
|
||||
"mtrr.c",
|
||||
"multiboot.c",
|
||||
"sipi.c",
|
||||
"squidboy.c",
|
||||
"syscall.c",
|
||||
"systab.c",
|
||||
"trap.c",
|
||||
"vsvm.c"
|
||||
"trap.c"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -3,25 +3,18 @@
|
||||
*/
|
||||
.globl cpuid
|
||||
cpuid:
|
||||
pushq %rbx
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
movq %rdi, %rax
|
||||
movq %rsi, %rcx
|
||||
pushq %r15
|
||||
movq %rdx, %r15
|
||||
|
||||
cpuid /* Argument in %rax */
|
||||
|
||||
// Plan 9 just moves them as a,b,c,d. Weird.
|
||||
movl %eax, 0(%r15)
|
||||
movl %ebx, 4(%r15)
|
||||
movl %ecx, 8(%r15)
|
||||
movl %edx, 12(%r15)
|
||||
popq %r15
|
||||
pushq %rbx
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
movl %edi, %eax
|
||||
cpuid
|
||||
movl %eax, 0(%rsi)
|
||||
movl %ebx, 4(%rsi)
|
||||
movl %ecx, 8(%rsi)
|
||||
movl %edx, 12(%rsi)
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
pop %rbx
|
||||
popq %rbx
|
||||
ret
|
||||
|
||||
/*
|
||||
|
@ -8,11 +8,11 @@ fi
|
||||
|
||||
cd $JEHANNE/sys/src/kern/amd64
|
||||
|
||||
gcc -c -O0 -static -fplan9-extensions -mno-red-zone -ffreestanding -fno-builtin -mcmodel=kernel l64sipi.S
|
||||
ld -Ttext 0x00003000 -e 0x00003000 l64sipi.o -o l64sipi
|
||||
objcopy -O binary -j .text l64sipi l64sipi.out
|
||||
x86_64-jehanne-gcc -c -O0 -static -fplan9-extensions -mno-red-zone -ffreestanding -fno-builtin -mcmodel=kernel l64sipi.S
|
||||
x86_64-jehanne-ld -Ttext 0x00003000 -e 0x00003000 l64sipi.o -o l64sipi
|
||||
x86_64-jehanne-objcopy -O binary -j .text l64sipi l64sipi.out
|
||||
|
||||
echo 'uint8_t sipihandler[]={' > sipi.h
|
||||
cat l64sipi.out | hexdump -v -e '7/1 "0x%02x, " 1/1 " 0x%02x,\n"' | sed '$s/0x ,/0x00,/g'>> sipi.h
|
||||
echo '};' >> sipi.h
|
||||
rm l64sipi.out
|
||||
#rm l64sipi.out
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
typedef struct BIOS32si BIOS32si;
|
||||
typedef struct BIOS32ci BIOS32ci;
|
||||
typedef struct Confmem Confmem;
|
||||
typedef struct Fxsave Fxsave;
|
||||
typedef struct IOConf IOConf;
|
||||
typedef struct ISAConf ISAConf;
|
||||
@ -25,27 +26,35 @@ typedef struct Lock Lock;
|
||||
typedef struct LockEntry LockEntry;
|
||||
typedef struct MCPU MCPU;
|
||||
typedef struct MFPU MFPU;
|
||||
typedef struct MMU MMU;
|
||||
typedef struct MMMU MMMU;
|
||||
typedef struct Mach Mach;
|
||||
typedef uint64_t Mpl;
|
||||
typedef Mpl Mreg; /* GAK */
|
||||
typedef struct Ptpage Ptpage;
|
||||
typedef struct Segdesc Segdesc;
|
||||
typedef struct Pcidev Pcidev;
|
||||
typedef struct PFPU PFPU;
|
||||
typedef struct PMMU PMMU;
|
||||
typedef struct PNOTIFY PNOTIFY;
|
||||
typedef struct PPAGE PPAGE;
|
||||
typedef uint64_t PTE;
|
||||
typedef struct RMap RMap;
|
||||
typedef struct Proc Proc;
|
||||
typedef struct Sys Sys;
|
||||
typedef uint64_t uintmem; /* horrible name */
|
||||
typedef long Tval;
|
||||
typedef struct Ureg Ureg;
|
||||
typedef struct Vctl Vctl;
|
||||
typedef struct PCArch PCArch;
|
||||
typedef union FPsave FPsave;
|
||||
typedef struct Fxsave Fxsave;
|
||||
typedef struct FPstate FPstate;
|
||||
typedef struct PCMmap PCMmap;
|
||||
|
||||
#pragma incomplete BIOS32si
|
||||
#pragma incomplete Ureg
|
||||
|
||||
#define MAXSYSARG 5 /* for mount(fd, afd, mpt, flag, arg) */
|
||||
#define MAXSYSARG 6 /* for mount(fd, afd, mpt, flag, arg, dc) */
|
||||
|
||||
/*
|
||||
* parameters for sysproc.c
|
||||
@ -58,10 +67,16 @@ typedef struct Vctl Vctl;
|
||||
*/
|
||||
struct Lock
|
||||
{
|
||||
LockEntry* head;
|
||||
LockEntry* e;
|
||||
uint64_t sr;
|
||||
uintptr_t pc;
|
||||
Proc *lp;
|
||||
Mach *lm;
|
||||
uint32_t key;
|
||||
uint16_t isilock;
|
||||
long lockcycles;
|
||||
};
|
||||
|
||||
|
||||
struct Label
|
||||
{
|
||||
uintptr_t sp;
|
||||
@ -69,6 +84,24 @@ struct Label
|
||||
uintptr_t regs[14];
|
||||
};
|
||||
|
||||
/*
|
||||
* FPsave.status
|
||||
*/
|
||||
enum
|
||||
{
|
||||
/* this is a state */
|
||||
FPinit= 0,
|
||||
FPactive= 1,
|
||||
FPinactive= 2,
|
||||
|
||||
/* the following is a bit that can be or'd into the state */
|
||||
FPillegal= 0x100,
|
||||
};
|
||||
|
||||
/*
|
||||
* the FP regs must be stored here, not somewhere pointed to from here.
|
||||
* port code assumes this.
|
||||
*/
|
||||
struct Fxsave {
|
||||
uint16_t fcw; /* x87 control word */
|
||||
uint16_t fsw; /* x87 status word */
|
||||
@ -84,40 +117,41 @@ struct Fxsave {
|
||||
uint8_t ign[96]; /* reserved, ignored */
|
||||
};
|
||||
|
||||
/*
|
||||
* FPU stuff in Proc
|
||||
*/
|
||||
struct PFPU {
|
||||
int fpustate;
|
||||
uint8_t fxsave[sizeof(Fxsave)+15];
|
||||
void* fpusave;
|
||||
union FPsave {
|
||||
uint8_t align[512+15];
|
||||
Fxsave;
|
||||
};
|
||||
|
||||
struct Ptpage
|
||||
struct Segdesc
|
||||
{
|
||||
PTE* pte; /* kernel-addressible page table entries */
|
||||
uintmem pa; /* physical address (from physalloc) */
|
||||
Ptpage* next; /* next in level's set, or free list */
|
||||
Ptpage* parent; /* parent page table page or page directory */
|
||||
uint32_t ptoff; /* index of this table's entry in parent */
|
||||
uint32_t d0;
|
||||
uint32_t d1;
|
||||
};
|
||||
|
||||
/*
|
||||
* MMU structure for PDP, PD, PT pages.
|
||||
*/
|
||||
struct MMU
|
||||
{
|
||||
MMU* next;
|
||||
uintptr_t* page;
|
||||
int index;
|
||||
int level;
|
||||
};
|
||||
|
||||
/*
|
||||
* MMU stuff in Proc
|
||||
*/
|
||||
#define NCOLOR 1
|
||||
struct PMMU
|
||||
{
|
||||
Ptpage* mmuptp[4]; /* page table pages for each level */
|
||||
Ptpage* ptpfree;
|
||||
int nptpbusy;
|
||||
};
|
||||
|
||||
/*
|
||||
* MMU stuff in Page
|
||||
*/
|
||||
struct PPAGE
|
||||
{
|
||||
uint8_t* nothing;
|
||||
MMU* mmuhead;
|
||||
MMU* mmutail;
|
||||
MMU* kmaphead;
|
||||
MMU* kmaptail;
|
||||
unsigned long kmapcount;
|
||||
unsigned long kmapindex;
|
||||
unsigned long mmucount;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -138,8 +172,6 @@ struct IOConf
|
||||
};
|
||||
extern IOConf ioconf;
|
||||
|
||||
#define MAXMDOM 8 /* maximum memory/cpu domains */
|
||||
|
||||
#include "../port/portdat.h"
|
||||
|
||||
/*
|
||||
@ -164,24 +196,26 @@ struct MFPU {
|
||||
/*
|
||||
* MMU stuff in Mach.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
NPGSZ = 4
|
||||
};
|
||||
typedef struct {
|
||||
uint32_t _0_;
|
||||
uint32_t rsp0[2];
|
||||
uint32_t rsp1[2];
|
||||
uint32_t rsp2[2];
|
||||
uint32_t _28_[2];
|
||||
uint32_t ist[14];
|
||||
uint16_t _92_[5];
|
||||
uint16_t iomap;
|
||||
} Tss;
|
||||
|
||||
struct MMMU
|
||||
{
|
||||
Ptpage* pml4; /* pml4 for this processor */
|
||||
PTE* pmap; /* unused as of yet */
|
||||
Ptpage* ptpfree; /* per-mach free list */
|
||||
int nptpfree;
|
||||
uint64_t* pml4; /* pml4 base for this processor (va) */
|
||||
Tss* tss; /* tss for this processor */
|
||||
Segdesc* gdt; /* gdt for this processor */
|
||||
|
||||
uint32_t pgszlg2[NPGSZ]; /* per Mach or per Sys? */
|
||||
uintmem pgszmask[NPGSZ];
|
||||
uint32_t pgsz[NPGSZ];
|
||||
int npgsz;
|
||||
|
||||
Ptpage pml4kludge;
|
||||
uint64_t mmumap[4]; /* bitmap of pml4 entries for zapping */
|
||||
MMU* mmufree; /* freelist for MMU structures */
|
||||
unsigned long mmucount; /* number of MMU structures in freelist */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -212,8 +246,6 @@ struct Mach
|
||||
|
||||
uintptr_t stack;
|
||||
uint8_t* vsvm;
|
||||
void* gdt;
|
||||
void* tss;
|
||||
|
||||
uint64_t ticks; /* of the clock since boot time */
|
||||
Label sched; /* scheduler wakeup */
|
||||
@ -241,18 +273,36 @@ struct Mach
|
||||
|
||||
int lastintr;
|
||||
|
||||
int loopconst;
|
||||
|
||||
Lock apictimerlock;
|
||||
uint64_t cyclefreq; /* Frequency of user readable cycle counter */
|
||||
int64_t cpuhz;
|
||||
uint64_t cpuhz;
|
||||
int cpumhz;
|
||||
int cpuidax;
|
||||
int cpuidcx;
|
||||
int cpuiddx;
|
||||
char cpuidid[16];
|
||||
char* cpuidtype;
|
||||
int havetsc;
|
||||
int havepge;
|
||||
uint64_t tscticks;
|
||||
uint64_t rdtsc;
|
||||
|
||||
LockEntry locks[8];
|
||||
// LockEntry locks[8];
|
||||
|
||||
MFPU FPU;
|
||||
MCPU;
|
||||
};
|
||||
|
||||
struct Confmem
|
||||
{
|
||||
uintptr_t base;
|
||||
unsigned long npage;
|
||||
uintptr_t kbase;
|
||||
uintptr_t klimit;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the low memory map, between 0x100000 and 0x110000.
|
||||
* It is located there to allow fundamental datastructures to be
|
||||
@ -265,62 +315,102 @@ struct Mach
|
||||
* Some of the elements must be aligned on page boundaries, hence
|
||||
* the unions.
|
||||
*/
|
||||
struct Sys {
|
||||
uint8_t machstk[MACHSTKSZ];
|
||||
struct Sys
|
||||
{
|
||||
Ureg* boot_regs;
|
||||
unsigned int nmach; /* processors */
|
||||
unsigned int nproc; /* processes */
|
||||
unsigned int monitor; /* has monitor? */
|
||||
unsigned int npages; /* total physical pages of memory */
|
||||
unsigned int upages; /* user page pool */
|
||||
unsigned int nimage; /* number of images */
|
||||
Confmem mem[16]; /* physical memory */
|
||||
|
||||
PTE pml4[PTSZ/sizeof(PTE)]; /* */
|
||||
PTE pdp[PTSZ/sizeof(PTE)];
|
||||
PTE pd[PTSZ/sizeof(PTE)];
|
||||
PTE pt[PTSZ/sizeof(PTE)];
|
||||
unsigned int copymode; /* 0 is copy on write, 1 is copy on reference */
|
||||
unsigned int ialloc; /* max interrupt time allocation in bytes */
|
||||
unsigned int pipeqsize; /* size in bytes of pipe queues */
|
||||
int nuart; /* number of uart devices */
|
||||
|
||||
uint8_t vsvmpage[4*KiB];
|
||||
char* architecture;
|
||||
uint64_t ticks;
|
||||
Mach* machptr[MACHMAX];
|
||||
|
||||
union {
|
||||
Mach mach;
|
||||
uint8_t machpage[MACHSZ];
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint64_t pmstart; /* physical memory */
|
||||
uint64_t pmoccupied; /* how much is occupied */
|
||||
uint64_t pmunassigned; /* how much to keep back from page pool */
|
||||
uint64_t pmpaged; /* how much assigned to page pool */
|
||||
|
||||
uintptr_t vmstart; /* base address for malloc */
|
||||
uintptr_t vmunused; /* 1st unused va */
|
||||
uintptr_t vmunmapped; /* 1st unmapped va in KSEG0 */
|
||||
|
||||
uint64_t epoch; /* crude time synchronisation */
|
||||
int nmach; /* how many machs */
|
||||
int nonline; /* how many machs are online */
|
||||
uint64_t ticks; /* since boot (type?) */
|
||||
uint32_t copymode; /* 0=copy on write; 1=copy on reference */
|
||||
};
|
||||
uint8_t syspage[4*KiB];
|
||||
};
|
||||
|
||||
union {
|
||||
Mach* machptr[MACHMAX];
|
||||
uint8_t ptrpage[4*KiB];
|
||||
};
|
||||
|
||||
uint8_t _57344_[2][4*KiB]; /* unused */
|
||||
uint64_t pmstart; /* physical memory */
|
||||
uint64_t pmoccupied; /* how much is occupied */
|
||||
uint64_t pmunassigned; /* how much to keep back from page pool */
|
||||
uint64_t pmpaged; /* how much assigned to page pool */
|
||||
};
|
||||
|
||||
extern Sys* sys;
|
||||
extern uint32_t MemMin; /* set by entry.S */
|
||||
|
||||
/*
|
||||
* KMap
|
||||
*/
|
||||
typedef void KMap;
|
||||
|
||||
#define kunmap(k)
|
||||
#define VA(k) (void*)(k)
|
||||
|
||||
/*
|
||||
* routines for things outside the PC model, like power management
|
||||
*/
|
||||
struct PCArch
|
||||
{
|
||||
char* id;
|
||||
int (*ident)(void); /* this should be in the model */
|
||||
void (*reset)(void); /* this should be in the model */
|
||||
int (*serialpower)(int); /* 1 == on, 0 == off */
|
||||
int (*modempower)(int); /* 1 == on, 0 == off */
|
||||
|
||||
void (*intrinit)(void);
|
||||
int (*intrenable)(Vctl*);
|
||||
int (*intrvecno)(int);
|
||||
int (*intrdisable)(int);
|
||||
void (*introff)(void);
|
||||
void (*intron)(void);
|
||||
|
||||
void (*clockenable)(void);
|
||||
uint64_t (*fastclock)(uint64_t*);
|
||||
void (*timerset)(uint64_t);
|
||||
};
|
||||
|
||||
extern PCArch *arch; /* PC architecture */
|
||||
|
||||
/* cpuid instruction result register bits */
|
||||
enum {
|
||||
/* cx */
|
||||
Monitor = 1<<3,
|
||||
|
||||
/* dx */
|
||||
Fpuonchip = 1<<0,
|
||||
Vmex = 1<<1, /* virtual-mode extensions */
|
||||
Pse = 1<<3, /* page size extensions */
|
||||
Tsc = 1<<4, /* time-stamp counter */
|
||||
Cpumsr = 1<<5, /* model-specific registers, rdmsr/wrmsr */
|
||||
Pae = 1<<6, /* physical-addr extensions */
|
||||
Mce = 1<<7, /* machine-check exception */
|
||||
Cmpxchg8b = 1<<8,
|
||||
Cpuapic = 1<<9,
|
||||
Mtrr = 1<<12, /* memory-type range regs. */
|
||||
Pge = 1<<13, /* page global extension */
|
||||
Mca = 1<<14, /* machine-check architecture */
|
||||
Pat = 1<<16, /* page attribute table */
|
||||
Pse2 = 1<<17, /* more page size extensions */
|
||||
Clflush = 1<<19,
|
||||
Acpif = 1<<22, /* therm control msr */
|
||||
Mmx = 1<<23,
|
||||
Fxsr = 1<<24, /* have SSE FXSAVE/FXRSTOR */
|
||||
Sse = 1<<25, /* thus sfence instr. */
|
||||
Sse2 = 1<<26, /* thus mfence & lfence instr.s */
|
||||
Rdrnd = 1<<30, /* RDRAND support bit */
|
||||
};
|
||||
|
||||
/* Informations about active processors for
|
||||
* bootstrap and shutdown.
|
||||
*/
|
||||
struct
|
||||
{
|
||||
Lock;
|
||||
char machs[MACHMAX]; /* bitmap of active CPUs */
|
||||
int exiting; /* shutdown */
|
||||
int ispanic; /* shutdown in response to a panic */
|
||||
int thunderbirdsarego; /* F.A.B. */
|
||||
@ -354,6 +444,8 @@ typedef struct BIOS32ci { /* BIOS32 Calling Interface */
|
||||
uint32_t edi;
|
||||
} BIOS32ci;
|
||||
|
||||
#define MACHP(n) (sys->machptr[n])
|
||||
|
||||
/*
|
||||
* The Mach structures must be available via the per-processor
|
||||
* MMU information array machptr, mainly for disambiguation and access to
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,8 @@
|
||||
"SourceFiles": [
|
||||
"../port/devdraw.c",
|
||||
"../port/devmouse.c",
|
||||
"../386/vgavesa.c",
|
||||
"../port/swcursor.c",
|
||||
"vgavesa.c",
|
||||
"screen.c",
|
||||
"devvga.c",
|
||||
"mouse.c",
|
||||
|
@ -503,8 +503,6 @@ ethershutdown(void)
|
||||
ether = etherxx[i];
|
||||
if(ether == nil)
|
||||
continue;
|
||||
if(ether->irq >= 0)
|
||||
intrdisable(ether->vector);
|
||||
if(ether->shutdown == nil) {
|
||||
jehanne_print("#l%d: no shutdown function\n", i);
|
||||
continue;
|
447
sys/src/kern/amd64/devkbd.c
Normal file
447
sys/src/kern/amd64/devkbd.c
Normal file
@ -0,0 +1,447 @@
|
||||
/*
|
||||
* keyboard input
|
||||
*/
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
|
||||
enum {
|
||||
Data= 0x60, /* data port */
|
||||
|
||||
Status= 0x64, /* status port */
|
||||
Inready= 0x01, /* input character ready */
|
||||
Outbusy= 0x02, /* output busy */
|
||||
Sysflag= 0x04, /* system flag */
|
||||
Cmddata= 0x08, /* cmd==0, data==1 */
|
||||
Inhibit= 0x10, /* keyboard/mouse inhibited */
|
||||
Minready= 0x20, /* mouse character ready */
|
||||
Rtimeout= 0x40, /* general timeout */
|
||||
Parity= 0x80,
|
||||
|
||||
Cmd= 0x64, /* command port (write only) */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/* controller command byte */
|
||||
Cscs1= (1<<6), /* scan code set 1 */
|
||||
Cauxdis= (1<<5), /* mouse disable */
|
||||
Ckbddis= (1<<4), /* kbd disable */
|
||||
Csf= (1<<2), /* system flag */
|
||||
Cauxint= (1<<1), /* mouse interrupt enable */
|
||||
Ckbdint= (1<<0), /* kbd interrupt enable */
|
||||
};
|
||||
|
||||
enum {
|
||||
Qdir,
|
||||
Qscancode,
|
||||
Qleds,
|
||||
};
|
||||
|
||||
static Dirtab kbdtab[] = {
|
||||
".", {Qdir, 0, QTDIR}, 0, 0555,
|
||||
"scancode", {Qscancode, 0}, 0, 0440,
|
||||
"leds", {Qleds, 0}, 0, 0220,
|
||||
};
|
||||
|
||||
static Lock i8042lock;
|
||||
static uint8_t ccc;
|
||||
static void (*auxputc)(int, int);
|
||||
static int nokbd = 1; /* flag: no PS/2 keyboard */
|
||||
|
||||
static struct {
|
||||
Ref ref;
|
||||
Queue *q;
|
||||
} kbd;
|
||||
|
||||
/*
|
||||
* wait for output no longer busy
|
||||
*/
|
||||
static int
|
||||
outready(void)
|
||||
{
|
||||
int tries;
|
||||
|
||||
for(tries = 0; (inb(Status) & Outbusy); tries++){
|
||||
if(tries > 500)
|
||||
return -1;
|
||||
delay(2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for input
|
||||
*/
|
||||
static int
|
||||
inready(void)
|
||||
{
|
||||
int tries;
|
||||
|
||||
for(tries = 0; !(inb(Status) & Inready); tries++){
|
||||
if(tries > 500)
|
||||
return -1;
|
||||
delay(2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ask 8042 to reset the machine
|
||||
*/
|
||||
void
|
||||
i8042reset(void)
|
||||
{
|
||||
int i, x;
|
||||
|
||||
if(nokbd)
|
||||
return;
|
||||
|
||||
*((uint16_t*)KADDR(0x472)) = 0x1234; /* BIOS warm-boot flag */
|
||||
|
||||
/*
|
||||
* newer reset the machine command
|
||||
*/
|
||||
outready();
|
||||
outb(Cmd, 0xFE);
|
||||
outready();
|
||||
|
||||
/*
|
||||
* Pulse it by hand (old somewhat reliable)
|
||||
*/
|
||||
x = 0xDF;
|
||||
for(i = 0; i < 5; i++){
|
||||
x ^= 1;
|
||||
outready();
|
||||
outb(Cmd, 0xD1);
|
||||
outready();
|
||||
outb(Data, x); /* toggle reset */
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
i8042auxcmd(int cmd)
|
||||
{
|
||||
unsigned int c;
|
||||
int tries;
|
||||
|
||||
c = 0;
|
||||
tries = 0;
|
||||
|
||||
ilock(&i8042lock);
|
||||
do{
|
||||
if(tries++ > 2)
|
||||
break;
|
||||
if(outready() < 0)
|
||||
break;
|
||||
outb(Cmd, 0xD4);
|
||||
if(outready() < 0)
|
||||
break;
|
||||
outb(Data, cmd);
|
||||
if(outready() < 0)
|
||||
break;
|
||||
if(inready() < 0)
|
||||
break;
|
||||
c = inb(Data);
|
||||
} while(c == 0xFE || c == 0);
|
||||
iunlock(&i8042lock);
|
||||
|
||||
if(c != 0xFA){
|
||||
print("i8042: %2.2ux returned to the %2.2ux command (pc=%#p)\n",
|
||||
c, cmd, getcallerpc());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* set keyboard's leds for lock states (scroll, numeric, caps).
|
||||
*
|
||||
* at least one keyboard (from Qtronics) also sets its numeric-lock
|
||||
* behaviour to match the led state, though it has no numeric keypad,
|
||||
* and some BIOSes bring the system up with numeric-lock set and no
|
||||
* setting to change that. this combination steals the keys for these
|
||||
* characters and makes it impossible to generate them: uiolkjm&*().
|
||||
* thus we'd like to be able to force the numeric-lock led (and behaviour) off.
|
||||
*/
|
||||
static void
|
||||
setleds(int leds)
|
||||
{
|
||||
static int old = -1;
|
||||
|
||||
if(nokbd || leds == old)
|
||||
return;
|
||||
leds &= 7;
|
||||
ilock(&i8042lock);
|
||||
for(;;){
|
||||
if(outready() < 0)
|
||||
break;
|
||||
outb(Data, 0xed); /* `reset keyboard lock states' */
|
||||
if(outready() < 0)
|
||||
break;
|
||||
outb(Data, leds);
|
||||
if(outready() < 0)
|
||||
break;
|
||||
old = leds;
|
||||
break;
|
||||
}
|
||||
iunlock(&i8042lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* keyboard interrupt
|
||||
*/
|
||||
static void
|
||||
i8042intr(Ureg* _, void* __)
|
||||
{
|
||||
int s, c;
|
||||
uint8_t b;
|
||||
|
||||
/*
|
||||
* get status
|
||||
*/
|
||||
ilock(&i8042lock);
|
||||
s = inb(Status);
|
||||
if(!(s&Inready)){
|
||||
iunlock(&i8042lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* get the character
|
||||
*/
|
||||
c = inb(Data);
|
||||
iunlock(&i8042lock);
|
||||
|
||||
/*
|
||||
* if it's the aux port...
|
||||
*/
|
||||
if(s & Minready){
|
||||
if(auxputc != nil)
|
||||
auxputc(c, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
b = c & 0xff;
|
||||
qproduce(kbd.q, &b, 1);
|
||||
}
|
||||
|
||||
void
|
||||
i8042auxenable(void (*putc)(int, int))
|
||||
{
|
||||
static char err[] = "i8042: aux init failed\n";
|
||||
|
||||
ilock(&i8042lock);
|
||||
|
||||
/* enable kbd/aux xfers and interrupts */
|
||||
ccc &= ~Cauxdis;
|
||||
ccc |= Cauxint;
|
||||
|
||||
if(outready() < 0)
|
||||
print(err);
|
||||
outb(Cmd, 0x60); /* write control register */
|
||||
if(outready() < 0)
|
||||
print(err);
|
||||
outb(Data, ccc);
|
||||
if(outready() < 0)
|
||||
print(err);
|
||||
outb(Cmd, 0xA8); /* auxiliary device enable */
|
||||
if(outready() < 0){
|
||||
print(err);
|
||||
iunlock(&i8042lock);
|
||||
return;
|
||||
}
|
||||
auxputc = putc;
|
||||
intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
|
||||
|
||||
iunlock(&i8042lock);
|
||||
}
|
||||
|
||||
static void
|
||||
kbdpoll(void)
|
||||
{
|
||||
if(nokbd || qlen(kbd.q) > 0)
|
||||
return;
|
||||
i8042intr(0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
kbdshutdown(void)
|
||||
{
|
||||
if(nokbd)
|
||||
return;
|
||||
/* disable kbd and aux xfers and interrupts */
|
||||
ccc &= ~(Ckbdint|Cauxint);
|
||||
ccc |= (Cauxdis|Ckbddis);
|
||||
outready();
|
||||
outb(Cmd, 0x60);
|
||||
outready();
|
||||
outb(Data, ccc);
|
||||
outready();
|
||||
}
|
||||
|
||||
static Chan *
|
||||
kbdattach(Chan *c, Chan *ac, char *spec, int flags)
|
||||
{
|
||||
return devattach(L'b', spec);
|
||||
}
|
||||
|
||||
static Walkqid*
|
||||
kbdwalk(Chan *c, Chan *nc, char **name, int nname)
|
||||
{
|
||||
return devwalk(c, nc, name, nname, kbdtab, nelem(kbdtab), devgen);
|
||||
}
|
||||
|
||||
static long
|
||||
kbdstat(Chan *c, uint8_t *dp, long n)
|
||||
{
|
||||
return devstat(c, dp, n, kbdtab, nelem(kbdtab), devgen);
|
||||
}
|
||||
|
||||
static Chan*
|
||||
kbdopen(Chan *c, unsigned long omode)
|
||||
{
|
||||
if(!iseve())
|
||||
error(Eperm);
|
||||
if(c->qid.path == Qscancode){
|
||||
if(waserror()){
|
||||
decref(&kbd.ref);
|
||||
nexterror();
|
||||
}
|
||||
if(incref(&kbd.ref) != 1)
|
||||
error(Einuse);
|
||||
c = devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
|
||||
poperror();
|
||||
return c;
|
||||
}
|
||||
return devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
|
||||
}
|
||||
|
||||
static void
|
||||
kbdclose(Chan *c)
|
||||
{
|
||||
if((c->flag & COPEN) && c->qid.path == Qscancode)
|
||||
decref(&kbd.ref);
|
||||
}
|
||||
|
||||
static Block*
|
||||
kbdbread(Chan *c, long n, int64_t off)
|
||||
{
|
||||
if(c->qid.path == Qscancode){
|
||||
kbdpoll();
|
||||
return qbread(kbd.q, n);
|
||||
}
|
||||
return devbread(c, n, off);
|
||||
}
|
||||
|
||||
static long
|
||||
kbdread(Chan *c, void *a, long n, int64_t _)
|
||||
{
|
||||
if(c->qid.path == Qscancode){
|
||||
kbdpoll();
|
||||
return qread(kbd.q, a, n);
|
||||
}
|
||||
if(c->qid.path == Qdir)
|
||||
return devdirread(c, a, n, kbdtab, nelem(kbdtab), devgen);
|
||||
error(Egreg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
kbdwrite(Chan *c, void *a, long n, int64_t _)
|
||||
{
|
||||
char tmp[8+1], *p;
|
||||
|
||||
if(c->qid.path != Qleds)
|
||||
error(Egreg);
|
||||
|
||||
p = tmp + n;
|
||||
if(n >= sizeof(tmp))
|
||||
p = tmp + sizeof(tmp)-1;
|
||||
memmove(tmp, a, p - tmp);
|
||||
*p = 0;
|
||||
|
||||
setleds(atoi(tmp));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
kbdreset(void)
|
||||
{
|
||||
static char initfailed[] = "i8042: kbd init failed\n";
|
||||
int c, try;
|
||||
|
||||
kbd.q = qopen(1024, Qcoalesce, 0, 0);
|
||||
if(kbd.q == nil)
|
||||
panic("kbdreset");
|
||||
qnoblock(kbd.q, 1);
|
||||
|
||||
/* wait for a quiescent controller */
|
||||
try = 1000;
|
||||
while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
|
||||
if(c & Inready)
|
||||
inb(Data);
|
||||
delay(1);
|
||||
}
|
||||
if (try <= 0) {
|
||||
print(initfailed);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get current controller command byte */
|
||||
outb(Cmd, 0x20);
|
||||
if(inready() < 0){
|
||||
print("i8042: can't read ccc\n");
|
||||
ccc = 0;
|
||||
} else
|
||||
ccc = inb(Data);
|
||||
|
||||
/* enable kbd xfers and interrupts */
|
||||
ccc &= ~Ckbddis;
|
||||
ccc |= Csf | Ckbdint | Cscs1;
|
||||
|
||||
/* disable ps2 mouse */
|
||||
ccc &= ~Cauxint;
|
||||
ccc |= Cauxdis;
|
||||
|
||||
if(outready() < 0) {
|
||||
print(initfailed);
|
||||
return;
|
||||
}
|
||||
outb(Cmd, 0x60);
|
||||
outready();
|
||||
outb(Data, ccc);
|
||||
outready();
|
||||
|
||||
nokbd = 0;
|
||||
ioalloc(Cmd, 1, 0, "i8042.cs");
|
||||
ioalloc(Data, 1, 0, "i8042.data");
|
||||
intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
|
||||
}
|
||||
|
||||
Dev kbddevtab = {
|
||||
L'b',
|
||||
"kbd",
|
||||
|
||||
kbdreset,
|
||||
devinit,
|
||||
kbdshutdown,
|
||||
kbdattach,
|
||||
kbdwalk,
|
||||
kbdstat,
|
||||
kbdopen,
|
||||
devcreate,
|
||||
kbdclose,
|
||||
kbdread,
|
||||
kbdbread,
|
||||
kbdwrite,
|
||||
devbwrite,
|
||||
devremove,
|
||||
devwstat,
|
||||
};
|
@ -24,9 +24,6 @@
|
||||
#include <cursor.h>
|
||||
#include "screen.h"
|
||||
|
||||
#define RMBUF ((void*)(KZERO + 0x9000)) // see 9/386/vgavesa.c
|
||||
#define LORMBUF (0x9000)
|
||||
|
||||
enum {
|
||||
Qdir,
|
||||
Qvgabios,
|
||||
@ -45,8 +42,6 @@ static Dirtab vgadir[] = {
|
||||
|
||||
enum {
|
||||
CMactualsize,
|
||||
CMblank,
|
||||
CMblanktime,
|
||||
CMdrawinit,
|
||||
CMhwaccel,
|
||||
CMhwblank,
|
||||
@ -57,13 +52,12 @@ enum {
|
||||
CMsize,
|
||||
CMtextmode,
|
||||
CMtype,
|
||||
CMunblank,
|
||||
CMsoftscreen,
|
||||
CMpcidev,
|
||||
};
|
||||
|
||||
static Cmdtab vgactlmsg[] = {
|
||||
CMactualsize, "actualsize", 2,
|
||||
CMblank, "blank", 1,
|
||||
CMblanktime, "blanktime", 2,
|
||||
CMdrawinit, "drawinit", 1,
|
||||
CMhwaccel, "hwaccel", 2,
|
||||
CMhwblank, "hwblank", 2,
|
||||
@ -74,66 +68,39 @@ static Cmdtab vgactlmsg[] = {
|
||||
CMsize, "size", 3,
|
||||
CMtextmode, "textmode", 1,
|
||||
CMtype, "type", 2,
|
||||
CMunblank, "unblank", 1,
|
||||
CMsoftscreen, "softscreen", 2,
|
||||
CMpcidev, "pcidev", 2,
|
||||
};
|
||||
|
||||
static long
|
||||
rmemrw(int isr, void *a, long n, int64_t off)
|
||||
{
|
||||
if(off < 0 || n < 0)
|
||||
error("bad offset/count");
|
||||
if(isr){
|
||||
if(off >= MB)
|
||||
return 0;
|
||||
if(off+n >= MB)
|
||||
n = MB - off;
|
||||
jehanne_memmove(a, KADDR((uintptr_t)off), n);
|
||||
}else{
|
||||
/* realmode buf page ok, allow vga framebuf's access */
|
||||
if(off >= MB)
|
||||
error("Offset > MB");
|
||||
if (off+n > MB)
|
||||
error("off+n > MB");
|
||||
if (off < LORMBUF)
|
||||
error("off < LORMBUF");
|
||||
if (off+n > LORMBUF+PGSZ)
|
||||
error("off+n > LORMBUF+BY2PG");
|
||||
if (off < 0xA0000)
|
||||
error("off < 0xa0000");
|
||||
if (off+n > 0xB0000+0x10000)
|
||||
error("off+n > 0xb0000+0x10000");
|
||||
jehanne_memmove(KADDR((uintptr_t)off), a, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static long
|
||||
rmemread(Chan*_, void *a, long n, int64_t off)
|
||||
{
|
||||
return rmemrw(1, a, n, off);
|
||||
}
|
||||
|
||||
static long
|
||||
rmemwrite(Chan*_, void *a, long n, int64_t off)
|
||||
{
|
||||
return rmemrw(0, a, n, off);
|
||||
}
|
||||
|
||||
static void
|
||||
vgareset(void)
|
||||
{
|
||||
Pcidev *pci;
|
||||
VGAscr *scr;
|
||||
|
||||
/* reserve the 'standard' vga registers */
|
||||
if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0)
|
||||
panic("vga ports already allocated");
|
||||
if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0)
|
||||
panic("vga ports already allocated");
|
||||
addarchfile("realmodemem", 0660, rmemread, rmemwrite);
|
||||
|
||||
/* find graphics card pci device */
|
||||
scr = &vgascreen[0];
|
||||
scr->pci = pci = nil;
|
||||
while((pci = pcimatch(pci, 0, 0)) != nil){
|
||||
if(pci->ccrb == Pcibcdisp){
|
||||
scr->pci = pci;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sys->monitor = 1;
|
||||
}
|
||||
|
||||
static Chan*
|
||||
vgaattach(Chan *c, Chan *ac, char *spec, int flags)
|
||||
{
|
||||
if(*spec && jehanne_strcmp(spec, "0"))
|
||||
if(*spec && strcmp(spec, "0"))
|
||||
error(Eio);
|
||||
return devattach('v', spec);
|
||||
}
|
||||
@ -159,7 +126,7 @@ vgaopen(Chan* c, unsigned long omode)
|
||||
scr = &vgascreen[0];
|
||||
if ((uint32_t)c->qid.path == Qvgaovlctl) {
|
||||
if (scr->dev && scr->dev->ovlctl)
|
||||
scr->dev->ovlctl(scr, c, openctl, jehanne_strlen(openctl));
|
||||
scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
|
||||
else
|
||||
error(Enonexist);
|
||||
}
|
||||
@ -176,10 +143,10 @@ vgaclose(Chan* c)
|
||||
if((uint32_t)c->qid.path == Qvgaovlctl)
|
||||
if(scr->dev && scr->dev->ovlctl){
|
||||
if(waserror()){
|
||||
jehanne_print("ovlctl error: %s\n", up->errstr);
|
||||
print("ovlctl error: %s\n", up->errstr);
|
||||
return;
|
||||
}
|
||||
scr->dev->ovlctl(scr, c, closectl, jehanne_strlen(closectl));
|
||||
scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
|
||||
poperror();
|
||||
}
|
||||
}
|
||||
@ -187,8 +154,7 @@ vgaclose(Chan* c)
|
||||
static long
|
||||
vgaread(Chan* c, void* a, long n, int64_t off)
|
||||
{
|
||||
int len;
|
||||
char *p, *s;
|
||||
char *p, *s, *e;
|
||||
VGAscr *scr;
|
||||
uint32_t offset = off;
|
||||
char chbuf[30];
|
||||
@ -203,50 +169,41 @@ vgaread(Chan* c, void* a, long n, int64_t off)
|
||||
return 0;
|
||||
if(offset+n >= 0x100000)
|
||||
n = 0x100000 - offset;
|
||||
jehanne_memmove(a, (unsigned char*)KADDR(0)+offset, n);
|
||||
memmove(a, (uint8_t*)KADDR(0)+offset, n);
|
||||
return n;
|
||||
|
||||
case Qvgactl:
|
||||
scr = &vgascreen[0];
|
||||
|
||||
p = jehanne_malloc(READSTR);
|
||||
if(p == nil)
|
||||
error(Enomem);
|
||||
s = smalloc(READSTR);
|
||||
if(waserror()){
|
||||
jehanne_free(p);
|
||||
free(s);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
len = 0;
|
||||
|
||||
if(scr->dev)
|
||||
s = scr->dev->name;
|
||||
else
|
||||
s = "cga";
|
||||
len += jehanne_snprint(p+len, READSTR-len, "type %s\n", s);
|
||||
|
||||
if(scr->gscreen) {
|
||||
len += jehanne_snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
|
||||
p = s, e = s+READSTR;
|
||||
p = seprint(p, e, "type %s\n",
|
||||
scr->dev != nil ? scr->dev->name : "cga");
|
||||
if(scr->gscreen != nil) {
|
||||
p = seprint(p, e, "size %dx%dx%d %s\n",
|
||||
scr->gscreen->r.max.x, scr->gscreen->r.max.y,
|
||||
scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
|
||||
|
||||
if(Dx(scr->gscreen->r) != Dx(physgscreenr)
|
||||
|| Dy(scr->gscreen->r) != Dy(physgscreenr))
|
||||
len += jehanne_snprint(p+len, READSTR-len, "actualsize %dx%d\n",
|
||||
p = seprint(p, e, "actualsize %dx%d\n",
|
||||
physgscreenr.max.x, physgscreenr.max.y);
|
||||
}
|
||||
|
||||
len += jehanne_snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
|
||||
blanktime, drawidletime(), scr->isblank ? "off" : "on");
|
||||
len += jehanne_snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
|
||||
len += jehanne_snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
|
||||
len += jehanne_snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
|
||||
len += jehanne_snprint(p+len, READSTR-len, "addr p 0x%lux v 0x%p size 0x%ux\n", scr->paddr, scr->vaddr, scr->apsize);
|
||||
USED(len);
|
||||
|
||||
n = readstr(offset, a, n, p);
|
||||
p = seprint(p, e, "hwgc %s\n",
|
||||
scr->cur != nil ? scr->cur->name : "off");
|
||||
p = seprint(p, e, "hwaccel %s\n", hwaccel ? "on" : "off");
|
||||
p = seprint(p, e, "hwblank %s\n", hwblank ? "on" : "off");
|
||||
p = seprint(p, e, "panning %s\n", panning ? "on" : "off");
|
||||
p = seprint(p, e, "addr p %#p v %#p size %#ux\n",
|
||||
scr->paddr, scr->vaddr, scr->apsize);
|
||||
p = seprint(p, e, "softscreen %s\n", scr->softscreen ? "on" : "off");
|
||||
USED(p);
|
||||
n = readstr(offset, a, n, s);
|
||||
poperror();
|
||||
jehanne_free(p);
|
||||
free(s);
|
||||
|
||||
return n;
|
||||
|
||||
@ -263,6 +220,8 @@ vgaread(Chan* c, void* a, long n, int64_t off)
|
||||
return 0;
|
||||
}
|
||||
|
||||
//static char Ebusy[] = "vga already configured";
|
||||
|
||||
static void
|
||||
vgactl(Cmdbuf *cb)
|
||||
{
|
||||
@ -278,47 +237,69 @@ vgactl(Cmdbuf *cb)
|
||||
ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
|
||||
switch(ct->index){
|
||||
case CMhwgc:
|
||||
if(jehanne_strcmp(cb->f[1], "off") == 0){
|
||||
lock(&cursor.l);
|
||||
if(scr->gscreen == nil)
|
||||
error("hwgc: no gscreen");
|
||||
|
||||
if(strcmp(cb->f[1], "off") == 0){
|
||||
lock(&cursor);
|
||||
if(scr->cur){
|
||||
if(scr->cur->disable)
|
||||
scr->cur->disable(scr);
|
||||
scr->cur = nil;
|
||||
}
|
||||
unlock(&cursor.l);
|
||||
unlock(&cursor);
|
||||
return;
|
||||
}
|
||||
if(jehanne_strcmp(cb->f[1], "soft") == 0){
|
||||
lock(&cursor.l);
|
||||
if(strcmp(cb->f[1], "soft") == 0){
|
||||
lock(&cursor);
|
||||
swcursorinit();
|
||||
if(scr->cur && scr->cur->disable)
|
||||
scr->cur->disable(scr);
|
||||
scr->cur = &swcursor;
|
||||
if(scr->cur->enable)
|
||||
scr->cur->enable(scr);
|
||||
unlock(&cursor.l);
|
||||
unlock(&cursor);
|
||||
return;
|
||||
}
|
||||
for(i = 0; vgacur[i]; i++){
|
||||
if(jehanne_strcmp(cb->f[1], vgacur[i]->name))
|
||||
if(strcmp(cb->f[1], vgacur[i]->name))
|
||||
continue;
|
||||
lock(&cursor.l);
|
||||
lock(&cursor);
|
||||
if(scr->cur && scr->cur->disable)
|
||||
scr->cur->disable(scr);
|
||||
scr->cur = vgacur[i];
|
||||
if(scr->cur->enable)
|
||||
scr->cur->enable(scr);
|
||||
unlock(&cursor.l);
|
||||
unlock(&cursor);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case CMpcidev:
|
||||
if(cb->nf == 2){
|
||||
Pcidev *p;
|
||||
|
||||
if((p = pcimatchtbdf(strtoul(cb->f[1], 0, 16))) != nil)
|
||||
scr->pci = p;
|
||||
} else
|
||||
error(Ebadarg);
|
||||
return;
|
||||
|
||||
case CMtype:
|
||||
for(i = 0; vgadev[i]; i++){
|
||||
if(jehanne_strcmp(cb->f[1], vgadev[i]->name))
|
||||
if(strcmp(cb->f[1], vgadev[i]->name))
|
||||
continue;
|
||||
if(scr->dev && scr->dev->disable)
|
||||
scr->dev->disable(scr);
|
||||
if(scr->dev){
|
||||
qlock(&drawlock);
|
||||
scr->fill = nil;
|
||||
scr->scroll = nil;
|
||||
scr->blank = nil;
|
||||
hwblank = 0;
|
||||
hwaccel = 0;
|
||||
qunlock(&drawlock);
|
||||
if(scr->dev->disable)
|
||||
scr->dev->disable(scr);
|
||||
}
|
||||
scr->dev = vgadev[i];
|
||||
if(scr->dev->enable)
|
||||
scr->dev->enable(scr);
|
||||
@ -327,23 +308,24 @@ vgactl(Cmdbuf *cb)
|
||||
break;
|
||||
|
||||
case CMtextmode:
|
||||
cgainit();
|
||||
screen_init();
|
||||
bootscreenconf(nil);
|
||||
return;
|
||||
|
||||
case CMsize:
|
||||
x = jehanne_strtoul(cb->f[1], &p, 0);
|
||||
x = strtoul(cb->f[1], &p, 0);
|
||||
if(x == 0 || x > 10240)
|
||||
error(Ebadarg);
|
||||
if(*p)
|
||||
p++;
|
||||
|
||||
y = jehanne_strtoul(p, &p, 0);
|
||||
y = strtoul(p, &p, 0);
|
||||
if(y == 0 || y > 10240)
|
||||
error(Ebadarg);
|
||||
if(*p)
|
||||
p++;
|
||||
|
||||
z = jehanne_strtoul(p, &p, 0);
|
||||
z = strtoul(p, &p, 0);
|
||||
|
||||
chanstr = cb->f[2];
|
||||
if((chan = strtochan(chanstr)) == 0)
|
||||
@ -352,26 +334,24 @@ vgactl(Cmdbuf *cb)
|
||||
if(chantodepth(chan) != z)
|
||||
error("depth, channel do not match");
|
||||
|
||||
cursoroff(1);
|
||||
cursoroff();
|
||||
deletescreenimage();
|
||||
if(screensize(x, y, z, chan))
|
||||
error(Egreg);
|
||||
vgascreenwin(scr);
|
||||
resetscreenimage();
|
||||
cursoron(1);
|
||||
bootscreenconf(scr);
|
||||
return;
|
||||
|
||||
case CMactualsize:
|
||||
if(scr->gscreen == nil)
|
||||
error("set the screen size first");
|
||||
|
||||
x = jehanne_strtoul(cb->f[1], &p, 0);
|
||||
x = strtoul(cb->f[1], &p, 0);
|
||||
if(x == 0 || x > 2048)
|
||||
error(Ebadarg);
|
||||
if(*p)
|
||||
p++;
|
||||
|
||||
y = jehanne_strtoul(p, nil, 0);
|
||||
y = strtoul(p, nil, 0);
|
||||
if(y == 0 || y > 2048)
|
||||
error(Ebadarg);
|
||||
|
||||
@ -383,51 +363,57 @@ vgactl(Cmdbuf *cb)
|
||||
return;
|
||||
|
||||
case CMpalettedepth:
|
||||
x = jehanne_strtoul(cb->f[1], &p, 0);
|
||||
x = strtoul(cb->f[1], &p, 0);
|
||||
if(x != 8 && x != 6)
|
||||
error(Ebadarg);
|
||||
|
||||
scr->palettedepth = x;
|
||||
return;
|
||||
|
||||
case CMsoftscreen:
|
||||
if(strcmp(cb->f[1], "on") == 0)
|
||||
scr->softscreen = 1;
|
||||
else if(strcmp(cb->f[1], "off") == 0)
|
||||
scr->softscreen = 0;
|
||||
else
|
||||
break;
|
||||
if(scr->gscreen == nil)
|
||||
return;
|
||||
x = scr->gscreen->r.max.x;
|
||||
y = scr->gscreen->r.max.y;
|
||||
z = scr->gscreen->depth;
|
||||
chan = scr->gscreen->chan;
|
||||
cursoroff();
|
||||
deletescreenimage();
|
||||
if(screensize(x, y, z, chan))
|
||||
error(Egreg);
|
||||
/* no break */
|
||||
case CMdrawinit:
|
||||
if(scr->gscreen == nil)
|
||||
error("drawinit: no gscreen");
|
||||
if(scr->dev && scr->dev->drawinit)
|
||||
scr->dev->drawinit(scr);
|
||||
hwblank = scr->blank != nil;
|
||||
hwaccel = scr->fill != nil || scr->scroll != nil;
|
||||
vgascreenwin(scr);
|
||||
resetscreenimage();
|
||||
cursoron();
|
||||
return;
|
||||
|
||||
case CMlinear:
|
||||
if(cb->nf!=2 && cb->nf!=3)
|
||||
error(Ebadarg);
|
||||
size = jehanne_strtoul(cb->f[1], 0, 0);
|
||||
size = strtoul(cb->f[1], 0, 0);
|
||||
if(cb->nf == 2)
|
||||
align = 0;
|
||||
else
|
||||
align = jehanne_strtoul(cb->f[2], 0, 0);
|
||||
align = strtoul(cb->f[2], 0, 0);
|
||||
if(screenaperture(size, align) < 0)
|
||||
error("not enough free address space");
|
||||
return;
|
||||
/*
|
||||
case CMmemset:
|
||||
jehanne_memset((void*)jehanne_strtoul(cb->f[1], 0, 0), jehanne_atoi(cb->f[2]), jehanne_atoi(cb->f[3]));
|
||||
return;
|
||||
*/
|
||||
|
||||
case CMblank:
|
||||
drawblankscreen(1);
|
||||
return;
|
||||
|
||||
case CMunblank:
|
||||
drawblankscreen(0);
|
||||
return;
|
||||
|
||||
case CMblanktime:
|
||||
blanktime = jehanne_strtoul(cb->f[1], 0, 0);
|
||||
return;
|
||||
|
||||
case CMpanning:
|
||||
if(jehanne_strcmp(cb->f[1], "on") == 0){
|
||||
if(strcmp(cb->f[1], "on") == 0){
|
||||
if(scr == nil || scr->cur == nil)
|
||||
error("set screen first");
|
||||
if(!scr->cur->doespanning)
|
||||
@ -435,7 +421,7 @@ vgactl(Cmdbuf *cb)
|
||||
scr->gscreen->clipr = scr->gscreen->r;
|
||||
panning = 1;
|
||||
}
|
||||
else if(jehanne_strcmp(cb->f[1], "off") == 0){
|
||||
else if(strcmp(cb->f[1], "off") == 0){
|
||||
scr->gscreen->clipr = physgscreenr;
|
||||
panning = 0;
|
||||
}else
|
||||
@ -443,18 +429,18 @@ vgactl(Cmdbuf *cb)
|
||||
return;
|
||||
|
||||
case CMhwaccel:
|
||||
if(jehanne_strcmp(cb->f[1], "on") == 0)
|
||||
if(strcmp(cb->f[1], "on") == 0)
|
||||
hwaccel = 1;
|
||||
else if(jehanne_strcmp(cb->f[1], "off") == 0)
|
||||
else if(strcmp(cb->f[1], "off") == 0)
|
||||
hwaccel = 0;
|
||||
else
|
||||
break;
|
||||
return;
|
||||
|
||||
case CMhwblank:
|
||||
if(jehanne_strcmp(cb->f[1], "on") == 0)
|
||||
if(strcmp(cb->f[1], "on") == 0)
|
||||
hwblank = 1;
|
||||
else if(jehanne_strcmp(cb->f[1], "off") == 0)
|
||||
else if(strcmp(cb->f[1], "off") == 0)
|
||||
hwblank = 0;
|
||||
else
|
||||
break;
|
||||
@ -483,12 +469,12 @@ vgawrite(Chan* c, void* a, long n, int64_t off)
|
||||
error(Ebadarg);
|
||||
cb = parsecmd(a, n);
|
||||
if(waserror()){
|
||||
jehanne_free(cb);
|
||||
free(cb);
|
||||
nexterror();
|
||||
}
|
||||
vgactl(cb);
|
||||
poperror();
|
||||
jehanne_free(cb);
|
||||
free(cb);
|
||||
return n;
|
||||
|
||||
case Qvgaovl:
|
||||
|
174
sys/src/kern/amd64/ec.c
Normal file
174
sys/src/kern/amd64/ec.c
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* embedded controller (usually at ports 0x66/0x62)
|
||||
*/
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
|
||||
enum {
|
||||
/* registers */
|
||||
EC_SC = 0,
|
||||
EC_DATA,
|
||||
|
||||
/* Embedded Controller Status, EC_SC (R) */
|
||||
OBF = 1<<0,
|
||||
IBF = 1<<1,
|
||||
CMD = 1<<3,
|
||||
BURST = 1<<4,
|
||||
SCI_EVT = 1<<5,
|
||||
SMI_EVT = 1<<6,
|
||||
|
||||
/* Embedded Controller Command Set */
|
||||
RD_EC = 0x80,
|
||||
WR_EC = 0x81,
|
||||
BE_EC = 0x82,
|
||||
BD_EC = 0x83,
|
||||
QR_EC = 0x84,
|
||||
};
|
||||
|
||||
static struct {
|
||||
Lock;
|
||||
int init;
|
||||
int port[2]; /* EC_SC and EC_DATA */
|
||||
} ec;
|
||||
|
||||
static uint8_t
|
||||
ecrr(int reg)
|
||||
{
|
||||
return inb(ec.port[reg]);
|
||||
}
|
||||
static void
|
||||
ecwr(int reg, uint8_t val)
|
||||
{
|
||||
outb(ec.port[reg], val);
|
||||
}
|
||||
|
||||
static int
|
||||
ecwait(uint8_t mask, uint8_t val)
|
||||
{
|
||||
int i, s;
|
||||
|
||||
s = 0;
|
||||
for(i=0; i<1000; i++){
|
||||
s = ecrr(EC_SC);
|
||||
if((s & mask) == val)
|
||||
return 0;
|
||||
delay(1);
|
||||
}
|
||||
print("ec: wait timeout status=%x pc=%#p\n", s, getcallerpc());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
ecread(uint8_t addr)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = -1;
|
||||
lock(&ec);
|
||||
if(!ec.init)
|
||||
goto out;
|
||||
if(ecwait(IBF, 0))
|
||||
goto out;
|
||||
ecwr(EC_SC, RD_EC);
|
||||
if(ecwait(IBF, 0))
|
||||
goto out;
|
||||
ecwr(EC_DATA, addr);
|
||||
if(ecwait(OBF, OBF))
|
||||
goto out;
|
||||
r = ecrr(EC_DATA);
|
||||
ecwait(OBF, 0);
|
||||
out:
|
||||
unlock(&ec);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
ecwrite(uint8_t addr, uint8_t val)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = -1;
|
||||
lock(&ec);
|
||||
if(!ec.init)
|
||||
goto out;
|
||||
if(ecwait(IBF, 0))
|
||||
goto out;
|
||||
ecwr(EC_SC, WR_EC);
|
||||
if(ecwait(IBF, 0))
|
||||
goto out;
|
||||
ecwr(EC_DATA, addr);
|
||||
if(ecwait(IBF, 0))
|
||||
goto out;
|
||||
ecwr(EC_DATA, val);
|
||||
if(ecwait(IBF, 0))
|
||||
goto out;
|
||||
r = 0;
|
||||
out:
|
||||
unlock(&ec);
|
||||
return r;
|
||||
}
|
||||
|
||||
static long
|
||||
ecarchread(Chan* _, void *a, long n, int64_t off)
|
||||
{
|
||||
int port, v;
|
||||
uint8_t *p;
|
||||
|
||||
if(off < 0 || off >= 256)
|
||||
return 0;
|
||||
if(off+n > 256)
|
||||
n = 256 - off;
|
||||
p = a;
|
||||
for(port = off; port < off+n; port++){
|
||||
if((v = ecread(port)) < 0)
|
||||
error(Eio);
|
||||
*p++ = v;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static long
|
||||
ecarchwrite(Chan* _, void *a, long n, int64_t off)
|
||||
{
|
||||
int port;
|
||||
uint8_t *p;
|
||||
|
||||
if(off < 0 || off+n > 256)
|
||||
error(Ebadarg);
|
||||
p = a;
|
||||
for(port = off; port < off+n; port++)
|
||||
if(ecwrite(port, *p++) < 0)
|
||||
error(Eio);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
ecinit(int cmdport, int dataport)
|
||||
{
|
||||
print("ec: cmd %X, data %X\n", cmdport, dataport);
|
||||
|
||||
if(ioalloc(cmdport, 1, 0, "ec.sc") < 0){
|
||||
print("ec: cant allocate cmd port %X\n", cmdport);
|
||||
return -1;
|
||||
}
|
||||
if(ioalloc(dataport, 1, 0, "ec.data") < 0){
|
||||
print("ec: cant allocate data port %X\n", dataport);
|
||||
iofree(cmdport);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lock(&ec);
|
||||
ec.port[EC_SC] = cmdport;
|
||||
ec.port[EC_DATA] = dataport;
|
||||
ec.init = 1;
|
||||
unlock(&ec);
|
||||
|
||||
addarchfile("ec", 0660, ecarchread, ecarchwrite);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,189 +1,145 @@
|
||||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "mem.h"
|
||||
#include "amd64.h"
|
||||
#ifndef __ASSEMBLER__
|
||||
#define __ASSEMBLER__
|
||||
#endif
|
||||
#include "multiboot.h"
|
||||
|
||||
.code32
|
||||
|
||||
/* do we enter in 16-bit mode? If so, take the code from coreboot that goes from
|
||||
* 16->32
|
||||
/* This code (up to _start64v) is linked and loaded at physical address
|
||||
* 0x00100000 (1MB), which is the start of extended memory. (See kernel.ld)
|
||||
*/
|
||||
/*
|
||||
* Enter here in 32-bit protected mode. Welcome to 1982.
|
||||
* Make sure the GDT is set as it should be:
|
||||
* disable interrupts;
|
||||
* load the GDT with the table in _gdt32p;
|
||||
* load all the data segments
|
||||
* load the code segment via a far jump.
|
||||
|
||||
/* boottext must be text: http://sourceware.org/binutils/docs/as/Section.html
|
||||
*/
|
||||
#define MULTIBOOT_PAGE_ALIGN (1<<0)
|
||||
#define MULTIBOOT_MEMORY_INFO (1<<1)
|
||||
#define MULTIBOOT_HEADER_MAGIC (0x1BADB002)
|
||||
#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_PAGE_ALIGN)
|
||||
#define CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS))
|
||||
|
||||
# The kernel bootstrap (this code) is linked and loaded at physical address
|
||||
# 0x00100000 (1MB), which is the start of extended memory. (See kernel.ld)
|
||||
|
||||
# Flagging boottext to be text. Check out:
|
||||
# http://sourceware.org/binutils/docs/as/Section.html
|
||||
.section .boottext, "awx"
|
||||
|
||||
.code32
|
||||
.align 4
|
||||
_protected:
|
||||
multiboot_header:
|
||||
.long MULTIBOOT_HEADER_MAGIC
|
||||
.long MULTIBOOT_HEADER_FLAGS
|
||||
.long CHECKSUM
|
||||
#define MBFLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_PAGE_ALIGN)
|
||||
_multibootheader:
|
||||
.long MULTIBOOT_HEADER_MAGIC
|
||||
.long MBFLAGS
|
||||
.long (-(MULTIBOOT_HEADER_MAGIC + MBFLAGS)) /* checksum */
|
||||
|
||||
.align 16
|
||||
.globl _boot_registers
|
||||
_boot_registers:
|
||||
_ax:
|
||||
.quad 0
|
||||
_bx:
|
||||
.quad 0
|
||||
_cx:
|
||||
.quad 0
|
||||
_dx:
|
||||
.quad 0
|
||||
_si:
|
||||
.quad 0
|
||||
_di:
|
||||
.quad 0
|
||||
_bp:
|
||||
.quad 0
|
||||
_r8:
|
||||
.quad 0
|
||||
_r9:
|
||||
.quad 0
|
||||
_r10:
|
||||
.quad 0
|
||||
_r11:
|
||||
.quad 0
|
||||
_r12:
|
||||
.quad 0
|
||||
_r13:
|
||||
.quad 0
|
||||
_r14:
|
||||
.quad 0
|
||||
_r15:
|
||||
.quad 0
|
||||
_type:
|
||||
.quad 0
|
||||
_error:
|
||||
.quad 0
|
||||
_ip:
|
||||
.quad 0
|
||||
_cs:
|
||||
.quad 0
|
||||
_flags:
|
||||
.quad 0
|
||||
_sp:
|
||||
.quad 0
|
||||
_ss:
|
||||
.quad 0
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
cli
|
||||
jmp 1f
|
||||
lgdt %cs:_gdtptr32p
|
||||
ljmp $0x18, $_protected
|
||||
|
||||
_protected:
|
||||
/* save initial registers (at least those available so far) */
|
||||
movl %eax, _ax
|
||||
movl %ebx, _bx
|
||||
movl %ecx, _cx
|
||||
movl %edx, _dx
|
||||
movl %ebp, _bp
|
||||
movl %esp, _sp
|
||||
movl %esi, _si
|
||||
movl %edi, _di
|
||||
|
||||
/* This is the GDT for the ROM stage part of coreboot. It
|
||||
* is different from the RAM stage GDT which is defined in
|
||||
* c_start.S
|
||||
*/
|
||||
|
||||
.align 4
|
||||
.globl gdtptr
|
||||
gdt:
|
||||
gdtptr:
|
||||
.word gdt_end - gdt -1 /* compute the table limit */
|
||||
.long gdt /* we know the offset */
|
||||
.word 0
|
||||
|
||||
/* selgdt 0x08, flat code segment */
|
||||
.word 0xffff, 0x0000
|
||||
.byte 0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, So we get 4Gbytes for limit */
|
||||
|
||||
/* selgdt 0x10,flat data segment */
|
||||
.word 0xffff, 0x0000
|
||||
.byte 0x00, 0x93, 0xcf, 0x00
|
||||
|
||||
/* long mode code segment. */
|
||||
.quad 0x0020980000000000 /* Long mode CS */
|
||||
|
||||
gdt_end:
|
||||
|
||||
|
||||
/*
|
||||
* When we come here we are in protected mode. We expand
|
||||
* the stack and copies the data segment from ROM to the
|
||||
* memory.
|
||||
*
|
||||
* After that, we call the chipset bootstrap routine that
|
||||
* does what is left of the chipset initialization.
|
||||
*
|
||||
* NOTE aligned to 4 so that we are sure that the prefetch
|
||||
* cache will be reloaded.
|
||||
*/
|
||||
1:
|
||||
.align 4
|
||||
.globl protected_start
|
||||
protected_start:
|
||||
|
||||
lgdt %cs:gdtptr
|
||||
ljmp $8, $__protected_start
|
||||
|
||||
__protected_start:
|
||||
/* Save the BIST value */
|
||||
movl %eax, %ebp
|
||||
movw $0x10, %ax
|
||||
movl $SELECTOR(2, SELGDT, 0), %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
|
||||
/* Restore the BIST value to %eax */
|
||||
movl %ebp, %eax
|
||||
jmp _warp64
|
||||
|
||||
entry32:
|
||||
1:
|
||||
movb $0x30, %al
|
||||
movw $0x30, %dx
|
||||
outb %dx
|
||||
// This gets us into a reasonable mode. We can skip the plan 9 gdt code.
|
||||
call 1f
|
||||
1:
|
||||
popl %ebp
|
||||
/* when you execute this instruction, bp has the value
|
||||
* of 1f.
|
||||
* So add the length of this instruction and the
|
||||
* 5 bytes of the jmp that follows it.
|
||||
* It will then point to start of header.
|
||||
*/
|
||||
addl $12, %ebp
|
||||
/* Now make it point to gdt32p (gdt, 32 bits, physical)
|
||||
*/
|
||||
addl $14, %ebp
|
||||
jmp _endofheader
|
||||
.align 16
|
||||
_gdt:
|
||||
/* null descriptor */
|
||||
.long 0
|
||||
.long 0
|
||||
|
||||
_startofheader:
|
||||
.byte 0x90 /* NOP */
|
||||
.byte 0x90 /* NOP */
|
||||
/* (KESEG) 64 bit long mode exec segment */
|
||||
.long 0xFFFF
|
||||
.long SEGL|SEGG|SEGP|(0xF<<16)|SEGPL(0)|SEGEXEC|SEGR
|
||||
|
||||
_multibootheader: /* must be 4-byte aligned */
|
||||
.long 0x1badb002 /* magic */
|
||||
.long 0x00000003 /* flags */
|
||||
.long -(0x1badb002 + 0x00000003) /* checksum */
|
||||
/* 32 bit data segment descriptor for 4 gigabytes (PL 0) */
|
||||
.long 0xFFFF
|
||||
.long SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW
|
||||
|
||||
_gdt32p:
|
||||
.quad 0x0000000000000000 /* NULL descriptor */
|
||||
.quad 0x00cf9a000000ffff /* CS */
|
||||
.quad 0x00cf92000000ffff /* DS */
|
||||
.quad 0x0020980000000000 /* Long mode CS */
|
||||
/* 32 bit exec segment descriptor for 4 gigabytes (PL 0) */
|
||||
.long 0xFFFF
|
||||
.long SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR
|
||||
|
||||
.align 4
|
||||
_gdtptr32p:
|
||||
.word 4*8-1
|
||||
.long _gdt32p
|
||||
|
||||
_gdt64p:
|
||||
.quad 0x0000000000000000 /* NULL descriptor */
|
||||
.quad 0x0020980000000000 /* CS */
|
||||
.long _gdt
|
||||
|
||||
.align 4
|
||||
_gdtptr64p:
|
||||
.word 2*8-1
|
||||
.quad _gdt64p
|
||||
|
||||
|
||||
_endofheader:
|
||||
pushl %eax /* possible passed-in magic */
|
||||
|
||||
/*
|
||||
* Make the basic page tables for CPU0 to map 0-4MiB physical
|
||||
* to KZERO, and include an identity map for the switch from protected
|
||||
* to paging mode. There`s an assumption here that the creation and later
|
||||
* removal of the identity map will not interfere with the KZERO mappings;
|
||||
* the conditions for clearing the identity map are
|
||||
* clear PML4 entry when (KZER0 & 0x0000ff8000000000) != 0;
|
||||
* clear PDP entry when (KZER0 & 0x0000007fc0000000) != 0;
|
||||
* don`t clear PD entry when (KZER0 & 0x000000003fe00000) == 0;
|
||||
* the code below assumes these conditions are met.
|
||||
*
|
||||
* Assume a recent processor with Page Size Extensions
|
||||
* and use two 2MiB entries.
|
||||
*/
|
||||
/*
|
||||
* The layout is decribed in data.h:
|
||||
* _protected: start of kernel text
|
||||
* - 4*KiB unused
|
||||
* - 4*KiB unused
|
||||
* - 4*KiB ptrpage
|
||||
* - 4*KiB syspage
|
||||
* - MACHSZ m
|
||||
* - 4*KiB vsvmpage for gdt, tss
|
||||
* - PTSZ PT for PMAPADDR unused - assumes in KZERO PD
|
||||
* - PTSZ PD
|
||||
* - PTSZ PDP
|
||||
* - PTSZ PML4
|
||||
* - MACHSTKSZ stack
|
||||
*/
|
||||
.word 4*8-1
|
||||
.quad _gdt
|
||||
|
||||
/*
|
||||
* Macros for accessing page table entries; change the
|
||||
@ -195,45 +151,55 @@ _endofheader:
|
||||
#define PTO(v) ((PTLX((v), 0))<<3)
|
||||
|
||||
_warp64:
|
||||
movl $_protected-(MACHSTKSZ+4*PTSZ+5*(4*KiB)+MACHSZ), %esi
|
||||
movl $((CPU0END-CPU0PML4)>>2), %ecx
|
||||
movl $(CPU0PML4-KZERO), %esi
|
||||
|
||||
movl %esi, %edi
|
||||
xorl %eax, %eax
|
||||
movl $((MACHSTKSZ+4*PTSZ+5*(4*KiB)+MACHSZ)>>2), %ecx
|
||||
|
||||
cld
|
||||
rep; stosl /* stack, P*, vsvm, m, sys */
|
||||
rep; stosl
|
||||
|
||||
movl %esi, %eax /* sys-KZERO */
|
||||
addl $(MACHSTKSZ), %eax /* PML4 */
|
||||
movl %eax, %cr3 /* load the mmu */
|
||||
movl %esi, %eax /* PML4 */
|
||||
movl %eax, %edx
|
||||
addl $(PTSZ|PteRW|PteP), %edx /* PDP at PML4 + PTSZ */
|
||||
movl %edx, PML4O(0)(%eax) /* PML4E for identity map */
|
||||
movl %edx, PML4O(KZERO)(%eax) /* PML4E for KZERO, PMAPADDR */
|
||||
addl $(PTSZ|PTEWRITE|PTEVALID), %edx /* PDP at PML4 + PTSZ */
|
||||
movl %edx, PML4O(0)(%eax) /* PML4E for double-map */
|
||||
movl %edx, PML4O(KZERO)(%eax) /* PML4E for KZERO */
|
||||
|
||||
addl $PTSZ, %eax /* PDP at PML4 + PTSZ */
|
||||
addl $PTSZ, %edx /* PD at PML4 + 2*PTSZ */
|
||||
movl %edx, PDPO(0)(%eax) /* PDPE for identity map */
|
||||
movl %edx, PDPO(KZERO)(%eax) /* PDPE for KZERO, PMAPADDR */
|
||||
addl $PTSZ, %eax /* PDP at PML4 + PTSZ */
|
||||
addl $PTSZ, %edx /* PD0 at PML4 + 2*PTSZ */
|
||||
movl %edx, PDPO(0)(%eax) /* PDPE for double-map */
|
||||
movl %edx, PDPO(KZERO)(%eax) /* PDPE for KZERO */
|
||||
|
||||
addl $PTSZ, %eax /* PD at PML4 + 2*PTSZ */
|
||||
movl $(PtePS|PteRW|PteP), %edx
|
||||
movl %edx, PDO(0)(%eax) /* PDE for identity 0-[24]MiB */
|
||||
/*
|
||||
* add PDPE for KZERO+1GB early as Vmware
|
||||
* hangs when modifying kernel PDP
|
||||
*/
|
||||
addl $PTSZ, %edx /* PD1 */
|
||||
movl %edx, PDPO(KZERO+GiB)(%eax)
|
||||
|
||||
movl %eax, %ecx
|
||||
addl $PDO(KZERO), %ecx
|
||||
addl $PTSZ, %eax /* PD0 at PML4 + 2*PTSZ */
|
||||
movl $(PTESIZE|PTEGLOBAL|PTEWRITE|PTEVALID), %edx
|
||||
movl %edx, PDO(0)(%eax) /* PDE for double-map */
|
||||
|
||||
/*
|
||||
* map from KZERO to end using 2MB pages
|
||||
*/
|
||||
addl $PDO(KZERO), %eax
|
||||
movl $end-KZERO, %ecx
|
||||
|
||||
addl $(16*MiB), %ecx /* qemu puts multiboot data after the kernel, including initrd */
|
||||
|
||||
addl $(PGLSZ(1)-1), %ecx
|
||||
andl $(~(PGLSZ(1)-1)), %ecx
|
||||
movl %ecx, MemMin-KZERO /* see memory.c */
|
||||
shr $(PTSHFT+PGSHIFT), %ecx
|
||||
|
||||
memloop:
|
||||
movl %edx, 0(%ecx)
|
||||
movl %edx, (%eax)
|
||||
addl $PGLSZ(1), %edx
|
||||
addl $8, %ecx
|
||||
cmpl $(32*MiB), %edx
|
||||
JL memloop
|
||||
|
||||
movl %eax, %edx /* PD at PML4 + 2*PTSZ */
|
||||
addl $(PTSZ|PteRW|PteP), %edx /* PT at PML4 + 3*PTSZ */
|
||||
movl %edx, PDO(PMAPADDR)(%eax) /* PDE for PMAPADDR */
|
||||
addl $8, %eax
|
||||
loop memloop
|
||||
|
||||
/*
|
||||
* Enable and activate Long Mode. From the manual:
|
||||
@ -245,6 +211,9 @@ memloop:
|
||||
* It`s all in 32-bit mode until the jump is made.
|
||||
*/
|
||||
lme:
|
||||
movl %esi, %cr3 /* load the mmu */
|
||||
jmp 1f
|
||||
1:
|
||||
movl %cr4, %eax
|
||||
andl $~Pse, %eax /* Page Size */
|
||||
orl $(Pge|Pae), %eax /* Page Global, Phys. Address */
|
||||
@ -260,7 +229,8 @@ lme:
|
||||
orl $(Pg|Wp), %edx /* Paging Enable */
|
||||
movl %edx, %cr0
|
||||
|
||||
ljmp $0x18, $_identity
|
||||
ljmp $0x8, $_identity
|
||||
|
||||
|
||||
/*
|
||||
* Long mode. Welcome to 2003.
|
||||
@ -270,69 +240,60 @@ lme:
|
||||
.code64
|
||||
|
||||
_identity:
|
||||
/* save initial registers */
|
||||
movq %r8, _r8
|
||||
movq %r9, _r9
|
||||
movq %r10, _r10
|
||||
movq %r11, _r11
|
||||
movq %r12, _r12
|
||||
movq %r13, _r13
|
||||
movq %r14, _r14
|
||||
movq %r15, _r15
|
||||
|
||||
movq $_start64v, %rax
|
||||
jmp *%rax
|
||||
|
||||
.section .text
|
||||
_gdt64v:
|
||||
.quad 0x0000000000000000 /* NULL descriptor */
|
||||
.quad 0x0020980000000000 /* CS */
|
||||
|
||||
.align 4
|
||||
.globl _gdtptr64v
|
||||
_gdtptr64v:
|
||||
.word 2*8-1
|
||||
.quad _gdt64v
|
||||
.word 4*8-1
|
||||
.quad _gdt+KZERO
|
||||
.word 0
|
||||
|
||||
// At this point, we are safe to use kernel addresses, as we are in
|
||||
// kernel virtual address space.
|
||||
.align 4
|
||||
_start64v:
|
||||
movq $_gdtptr64v, %rax
|
||||
lgdt (%rax)
|
||||
|
||||
xorq %rdx, %rdx
|
||||
movw %dx, %ds /* not used in long mode */
|
||||
movw %dx, %es /* not used in long mode */
|
||||
movw %dx, %fs
|
||||
movw %dx, %gs
|
||||
movw %dx, %ss /* not used in long mode */
|
||||
xorq %rax, %rax
|
||||
movw %ax, %ds /* not used in long mode */
|
||||
movw %ax, %es /* not used in long mode */
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss /* not used in long mode */
|
||||
|
||||
movq %rsi, %rsi /* sys-KZERO */
|
||||
movq %rsi, %rax
|
||||
addq $KZERO, %rax
|
||||
movq %rax, sys /* sys */
|
||||
lldt %ax
|
||||
|
||||
addq $(MACHSTKSZ), %rax /* PML4 and top of stack */
|
||||
movq %rax, %rsp /* set stack */
|
||||
movq $(CPU0MACH+MACHSIZE), %rsp
|
||||
movq $CPU0MACH, %r15 /* m = CPU0MACH */
|
||||
movq %rax, %r14 /* up = 0; */
|
||||
|
||||
_zap0pml4:
|
||||
cmpq $PML4O(KZERO), %rdx /* KZER0 & 0x0000ff8000000000 */
|
||||
je _zap0pdp
|
||||
movq %rdx, PML4O(0)(%rax) /* zap identity map PML4E */
|
||||
_zap0pdp:
|
||||
addq $PTSZ, %rax /* PDP at PML4 + PTSZ */
|
||||
cmpq $PDPO(KZERO), %rdx /* KZER0 & 0x0000007fc0000000 */
|
||||
je _zap0pd
|
||||
movq %rdx, PDPO(0)(%rax) /* zap identity map PDPE */
|
||||
_zap0pd:
|
||||
addq $PTSZ, %rax /* PD at PML4 + 2*PTSZ */
|
||||
cmpq $PDO(KZERO), %rdx /* KZER0 & 0x000000003fe00000 */
|
||||
je _zap0done
|
||||
movq %rdx, PDO(0)(%rax) /* zap identity map PDE */
|
||||
_zap0done:
|
||||
addq $(MACHSTKSZ), %rsi /* PML4-KZERO */
|
||||
movq %rsi, %cr3 /* flush TLB */
|
||||
_clearbss:
|
||||
movq $edata, %rdi
|
||||
movq $end, %rcx
|
||||
addq $(PGSZ-1), %rdi
|
||||
andq $(~(PGSZ-1)), %rdi
|
||||
subq %rdi, %rcx /* end-edata bytes */
|
||||
shrq $2, %rcx /* end-edata doublewords */
|
||||
|
||||
addq $(2*PTSZ+4*KiB), %rax /* PD+PT+vsvm */
|
||||
movq %rax, %r15
|
||||
movq %rdx, %r14
|
||||
movq $0, (%rax) /* m->machno = 0 */
|
||||
cld
|
||||
rep; stosl
|
||||
|
||||
pushq %rdx /* clear flags */
|
||||
pushq %rax
|
||||
popfq
|
||||
|
||||
movq %rbx, %rbx /* push multiboot args */
|
||||
movq %rbx, %rsi
|
||||
movq %rax, %rax
|
||||
movq %rax, %rdi /* multiboot magic */
|
||||
xorq %rbp, %rbp /* stack trace ends here */
|
||||
call main
|
||||
|
||||
.globl ndnr
|
||||
|
@ -746,7 +746,7 @@ rtl8139match(Ether* edev, int id)
|
||||
|
||||
if(pcigetpms(p) > 0){
|
||||
pcisetpms(p, 0);
|
||||
|
||||
|
||||
for(i = 0; i < 6; i++)
|
||||
pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
|
||||
pcicfgw8(p, PciINTL, p->intl);
|
@ -434,7 +434,7 @@ enum {
|
||||
i82575,
|
||||
i82576,
|
||||
i82577,
|
||||
i82577m,
|
||||
i82577m,
|
||||
i82578,
|
||||
i82578m,
|
||||
i82579,
|
||||
@ -1974,7 +1974,7 @@ didtype(int d)
|
||||
case 0x150f: /* fiber */
|
||||
case 0x1510: /* backplane */
|
||||
case 0x1511: /* sfp */
|
||||
case 0x1516:
|
||||
case 0x1516:
|
||||
return i82580;
|
||||
case 0x1506: /* v */
|
||||
return i82583;
|
@ -91,7 +91,7 @@ enum {
|
||||
MACPortMII = 1<<2,
|
||||
MACEnable = 1<<23 | 1<<22 | 1<<21 | 1 << 15 | 1 << 14 | 1<<12 | 1<<11,
|
||||
MACHalfDuplex = 1<<1,
|
||||
|
||||
|
||||
MACEventStatus = 0x404,
|
||||
MACEventEnable = 0x408,
|
||||
MACAddress = 0x410,
|
||||
@ -105,7 +105,7 @@ enum {
|
||||
TxMACLengths = 0x464,
|
||||
MACHash = 0x470,
|
||||
RxRules = 0x480,
|
||||
|
||||
|
||||
RxRulesConf = 0x500,
|
||||
LowWaterMax = 0x504,
|
||||
LowWaterMaxMask = ~0xFFFF,
|
||||
@ -120,13 +120,13 @@ enum {
|
||||
SendBDSelectorMode = 0x1400,
|
||||
SendBDInitiatorMode = 0x1800,
|
||||
SendBDCompletionMode = 0x1C00,
|
||||
|
||||
|
||||
RxListPlacementMode = 0x2000,
|
||||
RxListPlacement = 0x2010,
|
||||
RxListPlacementConf = 0x2014,
|
||||
RxStats = 1<<0,
|
||||
RxListPlacementMask = 0x2018,
|
||||
|
||||
|
||||
RxDataBDInitiatorMode = 0x2400,
|
||||
RxBDHostAddr = 0x2450,
|
||||
RxBDFlags = 0x2458,
|
||||
@ -134,7 +134,7 @@ enum {
|
||||
RxDataCompletionMode = 0x2800,
|
||||
RxBDInitiatorMode = 0x2C00,
|
||||
RxBDRepl = 0x2C18,
|
||||
|
||||
|
||||
RxBDCompletionMode = 0x3000,
|
||||
HostCoalMode = 0x3C00,
|
||||
HostCoalRxTicks = 0x3C08,
|
||||
@ -147,27 +147,27 @@ enum {
|
||||
FlowAttention = 0x3C48,
|
||||
|
||||
MemArbiterMode = 0x4000,
|
||||
|
||||
|
||||
BufferManMode = 0x4400,
|
||||
|
||||
|
||||
MBUFLowWater = 0x4414,
|
||||
MBUFHighWater = 0x4418,
|
||||
|
||||
|
||||
ReadDMAMode = 0x4800,
|
||||
ReadDMAStatus = 0x4804,
|
||||
WriteDMAMode = 0x4C00,
|
||||
WriteDMAStatus = 0x4C04,
|
||||
|
||||
|
||||
RISCState = 0x5004,
|
||||
FTQReset = 0x5C00,
|
||||
MSIMode = 0x6000,
|
||||
|
||||
|
||||
ModeControl = 0x6800,
|
||||
ByteWordSwap = 1<<4 | 1<<5 | 1<<2, // | 1<<1,
|
||||
HostStackUp = 1<<16,
|
||||
HostSendBDs = 1<<17,
|
||||
InterruptOnMAC = 1<<26,
|
||||
|
||||
|
||||
MiscConf = 0x6804,
|
||||
CoreClockBlocksReset = 1<<0,
|
||||
GPHYPwrdnOverride = 1<<26,
|
||||
@ -177,7 +177,7 @@ enum {
|
||||
MiscLocalControl = 0x6808,
|
||||
InterruptOnAttn = 1<<3,
|
||||
AutoSEEPROM = 1<<24,
|
||||
|
||||
|
||||
SwArbitration = 0x7020,
|
||||
SwArbitSet1 = 1<<1,
|
||||
SwArbitWon1 = 1<<9,
|
||||
@ -186,11 +186,11 @@ enum {
|
||||
PhyAuxControl = 0x18,
|
||||
PhyIntStatus = 0x1A,
|
||||
PhyIntMask = 0x1B,
|
||||
|
||||
|
||||
Updated = 1<<0,
|
||||
LinkStateChange = 1<<1,
|
||||
Error = 1<<2,
|
||||
|
||||
|
||||
PacketEnd = 1<<2,
|
||||
FrameError = 1<<10,
|
||||
};
|
||||
@ -389,7 +389,7 @@ replenish(Ctlr *ctlr)
|
||||
uint32_t incr;
|
||||
uint32_t *next;
|
||||
Block *bp;
|
||||
|
||||
|
||||
incr = (ctlr->recvprodi + 1) & (RxProdRingLen - 1);
|
||||
if(incr == (ctlr->status[2] >> 16))
|
||||
return -1;
|
||||
@ -419,7 +419,7 @@ bcmreceive(Ether *edev)
|
||||
uint32_t *pkt;
|
||||
Ctlr *ctlr;
|
||||
Block *bp;
|
||||
|
||||
|
||||
ctlr = edev->ctlr;
|
||||
for(; pkt = currentrecvret(ctlr); replenish(ctlr), consumerecvret(ctlr)) {
|
||||
bp = ctlr->rxs[pkt[7]];
|
||||
@ -444,7 +444,7 @@ static void
|
||||
bcmtransclean(Ether *edev)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
|
||||
|
||||
ctlr = edev->ctlr;
|
||||
ilock(&ctlr->txlock);
|
||||
while(ctlr->sendcleani != (ctlr->status[4] >> 16)) {
|
||||
@ -462,7 +462,7 @@ bcmtransmit(Ether *edev)
|
||||
uint32_t *next;
|
||||
Ctlr *ctlr;
|
||||
Block *bp;
|
||||
|
||||
|
||||
ctlr = edev->ctlr;
|
||||
ilock(&ctlr->txlock);
|
||||
for(;;){
|
||||
@ -491,7 +491,7 @@ static void
|
||||
bcmerror(Ether *edev)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
|
||||
|
||||
ctlr = edev->ctlr;
|
||||
if(csr32(ctlr, FlowAttention)) {
|
||||
if(csr32(ctlr, FlowAttention) & 0xf8ff8080)
|
||||
@ -518,7 +518,7 @@ bcminterrupt(Ureg*, void *arg)
|
||||
uint32_t status, tag, dummy;
|
||||
Ether *edev;
|
||||
Ctlr *ctlr;
|
||||
|
||||
|
||||
edev = arg;
|
||||
ctlr = edev->ctlr;
|
||||
ilock(&ctlr->imlock);
|
||||
@ -579,7 +579,7 @@ bcminit(Ether *edev)
|
||||
uint32_t i;
|
||||
uint32_t j;
|
||||
Ctlr *ctlr;
|
||||
|
||||
|
||||
ctlr = edev->ctlr;
|
||||
dprint("bcm: reset\n");
|
||||
/* initialization procedure according to the datasheet */
|
||||
@ -748,7 +748,7 @@ didtype(Pcidev *p)
|
||||
{
|
||||
if(p->vid != 0x14e4)
|
||||
return -1;
|
||||
|
||||
|
||||
switch(p->did){
|
||||
default:
|
||||
return -1;
|
||||
@ -825,7 +825,7 @@ static void
|
||||
bcmpromiscuous(void* arg, int on)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
|
||||
|
||||
ctlr = ((Ether*)arg)->ctlr;
|
||||
if(on)
|
||||
csr32(ctlr, RxMACMode) |= 1<<8;
|
||||
@ -848,7 +848,7 @@ bcmpnp(Ether* edev)
|
||||
bcmpci();
|
||||
done = 1;
|
||||
}
|
||||
|
||||
|
||||
redux:
|
||||
for(ctlr = bcmhead; ; ctlr = ctlr->next) {
|
||||
if(ctlr == nil)
|
@ -1958,7 +1958,7 @@ igbepci(void)
|
||||
case 0x08:
|
||||
case 0x10:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctlr = jehanne_malloc(sizeof(Ctlr));
|
||||
if(ctlr == nil){
|
||||
jehanne_print("igbe: can't allocate memory\n");
|
||||
@ -2038,4 +2038,3 @@ etherigbelink(void)
|
||||
addethercard("i82543", igbepnp);
|
||||
addethercard("igbe", igbepnp);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -390,7 +390,7 @@ atapirwfis(Sfis *f, uint8_t *c, uint8_t *cdb, int cdblen, int ndata)
|
||||
c[Ffeat] = 1; /* dma */
|
||||
else
|
||||
c[Ffeat] = 0; /* features (exp); */
|
||||
c[Flba0] = 0;
|
||||
c[Flba0] = 0;
|
||||
c[Flba8] = ndata;
|
||||
c[Flba16] = ndata >> 8;
|
||||
c[Fdev] = Ataobs;
|
@ -31,7 +31,6 @@ void mouseenable(void);
|
||||
int mousecmd(int);
|
||||
|
||||
void aamloop(int);
|
||||
void acpiinit(int);
|
||||
Dirtab* addarchfile(char*, int,
|
||||
long(*)(Chan*,void*,long,int64_t),
|
||||
long(*)(Chan*,void*,long,int64_t));
|
||||
@ -47,36 +46,49 @@ uint64_t asmalloc(uint64_t, uint64_t, int, int);
|
||||
void asminit(void);
|
||||
void asmmapinit(uint64_t, uint64_t, int);
|
||||
void asmmodinit(uint32_t, uint32_t, char*);
|
||||
void cgaconsputs(char*, int);
|
||||
void cgainit(void);
|
||||
void cgapost(int);
|
||||
void screen_init(void);
|
||||
uintptr_t cankaddr(uintptr_t pa);
|
||||
void (*coherence)(void);
|
||||
void cpuid(int, uint32_t regs[]);
|
||||
int cpuidentify(void);
|
||||
void cpuidprint(void);
|
||||
int corecolor(int);
|
||||
uint32_t cpuid(uint32_t, uint32_t, uint32_t[4]);
|
||||
void (*cycles)(uint64_t*);
|
||||
int dbgprint(char*, ...);
|
||||
void delay(int);
|
||||
int ecinit(int cmdport, int dataport);
|
||||
int ecread(uint8_t addr);
|
||||
int ecwrite(uint8_t addr, uint8_t val);
|
||||
#define evenaddr(x) /* x86 doesn't care */
|
||||
int e820(void);
|
||||
int fpudevprocio(Proc*, void*, int32_t, uintptr_t, int);
|
||||
void fpuinit(void);
|
||||
void fpunoted(void);
|
||||
void fpunotify(Ureg*);
|
||||
void fpuprocrestore(Proc*);
|
||||
void fpuprocsave(Proc*);
|
||||
void fpusysprocsetup(Proc*);
|
||||
void fpusysrfork(Ureg*);
|
||||
void fpusysrforkchild(Proc*, Proc*);
|
||||
void fpclear(void);
|
||||
void fpinit(void);
|
||||
void fpoff(void);
|
||||
void (*fprestore)(FPsave*);
|
||||
void (*fpsave)(FPsave*);
|
||||
void fpsserestore(FPsave*);
|
||||
void fpssesave(FPsave*);
|
||||
void fpprocfork(Proc *p);
|
||||
void fpprocsetup(Proc* p);
|
||||
char* getconf(char*);
|
||||
void guesscpuhz(int);
|
||||
void _halt(void);
|
||||
void halt(void);
|
||||
void hpetinit(uint32_t, uint32_t, uintmem, int);
|
||||
/*int i8042auxcmd(int);
|
||||
int i8042auxcmds(uint8_t*, int);
|
||||
void i8042auxenable(void (*)(int, int));*/
|
||||
void i8042systemreset(void);
|
||||
Uart* i8250console(char*);
|
||||
int i8042auxcmd(int);
|
||||
void i8042auxenable(void (*)(int, int));
|
||||
void i8042reset(void);
|
||||
void i8250console(void);
|
||||
void* i8250alloc(int, int, int);
|
||||
int64_t i8254hz(uint32_t[2][4]);
|
||||
|
||||
void i8253enable(void);
|
||||
void i8253init(void);
|
||||
void i8253reset(void);
|
||||
uint64_t i8253read(uint64_t*);
|
||||
void i8253timerset(uint64_t);
|
||||
|
||||
void idle(void);
|
||||
void idlehands(void);
|
||||
void idthandlers(void);
|
||||
int inb(int);
|
||||
@ -85,7 +97,7 @@ uint16_t ins(int);
|
||||
void inss(int, void*, int);
|
||||
uint32_t inl(int);
|
||||
void insl(int, void*, int);
|
||||
int intrdisable(void*);
|
||||
int intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name);
|
||||
void* intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
|
||||
void invlpg(uintptr_t va);
|
||||
void iofree(int);
|
||||
@ -96,16 +108,20 @@ int ioreserve(int, int, int, char*);
|
||||
int iprint(char*, ...);
|
||||
int isaconfig(char*, int, ISAConf*);
|
||||
int isdmaok(void*, usize, int);
|
||||
void keybenable(void); // 386/i8042.c
|
||||
void keybinit(void); // 386/i8042.c
|
||||
void keybenable(void); // i8042.c
|
||||
void keybinit(void); // i8042.c
|
||||
void kexit(Ureg*);
|
||||
KMap* kmap(uintptr_t pa);
|
||||
void kunmap(KMap*);
|
||||
#define kmapinval()
|
||||
void lfence(void);
|
||||
void links(void);
|
||||
#define lockgetpc(l) (l->pc)
|
||||
int machdom(Mach*);
|
||||
void machinit(void);
|
||||
void mach0init(void);
|
||||
void mapraminit(uint64_t, uint64_t);
|
||||
void mathinit(void);
|
||||
void memdebug(void);
|
||||
void meminit(void);
|
||||
int memcolor(uintmem addr, uintmem *sizep);
|
||||
@ -113,14 +129,18 @@ void memmaprange(uintptr_t, uintmem, uintmem, PTE (*alloc)(usize), PTE);
|
||||
void memreserve(uintmem, uintmem);
|
||||
void mfence(void);
|
||||
void mmudump(Proc*);
|
||||
void mmuflushtlb(uint64_t);
|
||||
#define mmuflushtlb() cr3put(cr3get())
|
||||
void mmuinit(void);
|
||||
#define mmucachectl(pg, why) USED(pg, why) /* x86 doesn't need it */
|
||||
uint64_t mmuphysaddr(uintptr_t);
|
||||
int mmuwalk(uintptr_t, int, PTE**, uint64_t (*)(usize));
|
||||
int multiboot(uint32_t, uint32_t, int);
|
||||
uintptr_t* mmuwalk(uintptr_t* table, uintptr_t va, int level, int create);
|
||||
char* mtrr(unsigned long, unsigned long, char *);
|
||||
void mtrrclock(void);
|
||||
int mtrrprint(char *, long);
|
||||
void mtrrsync(void);
|
||||
int multiboot(int);
|
||||
void mwait(void*);
|
||||
uint32_t mwait32(void*, uint32_t);
|
||||
uint64_t mwait64(void*, uint64_t);
|
||||
void ndnr(void);
|
||||
uint8_t nvramread(int);
|
||||
void nvramwrite(int, uint8_t);
|
||||
@ -131,6 +151,7 @@ void outs(int, uint16_t);
|
||||
void outss(int, void*, int);
|
||||
void outl(int, uint32_t);
|
||||
void outsl(int, void*, int);
|
||||
void patwc(void *a, int n);
|
||||
void pause(void);
|
||||
int pciscan(int, Pcidev**);
|
||||
uint32_t pcibarsize(Pcidev*, int);
|
||||
@ -156,7 +177,9 @@ void pcisetmwi(Pcidev*);
|
||||
int pcisetpms(Pcidev*, int);
|
||||
uintmem pcixcfgspace(int);
|
||||
void* pcixcfgaddr(Pcidev*, int);
|
||||
void pmap(uintptr_t *pml4, uintptr_t pa, uintptr_t va, long size);
|
||||
void printcpufreq(void);
|
||||
void* rampage(void);
|
||||
int screenprint(char*, ...); /* debugging */
|
||||
void sfence(void);
|
||||
void spldone(void);
|
||||
@ -170,11 +193,15 @@ void* sysexecregs(uintptr_t, uint32_t);
|
||||
uintptr_t sysexecstack(uintptr_t, int);
|
||||
void sysprocsetup(Proc*);
|
||||
void tssrsp0(uint64_t);
|
||||
uint64_t tscticks(uint64_t *hz);
|
||||
void trapenable(int, void (*)(Ureg*, void*), void*, char*);
|
||||
void trapinit(void);
|
||||
void trapinit0(void);
|
||||
int userureg(Ureg*);
|
||||
void* vmap(uintmem, usize);
|
||||
void vsvminit(int);
|
||||
uintptr_t upaalloc(int size, int align);
|
||||
void upafree(uintptr_t pa, int size);
|
||||
void upareserve(uintptr_t pa, int size);
|
||||
void* vmap(uintptr_t, usize);
|
||||
void vunmap(void*, usize);
|
||||
|
||||
extern Mreg cr0get(void);
|
||||
@ -186,13 +213,18 @@ extern Mreg cr4get(void);
|
||||
extern void cr4put(Mreg);
|
||||
extern void gdtget(void*);
|
||||
extern void gdtput(int, uint64_t, uint16_t);
|
||||
extern void lgdt(void*);
|
||||
extern void idtput(int, uint64_t);
|
||||
extern uint64_t rdmsr(uint32_t);
|
||||
extern void lidt(void*);
|
||||
extern int rdmsr(uint32_t reg, long* value);
|
||||
extern uint64_t rdtsc(void);
|
||||
extern void trput(uint64_t);
|
||||
extern void wrmsr(uint32_t, uint64_t);
|
||||
extern void wbinvd(void);
|
||||
extern int wrmsr(uint32_t, uint64_t);
|
||||
int xaddb(void*);
|
||||
|
||||
#define userureg(ur) (((ur)->cs & 3) == 3)
|
||||
|
||||
extern int islo(void);
|
||||
extern void spldone(void);
|
||||
extern Mreg splhi(void);
|
||||
@ -222,8 +254,10 @@ void sysrforkret(void);
|
||||
#define PTR2UINT(p) ((uintptr_t)(p))
|
||||
#define UINT2PTR(i) ((void*)(i))
|
||||
|
||||
void* KADDR(uintmem);
|
||||
uintptr_t PADDR(void*);
|
||||
uintptr_t mmu_physical_address(void*);
|
||||
void* mmu_kernel_address(uintptr_t);
|
||||
#define KADDR(a) mmu_kernel_address(a)
|
||||
#define PADDR(a) mmu_physical_address((void*)(a))
|
||||
|
||||
#define BIOSSEG(a) KADDR(((uint32_t)(a))<<4)
|
||||
|
||||
@ -235,10 +269,13 @@ extern void millidelay(int);
|
||||
/*
|
||||
* i8259.c
|
||||
*/
|
||||
extern int i8259init(int);
|
||||
extern int i8259irqdisable(int);
|
||||
extern int i8259irqenable(int);
|
||||
extern void i8259init(void);
|
||||
extern int i8259disable(int);
|
||||
extern int i8259enable(Vctl* v);
|
||||
extern int i8259isr(int);
|
||||
extern void i8259on(void);
|
||||
extern void i8259off(void);
|
||||
extern int i8259vecno(int irq);
|
||||
|
||||
/*
|
||||
* sipi.c
|
||||
@ -250,6 +287,9 @@ void basefree(void*, usize);
|
||||
void physallocinit(void);
|
||||
void uartpush(void);
|
||||
|
||||
void rdrandbuf(void*, uint32_t);
|
||||
|
||||
|
||||
/* horror */
|
||||
static inline void __clobber_callee_regs(void)
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
* Copyright (C) 2016-2017 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* This file is part of the UCB release of Plan 9. It is subject to the license
|
||||
* terms in the LICENSE file found in the top-level directory of this
|
||||
@ -12,7 +12,7 @@
|
||||
/*
|
||||
* SIMD Floating Point.
|
||||
* Assembler support to get at the individual instructions
|
||||
* is in l64fpu.s.
|
||||
* is in l64fpu.S.
|
||||
* There are opportunities to be lazier about saving and
|
||||
* restoring the state and allocating the storage needed.
|
||||
*/
|
||||
@ -21,6 +21,7 @@
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
|
||||
#include "amd64.h"
|
||||
#include "ureg.h"
|
||||
@ -87,368 +88,116 @@ extern void _fwait(void);
|
||||
extern void _ldmxcsr(uint32_t*);
|
||||
extern void _stts(void);
|
||||
|
||||
int
|
||||
fpudevprocio(Proc* proc, void* a, int32_t n, uintptr_t offset, int write)
|
||||
void
|
||||
fpclear(void)
|
||||
{
|
||||
uint8_t *p;
|
||||
_clts();
|
||||
_fnclex();
|
||||
_stts();
|
||||
}
|
||||
|
||||
void
|
||||
fpssesave(FPsave *fps)
|
||||
{
|
||||
Fxsave *fx = (Fxsave*)ROUND(((uintptr_t)fps), FPalign);
|
||||
|
||||
_fxsave(fx);
|
||||
_stts();
|
||||
if(fx != (Fxsave*)fps)
|
||||
memmove((Fxsave*)fps, fx, sizeof(Fxsave));
|
||||
}
|
||||
void
|
||||
fpsserestore(FPsave *fps)
|
||||
{
|
||||
Fxsave *fx = (Fxsave*)ROUND(((uintptr_t)fps), FPalign);
|
||||
|
||||
if(fx != (Fxsave*)fps)
|
||||
memmove(fx, (Fxsave*)fps, sizeof(Fxsave));
|
||||
_clts();
|
||||
_fxrstor(fx);
|
||||
}
|
||||
|
||||
static char* mathmsg[] =
|
||||
{
|
||||
nil, /* handled below */
|
||||
"denormalized operand",
|
||||
"division by zero",
|
||||
"numeric overflow",
|
||||
"numeric underflow",
|
||||
"precision loss",
|
||||
};
|
||||
|
||||
static void
|
||||
mathnote(unsigned int status, uintptr_t pc)
|
||||
{
|
||||
char *msg, note[ERRMAX];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Called from procdevtab.read and procdevtab.write
|
||||
* allow user process access to the FPU registers.
|
||||
* This is the only FPU routine which is called directly
|
||||
* from the port code; it would be nice to have dynamic
|
||||
* creation of entries in the device file trees...
|
||||
* Some attention should probably be paid here to the
|
||||
* exception masks and error summary.
|
||||
*/
|
||||
if(offset >= sizeof(Fxsave))
|
||||
return 0;
|
||||
if((p = proc->FPU.fpusave) == nil)
|
||||
return 0;
|
||||
switch(write){
|
||||
default:
|
||||
if(offset+n > sizeof(Fxsave))
|
||||
n = sizeof(Fxsave) - offset;
|
||||
jehanne_memmove(p+offset, a, n);
|
||||
break;
|
||||
case 0:
|
||||
if(offset+n > sizeof(Fxsave))
|
||||
n = sizeof(Fxsave) - offset;
|
||||
jehanne_memmove(a, p+offset, n);
|
||||
msg = "unknown exception";
|
||||
for(i = 1; i <= 5; i++){
|
||||
if(!((1<<i) & status))
|
||||
continue;
|
||||
msg = mathmsg[i];
|
||||
break;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
fpunotify(Ureg* u)
|
||||
{
|
||||
/*
|
||||
* Called when a note is about to be delivered to a
|
||||
* user process, usually at the end of a system call.
|
||||
* Note handlers are not allowed to use the FPU so
|
||||
* the state is marked (after saving if necessary) and
|
||||
* checked in the Device Not Available handler.
|
||||
*/
|
||||
if(up->FPU.fpustate == Busy){
|
||||
_fxsave(up->FPU.fpusave);
|
||||
_stts();
|
||||
up->FPU.fpustate = Idle;
|
||||
}
|
||||
up->FPU.fpustate |= Hold;
|
||||
}
|
||||
|
||||
void
|
||||
fpunoted(void)
|
||||
{
|
||||
/*
|
||||
* Called from sysnoted() via the machine-dependent
|
||||
* noted() routine.
|
||||
* Clear the flag set above in fpunotify().
|
||||
*/
|
||||
up->FPU.fpustate &= ~Hold;
|
||||
}
|
||||
|
||||
void
|
||||
fpusysrfork(Ureg* u)
|
||||
{
|
||||
/*
|
||||
* Called early in the non-interruptible path of
|
||||
* sysrfork() via the machine-dependent syscall() routine.
|
||||
* Save the state so that it can be easily copied
|
||||
* to the child process later.
|
||||
*/
|
||||
if(up->FPU.fpustate != Busy)
|
||||
return;
|
||||
|
||||
_fxsave(up->FPU.fpusave);
|
||||
_stts();
|
||||
up->FPU.fpustate = Idle;
|
||||
}
|
||||
|
||||
void
|
||||
fpusysrforkchild(Proc* child, Proc* parent)
|
||||
{
|
||||
/*
|
||||
* Called later in sysrfork() via the machine-dependent
|
||||
* sysrforkchild() routine.
|
||||
* Copy the parent FPU state to the child.
|
||||
*/
|
||||
child->FPU.fpustate = parent->FPU.fpustate;
|
||||
child->FPU.fpusave = (void*)((PTR2UINT(up->FPU.fxsave) + 15) & ~15);
|
||||
if(child->FPU.fpustate == Init)
|
||||
return;
|
||||
|
||||
jehanne_memmove(child->FPU.fpusave, parent->FPU.fpusave, sizeof(Fxsave));
|
||||
}
|
||||
|
||||
void
|
||||
fpuprocsave(Proc* p)
|
||||
{
|
||||
/*
|
||||
* Called from sched() and sleep() via the machine-dependent
|
||||
* procsave() routine.
|
||||
* About to go in to the scheduler.
|
||||
* If the process wasn't using the FPU
|
||||
* there's nothing to do.
|
||||
*/
|
||||
if(p->FPU.fpustate != Busy)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The process is dead so clear and disable the FPU
|
||||
* and set the state for whoever gets this proc struct
|
||||
* next.
|
||||
*/
|
||||
if(p->state == Moribund){
|
||||
_clts();
|
||||
_fnclex();
|
||||
_stts();
|
||||
p->FPU.fpustate = Init;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the FPU state without handling pending
|
||||
* unmasked exceptions and disable. Postnote() can't
|
||||
* be called here as sleep() already has up->rlock,
|
||||
* so the handling of pending exceptions is delayed
|
||||
* until the process runs again and generates a
|
||||
* Device Not Available exception fault to activate
|
||||
* the FPU.
|
||||
*/
|
||||
_fxsave(p->FPU.fpusave);
|
||||
_stts();
|
||||
p->FPU.fpustate = Idle;
|
||||
}
|
||||
|
||||
void
|
||||
fpuprocrestore(Proc* p)
|
||||
{
|
||||
/*
|
||||
* The process has been rescheduled and is about to run.
|
||||
* Nothing to do here right now. If the process tries to use
|
||||
* the FPU again it will cause a Device Not Available
|
||||
* exception and the state will then be restored.
|
||||
*/
|
||||
USED(p);
|
||||
}
|
||||
|
||||
void
|
||||
fpusysprocsetup(Proc* p)
|
||||
{
|
||||
/*
|
||||
* Disable the FPU.
|
||||
* Called from sysexec() via sysprocsetup() to
|
||||
* set the FPU for the new process.
|
||||
*/
|
||||
if(p->FPU.fpustate != Init){
|
||||
_clts();
|
||||
_fnclex();
|
||||
_stts();
|
||||
p->FPU.fpustate = Init;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
acfpusysprocsetup(Proc *p)
|
||||
{
|
||||
if(p->FPU.fpustate == Init){
|
||||
/* The FPU is initialized in the TC but we must initialize
|
||||
* it in the AC.
|
||||
*/
|
||||
p->FPU.fpustate = Idle;
|
||||
fpusysprocsetup(p);
|
||||
}
|
||||
}
|
||||
|
||||
static char*
|
||||
fpunote(void)
|
||||
{
|
||||
uint16_t fsw;
|
||||
Fxsave *fpusave;
|
||||
char *cm;
|
||||
|
||||
/*
|
||||
* The Sff bit is sticky, meaning it should be explicitly
|
||||
* cleared or there's no way to tell if the exception was an
|
||||
* invalid operation or a stack fault.
|
||||
*/
|
||||
fpusave = up->FPU.fpusave;
|
||||
fsw = (fpusave->fsw & ~fpusave->fcw) & (Sff|P|U|O|Z|D|I);
|
||||
if(fsw & I){
|
||||
if(fsw & Sff){
|
||||
if(fsw & C1)
|
||||
cm = "Stack Overflow";
|
||||
if(status & 0x01){
|
||||
if(status & 0x40){
|
||||
if(status & 0x200)
|
||||
msg = "stack overflow";
|
||||
else
|
||||
cm = "Stack Underflow";
|
||||
}
|
||||
else
|
||||
cm = "Invalid Operation";
|
||||
msg = "stack underflow";
|
||||
}else
|
||||
msg = "invalid operation";
|
||||
}
|
||||
else if(fsw & D)
|
||||
cm = "Denormal Operand";
|
||||
else if(fsw & Z)
|
||||
cm = "Divide-By-Zero";
|
||||
else if(fsw & O)
|
||||
cm = "Numeric Overflow";
|
||||
else if(fsw & U)
|
||||
cm = "Numeric Underflow";
|
||||
else if(fsw & P)
|
||||
cm = "Precision";
|
||||
else
|
||||
cm = "Unknown";
|
||||
|
||||
jehanne_snprint(up->genbuf, sizeof(up->genbuf),
|
||||
"sys: fp: %s Exception ipo=%#llux fsw=%#ux",
|
||||
cm, fpusave->rip, fsw);
|
||||
return up->genbuf;
|
||||
snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=0x%lux",
|
||||
msg, pc, status);
|
||||
postnote(up, 1, note, NDebug);
|
||||
}
|
||||
|
||||
char*
|
||||
xfpuxf(Ureg* ureg, void* v)
|
||||
/*
|
||||
* math coprocessor error
|
||||
*/
|
||||
static void
|
||||
matherror(Ureg* _, void* __)
|
||||
{
|
||||
uint32_t mxcsr;
|
||||
Fxsave *fpusave;
|
||||
char *cm;
|
||||
|
||||
/*
|
||||
* #XF - SIMD Floating Point Exception (Vector 18).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Save FPU state to check out the error.
|
||||
*/
|
||||
fpusave = up->FPU.fpusave;
|
||||
_fxsave(fpusave);
|
||||
_stts();
|
||||
up->FPU.fpustate = Idle;
|
||||
|
||||
if(ureg->ip & KZERO)
|
||||
panic("#MF: ip=%#p", ureg->ip);
|
||||
|
||||
/*
|
||||
* Notify the user process.
|
||||
* The path here is similar to the x87 path described
|
||||
* in fpupostnote above but without the fpupostnote()
|
||||
* call.
|
||||
*/
|
||||
mxcsr = fpusave->mxcsr;
|
||||
if((mxcsr & (Im|I)) == I)
|
||||
cm = "Invalid Operation";
|
||||
else if((mxcsr & (Dm|D)) == D)
|
||||
cm = "Denormal Operand";
|
||||
else if((mxcsr & (Zm|Z)) == Z)
|
||||
cm = "Divide-By-Zero";
|
||||
else if((mxcsr & (Om|O)) == O)
|
||||
cm = "Numeric Overflow";
|
||||
else if((mxcsr & (Um|U)) == U)
|
||||
cm = "Numeric Underflow";
|
||||
else if((mxcsr & (Pm|P)) == P)
|
||||
cm = "Precision";
|
||||
else
|
||||
cm = "Unknown";
|
||||
|
||||
jehanne_snprint(up->genbuf, sizeof(up->genbuf),
|
||||
"sys: fp: %s Exception mxcsr=%#ux", cm, mxcsr);
|
||||
return up->genbuf;
|
||||
fpsave(&up->fpsave);
|
||||
up->fpstate = FPinactive;
|
||||
mathnote(up->fpsave.fsw, up->fpsave.rip);
|
||||
}
|
||||
|
||||
void
|
||||
fpuxf(Ureg *ureg, void *p)
|
||||
/*
|
||||
* SIMD error
|
||||
*/
|
||||
static void
|
||||
simderror(Ureg *ureg, void* _)
|
||||
{
|
||||
char *n;
|
||||
|
||||
n = xfpuxf(ureg, p);
|
||||
if(n != nil)
|
||||
postnote(up, 1, n, NDebug);
|
||||
fpsave(&up->fpsave);
|
||||
up->fpstate = FPinactive;
|
||||
mathnote(up->fpsave.mxcsr & 0x3f, ureg->ip);
|
||||
}
|
||||
|
||||
char*
|
||||
acfpuxf(Ureg *ureg, void *p)
|
||||
/*
|
||||
* math coprocessor emulation fault
|
||||
*/
|
||||
static void
|
||||
mathemu(Ureg *ureg, void* _)
|
||||
{
|
||||
return xfpuxf(ureg, p);
|
||||
}
|
||||
unsigned int status, control;
|
||||
|
||||
static char*
|
||||
xfpumf(Ureg* ureg, void* v)
|
||||
{
|
||||
Fxsave *fpusave;
|
||||
|
||||
/*
|
||||
* #MF - x87 Floating Point Exception Pending (Vector 16).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Save FPU state to check out the error.
|
||||
*/
|
||||
fpusave = up->FPU.fpusave;
|
||||
_fxsave(fpusave);
|
||||
_stts();
|
||||
up->FPU.fpustate = Idle;
|
||||
|
||||
if(ureg->ip & KZERO)
|
||||
panic("#MF: ip=%#p rip=%#p", ureg->ip, fpusave->rip);
|
||||
|
||||
/*
|
||||
* Notify the user process.
|
||||
* The path here is
|
||||
* call trap->fpumf->fpupostnote->postnote
|
||||
* return ->fpupostnote->fpumf->trap
|
||||
* call notify->fpunotify
|
||||
* return ->notify
|
||||
* then either
|
||||
* call pexit
|
||||
* or
|
||||
* return ->trap
|
||||
* return ->user note handler
|
||||
*/
|
||||
return fpunote();
|
||||
}
|
||||
|
||||
void
|
||||
fpumf(Ureg *ureg, void *p)
|
||||
{
|
||||
char *n;
|
||||
|
||||
n = xfpumf(ureg, p);
|
||||
if(n != nil)
|
||||
postnote(up, 1, n, NDebug);
|
||||
}
|
||||
|
||||
char*
|
||||
acfpumf(Ureg *ureg, void *p)
|
||||
{
|
||||
return xfpumf(ureg, p);
|
||||
}
|
||||
|
||||
static char*
|
||||
xfpunm(Ureg* ureg, void* v)
|
||||
{
|
||||
Fxsave *fpusave;
|
||||
|
||||
/*
|
||||
* #NM - Device Not Available (Vector 7).
|
||||
*/
|
||||
if(up == nil)
|
||||
panic("#NM: fpu in kernel: ip %#p\n", ureg->ip);
|
||||
|
||||
/*
|
||||
* Someone tried to use the FPU in a note handler.
|
||||
* That's a no-no.
|
||||
*/
|
||||
if(up->FPU.fpustate & Hold)
|
||||
return "sys: floating point in note handler";
|
||||
|
||||
if(ureg->ip & KZERO)
|
||||
panic("#NM: proc %d %s state %d ip %#p\n",
|
||||
up->pid, up->text, up->FPU.fpustate, ureg->ip);
|
||||
|
||||
switch(up->FPU.fpustate){
|
||||
case Busy:
|
||||
default:
|
||||
panic("#NM: state %d ip %#p\n", up->FPU.fpustate, ureg->ip);
|
||||
break;
|
||||
case Init:
|
||||
if(up->fpstate & FPillegal){
|
||||
/* someone did floating point in a note handler */
|
||||
postnote(up, 1, "sys: floating point in note handler", NDebug);
|
||||
return;
|
||||
}
|
||||
switch(up->fpstate){
|
||||
case FPinit:
|
||||
/*
|
||||
* A process tries to use the FPU for the
|
||||
* first time and generates a 'device not available'
|
||||
@ -460,87 +209,78 @@ xfpunm(Ureg* ureg, void* v)
|
||||
_clts();
|
||||
_fninit();
|
||||
_fwait();
|
||||
_fldcw(&m->FPU.fcw);
|
||||
_ldmxcsr(&m->FPU.mxcsr);
|
||||
up->FPU.fpusave = (void*)((PTR2UINT(up->FPU.fxsave) + 15) & ~15);
|
||||
up->FPU.fpustate = Busy;
|
||||
up->fpsave.fcw = 0x0232;
|
||||
_fldcw(&up->fpsave.fcw);
|
||||
up->fpsave.mxcsr = 0x1900;
|
||||
_ldmxcsr(&up->fpsave.mxcsr);
|
||||
up->fpstate = FPactive;
|
||||
break;
|
||||
case Idle:
|
||||
case FPinactive:
|
||||
/*
|
||||
* Before restoring the state, check for any pending
|
||||
* exceptions, there's no way to restore the state without
|
||||
* generating an unmasked exception.
|
||||
* More attention should probably be paid here to the
|
||||
* exception masks and error summary.
|
||||
*/
|
||||
fpusave = up->FPU.fpusave;
|
||||
if((fpusave->fsw & ~fpusave->fcw) & (Sff|P|U|O|Z|D|I))
|
||||
return fpunote();
|
||||
|
||||
/*
|
||||
* Sff is sticky.
|
||||
*/
|
||||
fpusave->fcw &= ~Sff;
|
||||
_clts();
|
||||
_fxrstor(fpusave);
|
||||
up->FPU.fpustate = Busy;
|
||||
status = up->fpsave.fsw;
|
||||
control = up->fpsave.fcw;
|
||||
if((status & ~control) & 0x07F){
|
||||
mathnote(status, up->fpsave.rip);
|
||||
break;
|
||||
}
|
||||
fprestore(&up->fpsave);
|
||||
up->fpstate = FPactive;
|
||||
break;
|
||||
case FPactive:
|
||||
panic("math emu pid %ld %s pc %#p",
|
||||
up->pid, up->text, ureg->ip);
|
||||
break;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
fpunm(Ureg *ureg, void *p)
|
||||
fpprocsetup(Proc* p)
|
||||
{
|
||||
char *n;
|
||||
|
||||
n = xfpunm(ureg, p);
|
||||
if(n != nil)
|
||||
postnote(up, 1, n, NDebug);
|
||||
}
|
||||
|
||||
char*
|
||||
acfpunm(Ureg *ureg, void *p)
|
||||
{
|
||||
return xfpunm(ureg, p);
|
||||
}
|
||||
|
||||
void
|
||||
fpuinit(void)
|
||||
{
|
||||
uint64_t r;
|
||||
Fxsave *fxsave;
|
||||
uint8_t buf[sizeof(Fxsave)+15];
|
||||
|
||||
/*
|
||||
* It's assumed there is an integrated FPU, so Em is cleared;
|
||||
*/
|
||||
r = cr0get();
|
||||
r &= ~(Ts|Em);
|
||||
r |= Ne|Mp;
|
||||
cr0put(r);
|
||||
|
||||
r = cr4get();
|
||||
r |= Osxmmexcpt|Osfxsr;
|
||||
cr4put(r);
|
||||
|
||||
_fninit();
|
||||
fxsave = (Fxsave*)((PTR2UINT(buf) + 15) & ~15);
|
||||
jehanne_memset(fxsave, 0, sizeof(Fxsave));
|
||||
_fxsave(fxsave);
|
||||
m->FPU.fcw = RCn|PCd|P|U|D;
|
||||
if(fxsave->mxcsrmask == 0)
|
||||
m->FPU.mxcsrmask = 0x0000FFBF;
|
||||
else
|
||||
m->FPU.mxcsrmask = fxsave->mxcsrmask;
|
||||
m->FPU.mxcsr = (Rn|Pm|Um|Dm) & m->FPU.mxcsrmask;
|
||||
p->fpstate = FPinit;
|
||||
_stts();
|
||||
|
||||
if(m->machno != 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Set up the exception handlers.
|
||||
*/
|
||||
trapenable(IdtNM, fpunm, 0, "#NM");
|
||||
trapenable(IdtMF, fpumf, 0, "#MF");
|
||||
trapenable(IdtXF, fpuxf, 0, "#XF");
|
||||
}
|
||||
|
||||
void
|
||||
fpprocfork(Proc *p)
|
||||
{
|
||||
int s;
|
||||
|
||||
/* save floating point state */
|
||||
s = splhi();
|
||||
switch(up->fpstate & ~FPillegal){
|
||||
case FPactive:
|
||||
fpsave(&up->fpsave);
|
||||
up->fpstate = FPinactive;
|
||||
case FPinactive:
|
||||
p->fpsave = up->fpsave;
|
||||
p->fpstate = FPinactive;
|
||||
}
|
||||
splx(s);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* math coprocessor segment overrun
|
||||
*/
|
||||
static void
|
||||
mathover(Ureg* _, void* __)
|
||||
{
|
||||
pexit("math overrun", 0);
|
||||
}
|
||||
|
||||
void
|
||||
mathinit(void)
|
||||
{
|
||||
trapenable(VectorCERR, matherror, 0, "matherror");
|
||||
if(X86FAMILY(m->cpuidax) == 3)
|
||||
intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
|
||||
trapenable(VectorCNA, mathemu, 0, "mathemu");
|
||||
trapenable(VectorCSO, mathover, 0, "mathover");
|
||||
trapenable(VectorSIMD, simderror, 0, "simderror");
|
||||
}
|
||||
|
317
sys/src/kern/amd64/i8253.c
Normal file
317
sys/src/kern/amd64/i8253.c
Normal file
@ -0,0 +1,317 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
|
||||
/*
|
||||
* 8253 timer
|
||||
*/
|
||||
enum
|
||||
{
|
||||
T0cntr= 0x40, /* counter ports */
|
||||
T1cntr= 0x41, /* ... */
|
||||
T2cntr= 0x42, /* ... */
|
||||
Tmode= 0x43, /* mode port (control word register) */
|
||||
T2ctl= 0x61, /* counter 2 control port */
|
||||
|
||||
/* commands */
|
||||
Latch0= 0x00, /* latch counter 0's value */
|
||||
Load0l= 0x10, /* load counter 0's lsb */
|
||||
Load0m= 0x20, /* load counter 0's msb */
|
||||
Load0= 0x30, /* load counter 0 with 2 bytes */
|
||||
|
||||
Latch1= 0x40, /* latch counter 1's value */
|
||||
Load1l= 0x50, /* load counter 1's lsb */
|
||||
Load1m= 0x60, /* load counter 1's msb */
|
||||
Load1= 0x70, /* load counter 1 with 2 bytes */
|
||||
|
||||
Latch2= 0x80, /* latch counter 2's value */
|
||||
Load2l= 0x90, /* load counter 2's lsb */
|
||||
Load2m= 0xa0, /* load counter 2's msb */
|
||||
Load2= 0xb0, /* load counter 2 with 2 bytes */
|
||||
|
||||
/* 8254 read-back command: everything > pc-at has an 8254 */
|
||||
Rdback= 0xc0, /* readback counters & status */
|
||||
Rdnstat=0x10, /* don't read status */
|
||||
Rdncnt= 0x20, /* don't read counter value */
|
||||
Rd0cntr=0x02, /* read back for which counter */
|
||||
Rd1cntr=0x04,
|
||||
Rd2cntr=0x08,
|
||||
|
||||
/* modes */
|
||||
ModeMsk=0xe,
|
||||
Square= 0x6, /* periodic square wave */
|
||||
Trigger=0x0, /* interrupt on terminal count */
|
||||
Sstrobe=0x8, /* software triggered strobe */
|
||||
|
||||
/* T2ctl bits */
|
||||
T2gate= (1<<0), /* enable T2 counting */
|
||||
T2spkr= (1<<1), /* connect T2 out to speaker */
|
||||
T2out= (1<<5), /* output of T2 */
|
||||
|
||||
Freq= 1193182, /* Real clock frequency */
|
||||
Tickshift=8, /* extra accuracy */
|
||||
MaxPeriod=Freq/HZ,
|
||||
MinPeriod=Freq/(100*HZ),
|
||||
};
|
||||
|
||||
typedef struct I8253 I8253;
|
||||
struct I8253
|
||||
{
|
||||
Lock;
|
||||
uint32_t period; /* current clock period */
|
||||
|
||||
uint16_t last; /* last value of clock 1 */
|
||||
uint64_t ticks; /* cumulative ticks of counter 1 */
|
||||
|
||||
uint32_t periodset;
|
||||
};
|
||||
static I8253 i8253;
|
||||
|
||||
void
|
||||
i8253reset(void)
|
||||
{
|
||||
int loops, x;
|
||||
|
||||
ilock(&i8253);
|
||||
|
||||
i8253.last = 0;
|
||||
i8253.period = Freq/HZ;
|
||||
|
||||
/*
|
||||
* enable a 1/HZ interrupt for providing scheduling interrupts
|
||||
*/
|
||||
outb(Tmode, Load0|Square);
|
||||
outb(T0cntr, (Freq/HZ)); /* low byte */
|
||||
outb(T0cntr, (Freq/HZ)>>8); /* high byte */
|
||||
|
||||
/*
|
||||
* enable a longer period counter to use as a clock
|
||||
*/
|
||||
outb(Tmode, Load2|Square);
|
||||
outb(T2cntr, 0); /* low byte */
|
||||
outb(T2cntr, 0); /* high byte */
|
||||
x = inb(T2ctl);
|
||||
x |= T2gate;
|
||||
outb(T2ctl, x);
|
||||
|
||||
/*
|
||||
* Introduce a little delay to make sure the count is
|
||||
* latched and the timer is counting down; with a fast
|
||||
* enough processor this may not be the case.
|
||||
* The i8254 (which this probably is) has a read-back
|
||||
* command which can be used to make sure the counting
|
||||
* register has been written into the counting element.
|
||||
*/
|
||||
x = (Freq/HZ);
|
||||
for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
|
||||
outb(Tmode, Latch0);
|
||||
x = inb(T0cntr);
|
||||
x |= inb(T0cntr)<<8;
|
||||
}
|
||||
|
||||
iunlock(&i8253);
|
||||
}
|
||||
|
||||
void
|
||||
i8253init(void)
|
||||
{
|
||||
ioalloc(T0cntr, 4, 0, "i8253");
|
||||
ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
|
||||
|
||||
i8253reset();
|
||||
}
|
||||
|
||||
void
|
||||
guesscpuhz(int aalcycles)
|
||||
{
|
||||
int loops, x, y;
|
||||
uint64_t a, b, cpufreq;
|
||||
|
||||
if(m->machno != 0){
|
||||
m->cpuhz = MACHP(0)->cpuhz;
|
||||
m->cpumhz = MACHP(0)->cpumhz;
|
||||
m->loopconst = MACHP(0)->loopconst;
|
||||
return;
|
||||
}
|
||||
|
||||
ilock(&i8253);
|
||||
for(loops = 1000;;loops += 1000) {
|
||||
/*
|
||||
* measure time for the loop
|
||||
*
|
||||
* MOVL loops,CX
|
||||
* aaml1: AAM
|
||||
* LOOP aaml1
|
||||
*
|
||||
* the time for the loop should be independent of external
|
||||
* cache and memory system since it fits in the execution
|
||||
* prefetch buffer.
|
||||
*
|
||||
*/
|
||||
outb(Tmode, Latch2);
|
||||
cycles(&a);
|
||||
x = inb(T2cntr);
|
||||
x |= inb(T2cntr)<<8;
|
||||
aamloop(loops);
|
||||
outb(Tmode, Latch2);
|
||||
cycles(&b);
|
||||
y = inb(T2cntr);
|
||||
y |= inb(T2cntr)<<8;
|
||||
|
||||
x -= y;
|
||||
if(x < 0)
|
||||
x += 0x10000;
|
||||
|
||||
if(x >= MaxPeriod || loops >= 1000000)
|
||||
break;
|
||||
}
|
||||
iunlock(&i8253);
|
||||
|
||||
/* avoid division by zero on vmware 7 */
|
||||
if(x == 0)
|
||||
x = 1;
|
||||
|
||||
/*
|
||||
* figure out clock frequency and a loop multiplier for delay().
|
||||
* n.b. counter goes up by 2*Freq
|
||||
*/
|
||||
cpufreq = (long)loops*((aalcycles*2*Freq)/x);
|
||||
m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */
|
||||
|
||||
/* a == b means virtualbox has confused us */
|
||||
if(m->havetsc && b > a){
|
||||
b -= a;
|
||||
b *= 2*Freq;
|
||||
b /= x;
|
||||
m->cyclefreq = b;
|
||||
cpufreq = b;
|
||||
}
|
||||
m->cpuhz = cpufreq;
|
||||
|
||||
/*
|
||||
* round to the nearest megahz
|
||||
*/
|
||||
m->cpumhz = (cpufreq+500000)/1000000L;
|
||||
if(m->cpumhz == 0)
|
||||
m->cpumhz = 1;
|
||||
}
|
||||
|
||||
void
|
||||
i8253timerset(uint64_t next)
|
||||
{
|
||||
int period;
|
||||
uint32_t want;
|
||||
uint32_t now;
|
||||
|
||||
want = next>>Tickshift;
|
||||
now = i8253.ticks; /* assuming whomever called us just did fastticks() */
|
||||
|
||||
period = want - now;
|
||||
if(period < MinPeriod)
|
||||
period = MinPeriod;
|
||||
else if(period > MaxPeriod)
|
||||
period = MaxPeriod;
|
||||
|
||||
/* hysteresis */
|
||||
if(i8253.period != period){
|
||||
ilock(&i8253);
|
||||
/* load new value */
|
||||
outb(Tmode, Load0|Square);
|
||||
outb(T0cntr, period); /* low byte */
|
||||
outb(T0cntr, period >> 8); /* high byte */
|
||||
|
||||
/* remember period */
|
||||
i8253.period = period;
|
||||
i8253.periodset++;
|
||||
iunlock(&i8253);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
i8253clock(Ureg* ureg, void* _)
|
||||
{
|
||||
timerintr(ureg, 0);
|
||||
}
|
||||
|
||||
void
|
||||
i8253enable(void)
|
||||
{
|
||||
intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
|
||||
}
|
||||
|
||||
/*
|
||||
* return the total ticks of counter 2. We shift by
|
||||
* 8 to give timesync more wriggle room for interpretation
|
||||
* of the frequency
|
||||
*/
|
||||
uint64_t
|
||||
i8253read(uint64_t *hz)
|
||||
{
|
||||
uint16_t y, x;
|
||||
uint64_t ticks;
|
||||
|
||||
if(hz)
|
||||
*hz = Freq<<Tickshift;
|
||||
|
||||
ilock(&i8253);
|
||||
outb(Tmode, Latch2);
|
||||
y = inb(T2cntr);
|
||||
y |= inb(T2cntr)<<8;
|
||||
|
||||
if(y < i8253.last)
|
||||
x = i8253.last - y;
|
||||
else {
|
||||
x = i8253.last + (0x10000 - y);
|
||||
if (x > 3*MaxPeriod) {
|
||||
outb(Tmode, Load2|Square);
|
||||
outb(T2cntr, 0); /* low byte */
|
||||
outb(T2cntr, 0); /* high byte */
|
||||
y = 0xFFFF;
|
||||
x = i8253.period;
|
||||
}
|
||||
}
|
||||
i8253.last = y;
|
||||
i8253.ticks += x>>1;
|
||||
ticks = i8253.ticks;
|
||||
iunlock(&i8253);
|
||||
|
||||
return ticks<<Tickshift;
|
||||
}
|
||||
|
||||
void
|
||||
delay(int millisecs)
|
||||
{
|
||||
millisecs *= m->loopconst;
|
||||
if(millisecs <= 0)
|
||||
millisecs = 1;
|
||||
aamloop(millisecs);
|
||||
}
|
||||
|
||||
void
|
||||
microdelay(int microsecs)
|
||||
{
|
||||
microsecs *= m->loopconst;
|
||||
microsecs /= 1000;
|
||||
if(microsecs <= 0)
|
||||
microsecs = 1;
|
||||
aamloop(microsecs);
|
||||
}
|
||||
|
||||
/*
|
||||
* performance measurement ticks. must be low overhead.
|
||||
* doesn't have to count over a second.
|
||||
*/
|
||||
uint64_t
|
||||
perfticks(void)
|
||||
{
|
||||
uint64_t x;
|
||||
|
||||
if(m->havetsc)
|
||||
cycles(&x);
|
||||
else
|
||||
x = 0;
|
||||
return x;
|
||||
}
|
@ -3,131 +3,102 @@
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "io.h"
|
||||
|
||||
/*
|
||||
* 8259 Interrupt Controller and compatibles.
|
||||
* 8259 interrupt controllers
|
||||
*/
|
||||
enum { /* I/O ports */
|
||||
Cntrl1 = 0x20,
|
||||
Cntrl2 = 0xa0,
|
||||
enum
|
||||
{
|
||||
Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */
|
||||
Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */
|
||||
Int1ctl= 0xA0, /* control port */
|
||||
Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */
|
||||
|
||||
Icw1 = 0, /* Initialisation Command Word 1 */
|
||||
Icw2 = 1,
|
||||
Icw3 = 1,
|
||||
Icw4 = 1,
|
||||
Icw1= 0x10, /* select bit in ctl register */
|
||||
Ocw2= 0x00,
|
||||
Ocw3= 0x08,
|
||||
|
||||
Ocw1 = 1, /* Operational Control Word 1 */
|
||||
Ocw2 = 0,
|
||||
Ocw3 = 0,
|
||||
EOI= 0x20, /* non-specific end of interrupt */
|
||||
|
||||
Imr = Ocw1, /* Interrupt Mask Register */
|
||||
Isr = Ocw3, /* In-Service Register */
|
||||
Irr = Ocw3, /* Interrupt Request Register */
|
||||
|
||||
Elcr1 = 0x4d0, /* Edge/Level Control Register */
|
||||
Elcr2 = 0x4d1,
|
||||
};
|
||||
|
||||
enum { /* Icw1 */
|
||||
Ic4 = 0x01, /* there will be an Icw4 */
|
||||
Icw1sel = 0x10, /* Icw/Ocw select */
|
||||
};
|
||||
|
||||
enum { /* Icw3 */
|
||||
Cascaded = 0x04, /* Cntrl1 - Cascaded Mode Enable */
|
||||
SlaveIRQ2 = 0x02, /* Cntrl2 - Slave Identification Code */
|
||||
};
|
||||
|
||||
enum { /* Icw4 */
|
||||
Microprocessor = 0x01, /* 80x86-based system */
|
||||
};
|
||||
|
||||
enum { /* Ocw2 */
|
||||
Ocw2sel = 0x00, /* Ocw2 select */
|
||||
Eoi = 0x20, /* Non-spcific EOI command */
|
||||
};
|
||||
|
||||
enum { /* Ocw3 */
|
||||
Irrread = 0x02, /* Read IRQ register */
|
||||
Isrread = 0x03, /* Read IS register */
|
||||
Ocw3sel = 0x08, /* Ocw3 select */
|
||||
Elcr1= 0x4D0, /* Edge/Level Triggered Register */
|
||||
Elcr2= 0x4D1,
|
||||
};
|
||||
|
||||
static Lock i8259lock;
|
||||
static int i8259mask = ~0; /* mask of disabled interrupts */
|
||||
static int i8259elcr; /* mask of level interrupts */
|
||||
static int i8259mask = 0xFFFF; /* disabled interrupts */
|
||||
int i8259elcr; /* mask of level-triggered interrupts */
|
||||
|
||||
int
|
||||
i8259init(int vectorbase)
|
||||
void
|
||||
i8259init(void)
|
||||
{
|
||||
int elcr;
|
||||
|
||||
vectorbase &= ~0x07;
|
||||
int x;
|
||||
|
||||
ioalloc(Int0ctl, 2, 0, "i8259.0");
|
||||
ioalloc(Int1ctl, 2, 0, "i8259.1");
|
||||
ilock(&i8259lock);
|
||||
|
||||
/*
|
||||
* Boilerplate to initialise the pair of 8259 controllers,
|
||||
* see one of the Intel bridge datasheets for details,
|
||||
* e.g. 82371AB (PIIX4). The default settings are 80x86 mode,
|
||||
* edge-sensitive detection, normal EOI, non-buffered and
|
||||
* cascade mode. Cntrl1 is connected as the master and Cntrl2
|
||||
* as the slave; IRQ2 is used to cascade the two controllers.
|
||||
* Set up the first 8259 interrupt processor.
|
||||
* Make 8259 interrupts start at CPU vector VectorPIC.
|
||||
* Set the 8259 as master with edge triggered
|
||||
* input with fully nested interrupts.
|
||||
*/
|
||||
outb(Cntrl1+Icw1, Icw1sel|Ic4);
|
||||
outb(Cntrl1+Icw2, vectorbase);
|
||||
outb(Cntrl1+Icw3, Cascaded);
|
||||
outb(Cntrl1+Icw4, Microprocessor);
|
||||
|
||||
outb(Cntrl2+Icw1, Icw1sel|Ic4);
|
||||
outb(Cntrl2+Icw2, vectorbase+8);
|
||||
outb(Cntrl2+Icw3, SlaveIRQ2);
|
||||
outb(Cntrl2+Icw4, Microprocessor);
|
||||
outb(Int0ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered,
|
||||
ICW4 will be sent */
|
||||
outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */
|
||||
outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */
|
||||
outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */
|
||||
|
||||
/*
|
||||
* Set the interrupt masks, allowing interrupts
|
||||
* to pass from Cntrl2 to Cntrl1 on IRQ2.
|
||||
* Set up the second 8259 interrupt processor.
|
||||
* Make 8259 interrupts start at CPU vector VectorPIC+8.
|
||||
* Set the 8259 as slave with edge triggered
|
||||
* input with fully nested interrupts.
|
||||
*/
|
||||
i8259mask &= ~(1<<2);
|
||||
outb(Cntrl2+Imr, (i8259mask>>8) & 0xff);
|
||||
outb(Cntrl1+Imr, i8259mask & 0xff);
|
||||
|
||||
outb(Cntrl1+Ocw2, Ocw2sel|Eoi);
|
||||
outb(Cntrl2+Ocw2, Ocw2sel|Eoi);
|
||||
outb(Int1ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered,
|
||||
ICW4 will be sent */
|
||||
outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */
|
||||
outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */
|
||||
outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */
|
||||
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
||||
|
||||
/*
|
||||
* Set Ocw3 to return the ISR when read for i8259isr()
|
||||
* (after initialisation status read is set to return the IRR).
|
||||
* pass #2 8259 interrupts to #1
|
||||
*/
|
||||
i8259mask &= ~0x04;
|
||||
outb(Int0aux, i8259mask & 0xFF);
|
||||
|
||||
/*
|
||||
* Set Ocw3 to return the ISR when ctl read.
|
||||
* After initialisation status read is set to IRR.
|
||||
* Read IRR first to possibly deassert an outstanding
|
||||
* interrupt.
|
||||
*/
|
||||
inb(Cntrl1+Irr);
|
||||
outb(Cntrl1+Ocw3, Ocw3sel|Isrread);
|
||||
inb(Cntrl2+Irr);
|
||||
outb(Cntrl2+Ocw3, Ocw3sel|Isrread);
|
||||
inb(Int0ctl);
|
||||
outb(Int0ctl, Ocw3|0x03);
|
||||
inb(Int1ctl);
|
||||
outb(Int1ctl, Ocw3|0x03);
|
||||
|
||||
/*
|
||||
* Check for Edge/Level Control register.
|
||||
* Check for Edge/Level register.
|
||||
* This check may not work for all chipsets.
|
||||
* First try a non-intrusive test - the bits for
|
||||
* IRQs 13, 8, 2, 1 and 0 must be edge (0). If
|
||||
* that's OK try a R/W test.
|
||||
*/
|
||||
elcr = (inb(Elcr2)<<8)|inb(Elcr1);
|
||||
if(!(elcr & 0x2107)){
|
||||
x = (inb(Elcr2)<<8)|inb(Elcr1);
|
||||
if(!(x & 0x2107)){
|
||||
outb(Elcr1, 0);
|
||||
if(inb(Elcr1) == 0){
|
||||
outb(Elcr1, 0x20);
|
||||
if(inb(Elcr1) == 0x20)
|
||||
i8259elcr = elcr;
|
||||
outb(Elcr1, elcr & 0xff);
|
||||
i8259elcr = x;
|
||||
outb(Elcr1, x & 0xFF);
|
||||
print("ELCR: %4.4uX\n", i8259elcr);
|
||||
}
|
||||
}
|
||||
iunlock(&i8259lock);
|
||||
|
||||
return vectorbase;
|
||||
}
|
||||
|
||||
int
|
||||
@ -135,25 +106,108 @@ i8259isr(int vno)
|
||||
{
|
||||
int irq, isr;
|
||||
|
||||
if(vno < IdtPIC || vno > IdtPIC+15)
|
||||
if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC)
|
||||
return 0;
|
||||
irq = vno-IdtPIC;
|
||||
irq = vno-VectorPIC;
|
||||
|
||||
/*
|
||||
* Collect the interrupt status,
|
||||
* acknowledge the interrupt and return whether
|
||||
* the acknowledged interrupt was the correct
|
||||
* one (this could be better but it's not really
|
||||
* used).
|
||||
* tell the 8259 that we're done with the
|
||||
* highest level interrupt (interrupts are still
|
||||
* off at this point)
|
||||
*/
|
||||
ilock(&i8259lock);
|
||||
isr = inb(Cntrl1+Isr);
|
||||
outb(Cntrl1+Ocw2, Ocw2sel|Eoi);
|
||||
isr = inb(Int0ctl);
|
||||
outb(Int0ctl, EOI);
|
||||
if(irq >= 8){
|
||||
isr |= inb(Cntrl2+Isr)<<8;
|
||||
outb(Cntrl2+Ocw2, Ocw2sel|Eoi);
|
||||
isr |= inb(Int1ctl)<<8;
|
||||
outb(Int1ctl, EOI);
|
||||
}
|
||||
iunlock(&i8259lock);
|
||||
|
||||
return isr & (1<<irq);
|
||||
}
|
||||
|
||||
int
|
||||
i8259enable(Vctl* v)
|
||||
{
|
||||
int irq, irqbit;
|
||||
|
||||
/*
|
||||
* Given an IRQ, enable the corresponding interrupt in the i8259
|
||||
* and return the vector to be used. The i8259 is set to use a fixed
|
||||
* range of vectors starting at VectorPIC.
|
||||
*/
|
||||
irq = v->irq;
|
||||
if(irq < 0 || irq > MaxIrqPIC){
|
||||
print("i8259enable: irq %d out of range\n", irq);
|
||||
return -1;
|
||||
}
|
||||
irqbit = 1<<irq;
|
||||
|
||||
ilock(&i8259lock);
|
||||
if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){
|
||||
print("i8259enable: irq %d shared but not level\n", irq);
|
||||
iunlock(&i8259lock);
|
||||
return -1;
|
||||
}
|
||||
i8259mask &= ~irqbit;
|
||||
if(irq < 8)
|
||||
outb(Int0aux, i8259mask & 0xFF);
|
||||
else
|
||||
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
||||
|
||||
if(i8259elcr & irqbit)
|
||||
v->eoi = i8259isr;
|
||||
else
|
||||
v->isr = i8259isr;
|
||||
iunlock(&i8259lock);
|
||||
|
||||
return VectorPIC+irq;
|
||||
}
|
||||
|
||||
int
|
||||
i8259vecno(int irq)
|
||||
{
|
||||
return VectorPIC+irq;
|
||||
}
|
||||
|
||||
int
|
||||
i8259disable(int irq)
|
||||
{
|
||||
int irqbit;
|
||||
|
||||
/*
|
||||
* Given an IRQ, disable the corresponding interrupt
|
||||
* in the 8259.
|
||||
*/
|
||||
if(irq < 0 || irq > MaxIrqPIC){
|
||||
print("i8259disable: irq %d out of range\n", irq);
|
||||
return -1;
|
||||
}
|
||||
irqbit = 1<<irq;
|
||||
|
||||
ilock(&i8259lock);
|
||||
if(!(i8259mask & irqbit)){
|
||||
i8259mask |= irqbit;
|
||||
if(irq < 8)
|
||||
outb(Int0aux, i8259mask & 0xFF);
|
||||
else
|
||||
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
||||
}
|
||||
iunlock(&i8259lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
i8259on(void)
|
||||
{
|
||||
outb(Int0aux, i8259mask&0xFF);
|
||||
outb(Int1aux, (i8259mask>>8)&0xFF);
|
||||
}
|
||||
|
||||
void
|
||||
i8259off(void)
|
||||
{
|
||||
outb(Int0aux, 0xFF);
|
||||
outb(Int1aux, 0xFF);
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
|
||||
#define X86STEPPING(x) ((x) & 0x0F)
|
||||
/* incorporates extended-model and -family bits */
|
||||
#define X86MODEL(x) ((((x)>>4) & 0x0F) | (((x)>>16) & 0x0F)<<4)
|
||||
#define X86FAMILY(x) ((((x)>>8) & 0x0F) | (((x)>>20) & 0xFF)<<4)
|
||||
|
||||
enum {
|
||||
VectorNMI = 2, /* non-maskable interrupt */
|
||||
VectorBPT = 3, /* breakpoint */
|
||||
@ -14,9 +19,14 @@ enum {
|
||||
VectorCNA = 7, /* coprocessor not available */
|
||||
Vector2F = 8, /* double fault */
|
||||
VectorCSO = 9, /* coprocessor segment overrun */
|
||||
VectorSNP = 11, /* segment not present */
|
||||
VectorGPF = 13, /* general protection fault */
|
||||
VectorPF = 14, /* page fault */
|
||||
Vector15 = 15, /* reserved */
|
||||
VectorCERR = 16, /* coprocessor error */
|
||||
VectorAC = 17, /* alignment check */
|
||||
VectorMC = 18, /* machine check */
|
||||
VectorSIMD = 19, /* simd error */
|
||||
|
||||
VectorPIC = 32, /* external i8259 interrupts */
|
||||
IrqCLOCK = 0,
|
||||
@ -33,13 +43,13 @@ enum {
|
||||
IrqATA1 = 15,
|
||||
MaxIrqPIC = 15,
|
||||
|
||||
VectorLAPIC = VectorPIC+16, /* local APIC interrupts */
|
||||
IrqLINT0 = VectorLAPIC+0,
|
||||
VectorLAPIC = VectorPIC+16, /* local APIC interrupts */
|
||||
IrqLINT0 = VectorLAPIC+0, /* LINT[01] must be offsets 0 and 1 */
|
||||
IrqLINT1 = VectorLAPIC+1,
|
||||
IrqTIMER = VectorLAPIC+2,
|
||||
IrqERROR = VectorLAPIC+3,
|
||||
IrqPCINT = VectorLAPIC+4,
|
||||
IrqSPURIOUS = VectorLAPIC+15,
|
||||
IrqSPURIOUS = VectorLAPIC+15, /* must have bits [3-0] == 0x0F */
|
||||
MaxIrqLAPIC = VectorLAPIC+15,
|
||||
|
||||
VectorSYSCALL = 64,
|
||||
@ -69,20 +79,15 @@ enum {
|
||||
typedef struct Vctl {
|
||||
Vctl* next; /* handlers on this vector */
|
||||
|
||||
int isintr; /* interrupt or fault/trap */
|
||||
int affinity; /* processor affinity (-1 for none) */
|
||||
|
||||
int irq;
|
||||
void (*f)(Ureg*, void*); /* handler to call */
|
||||
void* a; /* argument to call it with */
|
||||
int tbdf;
|
||||
char name[KNAMELEN]; /* of driver */
|
||||
char *type;
|
||||
|
||||
int isintr; /* interrupt or fault/trap */
|
||||
int irq;
|
||||
int tbdf;
|
||||
int (*isr)(int); /* get isr bit for this irq */
|
||||
int (*eoi)(int); /* eoi */
|
||||
int (*mask)(Vctl*, int); /* interrupt enable returns masked vector */
|
||||
int vno;
|
||||
|
||||
void (*f)(Ureg*, void*); /* handler to call */
|
||||
void* a; /* argument to call it with */
|
||||
} Vctl;
|
||||
|
||||
enum {
|
||||
@ -139,8 +144,7 @@ enum { /* type 0 and type 1 pre-defined header */
|
||||
PciBAR0 = 0x10, /* base address */
|
||||
PciBAR1 = 0x14,
|
||||
|
||||
PciCP = 0x34, /* capabilities pointer */
|
||||
|
||||
PciCAP = 0x34, /* capabilities pointer */
|
||||
PciINTL = 0x3C, /* interrupt line */
|
||||
PciINTP = 0x3D, /* interrupt pin */
|
||||
};
|
||||
@ -197,7 +201,7 @@ enum {
|
||||
enum { /* type 0 pre-defined header */
|
||||
PciCIS = 0x28, /* cardbus CIS pointer */
|
||||
PciSVID = 0x2C, /* subsystem vendor ID */
|
||||
PciSID = 0x2E, /* cardbus CIS pointer */
|
||||
PciSID = 0x2E, /* subsystem ID */
|
||||
PciEBAR0 = 0x30, /* expansion ROM base address */
|
||||
PciMGNT = 0x3E, /* burst period length */
|
||||
PciMLT = 0x3F, /* maximum latency between bursts */
|
||||
@ -274,8 +278,6 @@ struct Pcidev
|
||||
int tbdf; /* type+bus+device+function */
|
||||
uint16_t vid; /* vendor ID */
|
||||
uint16_t did; /* device ID */
|
||||
uint16_t svid; /* subsystem vid */
|
||||
uint16_t sdid; /* subsystem did */
|
||||
|
||||
uint16_t pcr;
|
||||
|
||||
@ -308,7 +310,12 @@ struct Pcidev
|
||||
} ioa, mema;
|
||||
|
||||
int pmrb; /* power management register block */
|
||||
void* xcfg; /* PCIe configuration block */
|
||||
};
|
||||
|
||||
enum {
|
||||
/* vendor ids */
|
||||
Vintel = 0x8086,
|
||||
Vmyricom= 0x14c1,
|
||||
};
|
||||
|
||||
#define PCIWINDOW 0
|
||||
@ -319,4 +326,110 @@ struct Pcidev
|
||||
#define PCIWADDRL(va) ((uint32_t)PCIWADDR64(va))
|
||||
#define PCIWADDRH(va) ((uint32_t)(PCIWADDR64(va)>>32))
|
||||
|
||||
#pragma varargck type "T" int
|
||||
/* SMBus transactions */
|
||||
enum
|
||||
{
|
||||
SMBquick, /* sends address only */
|
||||
|
||||
/* write */
|
||||
SMBsend, /* sends address and cmd */
|
||||
SMBbytewrite, /* sends address and cmd and 1 byte */
|
||||
SMBwordwrite, /* sends address and cmd and 2 bytes */
|
||||
|
||||
/* read */
|
||||
SMBrecv, /* sends address, recvs 1 byte */
|
||||
SMBbyteread, /* sends address and cmd, recv's byte */
|
||||
SMBwordread, /* sends address and cmd, recv's 2 bytes */
|
||||
};
|
||||
|
||||
typedef struct SMBus SMBus;
|
||||
struct SMBus {
|
||||
QLock; /* mutex */
|
||||
Rendez r; /* rendezvous point for completion interrupts */
|
||||
void *arg; /* implementation dependent */
|
||||
uint32_t base; /* port or memory base of smbus */
|
||||
int busy;
|
||||
void (*transact)(SMBus*, int, int, int, uint8_t*);
|
||||
};
|
||||
|
||||
/*
|
||||
* PCMCIA support code.
|
||||
*/
|
||||
|
||||
typedef struct PCMslot PCMslot;
|
||||
typedef struct PCMconftab PCMconftab;
|
||||
|
||||
/*
|
||||
* Map between ISA memory space and PCMCIA card memory space.
|
||||
*/
|
||||
struct PCMmap {
|
||||
uint32_t ca; /* card address */
|
||||
uint32_t cea; /* card end address */
|
||||
uint32_t isa; /* ISA address */
|
||||
int len; /* length of the ISA area */
|
||||
int attr; /* attribute memory */
|
||||
int ref;
|
||||
};
|
||||
|
||||
/* configuration table entry */
|
||||
struct PCMconftab
|
||||
{
|
||||
int index;
|
||||
uint16_t irqs; /* legal irqs */
|
||||
uint8_t irqtype;
|
||||
uint8_t bit16; /* true for 16 bit access */
|
||||
struct {
|
||||
uint32_t start;
|
||||
uint32_t len;
|
||||
} io[16];
|
||||
int nio;
|
||||
uint8_t vpp1;
|
||||
uint8_t vpp2;
|
||||
uint8_t memwait;
|
||||
uint32_t maxwait;
|
||||
uint32_t readywait;
|
||||
uint32_t otherwait;
|
||||
};
|
||||
|
||||
/* a card slot */
|
||||
struct PCMslot
|
||||
{
|
||||
Lock;
|
||||
int ref;
|
||||
|
||||
void *cp; /* controller for this slot */
|
||||
long memlen; /* memory length */
|
||||
uint8_t base; /* index register base */
|
||||
uint8_t slotno; /* slot number */
|
||||
|
||||
/* status */
|
||||
uint8_t special; /* in use for a special device */
|
||||
uint8_t already; /* already inited */
|
||||
uint8_t occupied;
|
||||
uint8_t battery;
|
||||
uint8_t wrprot;
|
||||
uint8_t powered;
|
||||
uint8_t configed;
|
||||
uint8_t enabled;
|
||||
uint8_t busy;
|
||||
|
||||
/* cis info */
|
||||
uint32_t msec; /* time of last slotinfo call */
|
||||
char verstr[512]; /* version string */
|
||||
int ncfg; /* number of configurations */
|
||||
struct {
|
||||
uint16_t cpresent; /* config registers present */
|
||||
uint32_t caddr; /* relative address of config registers */
|
||||
} cfg[8];
|
||||
int nctab; /* number of config table entries */
|
||||
PCMconftab ctab[8];
|
||||
PCMconftab *def; /* default conftab */
|
||||
|
||||
/* memory maps */
|
||||
Lock mlock; /* lock down the maps */
|
||||
int time;
|
||||
PCMmap mmap[4]; /* maps, last is always for the kernel */
|
||||
};
|
||||
|
||||
#pragma varargck type "T" int
|
||||
#pragma varargck type "T" uint
|
||||
|
@ -1,595 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "apic.h"
|
||||
#include "io.h"
|
||||
#include "adr.h"
|
||||
|
||||
typedef struct Rbus Rbus;
|
||||
typedef struct Rdt Rdt;
|
||||
|
||||
struct Rbus {
|
||||
Rbus *next;
|
||||
int bustype;
|
||||
int devno;
|
||||
Rdt *rdt;
|
||||
};
|
||||
|
||||
struct Rdt {
|
||||
IOapic *apic;
|
||||
int intin;
|
||||
uint32_t lo;
|
||||
|
||||
int ref; /* could map to multiple busses */
|
||||
int enabled; /* times enabled */
|
||||
};
|
||||
|
||||
enum { /* IOAPIC registers */
|
||||
Ioregsel = 0x00, /* indirect register address */
|
||||
Iowin = 0x04, /* indirect register data */
|
||||
Ioipa = 0x08, /* IRQ Pin Assertion */
|
||||
Ioeoi = 0x10, /* EOI */
|
||||
|
||||
IOapicid = 0x00, /* Identification */
|
||||
IOapicver = 0x01, /* Version */
|
||||
IOapicarb = 0x02, /* Arbitration */
|
||||
Ioabcfg = 0x03, /* Boot Configuration */
|
||||
Ioredtbl = 0x10, /* Redirection Table */
|
||||
};
|
||||
|
||||
static Rdt rdtarray[Nrdt];
|
||||
static int nrdtarray;
|
||||
static int gsib;
|
||||
static Rbus* rdtbus[Nbus];
|
||||
static Rdt* rdtvecno[IdtMAX+1];
|
||||
static int dfpolicy = 1; /* round-robin */
|
||||
|
||||
static Lock idtnolock;
|
||||
static int idtno = IdtIOAPIC;
|
||||
|
||||
static IOapic xioapic[Napic];
|
||||
static int isabusno = -1;
|
||||
|
||||
/* BOTCH: no need for this concept; we've got the bustype */
|
||||
static void
|
||||
ioapicisabus(int busno)
|
||||
{
|
||||
if(isabusno != -1){
|
||||
if(busno == isabusno)
|
||||
return;
|
||||
jehanne_print("ioapic: isabus redefined: %d ↛ %d\n", isabusno, busno);
|
||||
// return;
|
||||
}
|
||||
DBG("ioapic: isa busno %d\n", busno);
|
||||
isabusno = busno;
|
||||
}
|
||||
|
||||
IOapic*
|
||||
ioapiclookup(uint32_t id)
|
||||
{
|
||||
IOapic *a;
|
||||
|
||||
if(id > nelem(xioapic))
|
||||
return nil;
|
||||
a = xioapic + id;
|
||||
if(a->useable)
|
||||
return a;
|
||||
return nil;
|
||||
}
|
||||
|
||||
int
|
||||
gsitoapicid(int gsi, uint32_t *intin)
|
||||
{
|
||||
int i;
|
||||
IOapic *a;
|
||||
|
||||
for(i=0; i<Napic; i++){
|
||||
a = xioapic + i;
|
||||
if(!a->useable)
|
||||
continue;
|
||||
if(gsi >= a->gsib && gsi < a->gsib+a->nrdt){
|
||||
if(intin != nil)
|
||||
*intin = gsi - a->gsib;
|
||||
return a - xioapic;
|
||||
}
|
||||
}
|
||||
// jehanne_print("gsitoapicid: no ioapic found for gsi %d\n", gsi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
rtblget(IOapic* apic, int sel, uint32_t* hi, uint32_t* lo)
|
||||
{
|
||||
sel = Ioredtbl + 2*sel;
|
||||
|
||||
apic->addr[Ioregsel] = sel+1;
|
||||
*hi = apic->addr[Iowin];
|
||||
apic->addr[Ioregsel] = sel;
|
||||
*lo = apic->addr[Iowin];
|
||||
}
|
||||
|
||||
static void
|
||||
rtblput(IOapic* apic, int sel, uint32_t hi, uint32_t lo)
|
||||
{
|
||||
sel = Ioredtbl + 2*sel;
|
||||
|
||||
apic->addr[Ioregsel] = sel+1;
|
||||
apic->addr[Iowin] = hi;
|
||||
apic->addr[Ioregsel] = sel;
|
||||
apic->addr[Iowin] = lo;
|
||||
}
|
||||
|
||||
Rdt*
|
||||
rdtlookup(IOapic *apic, int intin)
|
||||
{
|
||||
int i;
|
||||
Rdt *r;
|
||||
|
||||
for(i = 0; i < nrdtarray; i++){
|
||||
r = rdtarray + i;
|
||||
if(apic == r->apic && intin == r->intin)
|
||||
return r;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
ioapicintrinit(int bustype, int busno, int apicno, int intin, int devno, uint32_t lo)
|
||||
{
|
||||
Rbus *rbus;
|
||||
Rdt *rdt;
|
||||
IOapic *apic;
|
||||
|
||||
if(busno >= Nbus || apicno >= Napic || nrdtarray >= Nrdt)
|
||||
return;
|
||||
|
||||
if(bustype == BusISA)
|
||||
ioapicisabus(busno);
|
||||
|
||||
apic = &xioapic[apicno];
|
||||
if(!apic->useable || intin >= apic->nrdt)
|
||||
panic("ioapic: intrinit: usable %d nrdt %d: bus %d apic %d intin %d dev %d lo %.8ux\n",
|
||||
apic->useable, apic->nrdt, busno, apicno, intin, devno, lo);
|
||||
|
||||
rdt = rdtlookup(apic, intin);
|
||||
if(rdt == nil){
|
||||
if(nrdtarray == nelem(rdtarray)){
|
||||
jehanne_print("ioapic: intrinit: rdtarray too small\n");
|
||||
return;
|
||||
}
|
||||
rdt = &rdtarray[nrdtarray++];
|
||||
rdt->apic = apic;
|
||||
rdt->intin = intin;
|
||||
rdt->lo = lo;
|
||||
}else{
|
||||
if(lo != rdt->lo){
|
||||
if(bustype == BusISA && intin < 16 && lo == (Im|IPhigh|TMedge)){
|
||||
DBG("override: isa %d %.8ux\n", intin, rdt->lo);
|
||||
return; /* expected; default was overridden*/
|
||||
}
|
||||
jehanne_print("multiple irq botch type %d bus %d %d/%d/%d lo %.8ux vs %.8ux\n",
|
||||
bustype, busno, apicno, intin, devno, lo, rdt->lo);
|
||||
return;
|
||||
}
|
||||
DBG("dup rdt %d %d %d %d %.8ux\n", busno, apicno, intin, devno, lo);
|
||||
}
|
||||
rdt->ref++;
|
||||
rbus = jehanne_malloc(sizeof(*rbus));
|
||||
rbus->rdt = rdt;
|
||||
rbus->bustype = bustype;
|
||||
rbus->devno = devno;
|
||||
rbus->next = rdtbus[busno];
|
||||
rdtbus[busno] = rbus;
|
||||
}
|
||||
|
||||
/*
|
||||
* deal with ioapics at the same physical address. seen on
|
||||
* certain supermicro atom systems. the hope is that only
|
||||
* one will be used, and it will be the second one initialized.
|
||||
* (the pc kernel ignores this issue.) it could be that mp and
|
||||
* acpi have different numbering?
|
||||
*/
|
||||
static IOapic*
|
||||
dupaddr(uintmem pa)
|
||||
{
|
||||
int i;
|
||||
IOapic *p;
|
||||
|
||||
for(i = 0; i < nelem(xioapic); i++){
|
||||
p = xioapic + i;
|
||||
if(p->paddr == pa)
|
||||
return p;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
IOapic*
|
||||
ioapicinit(int id, int ibase, uintmem pa)
|
||||
{
|
||||
IOapic *apic, *p;
|
||||
|
||||
/*
|
||||
* Mark the IOAPIC useable if it has a good ID
|
||||
* and the registers can be mapped.
|
||||
*/
|
||||
if(id >= Napic)
|
||||
return nil;
|
||||
if((apic = xioapic+id)->useable)
|
||||
return apic;
|
||||
|
||||
if((p = dupaddr(pa)) != nil){
|
||||
jehanne_print("ioapic%d: same pa as apic%ld\n", id, p-xioapic);
|
||||
if(ibase != -1)
|
||||
return nil; /* mp irqs reference mp apic#s */
|
||||
apic->addr = p->addr;
|
||||
}
|
||||
else{
|
||||
//adrmapck(pa, 1024, Ammio, Mfree, Cnone); /* not in adr? */ /* TO DO */
|
||||
if((apic->addr = vmap(pa, 1024)) == nil){
|
||||
jehanne_print("ioapic%d: can't vmap %#P\n", id, pa);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
apic->useable = 1;
|
||||
apic->paddr = pa;
|
||||
|
||||
/*
|
||||
* Initialise the I/O APIC.
|
||||
* The MultiProcessor Specification says it is the
|
||||
* responsibility of the O/S to set the APIC ID.
|
||||
*/
|
||||
lock(apic);
|
||||
apic->addr[Ioregsel] = IOapicver;
|
||||
apic->nrdt = ((apic->addr[Iowin]>>16) & 0xff) + 1;
|
||||
if(ibase != -1)
|
||||
apic->gsib = ibase;
|
||||
else{
|
||||
apic->gsib = gsib;
|
||||
gsib += apic->nrdt;
|
||||
}
|
||||
apic->addr[Ioregsel] = IOapicid;
|
||||
apic->addr[Iowin] = id<<24;
|
||||
unlock(apic);
|
||||
|
||||
return apic;
|
||||
}
|
||||
|
||||
void
|
||||
iordtdump(void)
|
||||
{
|
||||
int i;
|
||||
Rbus *rbus;
|
||||
Rdt *rdt;
|
||||
|
||||
if(!DBGFLG)
|
||||
return;
|
||||
for(i = 0; i < Nbus; i++){
|
||||
if((rbus = rdtbus[i]) == nil)
|
||||
continue;
|
||||
jehanne_print("iointr bus %d:\n", i);
|
||||
for(; rbus != nil; rbus = rbus->next){
|
||||
rdt = rbus->rdt;
|
||||
jehanne_print(" apic %ld devno %#ux (%d %d) intin %d lo %#ux ref %d\n",
|
||||
rdt->apic-xioapic, rbus->devno, rbus->devno>>2,
|
||||
rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ioapicdump(void)
|
||||
{
|
||||
int i, n;
|
||||
IOapic *apic;
|
||||
uint32_t hi, lo;
|
||||
|
||||
if(!DBGFLG)
|
||||
return;
|
||||
for(i = 0; i < Napic; i++){
|
||||
apic = &xioapic[i];
|
||||
if(!apic->useable || apic->addr == 0)
|
||||
continue;
|
||||
jehanne_print("ioapic %d addr %#p nrdt %d ibase %d\n",
|
||||
i, apic->addr, apic->nrdt, apic->gsib);
|
||||
for(n = 0; n < apic->nrdt; n++){
|
||||
lock(apic);
|
||||
rtblget(apic, n, &hi, &lo);
|
||||
unlock(apic);
|
||||
jehanne_print(" rdt %2.2d %#8.8ux %#8.8ux\n", n, hi, lo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char*
|
||||
ioapicprint(char *p, char *e, IOapic *a, int i)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = "ioapic";
|
||||
p = jehanne_seprint(p, e, "%-8s ", s);
|
||||
p = jehanne_seprint(p, e, "%8ux ", i);
|
||||
p = jehanne_seprint(p, e, "%6d ", a->gsib);
|
||||
p = jehanne_seprint(p, e, "%6d ", a->gsib+a->nrdt-1);
|
||||
p = jehanne_seprint(p, e, "%#P ", a->paddr);
|
||||
p = jehanne_seprint(p, e, "\n");
|
||||
return p;
|
||||
}
|
||||
|
||||
static long
|
||||
ioapicread(Chan* _1, void *a, long n, int64_t off)
|
||||
{
|
||||
char *s, *e, *p;
|
||||
long i, r;
|
||||
|
||||
s = jehanne_malloc(READSTR);
|
||||
e = s+READSTR;
|
||||
p = s;
|
||||
|
||||
for(i = 0; i < nelem(xioapic); i++)
|
||||
if(xioapic[i].useable)
|
||||
p = ioapicprint(p, e, xioapic + i, i);
|
||||
r = -1;
|
||||
if(!waserror()){
|
||||
r = readstr(off, a, n, s);
|
||||
poperror();
|
||||
}
|
||||
jehanne_free(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
ioapiconline(void)
|
||||
{
|
||||
int i;
|
||||
IOapic *apic;
|
||||
|
||||
addarchfile("ioapic", 0444, ioapicread, nil);
|
||||
for(apic = xioapic; apic < &xioapic[Napic]; apic++){
|
||||
if(!apic->useable || apic->addr == nil)
|
||||
continue;
|
||||
for(i = 0; i < apic->nrdt; i++){
|
||||
lock(apic);
|
||||
rtblput(apic, i, 0, Im);
|
||||
unlock(apic);
|
||||
}
|
||||
}
|
||||
jehanne_print("init ioapic dump\n");
|
||||
ioapicdump();
|
||||
}
|
||||
|
||||
static int
|
||||
ioapicintrdd(uint32_t* hi, uint32_t* lo)
|
||||
{
|
||||
Lapic *lapic;
|
||||
Mach *mach;
|
||||
int i;
|
||||
static int df;
|
||||
|
||||
/*
|
||||
* Set delivery mode (lo) and destination field (hi)
|
||||
*
|
||||
* Currently, assign each interrupt to a different CPU
|
||||
* using physical mode delivery. Using the topology
|
||||
* (packages/cores/threads) could be helpful.
|
||||
*/
|
||||
switch(dfpolicy){
|
||||
case 0:
|
||||
i = sys->machptr[0]->apicno;
|
||||
break;
|
||||
default: /* round-robin */
|
||||
for(;;){
|
||||
i = df;
|
||||
if(++df >= Napic)
|
||||
df = 0;
|
||||
if((lapic = lapiclookup(i)) != nil &&
|
||||
(mach = sys->machptr[lapic->machno]) != nil &&
|
||||
mach->online)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*hi = i<<24;
|
||||
*lo |= Pm|MTf;
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
nextvec(void)
|
||||
{
|
||||
uint32_t vecno;
|
||||
|
||||
lock(&idtnolock);
|
||||
vecno = idtno;
|
||||
idtno = (idtno+8) % IdtMAX;
|
||||
if(idtno < IdtIOAPIC)
|
||||
idtno += IdtIOAPIC;
|
||||
unlock(&idtnolock);
|
||||
|
||||
return vecno;
|
||||
}
|
||||
|
||||
static int
|
||||
msimask(Vctl *v, int mask)
|
||||
{
|
||||
Pcidev *p;
|
||||
|
||||
p = pcimatchtbdf(v->tbdf);
|
||||
if(p == nil)
|
||||
return -1;
|
||||
return pcimsimask(p, mask);
|
||||
}
|
||||
|
||||
static int
|
||||
intrenablemsi(Vctl* v, Pcidev *p)
|
||||
{
|
||||
uint32_t vno, lo, hi;
|
||||
uint64_t msivec;
|
||||
|
||||
vno = nextvec();
|
||||
|
||||
lo = IPlow | TMedge | vno;
|
||||
v->affinity = ioapicintrdd(&hi, &lo);
|
||||
|
||||
if(lo & Lm)
|
||||
lo |= MTlp;
|
||||
|
||||
msivec = (uint64_t)hi<<32 | lo;
|
||||
if(pcimsienable(p, msivec) == -1)
|
||||
return -1;
|
||||
v->isr = lapicisr;
|
||||
v->eoi = lapiceoi;
|
||||
v->vno = vno;
|
||||
v->type = "msi";
|
||||
v->mask = msimask;
|
||||
|
||||
DBG("msiirq: %T: enabling %.16llux %s irq %d vno %d\n", p->tbdf, msivec, v->name, v->irq, vno);
|
||||
return vno;
|
||||
}
|
||||
|
||||
int
|
||||
disablemsi(Vctl* _1, Pcidev *p)
|
||||
{
|
||||
if(p == nil)
|
||||
return -1;
|
||||
return pcimsimask(p, 1);
|
||||
}
|
||||
|
||||
int
|
||||
ioapicintrenable(Vctl* v)
|
||||
{
|
||||
Rbus *rbus;
|
||||
Rdt *rdt;
|
||||
uint32_t hi, lo;
|
||||
int bustype, busno, devno, vecno;
|
||||
|
||||
if(v->tbdf == BUSUNKNOWN){
|
||||
if(v->irq >= IdtLINT0 && v->irq <= IdtMAX){
|
||||
if(v->irq != IdtSPURIOUS)
|
||||
v->isr = lapiceoi;
|
||||
v->type = "lapic";
|
||||
return v->irq;
|
||||
}
|
||||
else{
|
||||
/*
|
||||
* Legacy ISA.
|
||||
* Make a busno and devno using the
|
||||
* ISA bus number and the irq.
|
||||
*/
|
||||
if(isabusno == -1)
|
||||
panic("no ISA bus allocated");
|
||||
busno = isabusno;
|
||||
devno = v->irq;
|
||||
bustype = BusISA;
|
||||
}
|
||||
}
|
||||
else if((bustype = BUSTYPE(v->tbdf)) == BusPCI){
|
||||
/*
|
||||
* PCI.
|
||||
* Make a devno from BUSDNO(tbdf) and pcidev->intp.
|
||||
*/
|
||||
Pcidev *pcidev;
|
||||
|
||||
busno = BUSBNO(v->tbdf);
|
||||
if((pcidev = pcimatchtbdf(v->tbdf)) == nil)
|
||||
panic("no PCI dev for tbdf %T", v->tbdf);
|
||||
if((vecno = intrenablemsi(v, pcidev)) != -1)
|
||||
return vecno;
|
||||
disablemsi(v, pcidev);
|
||||
if((devno = pcicfgr8(pcidev, PciINTP)) == 0)
|
||||
panic("no INTP for tbdf %T", v->tbdf);
|
||||
devno = BUSDNO(v->tbdf)<<2|(devno-1);
|
||||
DBG("ioapicintrenable: tbdf %T busno %d devno %d\n",
|
||||
v->tbdf, busno, devno);
|
||||
}
|
||||
else{
|
||||
SET(busno, devno);
|
||||
panic("unknown tbdf %T", v->tbdf);
|
||||
}
|
||||
rdt = nil;
|
||||
for(rbus = rdtbus[busno]; rbus != nil; rbus = rbus->next)
|
||||
if(rbus->devno == devno && rbus->bustype == bustype){
|
||||
rdt = rbus->rdt;
|
||||
break;
|
||||
}
|
||||
if(rdt == nil){
|
||||
/*
|
||||
* PCI devices defaulted to ISA (ACPI).
|
||||
*/
|
||||
if((busno = isabusno) == -1)
|
||||
return -1;
|
||||
devno = v->irq;
|
||||
for(rbus = rdtbus[busno]; rbus != nil; rbus = rbus->next)
|
||||
if(rbus->devno == devno){
|
||||
rdt = rbus->rdt;
|
||||
break;
|
||||
}
|
||||
DBG("isa: tbdf %T busno %d devno %d %#p\n",
|
||||
v->tbdf, busno, devno, rdt);
|
||||
}
|
||||
if(rdt == nil)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Assume this is a low-frequency event so just lock
|
||||
* the whole IOAPIC to initialise the RDT entry
|
||||
* rather than putting a Lock in each entry.
|
||||
*/
|
||||
lock(rdt->apic);
|
||||
DBG("%T: %ld/%d/%d (%d)\n", v->tbdf, rdt->apic - xioapic, rbus->devno, rdt->intin, devno);
|
||||
if((rdt->lo & 0xff) == 0){
|
||||
vecno = nextvec();
|
||||
rdt->lo |= vecno;
|
||||
rdtvecno[vecno] = rdt;
|
||||
}else
|
||||
DBG("%T: mutiple irq bus %d dev %d\n", v->tbdf, busno, devno);
|
||||
|
||||
rdt->enabled++;
|
||||
lo = (rdt->lo & ~Im);
|
||||
v->affinity = ioapicintrdd(&hi, &lo);
|
||||
rtblput(rdt->apic, rdt->intin, hi, lo);
|
||||
vecno = lo & 0xff;
|
||||
unlock(rdt->apic);
|
||||
|
||||
DBG("busno %d devno %d hi %#.8ux lo %#.8ux vecno %d\n",
|
||||
busno, devno, hi, lo, vecno);
|
||||
v->isr = lapicisr;
|
||||
v->eoi = lapiceoi;
|
||||
v->vno = vecno;
|
||||
v->type = "ioapic";
|
||||
|
||||
return vecno;
|
||||
}
|
||||
|
||||
int
|
||||
ioapicintrdisable(int vecno)
|
||||
{
|
||||
Rdt *rdt;
|
||||
|
||||
/*
|
||||
* FOV. Oh dear. This isn't very good.
|
||||
* Fortunately rdtvecno[vecno] is static
|
||||
* once assigned.
|
||||
* Must do better.
|
||||
*
|
||||
* What about any pending interrupts?
|
||||
*/
|
||||
if(vecno < 0 || vecno > IdtMAX){
|
||||
panic("ioapicintrdisable: vecno %d out of range", vecno);
|
||||
return -1;
|
||||
}
|
||||
if((rdt = rdtvecno[vecno]) == nil){
|
||||
panic("ioapicintrdisable: vecno %d has no rdt", vecno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lock(rdt->apic);
|
||||
rdt->enabled--;
|
||||
if(rdt->enabled == 0)
|
||||
rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
|
||||
unlock(rdt->apic);
|
||||
|
||||
return 0;
|
||||
}
|
@ -10,7 +10,7 @@ ENTRY(_start)
|
||||
/* start the kernel at 0x110000.
|
||||
* That way we can use lower ram for critical structures
|
||||
*/
|
||||
KERN_LOAD_ADDR = 0xfffffffff0000000;
|
||||
KERN_LOAD_ADDR = 0xffffffff80000000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Interrupt/exception handling.
|
||||
*/
|
||||
#include "amd64.h"
|
||||
#include "mem.h"
|
||||
|
||||
.code64
|
||||
|
||||
@ -81,7 +82,7 @@ _intrcommon:
|
||||
xchgq %rax, 0(%rsp)
|
||||
// 0(%rsp) now has the vno
|
||||
subq $16, %rsp /* R1[45] */
|
||||
cmpw $SSEL(SiCS, SsTIGDT|SsRPL0), 40(%rsp) /* old CS */
|
||||
cmpw $KESEL, 40(%rsp) /* old CS */
|
||||
je _intrnested
|
||||
|
||||
movq %r14, 0(%rsp)
|
||||
@ -131,7 +132,7 @@ _intrr:
|
||||
popq %r12
|
||||
popq %r13
|
||||
|
||||
cmpw $SSEL(SiCS, SsTIGDT|SsRPL0), 40(%rsp) /* old CS */
|
||||
cmpw $KESEL, 40(%rsp) /* old CS */
|
||||
je _iretnested
|
||||
|
||||
swapgs
|
||||
|
@ -11,9 +11,6 @@
|
||||
* CS base set to startup memory address;
|
||||
* CS limit set to 64KiB;
|
||||
* CPL and IP set to 0.
|
||||
* Parameters are passed to this code via a vector in low memory
|
||||
* indexed by the APIC number of the processor. The layout, size,
|
||||
* and location have to be kept in sync with the setup in sipi.s.
|
||||
*/
|
||||
#include "mem.h"
|
||||
#include "amd64.h"
|
||||
@ -22,190 +19,126 @@
|
||||
#endif
|
||||
|
||||
.section .text
|
||||
|
||||
/*
|
||||
* Real mode. Welcome to 1978.
|
||||
* Load a basic GDT, turn on protected mode and make
|
||||
* inter-segment jump to the protected mode code.
|
||||
*/
|
||||
.code16
|
||||
.align 4096
|
||||
.globl sipihandler
|
||||
sipihandler:
|
||||
_real:
|
||||
ljmp $0x0, $_endofheader
|
||||
|
||||
_startofheader:
|
||||
NOP; NOP; NOP
|
||||
.quad 0xa5a5a5a5a5a5a5a5
|
||||
|
||||
_gdt32p:
|
||||
.quad 0x0000000000000000 /* NULL descriptor */
|
||||
.quad 0x00cf9a000000ffff /* CS */
|
||||
.quad 0x00cf92000000ffff /* DS */
|
||||
.quad 0x0020980000000000 /* .long mode CS */
|
||||
|
||||
_gdtptr32p:
|
||||
.word 4*8-1 /* includes .long mode */
|
||||
.long _gdt32p
|
||||
|
||||
_gdt64:
|
||||
.quad 0x0000000000000000 /* NULL descriptor */
|
||||
.quad 0x0020980000000000 /* CS */
|
||||
.quad 0x0000800000000000 /* DS */
|
||||
|
||||
_gdtptr64v:
|
||||
.word 3*8-1
|
||||
.quad _gdt64
|
||||
|
||||
_endofheader:
|
||||
.align 4
|
||||
apbootstrap:
|
||||
ljmp $0x0, $_apbootstrap
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
_apvector: /* address APBOOTSTRAP+0x08 */
|
||||
.quad 0
|
||||
_appml4: /* address APBOOTSTRAP+0x10 */
|
||||
.quad 0
|
||||
_apapic: /* address APBOOTSTRAP+0x18 */
|
||||
.quad 0
|
||||
_apmach: /* address APBOOTSTRAP+0x20 */
|
||||
.quad 0
|
||||
_apbootstrap:
|
||||
mov %cs, %ax
|
||||
mov %ax, %ds
|
||||
|
||||
lgdt _gdtptr32p /* load a basic gdt */
|
||||
|
||||
mov %cr0, %eax
|
||||
or $Pe, %ax
|
||||
mov %eax, %cr0 /* turn on protected mode */
|
||||
|
||||
lgdt _gdtptr32p
|
||||
movl %cr0, %eax
|
||||
or $0x1, %ax
|
||||
mov %eax, %cr0
|
||||
jmp 1f
|
||||
1:
|
||||
mov $(SSEL(SiDS, SsTIGDT|SsRPL0)), %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
mov %ax, %ss
|
||||
ljmp $0x18, $_ap32
|
||||
|
||||
ljmpl $8, $_protected /* 8 = SSEL(SiCS, SsTIGDT|SsRPL0) */
|
||||
|
||||
|
||||
/*
|
||||
* Protected mode. Welcome to 1982.
|
||||
* Get the local APIC ID from the memory mapped APIC
|
||||
* and use it to locate the index to the parameter vector;
|
||||
* load the PDB with the page table address from the
|
||||
* information vector;
|
||||
* make an identity map for the inter-segment jump below,
|
||||
* using the stack space to hold a temporary PDP and PD;
|
||||
* enable and activate .long mode;
|
||||
* make an inter-segment jump to the .long mode code.
|
||||
*/
|
||||
.code32
|
||||
_ap32:
|
||||
movl $SELECTOR(2, SELGDT, 0), %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
|
||||
/*
|
||||
* Macros for accessing page table entries; must turn
|
||||
* the C-style array-index macros into a page table byte
|
||||
* offset.
|
||||
*/
|
||||
#define PML4O(v) ((PTLX((v), 3))<<3)
|
||||
#define PDPO(v) ((PTLX((v), 2))<<3)
|
||||
#define PDO(v) ((PTLX((v), 1))<<3)
|
||||
#define PTO(v) ((PTLX((v), 0))<<3)
|
||||
movl _appml4, %eax /* physical address of PML4 */
|
||||
movl %eax, %cr3 /* load the mmu */
|
||||
jmp 2f
|
||||
2:
|
||||
movl %cr4, %eax
|
||||
andl $0xffffffef, %eax /* Page Size (~0x00000010 = 0xffffffef) */
|
||||
orl $0xa0, %eax /* Page Global, Phys. Address */
|
||||
movl %eax, %cr4
|
||||
|
||||
_protected:
|
||||
mov $0xfee00000, %ebp /* apicbase */
|
||||
mov 0x20(%ebp), %ebp /* Id */
|
||||
shr $0x18, %ebp /* becomes RARG later */
|
||||
|
||||
mov %ebp, %eax /* apicno */
|
||||
imul $0x20, %eax, %eax /* [apicno] */
|
||||
mov $_real, %ebx
|
||||
add $0x1000, %ebx /* sipi */
|
||||
add %eax, %ebx /* sipi[apicno] */
|
||||
|
||||
mov (%ebx), %esi /* sipi[apicno].pml4 */
|
||||
|
||||
mov %esi, %eax
|
||||
mov %eax, %cr3 /* load the mmu */
|
||||
|
||||
mov %eax, %edx
|
||||
sub $MACHSTKSZ, %edx /* PDP for identity map */
|
||||
add $(PteRW|PteP), %edx
|
||||
mov %edx, PML4O(0)(%eax) /* PML4E for identity map */
|
||||
|
||||
sub $MACHSTKSZ, %eax /* PDP for identity map */
|
||||
add $PTSZ, %edx
|
||||
mov %edx, PDPO(0)(%eax) /* PDPE for identity map */
|
||||
mov $(PtePS|PteRW|PteP), %edx
|
||||
add $PTSZ, %eax /* PD for identity map */
|
||||
mov %edx, PDO(0)(%eax) /* PDE for identity 0-[24]MiB */
|
||||
|
||||
/*
|
||||
* Enable and activate .long Mode. From the manual:
|
||||
* make sure Page Size Extentions are off, and Page Global
|
||||
* Extensions and Physical Address Extensions are on in CR4;
|
||||
* set .long Mode Enable in the Extended Feature Enable MSR;
|
||||
* set Paging Enable in CR0;
|
||||
* make an inter-segment jump to the .long Mode code.
|
||||
* It's all in 32-bit mode until the jump is made.
|
||||
*/
|
||||
_lme:
|
||||
mov %cr4, %eax
|
||||
and $~Pse, %eax /* Page Size */
|
||||
or $(Pge|Pae), %eax /* Page Global, Phys. Address */
|
||||
mov %eax, %cr4
|
||||
|
||||
mov $Efer, %ecx /* Extended Feature Enable */
|
||||
movl $0xc0000080, %ecx /* Extended Feature Enable */
|
||||
rdmsr
|
||||
or $Lme, %eax /* .long Mode Enable */
|
||||
orl $0x00000100, %eax /* Long Mode Enable */
|
||||
wrmsr
|
||||
|
||||
mov %cr0, %edx
|
||||
and $~(Cd|Nw|Ts|Mp), %edx
|
||||
or $(Pg|Wp), %edx /* Paging Enable */
|
||||
mov %edx, %cr0
|
||||
movl %cr0, %edx
|
||||
andl $0x9ffffff5, %edx /* (~0x6000000a = 0x9ffffff5) */
|
||||
orl $0x80010000, %edx /* Paging Enable, Write Protect */
|
||||
movl %edx, %cr0
|
||||
|
||||
ljmp $0x18, $_identity /* 0x18 = SSEL(3, SsTIGDT|SsRPL0) */
|
||||
ljmp $0x8, $(_ap64)
|
||||
|
||||
/*
|
||||
* .long mode. Welcome to 2003.
|
||||
* Jump out of the identity map space;
|
||||
* load a proper .long mode GDT;
|
||||
* zap the identity map;
|
||||
* initialise the stack, RMACH, RUSER,
|
||||
* and call the C startup code.
|
||||
*/
|
||||
.code64
|
||||
_identity:
|
||||
mov $(_start64v+KZERO), %rax
|
||||
jmpq *%rax
|
||||
|
||||
_start64v:
|
||||
mov $_gdtptr64v, %rax
|
||||
_ap64:
|
||||
movq $_gdtptr64v, %rax
|
||||
lgdt (%rax)
|
||||
|
||||
xor %rdx, %rdx /* DX is 0 from here on */
|
||||
mov %edx, %ds /* not used in .long mode */
|
||||
mov %edx, %es /* not used in .long mode */
|
||||
mov %edx, %fs
|
||||
mov %edx, %gs
|
||||
mov %edx, %ss /* not used in .long mode */
|
||||
xorq %rax, %rax
|
||||
movw %ax, %ds /* not used in long mode */
|
||||
movw %ax, %es /* not used in long mode */
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss /* not used in long mode */
|
||||
|
||||
mov %esi, %esi /* sipi[apicno].pml4 */
|
||||
mov %rsi, %rax
|
||||
add $KZERO, %rax /* PML4 */
|
||||
lldt %ax
|
||||
|
||||
mov %rdx, PML4O(0)(%rax) /* zap identity map */
|
||||
mov %rsi, %cr3 /* flush TLB */
|
||||
movq _apmach, %rsp
|
||||
|
||||
add $KZERO, %rbx /* &sipi[apicno] */
|
||||
movq %rax, %r14 /* up = nil; */
|
||||
movq %rsp, %r15 /* m = apmach */
|
||||
|
||||
mov 8(%rbx), %rsp /* sipi[apicno].stack */
|
||||
addq $MACHSIZE, %rsp
|
||||
|
||||
push %rdx /* clear flags */
|
||||
pushq %rax /* clear flags */
|
||||
popfq
|
||||
mov %ebp, %ebp /* APIC ID */
|
||||
push %rbp /* apicno */
|
||||
|
||||
mov 16(%rbx), %r15 /* sipi[apicno].mach */
|
||||
mov %rdx, %r14
|
||||
mov 24(%rbx), %rax /* sipi[apicno].pc */
|
||||
callq *%rax /* (*sipi[apicno].pc)(apicno) */
|
||||
movq _apvector, %rax
|
||||
movq _apapic, %rdi
|
||||
pushq %rbp
|
||||
|
||||
_ndnr:
|
||||
jmp _ndnr
|
||||
call *%rax
|
||||
|
||||
.globl sipihandlerend
|
||||
sipihandlerend:
|
||||
jmp sipihandlerend
|
||||
_halt:
|
||||
hlt
|
||||
jmp _halt
|
||||
|
||||
.align 16
|
||||
_gdt:
|
||||
/* null descriptor */
|
||||
.long 0
|
||||
.long 0
|
||||
|
||||
/* (KESEG) 64 bit long mode exec segment */
|
||||
.long 0xFFFF
|
||||
.long SEGL|SEGG|SEGP|(0xF<<16)|SEGPL(0)|SEGEXEC|SEGR
|
||||
|
||||
/* 32 bit data segment descriptor for 4 gigabytes (PL 0) */
|
||||
.long 0xFFFF
|
||||
.long SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW
|
||||
|
||||
/* 32 bit exec segment descriptor for 4 gigabytes (PL 0) */
|
||||
.long 0xFFFF
|
||||
.long SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR
|
||||
|
||||
.align 4
|
||||
_gdtptr32p:
|
||||
.word 4*8-1
|
||||
.long _gdt-KZERO
|
||||
|
||||
.align 4
|
||||
_gdtptr64p:
|
||||
.word 4*8-1
|
||||
.quad _gdt-KZERO
|
||||
|
||||
.align 4
|
||||
_gdtptr64v:
|
||||
.word 4*8-1
|
||||
.quad _gdt
|
||||
|
@ -116,6 +116,16 @@ gdtget:
|
||||
sgdt (%rdi) /* Note: 10 bytes returned */
|
||||
ret
|
||||
|
||||
.global lgdt
|
||||
lgdt:
|
||||
lgdt (%rdi)
|
||||
ret
|
||||
|
||||
.global lidt
|
||||
lidt:
|
||||
lgdt (%rdi)
|
||||
ret
|
||||
|
||||
// Called with the address of gdt in rdi.
|
||||
// Load the gdt, then do a ret which will use the argument on the stack as
|
||||
// a segment #. This stuff is just crazy.
|
||||
@ -159,8 +169,6 @@ idtput:
|
||||
|
||||
.global trput
|
||||
trput:
|
||||
// panic
|
||||
//mov 0, %rax
|
||||
ltr %di
|
||||
ret
|
||||
|
||||
@ -219,36 +227,45 @@ rdtsc:
|
||||
ORQ %rdx, %rax /* (hi<<32)|lo */
|
||||
ret
|
||||
|
||||
.global _rdmsr
|
||||
_rdmsr:
|
||||
/* int rdmsr(uint32_t reg, uint64_t* value); */
|
||||
.global rdmsr
|
||||
rdmsr:
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
pushq %rbp
|
||||
xorq %rbp, %rbp
|
||||
movl %edi, %ecx
|
||||
|
||||
.global _rdmsrinst
|
||||
_rdmsrinst:
|
||||
rdmsr
|
||||
/* u64int rdmsr(u32int); */
|
||||
xchgl %edx, %eax /* swap lo/hi, zero-extend */
|
||||
SHLQ $32, %rax /* hi<<32 */
|
||||
ORQ %rdx, %rax /* (hi<<32)|lo */
|
||||
xchgl %edx, %eax /* swap lo/hi, zero-extend */
|
||||
shlq $32, %rax /* hi<<32 */
|
||||
orq %rdx, %rax /* (hi<<32)|lo */
|
||||
movq %rax, (%rsi) /* set value */
|
||||
movq %rbp, %rax /* %rbp set to -1 if traped */
|
||||
popq %rbp
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
ret
|
||||
|
||||
.global _wrmsr
|
||||
_wrmsr:
|
||||
pushq %rax // do we need to do this?
|
||||
/*int wrmsr(uint32_t reg, uint64_t value) */
|
||||
.global wrmsr
|
||||
wrmsr:
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
pushq %rbp
|
||||
movl %edi, %ecx
|
||||
movl %esi, %eax
|
||||
movq %rsi, %rdx
|
||||
shrq $32, %rdx
|
||||
|
||||
xorq %rbp, %rbp
|
||||
.global _wrmsrinst
|
||||
_wrmsrinst:
|
||||
wrmsr
|
||||
|
||||
movq %rbp, %rax
|
||||
popq %rbp
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
popq %rax
|
||||
ret
|
||||
|
||||
.global invlpg
|
||||
@ -380,6 +397,24 @@ adec:
|
||||
subl $1, %eax
|
||||
ret
|
||||
|
||||
/*
|
||||
* Synchronisation
|
||||
*/
|
||||
.global ainc16
|
||||
ainc16:
|
||||
mov $1, %ax
|
||||
lock; xadd %ax, (%rdi)
|
||||
add $1, %ax
|
||||
ret
|
||||
|
||||
.global adec16
|
||||
adec16:
|
||||
mov $-1, %ax
|
||||
lock; xadd %ax, (%rdi)
|
||||
sub $1, %ax
|
||||
ret
|
||||
|
||||
|
||||
/*
|
||||
* Semaphores rely on negative values for the counter,
|
||||
* and don't have the same overflow/underflow conditions
|
||||
@ -564,38 +599,86 @@ _mm32done:
|
||||
movl (%rdi), %eax
|
||||
ret
|
||||
|
||||
/* void mwait(void*); */
|
||||
.globl mwait
|
||||
mwait:
|
||||
movq %rdi, %rax
|
||||
movl (%eax), %ecx
|
||||
orl %ecx, %ecx
|
||||
jnz _mwaitdone
|
||||
xorq %rdx, %rdx
|
||||
monitor
|
||||
movl (%eax), %ecx
|
||||
orl %ecx, %ecx
|
||||
jnz _mwaitdone
|
||||
xorq %rax, %rax
|
||||
mwait
|
||||
_mwaitdone:
|
||||
RET
|
||||
|
||||
.global _monitor
|
||||
_monitor:
|
||||
movq %rdi, %rax /* linear address to monitor */
|
||||
xorq %rcx, %rcx /* no optional extensions yet */
|
||||
xorq %rdx, %rdx /* no optional hints yet */
|
||||
.byte 0x0f; .byte 0x01; .byte 0xc8 /* monitor */
|
||||
rdrand32:
|
||||
loop32:
|
||||
rdrand %eax
|
||||
jc loop32
|
||||
ret
|
||||
|
||||
.global _mwait
|
||||
_mwait:
|
||||
movq %rdi, %rcx /* optional extensions */
|
||||
.byte 0x0f; .byte 0x01; .byte 0xc9 /* mwait */
|
||||
rdrand64:
|
||||
loop64:
|
||||
rdrand %rax
|
||||
jc loop64
|
||||
ret
|
||||
|
||||
.global k10mwait
|
||||
k10mwait:
|
||||
k10mwloop:
|
||||
movq %rdi,%rcx
|
||||
movq (%rcx), %rax
|
||||
cmpq $0, %rax
|
||||
jne k10mwdone
|
||||
movq %rdi, %rax /* linear address to monitor */
|
||||
xorq %rcx, %rcx /* no optional extensions yet */
|
||||
xorq %rdx, %rdx /* no optional hints yet */
|
||||
.byte 0x0f; .byte 0x01; .byte 0xc8 /* monitor */
|
||||
movq %rdi, %rcx
|
||||
movq 0(%rcx), %rax
|
||||
cmpq $0, %rax
|
||||
jne k10mwdone
|
||||
xorq %rcx, %rcx /* optional extensions */
|
||||
.byte 0x0f; .byte 0x01; .byte 0xc9 /* mwait */
|
||||
jmp k10mwloop
|
||||
k10mwdone:
|
||||
ret
|
||||
.globl rdrandbuf
|
||||
rdrandbuf:
|
||||
movq %rdi, %rdx
|
||||
|
||||
movl %esi, %ecx
|
||||
shrq $3, %rcx
|
||||
eights:
|
||||
cmpl $0, %ecx
|
||||
jg f1
|
||||
call rdrand64
|
||||
movq %rax, 0(%rdx)
|
||||
addq $8, %rdx
|
||||
subl $1, %ecx
|
||||
jmp eights
|
||||
|
||||
f1:
|
||||
movl %esi, %ecx
|
||||
andl $7, %ecx
|
||||
shrq $2, %rcx
|
||||
fours:
|
||||
cmpl $0, %ecx
|
||||
jg f2
|
||||
call rdrand32
|
||||
movl %eax, 0(%rdx)
|
||||
addq $4, %rdx
|
||||
subl $1, %ecx
|
||||
jmp fours
|
||||
|
||||
f2:
|
||||
movl %esi, %ecx
|
||||
andl $3, %ecx
|
||||
ones:
|
||||
cmpl $0, %ecx
|
||||
jg f3
|
||||
call rdrand32
|
||||
movb %al, 0(%rdx)
|
||||
addq $1, %rdx
|
||||
subl $1, %ecx
|
||||
jmp ones
|
||||
|
||||
f3:
|
||||
RET
|
||||
|
||||
|
||||
/*
|
||||
* Park a processor. Should never fall through a return from main to here,
|
||||
* should only be called by application processors when shutting down.
|
||||
*/
|
||||
.global idle
|
||||
idle:
|
||||
_idle:
|
||||
STI
|
||||
HLT
|
||||
JMP _idle
|
||||
|
@ -11,6 +11,10 @@
|
||||
touser:
|
||||
cli
|
||||
swapgs
|
||||
|
||||
movq $0, %r15
|
||||
movq $0, %r14
|
||||
|
||||
movq $SSEL(SiUDS, SsRPL3), %rax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
@ -39,10 +43,10 @@ syscallentry:
|
||||
addq $KSTACK, %rsp
|
||||
|
||||
/* build Ureg */
|
||||
pushq $SSEL(SiUDS, SsRPL3) /* old stack segment */
|
||||
pushq $UDSEL /* old stack segment */
|
||||
pushq %r13 /* old sp */
|
||||
pushq %r11 /* old flags */
|
||||
pushq $SSEL(SiUCS, SsRPL3) /* old code segment */
|
||||
pushq $UESEL /* old code segment */
|
||||
pushq %rcx /* old ip */
|
||||
|
||||
movq %r14, 24(%r15) /* restore %r14 from m->tmp0 */
|
||||
@ -116,6 +120,6 @@ syscallreturn:
|
||||
.globl sysrforkret
|
||||
sysrforkret:
|
||||
movq $0, 0(%rsp)
|
||||
movq %r14, (13*8)(%rsp) /* preserve up-> */
|
||||
movq %r15, (14*8)(%rsp) /* preserve m-> */
|
||||
// movq %r14, (13*8)(%rsp) /* preserve up-> */
|
||||
// movq %r15, (14*8)(%rsp) /* preserve m-> */
|
||||
jmp syscallreturn
|
||||
|
@ -3,475 +3,445 @@
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "apic.h"
|
||||
#include "io.h"
|
||||
#include "adr.h"
|
||||
|
||||
#undef DBG
|
||||
#define DBG jehanne_print
|
||||
#include "mp.h"
|
||||
|
||||
enum { /* Local APIC registers */
|
||||
Id = 0x0020, /* Identification */
|
||||
Ver = 0x0030, /* Version */
|
||||
Tp = 0x0080, /* Task Priority */
|
||||
Ap = 0x0090, /* Arbitration Priority */
|
||||
Pp = 0x00a0, /* Processor Priority */
|
||||
Eoi = 0x00b0, /* EOI */
|
||||
Ld = 0x00d0, /* Logical Destination */
|
||||
Df = 0x00e0, /* Destination Format */
|
||||
Siv = 0x00f0, /* Spurious Interrupt Vector */
|
||||
Is = 0x0100, /* Interrupt Status (8) */
|
||||
Tm = 0x0180, /* Trigger Mode (8) */
|
||||
Ir = 0x0200, /* Interrupt Request (8) */
|
||||
Es = 0x0280, /* Error Status */
|
||||
Iclo = 0x0300, /* Interrupt Command */
|
||||
Ichi = 0x0310, /* Interrupt Command [63:32] */
|
||||
Lvt0 = 0x0320, /* Local Vector Table 0 */
|
||||
Lvt5 = 0x0330, /* Local Vector Table 5 */
|
||||
Lvt4 = 0x0340, /* Local Vector Table 4 */
|
||||
Lvt1 = 0x0350, /* Local Vector Table 1 */
|
||||
Lvt2 = 0x0360, /* Local Vector Table 2 */
|
||||
Lvt3 = 0x0370, /* Local Vector Table 3 */
|
||||
Tic = 0x0380, /* Timer Initial Count */
|
||||
Tcc = 0x0390, /* Timer Current Count */
|
||||
Tdc = 0x03e0, /* Timer Divide Configuration */
|
||||
|
||||
Tlvt = Lvt0, /* Timer */
|
||||
Lint0 = Lvt1, /* Local Interrupt 0 */
|
||||
Lint1 = Lvt2, /* Local Interrupt 1 */
|
||||
Elvt = Lvt3, /* Error */
|
||||
Pclvt = Lvt4, /* Performance Counter */
|
||||
Tslvt = Lvt5, /* Thermal Sensor */
|
||||
enum { /* Local APIC registers */
|
||||
LapicID = 0x0020, /* ID */
|
||||
LapicVER = 0x0030, /* Version */
|
||||
LapicTPR = 0x0080, /* Task Priority */
|
||||
LapicAPR = 0x0090, /* Arbitration Priority */
|
||||
LapicPPR = 0x00A0, /* Processor Priority */
|
||||
LapicEOI = 0x00B0, /* EOI */
|
||||
LapicLDR = 0x00D0, /* Logical Destination */
|
||||
LapicDFR = 0x00E0, /* Destination Format */
|
||||
LapicSVR = 0x00F0, /* Spurious Interrupt Vector */
|
||||
LapicISR = 0x0100, /* Interrupt Status (8 registers) */
|
||||
LapicTMR = 0x0180, /* Trigger Mode (8 registers) */
|
||||
LapicIRR = 0x0200, /* Interrupt Request (8 registers) */
|
||||
LapicESR = 0x0280, /* Error Status */
|
||||
LapicICRLO = 0x0300, /* Interrupt Command */
|
||||
LapicICRHI = 0x0310, /* Interrupt Command [63:32] */
|
||||
LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */
|
||||
LapicPCINT = 0x0340, /* Performance Counter LVT */
|
||||
LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */
|
||||
LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */
|
||||
LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */
|
||||
LapicTICR = 0x0380, /* Timer Initial Count */
|
||||
LapicTCCR = 0x0390, /* Timer Current Count */
|
||||
LapicTDCR = 0x03E0, /* Timer Divide Configuration */
|
||||
};
|
||||
|
||||
enum { /* Siv */
|
||||
Swen = 0x00000100, /* Software Enable */
|
||||
Fdis = 0x00000200, /* Focus Disable */
|
||||
enum { /* LapicSVR */
|
||||
LapicENABLE = 0x00000100, /* Unit Enable */
|
||||
LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */
|
||||
};
|
||||
|
||||
enum { /* Iclo */
|
||||
Lassert = 0x00004000, /* Assert level */
|
||||
enum { /* LapicICRLO */
|
||||
/* [14] IPI Trigger Mode Level (RW) */
|
||||
LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */
|
||||
LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */
|
||||
|
||||
DSnone = 0x00000000, /* Use Destination Field */
|
||||
DSself = 0x00040000, /* Self is only destination */
|
||||
DSallinc = 0x00080000, /* All including self */
|
||||
DSallexc = 0x000c0000, /* All Excluding self */
|
||||
/* [17:16] Remote Read Status */
|
||||
LapicINVALID = 0x00000000, /* Invalid */
|
||||
LapicWAIT = 0x00010000, /* In-Progress */
|
||||
LapicVALID = 0x00020000, /* Valid */
|
||||
|
||||
/* [19:18] Destination Shorthand */
|
||||
LapicFIELD = 0x00000000, /* No shorthand */
|
||||
LapicSELF = 0x00040000, /* Self is single destination */
|
||||
LapicALLINC = 0x00080000, /* All including self */
|
||||
LapicALLEXC = 0x000C0000, /* All Excluding self */
|
||||
};
|
||||
|
||||
enum { /* Tlvt */
|
||||
Periodic = 0x00020000, /* Periodic Timer Mode */
|
||||
enum { /* LapicESR */
|
||||
LapicSENDCS = 0x00000001, /* Send CS Error */
|
||||
LapicRCVCS = 0x00000002, /* Receive CS Error */
|
||||
LapicSENDACCEPT = 0x00000004, /* Send Accept Error */
|
||||
LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */
|
||||
LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */
|
||||
LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */
|
||||
LapicREGISTER = 0x00000080, /* Illegal Register Address */
|
||||
};
|
||||
|
||||
enum { /* Tdc */
|
||||
DivX2 = 0x00000000, /* Divide by 2 */
|
||||
DivX4 = 0x00000001, /* Divide by 4 */
|
||||
DivX8 = 0x00000002, /* Divide by 8 */
|
||||
DivX16 = 0x00000003, /* Divide by 16 */
|
||||
DivX32 = 0x00000008, /* Divide by 32 */
|
||||
DivX64 = 0x00000009, /* Divide by 64 */
|
||||
DivX128 = 0x0000000a, /* Divide by 128 */
|
||||
DivX1 = 0x0000000b, /* Divide by 1 */
|
||||
enum { /* LapicTIMER */
|
||||
/* [17] Timer Mode (RW) */
|
||||
LapicONESHOT = 0x00000000, /* One-shot */
|
||||
LapicPERIODIC = 0x00020000, /* Periodic */
|
||||
|
||||
/* [19:18] Timer Base (RW) */
|
||||
LapicCLKIN = 0x00000000, /* use CLKIN as input */
|
||||
LapicTMBASE = 0x00040000, /* use TMBASE */
|
||||
LapicDIVIDER = 0x00080000, /* use output of the divider */
|
||||
};
|
||||
|
||||
static uint8_t lapictdxtab[] = { /* LapicTDCR */
|
||||
0x0B, /* divide by 1 */
|
||||
0x00, /* divide by 2 */
|
||||
0x01, /* divide by 4 */
|
||||
0x02, /* divide by 8 */
|
||||
0x03, /* divide by 16 */
|
||||
0x08, /* divide by 32 */
|
||||
0x09, /* divide by 64 */
|
||||
0x0A, /* divide by 128 */
|
||||
};
|
||||
|
||||
static uint32_t* lapicbase;
|
||||
|
||||
static Lapic xlapic[Napic];
|
||||
|
||||
Lapic*
|
||||
lapiclookup(uint32_t id)
|
||||
typedef struct Apictimer Apictimer;
|
||||
struct Apictimer
|
||||
{
|
||||
Lapic *a;
|
||||
uint64_t hz;
|
||||
uint32_t max;
|
||||
uint32_t min;
|
||||
uint32_t div;
|
||||
int tdx;
|
||||
};
|
||||
|
||||
if(id >= nelem(xlapic))
|
||||
return nil;
|
||||
a = xlapic + id;
|
||||
if(a->useable)
|
||||
return a;
|
||||
return nil;
|
||||
}
|
||||
static Apictimer lapictimer[MACHMAX];
|
||||
|
||||
static uint32_t
|
||||
lapicrget(int r)
|
||||
lapicr(int r)
|
||||
{
|
||||
return lapicbase[r/4];
|
||||
return *(lapicbase+(r/sizeof(*lapicbase)));
|
||||
}
|
||||
|
||||
static void
|
||||
lapicrput(int r, uint32_t data)
|
||||
lapicw(int r, uint32_t data)
|
||||
{
|
||||
if(lapicbase != nil){
|
||||
/* early panics can occur with lapicbase uninitialized
|
||||
* but if we let it fault, we loose the actual panic PC
|
||||
*/
|
||||
lapicbase[r/4] = data;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
lapiceoi(int vecno)
|
||||
{
|
||||
lapicrput(Eoi, 0);
|
||||
return vecno;
|
||||
}
|
||||
|
||||
int
|
||||
lapicisr(int vecno)
|
||||
{
|
||||
int isr;
|
||||
|
||||
isr = lapicrget(Is + (vecno/32)*16);
|
||||
|
||||
return isr & (1<<(vecno%32));
|
||||
}
|
||||
|
||||
static char*
|
||||
lapicprint(char *p, char *e, Lapic *a, int i)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = "proc";
|
||||
p = jehanne_seprint(p, e, "%-8s ", s);
|
||||
p = jehanne_seprint(p, e, "%8ux ", i);
|
||||
// p = jehanne_seprint(p, e, "%.8ux ", a->dest);
|
||||
// p = jehanne_seprint(p, e, "%.8ux ", a->mask);
|
||||
// p = jehanne_seprint(p, e, "%c", a->flags & PcmpBP? 'b': ' ');
|
||||
// p = jehanne_seprint(p, e, "%c ", a->flags & PcmpEN? 'e': ' ');
|
||||
// p = jehanne_seprint(p, e, "%8ux %8ux", a->lintr[0], a->lintr[1]);
|
||||
p = jehanne_seprint(p, e, "%12d\n", a->machno);
|
||||
return p;
|
||||
}
|
||||
|
||||
static long
|
||||
lapicread(Chan* _1, void *a, long n, int64_t off)
|
||||
{
|
||||
char *s, *e, *p;
|
||||
long i, r;
|
||||
|
||||
s = jehanne_malloc(READSTR);
|
||||
e = s+READSTR;
|
||||
p = s;
|
||||
|
||||
for(i = 0; i < nelem(xlapic); i++)
|
||||
if(xlapic[i].useable)
|
||||
p = lapicprint(p, e, xlapic + i, i);
|
||||
r = -1;
|
||||
if(!waserror()){
|
||||
r = readstr(off, a, n, s);
|
||||
poperror();
|
||||
}
|
||||
jehanne_free(s);
|
||||
return r;
|
||||
*(lapicbase+(r/sizeof(*lapicbase))) = data;
|
||||
data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
|
||||
USED(data);
|
||||
}
|
||||
|
||||
void
|
||||
lapicinit(int lapicno, uintmem pa, int isbp)
|
||||
{
|
||||
Lapic *apic;
|
||||
|
||||
/*
|
||||
* Mark the LAPIC useable if it has a good ID, and the registers can
|
||||
* be mapped. There is x2LAPIC to be dealt with at some point.
|
||||
*/
|
||||
DBG("lapicinit: lapicno %d pa %#P isbp %d caller %#p\n", lapicno, pa, isbp, getcallerpc());
|
||||
addarchfile("lapic", 0444, lapicread, nil);
|
||||
|
||||
if(lapicno >= Napic){
|
||||
panic("lapicinit%d: out of range", lapicno);
|
||||
return;
|
||||
}
|
||||
if((apic = &xlapic[lapicno])->useable){
|
||||
jehanne_print("lapicinit%d: already initialised\n", lapicno);
|
||||
return;
|
||||
}
|
||||
if(lapicbase == nil){
|
||||
//adrmapck(pa, 1024, Ammio, Mfree, Cnone);
|
||||
if((lapicbase = vmap(pa, 1024)) == nil){
|
||||
panic("lapicinit%d: can't map lapicbase %#P", lapicno, pa);
|
||||
return;
|
||||
}
|
||||
DBG("lapicinit%d: lapicbase %#P -> %#p\n", lapicno, pa, lapicbase);
|
||||
}
|
||||
apic->useable = 1;
|
||||
|
||||
/*
|
||||
* Assign a machno to the processor associated with this
|
||||
* LAPIC, it may not be an identity map.
|
||||
* Machno 0 is always the bootstrap processor.
|
||||
*/
|
||||
if(isbp){
|
||||
apic->machno = 0;
|
||||
m->apicno = lapicno;
|
||||
}
|
||||
else
|
||||
apic->machno = sys->nmach++;
|
||||
}
|
||||
|
||||
void
|
||||
lapicsetdom(int lapicno, int dom)
|
||||
{
|
||||
Lapic *apic;
|
||||
|
||||
DBG("lapic%d: setdom: %d\n", lapicno, dom);
|
||||
if(lapicno >= Napic){
|
||||
panic("lapic%d: lapicsetdom: apic out of range", lapicno);
|
||||
return;
|
||||
}
|
||||
if((apic = &xlapic[lapicno])->useable)
|
||||
apic->dom = dom;
|
||||
else
|
||||
jehanne_print("lapic%d: lapicsetdom: apic not usable\n", lapicno);
|
||||
}
|
||||
|
||||
int
|
||||
machdom(Mach *mp)
|
||||
{
|
||||
return xlapic[mp->apicno].dom;
|
||||
}
|
||||
|
||||
static void
|
||||
lapicdump0(Lapic *apic, int i)
|
||||
{
|
||||
if(!apic->useable)
|
||||
return;
|
||||
DBG("lapic%d: machno %d lint0 %#8.8ux lint1 %#8.8ux\n",
|
||||
i, apic->machno, apic->lvt[0], apic->lvt[1]);
|
||||
DBG(" tslvt %#8.8ux pclvt %#8.8ux elvt %#8.8ux\n",
|
||||
lapicrget(Tslvt), lapicrget(Pclvt), lapicrget(Elvt));
|
||||
DBG(" tlvt %#8.8ux lint0 %#8.8ux lint1 %#8.8ux siv %#8.8ux\n",
|
||||
lapicrget(Tlvt), lapicrget(Lint0),
|
||||
lapicrget(Lint1), lapicrget(Siv));
|
||||
}
|
||||
|
||||
void
|
||||
lapicdump(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(!DBGFLG)
|
||||
return;
|
||||
|
||||
DBG("lapicbase %#p\n", lapicbase);
|
||||
for(i = 0; i < Napic; i++)
|
||||
lapicdump0(xlapic + i, i);
|
||||
}
|
||||
|
||||
int
|
||||
lapiconline(void)
|
||||
{
|
||||
Lapic *apic;
|
||||
uint64_t tsc;
|
||||
uint32_t dfr, ver;
|
||||
int apicno, nlvt;
|
||||
Apictimer *a;
|
||||
|
||||
if(lapicbase == nil)
|
||||
panic("lapiconline: no lapic base");
|
||||
|
||||
if((apicno = ((lapicrget(Id)>>24) & 0xff)) >= Napic)
|
||||
panic("lapic: id too large %d", apicno);
|
||||
if(apicno != m->apicno){
|
||||
panic("lapic: %d != %d", m->apicno, apicno);
|
||||
dfr = lapicrget(Id) & ~(0xff<<24);
|
||||
dfr |= m->apicno<<24;
|
||||
lapicrput(Id, dfr);
|
||||
apicno = m->apicno;
|
||||
}
|
||||
apic = &xlapic[apicno];
|
||||
if(!apic->useable)
|
||||
panic("lapiconline: lapic%d: unusable %d", apicno, apic->useable);
|
||||
a = &lapictimer[m->machno];
|
||||
|
||||
/*
|
||||
* Things that can only be done when on the processor
|
||||
* owning the APIC, apicinit above runs on the bootstrap
|
||||
* processor.
|
||||
* Reload the timer to de-synchronise the processors,
|
||||
* then lower the task priority to allow interrupts to be
|
||||
* accepted by the APIC.
|
||||
*/
|
||||
ver = lapicrget(Ver);
|
||||
nlvt = ((ver>>16) & 0xff) + 1;
|
||||
if(nlvt > nelem(apic->lvt)){
|
||||
jehanne_print("lapiconline%d: nlvt %d > max (%d)\n",
|
||||
apicno, nlvt, nelem(apic->lvt));
|
||||
nlvt = nelem(apic->lvt);
|
||||
microdelay((TK2MS(1)*1000/sys->nmach) * m->machno);
|
||||
lapicw(LapicTICR, a->max);
|
||||
lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
|
||||
|
||||
/*
|
||||
* not strickly neccesary, but reported (osdev.org) to be
|
||||
* required for some machines.
|
||||
*/
|
||||
lapicw(LapicTDCR, lapictdxtab[a->tdx]);
|
||||
|
||||
lapicw(LapicTPR, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* use the i8253/tsc clock to figure out our lapic timer rate.
|
||||
*/
|
||||
static void
|
||||
lapictimerinit(void)
|
||||
{
|
||||
uint64_t x, v, hz;
|
||||
Apictimer *a;
|
||||
int s;
|
||||
|
||||
if(m->machno != 0){
|
||||
lapictimer[m->machno] = lapictimer[0];
|
||||
return;
|
||||
}
|
||||
apic->nlvt = nlvt;
|
||||
apic->ver = ver & 0xff;
|
||||
|
||||
s = splhi();
|
||||
a = &lapictimer[m->machno];
|
||||
a->tdx = 0;
|
||||
Retry:
|
||||
lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
|
||||
lapicw(LapicTDCR, lapictdxtab[a->tdx]);
|
||||
|
||||
x = fastticks(&hz);
|
||||
x += hz/10;
|
||||
lapicw(LapicTICR, 0xffffffff);
|
||||
do{
|
||||
v = fastticks(nil);
|
||||
}while(v < x);
|
||||
|
||||
v = (0xffffffffUL-lapicr(LapicTCCR))*10;
|
||||
if(v > hz-(hz/10)){
|
||||
if(v > hz+(hz/10) && a->tdx < nelem(lapictdxtab)-1){
|
||||
a->tdx++;
|
||||
goto Retry;
|
||||
}
|
||||
v = hz;
|
||||
}
|
||||
|
||||
assert(v >= (100*HZ));
|
||||
|
||||
a->hz = v;
|
||||
a->div = hz/a->hz;
|
||||
a->max = a->hz/HZ;
|
||||
a->min = a->hz/(100*HZ);
|
||||
|
||||
splx(s);
|
||||
|
||||
v = (v+500000LL)/1000000LL;
|
||||
print("cpu%d: lapic clock at %lludMHz\n", m->machno, v);
|
||||
}
|
||||
|
||||
void
|
||||
lapicinit(Apic* apic)
|
||||
{
|
||||
uint32_t dfr, ldr, lvt;
|
||||
|
||||
if(lapicbase == 0)
|
||||
lapicbase = apic->addr;
|
||||
|
||||
/*
|
||||
* These don't really matter in Physical mode;
|
||||
* set the defaults anyway.
|
||||
*/
|
||||
// if(jehanne_memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
|
||||
// dfr = 0xf0000000;
|
||||
// else
|
||||
if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
|
||||
dfr = 0xf0000000;
|
||||
else
|
||||
dfr = 0xffffffff;
|
||||
lapicrput(Df, dfr);
|
||||
lapicrput(Ld, 0x00000000);
|
||||
ldr = 0x00000000;
|
||||
|
||||
lapicw(LapicDFR, dfr);
|
||||
lapicw(LapicLDR, ldr);
|
||||
lapicw(LapicTPR, 0xff);
|
||||
lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
|
||||
|
||||
lapictimerinit();
|
||||
|
||||
/*
|
||||
* Disable interrupts until ready by setting the Task Priority
|
||||
* register to 0xff.
|
||||
* Some Pentium revisions have a bug whereby spurious
|
||||
* interrupts are generated in the through-local mode.
|
||||
*/
|
||||
lapicrput(Tp, 0xff);
|
||||
|
||||
/*
|
||||
* Software-enable the APIC in the Spurious Interrupt Vector
|
||||
* register and set the vector number. The vector number must have
|
||||
* bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
|
||||
* is set in the HyperTransport Transaction Control register.
|
||||
*/
|
||||
lapicrput(Siv, Swen|IdtSPURIOUS);
|
||||
|
||||
/*
|
||||
* Acknowledge any outstanding interrupts.
|
||||
*/
|
||||
lapicrput(Eoi, 0);
|
||||
|
||||
/*
|
||||
* Use the TSC to determine the lapic timer frequency.
|
||||
* It might be possible to snarf this from a chipset
|
||||
* register instead.
|
||||
*/
|
||||
lapicrput(Tdc, DivX1);
|
||||
lapicrput(Tlvt, Im);
|
||||
tsc = rdtsc() + m->cpuhz/10;
|
||||
lapicrput(Tic, 0xffffffff);
|
||||
|
||||
while(rdtsc() < tsc)
|
||||
;
|
||||
|
||||
apic->hz = (0xffffffff-lapicrget(Tcc))*10;
|
||||
apic->max = apic->hz/HZ;
|
||||
apic->min = apic->hz/(100*HZ);
|
||||
apic->div = ((m->cpuhz/apic->max)+HZ/2)/HZ;
|
||||
|
||||
if(m->machno == 0 || DBGFLG){
|
||||
jehanne_print("lapic%d: hz %lld max %lld min %lld div %lld\n", apicno,
|
||||
apic->hz, apic->max, apic->min, apic->div);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mask interrupts on Performance Counter overflow and
|
||||
* Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
|
||||
* and Lintr1 (Legacy NMI).
|
||||
* Clear any Error Status (write followed by read) and enable
|
||||
* the Error interrupt.
|
||||
*/
|
||||
switch(apic->nlvt){
|
||||
case 7:
|
||||
case 6:
|
||||
lapicrput(Tslvt, Im);
|
||||
/*FALLTHROUGH*/
|
||||
case 5:
|
||||
lapicrput(Pclvt, Im);
|
||||
/*FALLTHROUGH*/
|
||||
default:
|
||||
switch(m->cpuidax & 0xFFF){
|
||||
case 0x526: /* stepping cB1 */
|
||||
case 0x52B: /* stepping E0 */
|
||||
case 0x52C: /* stepping cC0 */
|
||||
wrmsr(0x0E, 1<<14); /* TR12 */
|
||||
break;
|
||||
}
|
||||
lapicrput(Lint1, apic->lvt[1]|Im|IdtLINT1);
|
||||
lapicrput(Lint0, apic->lvt[0]|Im|IdtLINT0);
|
||||
|
||||
lapicrput(Es, 0);
|
||||
lapicrget(Es);
|
||||
lapicrput(Elvt, IdtERROR);
|
||||
|
||||
/*
|
||||
* Reload the timer to de-synchronise the processors.
|
||||
* When the caller is ready for the APIC to accept interrupts,
|
||||
* it should call lapicpri to lower the task priority.
|
||||
*
|
||||
* The timer is enabled later by the core-specific startup
|
||||
* i.e. don't start the timer unless the core needs it,
|
||||
* to reduce the likelihood of at least one (spurious) interrupt
|
||||
* from the timer when priority is lowered.
|
||||
* Set the local interrupts. It's likely these should just be
|
||||
* masked off for SMP mode as some Pentium Pros have problems if
|
||||
* LINT[01] are set to ExtINT.
|
||||
* Acknowledge any outstanding interrupts.
|
||||
lapicw(LapicLINT0, apic->lintr[0]);
|
||||
lapicw(LapicLINT1, apic->lintr[1]);
|
||||
*/
|
||||
microdelay((TK2MS(1)*1000/sys->nmach) * m->machno);
|
||||
lapicrput(Tic, apic->max);
|
||||
return 1;
|
||||
}
|
||||
lapiceoi(0);
|
||||
|
||||
lvt = (lapicr(LapicVER)>>16) & 0xFF;
|
||||
if(lvt >= 4)
|
||||
lapicw(LapicPCINT, ApicIMASK);
|
||||
lapicw(LapicERROR, VectorPIC+IrqERROR);
|
||||
lapicw(LapicESR, 0);
|
||||
lapicr(LapicESR);
|
||||
|
||||
void
|
||||
lapictimerenable(void)
|
||||
{
|
||||
/*
|
||||
* Perhaps apictimerenable/apictimerdisable should just
|
||||
* clear/set Im in the existing settings of Tlvt, there may
|
||||
* be a time when the timer is used in a different mode;
|
||||
* if so will need to ensure the mode is set when the timer
|
||||
* is initialised.
|
||||
* Issue an INIT Level De-Assert to synchronise arbitration ID's.
|
||||
*/
|
||||
lapicw(LapicICRHI, 0);
|
||||
lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
|
||||
while(lapicr(LapicICRLO) & ApicDELIVS)
|
||||
;
|
||||
|
||||
/*
|
||||
* Do not allow acceptance of interrupts until all initialisation
|
||||
* for this processor is done. For the bootstrap processor this can be
|
||||
* early duing initialisation. For the application processors this should
|
||||
* be after the bootstrap processor has lowered priority and is accepting
|
||||
* interrupts.
|
||||
lapicw(LapicTPR, 0);
|
||||
*/
|
||||
lapicrput(Tlvt, Periodic|IdtTIMER);
|
||||
}
|
||||
|
||||
void
|
||||
lapictimerdisable(void)
|
||||
{
|
||||
lapicrput(Tlvt, Im|IdtTIMER);
|
||||
}
|
||||
|
||||
void
|
||||
lapictimerset(uint64_t next)
|
||||
{
|
||||
Lapic *apic;
|
||||
int64_t period;
|
||||
|
||||
apic = &xlapic[(lapicrget(Id)>>24) & 0xff];
|
||||
|
||||
ilock(&m->apictimerlock);
|
||||
|
||||
period = apic->max;
|
||||
if(next != 0){
|
||||
if(apic->div == 0){
|
||||
jehanne_print("lapictimerset: apic not ready, wait for gdb\n");
|
||||
waitdebugger();
|
||||
}
|
||||
period = next - fastticks(nil); /* fastticks is just rdtsc() */
|
||||
period /= apic->div;
|
||||
|
||||
if(period < apic->min)
|
||||
period = apic->min;
|
||||
else if(period > apic->max - apic->min)
|
||||
period = apic->max;
|
||||
}
|
||||
lapicrput(Tic, period);
|
||||
|
||||
iunlock(&m->apictimerlock);
|
||||
}
|
||||
|
||||
void
|
||||
lapicsipi(int lapicno, uintmem pa)
|
||||
lapicstartap(Apic* apic, uintptr_t pa)
|
||||
{
|
||||
int i;
|
||||
uint32_t crhi, crlo;
|
||||
|
||||
/*
|
||||
* SIPI - Start-up IPI.
|
||||
* To do: checks on lapic validity.
|
||||
*/
|
||||
crhi = lapicno<<24;
|
||||
lapicrput(Ichi, crhi);
|
||||
lapicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
|
||||
/* make apic's processor do a warm reset */
|
||||
crhi = apic->apicno<<24;
|
||||
lapicw(LapicICRHI, crhi);
|
||||
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
|
||||
microdelay(200);
|
||||
lapicrput(Iclo, DSnone|TMlevel|MTir);
|
||||
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
|
||||
delay(10);
|
||||
|
||||
crlo = DSnone|TMedge|MTsipi|((uint32_t)pa/(4*KiB));
|
||||
/* assumes apic is not an 82489dx */
|
||||
crlo = LapicFIELD|ApicEDGE|ApicSTARTUP|((uint32_t)pa/BY2PG);
|
||||
for(i = 0; i < 2; i++){
|
||||
lapicrput(Ichi, crhi);
|
||||
lapicrput(Iclo, crlo);
|
||||
lapicw(LapicICRHI, crhi);
|
||||
/* make apic's processor start at v in real mode */
|
||||
lapicw(LapicICRLO, crlo);
|
||||
microdelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lapicipi(int lapicno)
|
||||
lapicerror(Ureg* _, void* __)
|
||||
{
|
||||
lapicrput(Ichi, lapicno<<24);
|
||||
lapicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
|
||||
while(lapicrget(Iclo) & Ds)
|
||||
;
|
||||
uint32_t esr;
|
||||
|
||||
lapicw(LapicESR, 0);
|
||||
esr = lapicr(LapicESR);
|
||||
switch(m->cpuidax & 0xFFF){
|
||||
case 0x526: /* stepping cB1 */
|
||||
case 0x52B: /* stepping E0 */
|
||||
case 0x52C: /* stepping cC0 */
|
||||
return;
|
||||
}
|
||||
print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
|
||||
}
|
||||
|
||||
void
|
||||
lapicpri(int pri)
|
||||
lapicspurious(Ureg* _, void* __)
|
||||
{
|
||||
lapicrput(Tp, pri);
|
||||
print("cpu%d: lapicspurious\n", m->machno);
|
||||
}
|
||||
|
||||
int
|
||||
lapicisr(int v)
|
||||
{
|
||||
uint32_t isr;
|
||||
|
||||
isr = lapicr(LapicISR + (v/32));
|
||||
|
||||
return isr & (1<<(v%32));
|
||||
}
|
||||
|
||||
int
|
||||
lapiceoi(int v)
|
||||
{
|
||||
lapicw(LapicEOI, 0);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void
|
||||
lapicicrw(uint32_t hi, uint32_t lo)
|
||||
{
|
||||
lapicw(LapicICRHI, hi);
|
||||
lapicw(LapicICRLO, lo);
|
||||
}
|
||||
|
||||
void
|
||||
ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
|
||||
{
|
||||
uint32_t *iowin;
|
||||
|
||||
iowin = apic->addr+(0x10/sizeof(uint32_t));
|
||||
sel = IoapicRDT + 2*sel;
|
||||
|
||||
lock(apic);
|
||||
*apic->addr = sel+1;
|
||||
if(hi)
|
||||
*hi = *iowin;
|
||||
*apic->addr = sel;
|
||||
if(lo)
|
||||
*lo = *iowin;
|
||||
unlock(apic);
|
||||
}
|
||||
|
||||
void
|
||||
ioapicrdtw(Apic* apic, int sel, int hi, int lo)
|
||||
{
|
||||
uint32_t *iowin;
|
||||
|
||||
iowin = apic->addr+(0x10/sizeof(uint32_t));
|
||||
sel = IoapicRDT + 2*sel;
|
||||
|
||||
lock(apic);
|
||||
*apic->addr = sel+1;
|
||||
*iowin = hi;
|
||||
*apic->addr = sel;
|
||||
*iowin = lo;
|
||||
unlock(apic);
|
||||
}
|
||||
|
||||
void
|
||||
ioapicinit(Apic* apic, int apicno)
|
||||
{
|
||||
int hi, lo, v;
|
||||
uint32_t *iowin;
|
||||
|
||||
/*
|
||||
* Initialise the I/O APIC.
|
||||
* The MultiProcessor Specification says it is the responsibility
|
||||
* of the O/S to set the APIC id.
|
||||
* Make sure interrupts are all masked off for now.
|
||||
*/
|
||||
iowin = apic->addr+(0x10/sizeof(uint32_t));
|
||||
lock(apic);
|
||||
*apic->addr = IoapicVER;
|
||||
apic->mre = (*iowin>>16) & 0xFF;
|
||||
|
||||
*apic->addr = IoapicID;
|
||||
*iowin = apicno<<24;
|
||||
unlock(apic);
|
||||
|
||||
hi = 0;
|
||||
lo = ApicIMASK;
|
||||
for(v = 0; v <= apic->mre; v++)
|
||||
ioapicrdtw(apic, v, hi, lo);
|
||||
}
|
||||
|
||||
void
|
||||
lapictimerset(uint64_t next)
|
||||
{
|
||||
int64_t period;
|
||||
Apictimer *a;
|
||||
|
||||
a = &lapictimer[m->machno];
|
||||
period = next - fastticks(nil);
|
||||
period /= a->div;
|
||||
if(period < a->min)
|
||||
period = a->min;
|
||||
else if(period > a->max - a->min)
|
||||
period = a->max;
|
||||
lapicw(LapicTICR, period);
|
||||
}
|
||||
|
||||
void
|
||||
lapicclock(Ureg *u, void* _)
|
||||
{
|
||||
/*
|
||||
* since the MTRR updates need to be synchronized across processors,
|
||||
* we want to do this within the clock tick.
|
||||
*/
|
||||
mtrrclock();
|
||||
timerintr(u, 0);
|
||||
}
|
||||
|
||||
void
|
||||
lapicintron(void)
|
||||
{
|
||||
lapicw(LapicTPR, 0);
|
||||
}
|
||||
|
||||
void
|
||||
lapicintroff(void)
|
||||
{
|
||||
lapicw(LapicTPR, 0xFF);
|
||||
}
|
||||
|
||||
void
|
||||
lapicnmienable(void)
|
||||
{
|
||||
lapicw(LapicPCINT, ApicNMI);
|
||||
}
|
||||
|
||||
void
|
||||
lapicnmidisable(void)
|
||||
{
|
||||
lapicw(LapicPCINT, ApicIMASK);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
"Include": [
|
||||
"core.json",
|
||||
"devdraw.json",
|
||||
"../386/include.json",
|
||||
"../ip/include.json",
|
||||
"../port/include.json"
|
||||
],
|
||||
@ -18,7 +17,6 @@
|
||||
"int printallsyscalls;"
|
||||
],
|
||||
"Dev": [
|
||||
"acpi",
|
||||
"arch",
|
||||
"bridge",
|
||||
"cap",
|
||||
@ -85,7 +83,6 @@
|
||||
"SourceFiles": [
|
||||
"autogenerated.c",
|
||||
"cga.c",
|
||||
"devacpi.c",
|
||||
"usbehcipc.c",
|
||||
"usbohci.c",
|
||||
"usbuhci.c"
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,19 +20,16 @@
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "ureg.h"
|
||||
#include "pool.h"
|
||||
|
||||
#include "io.h"
|
||||
#include "apic.h"
|
||||
|
||||
extern void confoptions(void); /* XXX - must go */
|
||||
extern void confsetenv(void); /* XXX - must go */
|
||||
|
||||
static uintptr_t sp; /* XXX - must go - user stack of init proc */
|
||||
|
||||
Sys* sys = (Sys*)0x1; /* dummy value to put it in data section
|
||||
* - entry.S set it correctly
|
||||
* - main memset to zero the bss section
|
||||
*/
|
||||
Sys system;
|
||||
Sys* sys;
|
||||
usize sizeofSys = sizeof(Sys);
|
||||
|
||||
/*
|
||||
@ -42,7 +39,7 @@ usize sizeofSys = sizeof(Sys);
|
||||
* set it all up.
|
||||
*/
|
||||
static int64_t oargc;
|
||||
static char* oargv[20];
|
||||
static char* oargv[64];
|
||||
static char oargb[1024];
|
||||
static int oargblen;
|
||||
|
||||
@ -51,7 +48,6 @@ static int numtcs = 32; /* initial # of TCs */
|
||||
IOConf ioconf;
|
||||
int procmax;
|
||||
|
||||
char *cputype = "amd64";
|
||||
char dbgflg[256];
|
||||
static int vflag = 1;
|
||||
|
||||
@ -63,6 +59,30 @@ optionsinit(char* s)
|
||||
oargv[oargc] = nil;
|
||||
}
|
||||
|
||||
char*
|
||||
getconf(char *name)
|
||||
{
|
||||
int i;
|
||||
char *a;
|
||||
|
||||
for(i = 0; i < oargc; i++){
|
||||
a = strstr(oargv[i], name);
|
||||
if(a == oargv[i]){
|
||||
a += strlen(name);
|
||||
if(a[0] == '=')
|
||||
return ++a;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
int
|
||||
isaconfig(char *class, int ctlrno, ISAConf *isa)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
options(int argc, char* argv[])
|
||||
{
|
||||
@ -129,251 +149,204 @@ loadenv(int argc, char* argv[])
|
||||
}
|
||||
|
||||
void
|
||||
squidboy(int apicno)
|
||||
initialize_processor(void)
|
||||
{
|
||||
int64_t hz;
|
||||
int machno;
|
||||
Segdesc *gdt;
|
||||
uintptr_t *pml4;
|
||||
|
||||
sys->machptr[m->machno] = m;
|
||||
|
||||
/*
|
||||
* Need something for initial delays
|
||||
* until a timebase is worked out.
|
||||
*/
|
||||
m->cpuhz = sys->machptr[0]->cpuhz;
|
||||
m->cyclefreq = m->cpuhz;
|
||||
m->cpumhz = sys->machptr[0]->cpumhz;
|
||||
machno = m->machno;
|
||||
pml4 = m->pml4;
|
||||
gdt = m->gdt;
|
||||
memset(m, 0, sizeof(Mach));
|
||||
m->machno = machno;
|
||||
m->pml4 = pml4;
|
||||
m->gdt = gdt;
|
||||
m->perf.period = 1;
|
||||
|
||||
DBG("Hello Squidboy %d %d\n", apicno, m->machno);
|
||||
|
||||
vsvminit(MACHSTKSZ);
|
||||
|
||||
/*
|
||||
* Beware the Curse of The Non-Interruptable Were-Temporary.
|
||||
* For polled uart output at boot, need
|
||||
* a default delay constant. 100000 should
|
||||
* be enough for a while. Cpuidentify will
|
||||
* calculate the real value later.
|
||||
*/
|
||||
hz = archhz();
|
||||
if(hz == 0)
|
||||
ndnr();
|
||||
m->cpuhz = hz;
|
||||
m->cpumhz = hz/1000000ll;
|
||||
|
||||
archenable();
|
||||
|
||||
mmuinit();
|
||||
if(!lapiconline())
|
||||
ndnr();
|
||||
|
||||
fpuinit();
|
||||
|
||||
/*
|
||||
* Handshake with sipi to let it
|
||||
* know the Startup IPI succeeded.
|
||||
*/
|
||||
m->splpc = 0;
|
||||
|
||||
/*
|
||||
* Handshake with main to proceed with initialisation.
|
||||
*/
|
||||
while(sys->epoch == 0)
|
||||
;
|
||||
wrmsr(0x10, sys->epoch);
|
||||
m->rdtsc = rdtsc();
|
||||
|
||||
DBG("mach %d is go %#p %#p %3p\n", m->machno, m, m->pml4->pte, &apicno);
|
||||
|
||||
timersinit();
|
||||
|
||||
/*
|
||||
* Cannot allow interrupts while waiting for online.
|
||||
* However, by taking the lowering of the APIC task priority
|
||||
* out of apiconline something could be done here with
|
||||
* MONITOR/MWAIT perhaps to drop the energy used by the
|
||||
* idle core.
|
||||
*/
|
||||
while(!m->online)
|
||||
pause();
|
||||
lapictimerenable();
|
||||
lapicpri(0);
|
||||
|
||||
jehanne_print("mach%d: online color %d\n", m->machno, m->color);
|
||||
schedinit();
|
||||
|
||||
panic("squidboy returns (type %d)", m->mode);
|
||||
m->loopconst = 100000;
|
||||
}
|
||||
|
||||
#define D(c) if(0)outb(0x3f8, (c))
|
||||
|
||||
void
|
||||
main(uint32_t ax, uint32_t bx)
|
||||
static void
|
||||
initialize_boot_processor(void)
|
||||
{
|
||||
int i;
|
||||
int64_t hz;
|
||||
char *p;
|
||||
system.nmach = 1;
|
||||
|
||||
jehanne_memset(edata, 0, end - edata);
|
||||
system.machptr[0] = m; /* m was set by entry.S */
|
||||
|
||||
/*
|
||||
* ilock via i8250enable via i8250console
|
||||
* needs m->machno, sys->machptr[] set, and
|
||||
* also 'up' set to nil.
|
||||
*/
|
||||
cgapost(sizeof(uintptr_t)*8);
|
||||
jehanne_memset(m, 0, sizeof(Mach));
|
||||
m->machno = 0;
|
||||
m->pml4 = (uint64_t*)CPU0PML4;
|
||||
m->gdt = (Segdesc*)CPU0GDT;
|
||||
|
||||
initialize_processor();
|
||||
|
||||
active.machs[0] = 1;
|
||||
m->online = 1;
|
||||
sys->machptr[m->machno] = &sys->mach;
|
||||
m->stack = PTR2UINT(sys->machstk);
|
||||
m->vsvm = sys->vsvmpage;
|
||||
sys->nmach = 1;
|
||||
sys->nonline = 1;
|
||||
sys->copymode = 0; /* copy on write */
|
||||
up = nil;
|
||||
|
||||
confoptions();
|
||||
asminit();
|
||||
multiboot(ax, bx, 0);
|
||||
options(oargc, oargv);
|
||||
p = getconf("*dbflags");
|
||||
if(p != nil){
|
||||
for(; *p != 0; p++)
|
||||
if(*p >= 'a' && *p <= 'z' || *p >= 'A' && *p <= 'Z')
|
||||
dbgflg[(uint8_t)*p] = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need something for initial delays
|
||||
* until a timebase is worked out.
|
||||
*/
|
||||
m->cpuhz = 2000000000ll;
|
||||
m->cpumhz = 2000;
|
||||
|
||||
cgainit();
|
||||
i8250console("0");
|
||||
consputs = cgaconsputs;
|
||||
|
||||
vsvminit(MACHSTKSZ);
|
||||
|
||||
active.exiting = 0;
|
||||
}
|
||||
|
||||
fmtinit();
|
||||
jehanne_print("\nJehanne Operating System\n");
|
||||
static void
|
||||
intialize_system(void)
|
||||
{
|
||||
extern Ureg _boot_registers;
|
||||
uintptr_t p;
|
||||
|
||||
if(vflag){
|
||||
jehanne_print("&ax = %#p, ax = %#ux, bx = %#ux\n", &ax, ax, bx);
|
||||
multiboot(ax, bx, vflag);
|
||||
}
|
||||
e820();
|
||||
p = (uintptr_t)&_boot_registers;
|
||||
p += KZERO;
|
||||
sys = &system;
|
||||
sys->boot_regs = (void*)p;
|
||||
sys->architecture = "amd64";
|
||||
}
|
||||
|
||||
m->perf.period = 1;
|
||||
if((hz = archhz()) != 0ll){
|
||||
m->cpuhz = hz;
|
||||
m->cpumhz = hz/1000000ll;
|
||||
static void
|
||||
configure_kernel(void)
|
||||
{
|
||||
char *p;
|
||||
int i, userpcnt;
|
||||
unsigned int kpages;
|
||||
|
||||
if(p = getconf("service")){
|
||||
if(strcmp(p, "cpu") == 0)
|
||||
cpuserver = 1;
|
||||
else if(strcmp(p,"terminal") == 0)
|
||||
cpuserver = 0;
|
||||
}
|
||||
|
||||
archenable();
|
||||
/* memory */
|
||||
if(p = getconf("*kernelpercent"))
|
||||
userpcnt = 100 - strtol(p, 0, 0);
|
||||
else
|
||||
userpcnt = 0;
|
||||
|
||||
/*
|
||||
* Mmuinit before meminit because it
|
||||
* makes mappings and
|
||||
* flushes the TLB via m->pml4->pa.
|
||||
*/
|
||||
mmuinit();
|
||||
sys->npages = 0;
|
||||
for(i=0; i<nelem(sys->mem); i++)
|
||||
sys->npages += sys->mem[i].npage;
|
||||
|
||||
ioinit();
|
||||
keybinit();
|
||||
|
||||
meminit();
|
||||
archinit();
|
||||
physallocinit();
|
||||
D('a');
|
||||
mallocinit();
|
||||
D('b');
|
||||
memdebug();
|
||||
trapinit();
|
||||
D('c');
|
||||
|
||||
/*
|
||||
* Printinit will cause the first malloc
|
||||
* call to happen (printinit->qopen->malloc).
|
||||
* If the system dies here it's probably due
|
||||
* to malloc not being initialised
|
||||
* correctly, or the data segment is misaligned
|
||||
* (it's amazing how far you can get with
|
||||
* things like that completely broken).
|
||||
*/
|
||||
printinit();
|
||||
D('d');
|
||||
/*
|
||||
* This is necessary with GRUB and QEMU.
|
||||
* Without it an interrupt can occur at a weird vector,
|
||||
* because the vector base is likely different, causing
|
||||
* havoc. Do it before any APIC initialisation.
|
||||
*/
|
||||
i8259init(IdtPIC);
|
||||
D('e');
|
||||
|
||||
acpiinit(MACHMAX);
|
||||
D('f');
|
||||
// mpsinit();
|
||||
D('g');
|
||||
lapiconline();
|
||||
ioapiconline();
|
||||
D('h');
|
||||
intrenable(IdtTIMER, timerintr, 0, -1, "APIC timer");
|
||||
lapictimerenable();
|
||||
lapicpri(0);
|
||||
D('i');
|
||||
|
||||
timersinit();
|
||||
D('j');
|
||||
keybenable();
|
||||
mouseenable();
|
||||
D('k');
|
||||
fpuinit();
|
||||
D('l');
|
||||
/* processes */
|
||||
p = getconf("*procmax");
|
||||
if(p != nil)
|
||||
procmax = jehanne_strtoull(p, nil, 0);
|
||||
if(procmax == 0)
|
||||
procmax = 2000;
|
||||
psinit(procmax);
|
||||
D('m');
|
||||
// imagepool_init();
|
||||
D('n');
|
||||
links();
|
||||
D('o');
|
||||
devtabreset();
|
||||
D('p');
|
||||
umem_init();
|
||||
D('r');
|
||||
|
||||
userinit();
|
||||
D('s');
|
||||
if(!dbgflg['S'])
|
||||
sipi();
|
||||
D('t');
|
||||
|
||||
sys->epoch = rdtsc();
|
||||
wrmsr(0x10, sys->epoch);
|
||||
m->rdtsc = rdtsc();
|
||||
|
||||
D('u');
|
||||
/*
|
||||
* Release the hounds.
|
||||
*/
|
||||
for(i = 1; i < MACHMAX; i++){
|
||||
if(sys->machptr[i] == nil)
|
||||
continue;
|
||||
|
||||
ainc(&sys->nonline);
|
||||
|
||||
sys->machptr[i]->color = corecolor(i);
|
||||
if(sys->machptr[i]->color < 0)
|
||||
sys->machptr[i]->color = 0;
|
||||
sys->machptr[i]->online = 1;
|
||||
sys->nproc = jehanne_strtoull(p, nil, 0);
|
||||
if(sys->nproc == 0){
|
||||
sys->nproc = 100 + ((sys->npages*PGSZ)/MiB)*5;
|
||||
if(cpuserver)
|
||||
sys->nproc *= 3;
|
||||
}
|
||||
D('v');
|
||||
prflush();
|
||||
if(sys->nproc > 2046){
|
||||
/* devproc supports at most (2^11)-2 processes */
|
||||
sys->nproc = 2046;
|
||||
}
|
||||
sys->nimage = 256;
|
||||
|
||||
if(cpuserver) {
|
||||
if(userpcnt < 10)
|
||||
userpcnt = 70;
|
||||
kpages = sys->npages - (sys->npages*userpcnt)/100;
|
||||
sys->nimage = sys->nproc;
|
||||
} else {
|
||||
if(userpcnt < 10) {
|
||||
if(sys->npages*PGSZ < 16*MiB)
|
||||
userpcnt = 50;
|
||||
else
|
||||
userpcnt = 60;
|
||||
}
|
||||
kpages = sys->npages - (sys->npages*userpcnt)/100;
|
||||
|
||||
/*
|
||||
* Make sure terminals with low memory get at least
|
||||
* 4MB on the first Image chunk allocation.
|
||||
*/
|
||||
if(sys->npages*PGSZ < 16*MiB)
|
||||
imagmem->minarena = 4*MiB;
|
||||
}
|
||||
|
||||
/*
|
||||
* can't go past the end of virtual memory.
|
||||
*/
|
||||
if(kpages > ((uintptr_t)-KZERO)/PGSZ)
|
||||
kpages = ((uintptr_t)-KZERO)/PGSZ;
|
||||
|
||||
sys->upages = sys->npages - kpages;
|
||||
sys->ialloc = (kpages/2)*PGSZ;
|
||||
|
||||
/*
|
||||
* Guess how much is taken by the large permanent
|
||||
* datastructures. Mntcache and Mntrpc are not accounted for.
|
||||
*/
|
||||
kpages *= PGSZ;
|
||||
kpages -= sys->nproc*sizeof(Proc);
|
||||
mainmem->maxsize = kpages;
|
||||
|
||||
/*
|
||||
* the dynamic allocation will balance the load properly,
|
||||
* hopefully. be careful with 32-bit overflow.
|
||||
*/
|
||||
imagmem->maxsize = kpages - (kpages/10);
|
||||
if(p = getconf("*imagemaxmb")){
|
||||
imagmem->maxsize = strtol(p, nil, 0)*MiB;
|
||||
if(imagmem->maxsize > mainmem->maxsize)
|
||||
imagmem->maxsize = mainmem->maxsize;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
intialize_system();
|
||||
initialize_boot_processor();
|
||||
|
||||
multiboot(0);
|
||||
options(oargc, oargv);
|
||||
|
||||
ioinit();
|
||||
i8250console();
|
||||
fmtinit();
|
||||
screen_init();
|
||||
|
||||
jehanne_print("\nJehanne Operating System\n");
|
||||
|
||||
trapinit0();
|
||||
// i8259init();
|
||||
|
||||
i8253init();
|
||||
cpuidentify();
|
||||
meminit();
|
||||
|
||||
configure_kernel();
|
||||
|
||||
xinit();
|
||||
archinit();
|
||||
// bootscreeninit();
|
||||
// if(i8237alloc != nil)
|
||||
// i8237alloc();
|
||||
trapinit();
|
||||
printinit();
|
||||
cpuidprint();
|
||||
mmuinit();
|
||||
if(arch->intrinit)
|
||||
arch->intrinit();
|
||||
timersinit();
|
||||
// keybinit();
|
||||
// keybenable();
|
||||
// mouseenable();
|
||||
mathinit();
|
||||
if(arch->clockenable)
|
||||
arch->clockenable();
|
||||
|
||||
psinit(sys->nproc);
|
||||
|
||||
links();
|
||||
|
||||
devtabreset();
|
||||
umem_init();
|
||||
userinit();
|
||||
schedinit();
|
||||
}
|
||||
|
||||
@ -384,8 +357,6 @@ init0(void)
|
||||
|
||||
up->nerrlab = 0;
|
||||
|
||||
// if(consuart == nil)
|
||||
// i8250console("0");
|
||||
spllo();
|
||||
|
||||
/*
|
||||
@ -408,11 +379,11 @@ init0(void)
|
||||
ksetenv("service", "cpu", 0);
|
||||
else
|
||||
ksetenv("service", "terminal", 0);
|
||||
confsetenv();
|
||||
poperror();
|
||||
}
|
||||
kproc("alarm", alarmkproc, 0);
|
||||
kproc("awake", awakekproc, 0);
|
||||
kproc("atimer", awake_timer, 0);
|
||||
kproc("aringer", awake_ringer, 0);
|
||||
touser(sp);
|
||||
}
|
||||
|
||||
@ -461,17 +432,23 @@ userinit(void)
|
||||
char *k;
|
||||
uintptr_t va, mmuphys;
|
||||
|
||||
p = newproc();
|
||||
p->pgrp = newpgrp();
|
||||
p->egrp = smalloc(sizeof(Egrp));
|
||||
p->egrp->r.ref = 1;
|
||||
p->fgrp = dupfgrp(nil);
|
||||
p->rgrp = newrgrp();
|
||||
p->procmode = 0640;
|
||||
/* NOTE: we use the global pointer up so that the kaddr()
|
||||
* (called by segment_fault) can find it
|
||||
*/
|
||||
up = newproc();
|
||||
up->pgrp = newpgrp();
|
||||
up->egrp = smalloc(sizeof(Egrp));
|
||||
up->egrp->r.ref = 1;
|
||||
up->fgrp = dupfgrp(nil);
|
||||
up->rgrp = newrgrp();
|
||||
up->procmode = 0640;
|
||||
|
||||
kstrdup(&eve, "");
|
||||
kstrdup(&p->text, "*init*");
|
||||
kstrdup(&p->user, eve);
|
||||
kstrdup(&up->text, "*init*");
|
||||
kstrdup(&up->user, eve);
|
||||
|
||||
sysprocsetup(up);
|
||||
|
||||
/*
|
||||
* Kernel Stack
|
||||
*
|
||||
@ -480,23 +457,23 @@ userinit(void)
|
||||
* space for gotolabel's return PC
|
||||
* AMD64 stack must be quad-aligned.
|
||||
*/
|
||||
p->sched.pc = PTR2UINT(init0);
|
||||
p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t));
|
||||
p->sched.sp = STACKALIGN(p->sched.sp);
|
||||
D('0');
|
||||
up->sched.pc = PTR2UINT(init0);
|
||||
up->sched.sp = PTR2UINT(up->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t));
|
||||
up->sched.sp = STACKALIGN(up->sched.sp);
|
||||
|
||||
/*
|
||||
* User Stack
|
||||
*/
|
||||
s = nil;
|
||||
if(!segment_virtual(&s, SgStack, SgRead|SgWrite, 0, USTKTOP-USTKSIZE, USTKTOP))
|
||||
panic("userinit: cannot create stack segment");
|
||||
D('1');
|
||||
p->seg[SSEG] = s;
|
||||
|
||||
up->seg[SSEG] = s;
|
||||
va = USTKTOP-USTKSIZE;
|
||||
mmuphys = 0;
|
||||
if(!segment_fault(&mmuphys, &va, s, FaultWrite))
|
||||
panic("userinit: cannot allocate first stack page");
|
||||
D('2');
|
||||
|
||||
page = segment_page(s, va);
|
||||
if(page == 0)
|
||||
panic("userinit: cannot find first stack page (previously faulted)");
|
||||
@ -510,7 +487,7 @@ D('2');
|
||||
s = nil;
|
||||
if(!segment_userinit(&s, 0))
|
||||
panic("userinit: cannot create text segment");
|
||||
p->seg[TSEG] = s;
|
||||
up->seg[TSEG] = s;
|
||||
|
||||
/*
|
||||
* Data
|
||||
@ -518,8 +495,11 @@ D('2');
|
||||
s = nil;
|
||||
if(!segment_userinit(&s, 1))
|
||||
panic("userinit: cannot create data segment");
|
||||
p->seg[DSEG] = s;
|
||||
up->seg[DSEG] = s;
|
||||
|
||||
/* reset global pointer up */
|
||||
p = up;
|
||||
up = nil;
|
||||
ready(p);
|
||||
}
|
||||
|
||||
@ -527,7 +507,7 @@ static void
|
||||
fullstop(void)
|
||||
{
|
||||
splhi();
|
||||
lapicpri(0xff);
|
||||
// lapicpri(0xff);
|
||||
/* i8259 was initialised as disabled */
|
||||
for(;;)
|
||||
_halt();
|
||||
@ -544,7 +524,7 @@ shutdown(int ispanic)
|
||||
active.ispanic = ispanic;
|
||||
m->online = 0;
|
||||
active.exiting = 1;
|
||||
adec(&sys->nonline);
|
||||
adec((int*)&sys->nmach);
|
||||
|
||||
iprint("cpu%d: exiting\n", m->machno);
|
||||
/* wait for any other processors to shutdown */
|
||||
@ -552,7 +532,7 @@ shutdown(int ispanic)
|
||||
prflush();
|
||||
for(ms = 10*1000; ms > 0; ms -= 2){
|
||||
delay(2);
|
||||
if(sys->nonline == 0 && consactive() == 0)
|
||||
if(sys->nmach == 0 && consactive() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
/*
|
||||
* Before mmuinit is done, we can't rely on sys->vmunmapped
|
||||
* being set, so we use the static limit TMFM
|
||||
*/
|
||||
|
||||
void*
|
||||
KADDR(uintmem pa)
|
||||
{
|
||||
uint8_t* va;
|
||||
|
||||
va = UINT2PTR(pa);
|
||||
if(sys->vmunmapped != 0){
|
||||
if(pa < sys->vmunmapped-KSEG0)
|
||||
return KSEG0+va;
|
||||
}else if(pa < TMFM)
|
||||
return KSEG0+va;
|
||||
return KSEG2+va;
|
||||
}
|
||||
|
||||
uintmem
|
||||
PADDR(void* va)
|
||||
{
|
||||
uintmem pa;
|
||||
|
||||
pa = PTR2UINT(va);
|
||||
if(pa >= KSEG2 && pa < KSEG1)
|
||||
return pa-KSEG2;
|
||||
if(pa >= KSEG0 && pa < KSEG0+TMFM)
|
||||
return pa-KSEG0;
|
||||
if(pa > KSEG2)
|
||||
return pa-KSEG2;
|
||||
|
||||
panic("PADDR: va %#p pa #%p @ %#p\n", va, mmuphysaddr(PTR2UINT(va)), getcallerpc());
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
#define EiB gULL(1152921504606846976)
|
||||
|
||||
#define HOWMANY(x, y) (((x)+((y)-1))/(y))
|
||||
#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
|
||||
#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1))
|
||||
#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
|
||||
#define ROUNDDN(x, y) (((x)/(y))*(y))
|
||||
#define MIN(a, b) ((a) < (b)? (a): (b))
|
||||
@ -44,21 +44,25 @@
|
||||
*/
|
||||
#define BI2BY 8 /* bits per byte */
|
||||
#define BI2WD 32 /* bits per word */
|
||||
#define BY2WD 4 /* bytes per word */
|
||||
#define BY2WD 8 /* bytes per word */
|
||||
#define BY2V 8 /* bytes per double word */
|
||||
#define BY2SE 8 /* bytes per stack element */
|
||||
#define BLOCKALIGN 8
|
||||
|
||||
#define PGSZ (4*KiB) /* page size */
|
||||
#define BY2PG PGSZ
|
||||
#define WD2PG (BY2PG/BY2WD)
|
||||
#define PGSHFT 12 /* log(PGSZ) */
|
||||
#define PTSZ (4*KiB) /* page table page size */
|
||||
#define PTSHFT 9 /* */
|
||||
#define PGSHIFT PGSHFT
|
||||
#define PGROUND(s) ROUNDUP(s, PGSZ)
|
||||
#define BLOCKALIGN 8
|
||||
#define FPalign 16
|
||||
|
||||
#define MACHSZ (4*KiB) /* Mach+stack size */
|
||||
#define MACHSZ (2*PGSZ) /* Mach+stack size */
|
||||
#define MACHMAX 32 /* max. number of cpus */
|
||||
#define MACHSTKSZ (6*(4*KiB)) /* Mach stack size */
|
||||
|
||||
#define KSTACK (16*1024) /* Size of Proc kernel stack */
|
||||
#define KSTACK (16*KiB) /* Size of Proc kernel stack */
|
||||
#define STACKALIGN(sp) ((sp) & ~(BY2SE-1)) /* bug: assure with alloc */
|
||||
|
||||
/*
|
||||
@ -82,18 +86,46 @@
|
||||
|
||||
#define UTZERO (0+2*MiB) /* first address in user text */
|
||||
#define UTROUND(t) ROUNDUP((t), 2*MiB) /* first address beyond text for user data */
|
||||
#define USTKTOP gULL(0x00007ffffffff000)
|
||||
#define USTKSIZE (16*1024*1024) /* size of user stack */
|
||||
#define TSTKTOP (USTKTOP-USTKSIZE) /* end of new stack in sysexec */
|
||||
#define UADDRMASK gULL(0x00007fffffffffff) /* canonical address mask */
|
||||
#define TSTKTOP (0x00007ffffffff000ull)
|
||||
#define USTKSIZE (16*MiB) /* size of user stack */
|
||||
#define USTKTOP (TSTKTOP-USTKSIZE) /* end of new stack in sysexec */
|
||||
|
||||
#define KSEG0 gULL(0xfffffffff0000000) /* 256MB - this is confused */
|
||||
#define KSEG1 gULL(0xffffff0000000000) /* 512GB - embedded PML4 */
|
||||
#define KSEG2 gULL(0xfffffe0000000000) /* 1TB - KMAP */
|
||||
//#define KSEG0 gULL(0xffffffff80000000) /* 256MB - this is confused */
|
||||
//#define KSEG1 gULL(0xffffff0000000000) /* 512GB - embedded PML4 */
|
||||
//#define KSEG2 gULL(0xfffffe8000000000) /* 1TB - KMAP */
|
||||
#define PMAPADDR gULL(0xffffffffffe00000) /* unused as of yet (KMAP?) */
|
||||
|
||||
#define KZERO gULL(0xfffffffff0000000)
|
||||
|
||||
#define KZERO gULL(0xffffffff80000000)
|
||||
#define KTZERO (KZERO+1*MiB+64*KiB)
|
||||
|
||||
#define VMAP gULL(0xffffff0000000000)
|
||||
#define VMAPSIZE (512*GiB)
|
||||
|
||||
#define KMAP gULL(0xfffffe8000000000)
|
||||
#define KMAPSIZE (2*MiB)
|
||||
|
||||
/*
|
||||
* Fundamental addresses - bottom 64kB saved for return to real mode
|
||||
*/
|
||||
#define CONFADDR (KZERO+0x1200) /* info passed from boot loader */
|
||||
#define APBOOTSTRAP (KZERO+0x3000) /* AP bootstrap code */
|
||||
#define IDTADDR (KZERO+0x10000) /* idt */
|
||||
#define REBOOTADDR (0x11000) /* reboot code - physical address */
|
||||
|
||||
#define CPU0PML4 (KZERO+0x13000)
|
||||
#define CPU0PDP (KZERO+0x14000)
|
||||
#define CPU0PD0 (KZERO+0x15000) /* KZERO */
|
||||
#define CPU0PD1 (KZERO+0x16000) /* KZERO+1GB */
|
||||
|
||||
#define CPU0GDT (KZERO+0x17000) /* bootstrap processor GDT */
|
||||
|
||||
#define CPU0MACH (KZERO+0x18000) /* Mach for bootstrap processor */
|
||||
#define CPU0END (CPU0MACH+MACHSIZE)
|
||||
|
||||
#define MACHSIZE (2*KSTACK)
|
||||
|
||||
/*
|
||||
* virtual MMU
|
||||
*/
|
||||
@ -104,14 +136,17 @@
|
||||
#define SEGMAXPG (SEGMAPSIZE)
|
||||
|
||||
/*
|
||||
* This is the interface between fixfault and mmuput.
|
||||
* Should be in port.
|
||||
* physical MMU
|
||||
*/
|
||||
#define PTEVALID (1<<0)
|
||||
#define PTEWRITE (1<<1)
|
||||
#define PTERONLY (0<<1)
|
||||
#define PTEUSER (1<<2)
|
||||
#define PTEUNCACHED (1<<4)
|
||||
#define PTEVALID (gULL(1)<<0)
|
||||
#define PTEWT (gULL(1)<<3)
|
||||
#define PTEUNCACHED (gULL(1)<<4)
|
||||
#define PTEWRITE (gULL(1)<<1)
|
||||
#define PTERONLY (gULL(0)<<1)
|
||||
#define PTEKERNEL (gULL(0)<<2)
|
||||
#define PTEUSER (gULL(1)<<2)
|
||||
#define PTESIZE (gULL(1)<<7)
|
||||
#define PTEGLOBAL (gULL(1)<<8)
|
||||
|
||||
#define getpgcolor(a) 0
|
||||
|
||||
@ -123,6 +158,9 @@
|
||||
* and level 0 the PT pages. The PTLX macro gives an index into the
|
||||
* page-table page at level 'l' for the virtual address 'v'.
|
||||
*/
|
||||
#define PTSZ (4*KiB) /* page table page size */
|
||||
#define PTSHFT 9 /* */
|
||||
|
||||
#define PTLX(v, l) (((v)>>(((l)*PTSHFT)+PGSHFT)) & ((1<<PTSHFT)-1))
|
||||
#define PGLSZ(l) (1<<(((l)*PTSHFT)+PGSHFT))
|
||||
|
||||
@ -133,26 +171,21 @@
|
||||
|
||||
#define CACHELINESZ 64
|
||||
|
||||
#define KVATOP (KSEG0&KSEG1&KSEG2)
|
||||
#define iskaddr(a) (((uintptr_t)(a)&KVATOP) == KVATOP)
|
||||
//#define KVATOP (KSEG0&KSEG1&KSEG2)
|
||||
#define iskaddr(a) (((uintptr_t)(a)) > KZERO)
|
||||
|
||||
/*
|
||||
* known x86 segments (in GDT) and their selectors
|
||||
*/
|
||||
#define NULLSEG 0 /* null segment */
|
||||
#define KDSEG 1 /* kernel data/stack */
|
||||
#define KESEG 2 /* kernel executable */
|
||||
#define UDSEG 3 /* user data/stack */
|
||||
#define UESEG 4 /* user executable */
|
||||
#define TSSSEG 5 /* task segment */
|
||||
#define APMCSEG 6 /* APM code segment */
|
||||
#define APMCSEG16 7 /* APM 16-bit code segment */
|
||||
#define APMDSEG 8 /* APM data segment */
|
||||
#define KESEG16 9 /* kernel executable 16-bit */
|
||||
#define LDTSEG 10 /* local descriptor table */
|
||||
#define PROCSEG0 11 /* per process descriptor0 */
|
||||
#define NPROCSEG 3 /* number of per process descriptors */
|
||||
#define NGDT 14 /* number of GDT entries required */
|
||||
#define KESEG 1 /* kernel executable */
|
||||
#define KDSEG 2 /* kernel data */
|
||||
#define UE32SEG 3 /* user executable 32bit */
|
||||
#define UDSEG 4 /* user data/stack */
|
||||
#define UESEG 5 /* user executable 64bit */
|
||||
#define TSSSEG 8 /* task segment (two descriptors) */
|
||||
|
||||
#define NGDT 10 /* number of GDT entries required */
|
||||
|
||||
#define SELGDT (0<<2) /* selector is in gdt */
|
||||
#define SELLDT (1<<2) /* selector is in ldt */
|
||||
@ -160,12 +193,33 @@
|
||||
#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p))
|
||||
|
||||
#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0)
|
||||
#define KDSEL SELECTOR(KDSEG, SELGDT, 0)
|
||||
#define KESEL SELECTOR(KESEG, SELGDT, 0)
|
||||
#define UESEL SELECTOR(UESEG, SELGDT, 3)
|
||||
#define UE32SEL SELECTOR(UE32SEG, SELGDT, 3)
|
||||
#define UDSEL SELECTOR(UDSEG, SELGDT, 3)
|
||||
#define UESEL SELECTOR(UESEG, SELGDT, 3)
|
||||
#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0)
|
||||
#define APMCSEL SELECTOR(APMCSEG, SELGDT, 0)
|
||||
#define APMCSEL16 SELECTOR(APMCSEG16, SELGDT, 0)
|
||||
#define APMDSEL SELECTOR(APMDSEG, SELGDT, 0)
|
||||
#define LDTSEL SELECTOR(LDTSEG, SELGDT, 0)
|
||||
|
||||
/*
|
||||
* fields in segment descriptors
|
||||
*/
|
||||
#define SEGDATA (0x10<<8) /* data/stack segment */
|
||||
#define SEGEXEC (0x18<<8) /* executable segment */
|
||||
#define SEGTSS (0x9<<8) /* TSS segment */
|
||||
#define SEGCG (0x0C<<8) /* call gate */
|
||||
#define SEGIG (0x0E<<8) /* interrupt gate */
|
||||
#define SEGTG (0x0F<<8) /* trap gate */
|
||||
#define SEGLDT (0x02<<8) /* local descriptor table */
|
||||
#define SEGTYPE (0x1F<<8)
|
||||
|
||||
#define SEGP (1<<15) /* segment present */
|
||||
#define SEGPL(x) ((x)<<13) /* priority level */
|
||||
#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */
|
||||
#define SEGD (1<<22) /* default 1==32bit (for code) */
|
||||
#define SEGE (1<<10) /* expand down */
|
||||
#define SEGW (1<<9) /* writable (for data/stack) */
|
||||
#define SEGR (1<<9) /* readable (for code) */
|
||||
#define SEGL (1<<21) /* 64 bit */
|
||||
#define SEGG (1<<23) /* granularity 1==4k (for other) */
|
||||
|
||||
#define getpgcolor(a) 0
|
||||
#define MBPGMASK (~(PGSZ-1))
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,6 @@
|
||||
* modified, propagated, or distributed except according to the terms contained
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
@ -31,8 +30,6 @@ enum
|
||||
MousePS2= 2,
|
||||
};
|
||||
|
||||
extern int mouseshifted;
|
||||
|
||||
static QLock mousectlqlock;
|
||||
static int mousetype;
|
||||
static int intellimouse;
|
||||
@ -40,6 +37,7 @@ static int packetsize;
|
||||
static int resolution;
|
||||
static int accelerated;
|
||||
static int mousehwaccel;
|
||||
static char mouseport[5];
|
||||
|
||||
enum
|
||||
{
|
||||
@ -80,7 +78,7 @@ static Cmdtab mousectlmsg[] =
|
||||
* byte 3 - 00 or 01 or FF according to extra button state.
|
||||
* extra buttons are mapped in this code to buttons 4 and 5.
|
||||
* AccuPoint generates repeated events for these buttons;
|
||||
* it and Intellimouse generate 'down' events only, so
|
||||
* it and Intellimouse generate 'down' events only, so
|
||||
* user-level code is required to generate button 'up' events
|
||||
* if they are needed by the application.
|
||||
* Also on laptops with AccuPoint AND external mouse, the
|
||||
@ -93,6 +91,76 @@ static Cmdtab mousectlmsg[] =
|
||||
* To resynchronize, if we get a byte more than two seconds
|
||||
* after the previous byte, we assume it's the first in a packet.
|
||||
*/
|
||||
static void
|
||||
ps2mouseputc(int c, int shift)
|
||||
{
|
||||
static short msg[4];
|
||||
static int nb;
|
||||
static uint8_t b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
|
||||
static uint32_t lasttick;
|
||||
uint32_t m;
|
||||
int buttons, dx, dy;
|
||||
|
||||
/*
|
||||
* Resynchronize in stream with timing; see comment above.
|
||||
*/
|
||||
m = MACHP(0)->ticks;
|
||||
if(TK2SEC(m - lasttick) > 2)
|
||||
nb = 0;
|
||||
lasttick = m;
|
||||
|
||||
/*
|
||||
* check byte 0 for consistency
|
||||
*/
|
||||
if(nb==0 && (c&0xc8)!=0x08){
|
||||
if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){
|
||||
/* last byte of 4-byte packet */
|
||||
packetsize = 4;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
msg[nb] = c;
|
||||
if(++nb >= packetsize){
|
||||
nb = 0;
|
||||
if(msg[0] & 0x10)
|
||||
msg[1] |= 0xFF00;
|
||||
if(msg[0] & 0x20)
|
||||
msg[2] |= 0xFF00;
|
||||
|
||||
buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
|
||||
if(intellimouse && packetsize==4){
|
||||
if((msg[3]&0xc8) == 0x08){
|
||||
/* first byte of 3-byte packet */
|
||||
packetsize = 3;
|
||||
msg[0] = msg[3];
|
||||
nb = 1;
|
||||
/* fall through to emit previous packet */
|
||||
}else{
|
||||
/* The AccuPoint on the Toshiba 34[48]0CT
|
||||
* encodes extra buttons as 4 and 5. They repeat
|
||||
* and don't release, however, so user-level
|
||||
* timing code is required. Furthermore,
|
||||
* intellimice with 3buttons + scroll give a
|
||||
* two's complement number in the lower 4 bits
|
||||
* (bit 4 is sign extension) that describes
|
||||
* the amount the scroll wheel has moved during
|
||||
* the last sample. Here we use only the sign to
|
||||
* decide whether the wheel is moving up or down
|
||||
* and generate a single button 4 or 5 click
|
||||
* accordingly.
|
||||
*/
|
||||
if((msg[3] >> 3) & 1)
|
||||
buttons |= 1<<3;
|
||||
else if(msg[3] & 0x7)
|
||||
buttons |= 1<<4;
|
||||
}
|
||||
}
|
||||
dx = msg[1];
|
||||
dy = -msg[2];
|
||||
mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set up a ps2 mouse
|
||||
@ -103,14 +171,12 @@ ps2mouse(void)
|
||||
if(mousetype == MousePS2)
|
||||
return;
|
||||
|
||||
mouseenable();
|
||||
/* make mouse streaming, enabled */
|
||||
mousecmd(0xEA);
|
||||
mousecmd(0xF4);
|
||||
|
||||
mousetype = MousePS2;
|
||||
packetsize = 3;
|
||||
mousehwaccel = 1;
|
||||
mousehwaccel = 0;
|
||||
|
||||
i8042auxenable(ps2mouseputc);
|
||||
i8042auxcmd(0xEA); /* set stream mode */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -131,7 +197,7 @@ setaccelerated(int x)
|
||||
if(mousehwaccel){
|
||||
switch(mousetype){
|
||||
case MousePS2:
|
||||
mousecmd(0xE7);
|
||||
i8042auxcmd(0xE7);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -145,7 +211,7 @@ setlinear(void)
|
||||
if(mousehwaccel){
|
||||
switch(mousetype){
|
||||
case MousePS2:
|
||||
mousecmd(0xE6);
|
||||
i8042auxcmd(0xE6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -158,8 +224,8 @@ setres(int n)
|
||||
resolution = n;
|
||||
switch(mousetype){
|
||||
case MousePS2:
|
||||
mousecmd(0xE8);
|
||||
mousecmd(n);
|
||||
i8042auxcmd(0xE8);
|
||||
i8042auxcmd(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -171,12 +237,15 @@ setintellimouse(void)
|
||||
packetsize = 4;
|
||||
switch(mousetype){
|
||||
case MousePS2:
|
||||
mousecmd(0xF3); /* set sample */
|
||||
mousecmd(0xC8);
|
||||
mousecmd(0xF3); /* set sample */
|
||||
mousecmd(0x64);
|
||||
mousecmd(0xF3); /* set sample */
|
||||
mousecmd(0x50);
|
||||
i8042auxcmd(0xF3); /* set sample */
|
||||
i8042auxcmd(0xC8);
|
||||
i8042auxcmd(0xF3); /* set sample */
|
||||
i8042auxcmd(0x64);
|
||||
i8042auxcmd(0xF3); /* set sample */
|
||||
i8042auxcmd(0x50);
|
||||
break;
|
||||
case Mouseserial:
|
||||
uartsetmouseputc(mouseport, m5mouseputc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -187,11 +256,30 @@ resetmouse(void)
|
||||
packetsize = 3;
|
||||
switch(mousetype){
|
||||
case MousePS2:
|
||||
mousecmd(0xF6);
|
||||
mousecmd(0xEA); /* streaming */
|
||||
mousecmd(0xE8); /* set resolution */
|
||||
mousecmd(3);
|
||||
mousecmd(0xF4); /* enabled */
|
||||
i8042auxcmd(0xF6);
|
||||
i8042auxcmd(0xEA); /* streaming */
|
||||
i8042auxcmd(0xE8); /* set resolution */
|
||||
i8042auxcmd(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setstream(int on)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch(mousetype){
|
||||
case MousePS2:
|
||||
/*
|
||||
* disabling streaming can fail when
|
||||
* a packet is currently transmitted.
|
||||
*/
|
||||
for(i=0; i<4; i++){
|
||||
if(i8042auxcmd(on ? 0xF4 : 0xF5) != -1)
|
||||
break;
|
||||
delay(50);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -210,27 +298,37 @@ mousectl(Cmdbuf *cb)
|
||||
ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
|
||||
switch(ct->index){
|
||||
case CMaccelerated:
|
||||
setaccelerated(cb->nf == 1 ? 1 : jehanne_atoi(cb->f[1]));
|
||||
setstream(0);
|
||||
setaccelerated(cb->nf == 1 ? 1 : atoi(cb->f[1]));
|
||||
setstream(1);
|
||||
break;
|
||||
case CMintellimouse:
|
||||
setstream(0);
|
||||
setintellimouse();
|
||||
setstream(1);
|
||||
break;
|
||||
case CMlinear:
|
||||
setstream(0);
|
||||
setlinear();
|
||||
setstream(1);
|
||||
break;
|
||||
case CMps2:
|
||||
intellimouse = 0;
|
||||
ps2mouse();
|
||||
setstream(1);
|
||||
break;
|
||||
case CMps2intellimouse:
|
||||
ps2mouse();
|
||||
setintellimouse();
|
||||
setstream(1);
|
||||
break;
|
||||
case CMres:
|
||||
setstream(0);
|
||||
if(cb->nf >= 2)
|
||||
setres(jehanne_atoi(cb->f[1]));
|
||||
setres(atoi(cb->f[1]));
|
||||
else
|
||||
setres(1);
|
||||
setstream(1);
|
||||
break;
|
||||
case CMreset:
|
||||
resetmouse();
|
||||
@ -240,11 +338,31 @@ mousectl(Cmdbuf *cb)
|
||||
setres(resolution);
|
||||
if(intellimouse)
|
||||
setintellimouse();
|
||||
setstream(1);
|
||||
break;
|
||||
case CMserial:
|
||||
if(mousetype == Mouseserial)
|
||||
error(Emouseset);
|
||||
|
||||
if(cb->nf > 2){
|
||||
if(strcmp(cb->f[2], "M") == 0)
|
||||
uartmouse(cb->f[1], m3mouseputc, 0);
|
||||
else if(strcmp(cb->f[2], "MI") == 0)
|
||||
uartmouse(cb->f[1], m5mouseputc, 0);
|
||||
else
|
||||
uartmouse(cb->f[1], mouseputc, cb->nf == 1);
|
||||
} else
|
||||
uartmouse(cb->f[1], mouseputc, cb->nf == 1);
|
||||
|
||||
mousetype = Mouseserial;
|
||||
strncpy(mouseport, cb->f[1], sizeof(mouseport)-1);
|
||||
mouseport[sizeof(mouseport)-1] = 0;
|
||||
packetsize = 3;
|
||||
break;
|
||||
case CMhwaccel:
|
||||
if(jehanne_strcmp(cb->f[1], "on")==0)
|
||||
if(strcmp(cb->f[1], "on")==0)
|
||||
mousehwaccel = 1;
|
||||
else if(jehanne_strcmp(cb->f[1], "off")==0)
|
||||
else if(strcmp(cb->f[1], "off")==0)
|
||||
mousehwaccel = 0;
|
||||
else
|
||||
cmderror(cb, "bad mouse control message");
|
||||
|
@ -3,426 +3,588 @@
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "io.h"
|
||||
#include "apic.h"
|
||||
#include "ureg.h"
|
||||
|
||||
#undef DBG
|
||||
#define DBG jehanne_print
|
||||
/*
|
||||
* MultiProcessor Specification Version 1.[14].
|
||||
*/
|
||||
typedef struct { /* MP Floating Pointer */
|
||||
uint8_t signature[4]; /* "_MP_" */
|
||||
uint8_t addr[4]; /* PCMP */
|
||||
uint8_t length; /* 1 */
|
||||
uint8_t revision; /* [14] */
|
||||
uint8_t checksum;
|
||||
uint8_t feature[5];
|
||||
} _MP_;
|
||||
#include "mp.h"
|
||||
#include "sipi.h"
|
||||
|
||||
typedef struct { /* MP Configuration Table */
|
||||
uint8_t signature[4]; /* "PCMP" */
|
||||
uint8_t length[2];
|
||||
uint8_t revision; /* [14] */
|
||||
uint8_t checksum;
|
||||
uint8_t string[20]; /* OEM + Product ID */
|
||||
uint8_t oaddr[4]; /* OEM table pointer */
|
||||
uint8_t olength[2]; /* OEM table length */
|
||||
uint8_t entry[2]; /* entry count */
|
||||
uint8_t apicpa[4]; /* local APIC address */
|
||||
uint8_t xlength[2]; /* extended table length */
|
||||
uint8_t xchecksum; /* extended table checksum */
|
||||
uint8_t reserved;
|
||||
/* filled in by pcmpinit or acpiinit */
|
||||
Bus* mpbus;
|
||||
Bus* mpbuslast;
|
||||
int mpisabus = -1;
|
||||
int mpeisabus = -1;
|
||||
Apic *mpioapic[MaxAPICNO+1];
|
||||
Apic *mpapic[MaxAPICNO+1];
|
||||
|
||||
uint8_t entries[];
|
||||
} PCMP;
|
||||
|
||||
typedef struct {
|
||||
char type[6];
|
||||
int polarity; /* default for this bus */
|
||||
int trigger; /* default for this bus */
|
||||
} Mpbus;
|
||||
|
||||
static Mpbus mpbusdef[] = {
|
||||
{ "PCI ", IPlow, TMlevel, },
|
||||
{ "ISA ", IPhigh, TMedge, },
|
||||
};
|
||||
static Mpbus* mpbus[Nbus];
|
||||
int mpisabusno = -1;
|
||||
|
||||
static void
|
||||
mpintrprint(char* s, uint8_t* p)
|
||||
int
|
||||
mpintrinit(Bus* bus, PCMPintr* intr, int vno, int irq)
|
||||
{
|
||||
char buf[128], *b, *e;
|
||||
char format[] = " type %d flags %#ux bus %d IRQ %d APIC %d INTIN %d\n";
|
||||
|
||||
b = buf;
|
||||
e = b + sizeof(buf);
|
||||
b = jehanne_seprint(b, e, "mpparse: intr:");
|
||||
if(s != nil)
|
||||
b = jehanne_seprint(b, e, " %s:", s);
|
||||
jehanne_seprint(b, e, format, p[1], l16get(p+2), p[4], p[5], p[6], p[7]);
|
||||
jehanne_print(buf);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
mpmkintr(uint8_t* p)
|
||||
{
|
||||
uint32_t v;
|
||||
Lapic *apic;
|
||||
IOapic *ioapic;
|
||||
int n, polarity, trigger;
|
||||
int el, po, v;
|
||||
|
||||
/*
|
||||
* Check valid bus, interrupt input pin polarity
|
||||
* and trigger mode. If the APIC ID is 0xff it means
|
||||
* all APICs of this type so those checks for useable
|
||||
* APIC and valid INTIN must also be done later in
|
||||
* the appropriate init routine in that case. It's hard
|
||||
* to imagine routing a signal to all IOAPICs, the
|
||||
* usual case is routing NMI and ExtINT to all LAPICs.
|
||||
* Parse an I/O or Local APIC interrupt table entry and
|
||||
* return the encoded vector.
|
||||
*/
|
||||
if(mpbus[p[4]] == nil){
|
||||
mpintrprint("no source bus", p);
|
||||
return 0;
|
||||
}
|
||||
if(p[6] != 0xff){
|
||||
if(Napic < 256 && p[6] >= Napic){
|
||||
mpintrprint("APIC ID out of range", p);
|
||||
return 0;
|
||||
}
|
||||
switch(p[0]){
|
||||
default:
|
||||
mpintrprint("INTIN botch", p);
|
||||
return 0;
|
||||
case 3: /* IOINTR */
|
||||
ioapic = ioapiclookup(p[6]);
|
||||
if(ioapic == nil){
|
||||
mpintrprint("unuseable IO APIC", p);
|
||||
return 0;
|
||||
}
|
||||
if(p[7] >= ioapic->nrdt){
|
||||
mpintrprint("IO INTIN out of range", p);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 4: /* LINTR */
|
||||
apic = lapiclookup(p[6]);
|
||||
if(apic == nil){
|
||||
mpintrprint("unuseable local APIC", p);
|
||||
return 0;
|
||||
}
|
||||
if(p[7] >= nelem(apic->lvt)){
|
||||
mpintrprint("LOCAL INTIN out of range", p);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
n = l16get(p+2);
|
||||
if((polarity = (n & 0x03)) == 2 || (trigger = ((n>>2) & 0x03)) == 2){
|
||||
mpintrprint("invalid polarity/trigger", p);
|
||||
return 0;
|
||||
v = vno;
|
||||
|
||||
po = intr->flags & PcmpPOMASK;
|
||||
el = intr->flags & PcmpELMASK;
|
||||
|
||||
switch(intr->intr){
|
||||
default: /* PcmpINT */
|
||||
v |= ApicFIXED; /* no-op */
|
||||
break;
|
||||
|
||||
case PcmpNMI:
|
||||
v |= ApicNMI;
|
||||
po = PcmpHIGH;
|
||||
el = PcmpEDGE;
|
||||
break;
|
||||
|
||||
case PcmpSMI:
|
||||
v |= ApicSMI;
|
||||
break;
|
||||
|
||||
case PcmpExtINT:
|
||||
v |= ApicExtINT;
|
||||
/*
|
||||
* The AMI Goliath doesn't boot successfully with it's LINTR0
|
||||
* entry which decodes to low+level. The PPro manual says ExtINT
|
||||
* should be level, whereas the Pentium is edge. Setting the
|
||||
* Goliath to edge+high seems to cure the problem. Other PPro
|
||||
* MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes
|
||||
* to edge+high, so who knows.
|
||||
* Perhaps it would be best just to not set an ExtINT entry at
|
||||
* all, it shouldn't be needed for SMP mode.
|
||||
*/
|
||||
po = PcmpHIGH;
|
||||
el = PcmpEDGE;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the low half of the vector table entry (LVT or RDT).
|
||||
* For the NMI, SMI and ExtINT cases, the polarity and trigger
|
||||
* are fixed (but are not always consistent over IA-32 generations).
|
||||
* For the INT case, either the polarity/trigger are given or
|
||||
* it defaults to that of the source bus;
|
||||
* whether INT is Fixed or Lowest Priority is left until later.
|
||||
*/
|
||||
v = Im;
|
||||
switch(p[1]){
|
||||
default:
|
||||
mpintrprint("invalid type", p);
|
||||
return 0;
|
||||
case 0: /* INT */
|
||||
switch(polarity){
|
||||
case 0:
|
||||
v |= mpbus[p[4]]->polarity;
|
||||
break;
|
||||
case 1:
|
||||
v |= IPhigh;
|
||||
break;
|
||||
case 3:
|
||||
v |= IPlow;
|
||||
break;
|
||||
}
|
||||
switch(trigger){
|
||||
case 0:
|
||||
v |= mpbus[p[4]]->trigger;
|
||||
break;
|
||||
case 1:
|
||||
v |= TMedge;
|
||||
break;
|
||||
case 3:
|
||||
v |= TMlevel;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1: /* NMI */
|
||||
v |= TMedge|IPhigh|MTnmi;
|
||||
break;
|
||||
case 2: /* SMI */
|
||||
v |= TMedge|IPhigh|MTsmi;
|
||||
break;
|
||||
case 3: /* ExtINT */
|
||||
v |= TMedge|IPhigh|MTei;
|
||||
break;
|
||||
if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
|
||||
po = PcmpHIGH;
|
||||
el = PcmpEDGE;
|
||||
}
|
||||
if(!po)
|
||||
po = bus->po;
|
||||
if(po == PcmpLOW)
|
||||
v |= ApicLOW;
|
||||
else if(po != PcmpHIGH){
|
||||
print("mpintrinit: bad polarity 0x%uX\n", po);
|
||||
return ApicIMASK;
|
||||
}
|
||||
|
||||
if(!el)
|
||||
el = bus->el;
|
||||
if(el == PcmpLEVEL)
|
||||
v |= ApicLEVEL;
|
||||
else if(el != PcmpEDGE){
|
||||
print("mpintrinit: bad trigger 0x%uX\n", el);
|
||||
return ApicIMASK;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
mpparse(PCMP* pcmp)
|
||||
uint64_t
|
||||
tscticks(uint64_t *hz)
|
||||
{
|
||||
uint32_t lo;
|
||||
uint8_t *e, *p;
|
||||
int i, n, bustype;
|
||||
Lapic *apic;
|
||||
if(hz != nil)
|
||||
*hz = m->cpuhz;
|
||||
|
||||
p = pcmp->entries;
|
||||
e = ((uint8_t*)pcmp)+l16get(pcmp->length);
|
||||
while(p < e) switch(*p){
|
||||
default:
|
||||
jehanne_print("mpparse: unknown PCMP type %d (e-p %#ld)\n", *p, e-p);
|
||||
for(i = 0; p < e; i++){
|
||||
if(i && ((i & 0x0f) == 0))
|
||||
jehanne_print("\n");
|
||||
jehanne_print(" %#2.2ux", *p);
|
||||
p++;
|
||||
}
|
||||
jehanne_print("\n");
|
||||
break;
|
||||
case 0: /* processor */
|
||||
/*
|
||||
* Initialise the APIC if it is enabled (p[3] & 0x01).
|
||||
* p[1] is the APIC ID, the memory mapped address comes
|
||||
* from the PCMP structure as the addess is local to the
|
||||
* CPU and identical for all. Indicate whether this is
|
||||
* the bootstrap processor (p[3] & 0x02).
|
||||
*/
|
||||
DBG("mpparse: APIC %d pa %#ux useable %d\n",
|
||||
p[1], l32get(pcmp->apicpa), p[3] & 0x01);
|
||||
if(p[3] & 0x01)
|
||||
lapicinit(p[1], l32get(pcmp->apicpa), p[3] & 0x02);
|
||||
p += 20;
|
||||
break;
|
||||
case 1: /* bus */
|
||||
DBG("mpparse: bus: %d type %6.6s\n", p[1], (char*)p+2);
|
||||
if(mpbus[p[1]] != nil){
|
||||
jehanne_print("mpparse: bus %d already allocated\n", p[1]);
|
||||
p += 8;
|
||||
break;
|
||||
}
|
||||
for(i = 0; i < nelem(mpbusdef); i++){
|
||||
if(jehanne_memcmp(p+2, mpbusdef[i].type, 6) != 0)
|
||||
continue;
|
||||
if(jehanne_memcmp(p+2, "ISA ", 6) == 0){
|
||||
if(mpisabusno != -1){
|
||||
jehanne_print("mpparse: bus %d already have ISA bus %d\n",
|
||||
p[1], mpisabusno);
|
||||
continue;
|
||||
}
|
||||
mpisabusno = p[1];
|
||||
}
|
||||
mpbus[p[1]] = &mpbusdef[i];
|
||||
break;
|
||||
}
|
||||
if(mpbus[p[1]] == nil)
|
||||
jehanne_print("mpparse: bus %d type %6.6s unknown\n",
|
||||
p[1], (char*)p+2);
|
||||
cycles(&m->tscticks); /* Uses the rdtsc instruction */
|
||||
return m->tscticks;
|
||||
}
|
||||
|
||||
p += 8;
|
||||
break;
|
||||
case 2: /* IOAPIC */
|
||||
/*
|
||||
* Initialise the IOAPIC if it is enabled (p[3] & 0x01).
|
||||
* p[1] is the APIC ID, p[4-7] is the memory mapped address.
|
||||
*/
|
||||
DBG("mpparse: IOAPIC %d pa %#ux useable %d\n",
|
||||
p[1], l32get(p+4), p[3] & 0x01);
|
||||
if(p[3] & 0x01)
|
||||
ioapicinit(p[1], -1, l32get(p+4));
|
||||
void
|
||||
syncclock(void)
|
||||
{
|
||||
uint64_t x;
|
||||
|
||||
p += 8;
|
||||
break;
|
||||
case 3: /* IOINTR */
|
||||
/*
|
||||
* p[1] is the interrupt type;
|
||||
* p[2-3] contains the polarity and trigger mode;
|
||||
* p[4] is the source bus;
|
||||
* p[5] is the IRQ on the source bus;
|
||||
* p[6] is the destination APIC;
|
||||
* p[7] is the INITIN pin on the destination APIC.
|
||||
*/
|
||||
if(p[6] == 0xff){
|
||||
mpintrprint("routed to all IOAPICs", p);
|
||||
p += 8;
|
||||
break;
|
||||
}
|
||||
if((lo = mpmkintr(p)) == 0){
|
||||
p += 8;
|
||||
break;
|
||||
}
|
||||
if(DBGFLG)
|
||||
mpintrprint(nil, p);
|
||||
if(arch->fastclock != tscticks)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Always present the device number in the style
|
||||
* of a PCI Interrupt Assignment Entry. For the ISA
|
||||
* bus the IRQ is the device number but unencoded.
|
||||
* May need to handle other buses here in the future
|
||||
* (but unlikely).
|
||||
*/
|
||||
bustype = -1;
|
||||
if(jehanne_memcmp(mpbus[p[4]]->type, "PCI ", 6) == 0)
|
||||
bustype = BusPCI; /* had devno = p[5]<<2 */
|
||||
else if(jehanne_memcmp(mpbus[p[4]]->type, "ISA ", 6) == 0)
|
||||
bustype = BusISA;
|
||||
if(bustype != -1)
|
||||
ioapicintrinit(bustype, p[4], p[6], p[7], p[5], lo);
|
||||
|
||||
p += 8;
|
||||
break;
|
||||
case 4: /* LINTR */
|
||||
/*
|
||||
* Format is the same as IOINTR above.
|
||||
*/
|
||||
if((lo = mpmkintr(p)) == 0){
|
||||
p += 8;
|
||||
break;
|
||||
}
|
||||
if(DBGFLG)
|
||||
mpintrprint(nil, p);
|
||||
|
||||
/*
|
||||
* Everything was checked in mpmkintr above.
|
||||
*/
|
||||
if(p[6] == 0xff){
|
||||
for(i = 0; i < Napic; i++){
|
||||
apic = lapiclookup(i);
|
||||
if(apic != nil)
|
||||
apic->lvt[p[7]] = lo;
|
||||
}
|
||||
}
|
||||
else{
|
||||
apic = lapiclookup(p[6]);
|
||||
if(apic != nil)
|
||||
apic->lvt[p[7]] = lo;
|
||||
}
|
||||
p += 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* There's nothing of real interest in the extended table,
|
||||
* should just move along, but check it for consistency.
|
||||
*/
|
||||
p = e;
|
||||
e = p + l16get(pcmp->xlength);
|
||||
while(p < e) switch(*p){
|
||||
default:
|
||||
n = p[1];
|
||||
jehanne_print("mpparse: unknown extended entry %d length %d\n", *p, n);
|
||||
for(i = 0; i < n; i++){
|
||||
if(i && ((i & 0x0f) == 0))
|
||||
jehanne_print("\n");
|
||||
jehanne_print(" %#2.2ux", *p);
|
||||
p++;
|
||||
}
|
||||
jehanne_print("\n");
|
||||
break;
|
||||
case 128:
|
||||
DBG("address space mapping\n");
|
||||
DBG(" bus %d type %d base %#llux length %#llux\n",
|
||||
p[2], p[3], l64get(p+4), l64get(p+12));
|
||||
p += p[1];
|
||||
break;
|
||||
case 129:
|
||||
DBG("bus hierarchy descriptor\n");
|
||||
DBG(" bus %d sd %d parent bus %d\n",
|
||||
p[2], p[3], p[4]);
|
||||
p += p[1];
|
||||
break;
|
||||
case 130:
|
||||
DBG("compatibility bus address space modifier\n");
|
||||
DBG(" bus %d pr %d range list %d\n",
|
||||
p[2], p[3], l32get(p+4));
|
||||
p += p[1];
|
||||
break;
|
||||
if(m->machno == 0){
|
||||
wrmsr(0x10, 0);
|
||||
m->tscticks = 0;
|
||||
} else {
|
||||
x = MACHP(0)->tscticks;
|
||||
while(x == MACHP(0)->tscticks)
|
||||
;
|
||||
wrmsr(0x10, MACHP(0)->tscticks);
|
||||
cycles(&m->tscticks);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mpsinit(void)
|
||||
mpinit(void)
|
||||
{
|
||||
uint8_t *p;
|
||||
int i, n;
|
||||
_MP_ *mp;
|
||||
PCMP *pcmp;
|
||||
int ncpu, i;
|
||||
Apic *apic;
|
||||
char *cp;
|
||||
|
||||
if((mp = sigsearch("_MP_")) == nil)
|
||||
return;
|
||||
if(DBGFLG){
|
||||
DBG("_MP_ @ %#p, addr %#ux length %ud rev %d",
|
||||
mp, l32get(mp->addr), mp->length, mp->revision);
|
||||
for(i = 0; i < sizeof(mp->feature); i++)
|
||||
DBG(" %2.2#ux", mp->feature[i]);
|
||||
DBG("\n");
|
||||
}
|
||||
if(mp->revision != 1 && mp->revision != 4)
|
||||
return;
|
||||
if(checksum(mp, mp->length*16) != 0)
|
||||
return;
|
||||
i8259init();
|
||||
syncclock();
|
||||
|
||||
if((pcmp = vmap(l32get(mp->addr), sizeof(PCMP))) == nil)
|
||||
return;
|
||||
if(pcmp->revision != 1 && pcmp->revision != 4){
|
||||
vunmap(pcmp, sizeof(PCMP));
|
||||
return;
|
||||
}
|
||||
n = l16get(pcmp->length) + l16get(pcmp->xlength);
|
||||
vunmap(pcmp, sizeof(PCMP));
|
||||
if((pcmp = vmap(l32get(mp->addr), n)) == nil)
|
||||
return;
|
||||
if(checksum(pcmp, l16get(pcmp->length)) != 0){
|
||||
vunmap(pcmp, n);
|
||||
return;
|
||||
}
|
||||
if(DBGFLG){
|
||||
DBG("PCMP @ %#p length %#ux revision %d\n",
|
||||
pcmp, l16get(pcmp->length), pcmp->revision);
|
||||
DBG(" %20.20s oaddr %#ux olength %#ux\n",
|
||||
(char*)pcmp->string, l32get(pcmp->oaddr),
|
||||
l16get(pcmp->olength));
|
||||
DBG(" entry %d apicpa %#ux\n",
|
||||
l16get(pcmp->entry), l32get(pcmp->apicpa));
|
||||
if(getconf("*apicdebug")){
|
||||
Bus *b;
|
||||
Aintr *ai;
|
||||
PCMPintr *pi;
|
||||
|
||||
DBG(" xlength %#ux xchecksum %#ux\n",
|
||||
l16get(pcmp->xlength), pcmp->xchecksum);
|
||||
for(i=0; i<=MaxAPICNO; i++){
|
||||
if(apic = mpapic[i])
|
||||
print("LAPIC%d: pa=%lux va=%#p flags=%x\n",
|
||||
i, apic->paddr, apic->addr, apic->flags);
|
||||
if(apic = mpioapic[i])
|
||||
print("IOAPIC%d: pa=%lux va=%#p flags=%x gsibase=%d mre=%d\n",
|
||||
i, apic->paddr, apic->addr, apic->flags, apic->gsibase, apic->mre);
|
||||
}
|
||||
for(b = mpbus; b; b = b->next){
|
||||
print("BUS%d type=%d flags=%x\n", b->busno, b->type, b->po|b->el);
|
||||
for(ai = b->aintr; ai; ai = ai->next){
|
||||
if(pi = ai->intr)
|
||||
print("\ttype=%d irq=%d (%d [%c]) apic=%d intin=%d flags=%x\n",
|
||||
pi->type, pi->irq, pi->irq>>2, "ABCD"[pi->irq&3],
|
||||
pi->apicno, pi->intin, pi->flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pcmp->xchecksum != 0){
|
||||
p = ((uint8_t*)pcmp) + l16get(pcmp->length);
|
||||
i = checksum(p, l16get(pcmp->xlength));
|
||||
if(((i+pcmp->xchecksum) & 0xff) != 0){
|
||||
jehanne_print("extended table checksums to %#ux\n", i);
|
||||
vunmap(pcmp, n);
|
||||
return;
|
||||
|
||||
apic = nil;
|
||||
for(i=0; i<=MaxAPICNO; i++){
|
||||
if(mpapic[i] == nil)
|
||||
continue;
|
||||
if(mpapic[i]->flags & PcmpBP){
|
||||
apic = mpapic[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(apic == nil){
|
||||
panic("mpinit: no bootstrap processor");
|
||||
return;
|
||||
}
|
||||
apic->online = 1;
|
||||
|
||||
lapicinit(apic);
|
||||
|
||||
/*
|
||||
* These interrupts are local to the processor
|
||||
* and do not appear in the I/O APIC so it is OK
|
||||
* to set them now.
|
||||
*/
|
||||
intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
|
||||
intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
|
||||
intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
|
||||
lapiconline();
|
||||
|
||||
/*
|
||||
* Initialise the application processors.
|
||||
*/
|
||||
if(cp = getconf("*ncpu")){
|
||||
ncpu = strtol(cp, 0, 0);
|
||||
if(ncpu < 1)
|
||||
ncpu = 1;
|
||||
else if(ncpu > MACHMAX)
|
||||
ncpu = MACHMAX;
|
||||
}
|
||||
else
|
||||
ncpu = MACHMAX;
|
||||
memmove((void*)APBOOTSTRAP, sipihandler, sizeof(sipihandler));
|
||||
for(i=0; i<nelem(mpapic); i++){
|
||||
if((apic = mpapic[i]) == nil)
|
||||
continue;
|
||||
if(apic->machno >= MACHMAX)
|
||||
continue;
|
||||
if(ncpu <= 1)
|
||||
break;
|
||||
if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN){
|
||||
mpstartap(apic);
|
||||
sys->nmach++;
|
||||
ncpu--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the PCMP table and set up the datastructures
|
||||
* for later interrupt enabling and application processor
|
||||
* startup.
|
||||
* we don't really know the number of processors till
|
||||
* here.
|
||||
*
|
||||
* set sys->copymode here if nmach > 1.
|
||||
* Should look for an ExtINT line and enable it.
|
||||
*/
|
||||
mpparse(pcmp);
|
||||
|
||||
lapicdump();
|
||||
iordtdump();
|
||||
if(X86FAMILY(m->cpuidax) == 3 || sys->nmach > 1)
|
||||
sys->copymode = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
mpintrcpu(void)
|
||||
{
|
||||
static Lock physidlock;
|
||||
static int physid;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The bulk of this code was written ~1995, when there was
|
||||
* one architecture and one generation of hardware, the number
|
||||
* of CPUs was up to 4(8) and the choices for interrupt routing
|
||||
* were physical, or flat logical (optionally with lowest
|
||||
* priority interrupt). Logical mode hasn't scaled well with
|
||||
* the increasing number of packages/cores/threads, so the
|
||||
* fall-back is to physical mode, which works across all processor
|
||||
* generations, both AMD and Intel, using the APIC and xAPIC.
|
||||
*
|
||||
* Interrupt routing policy can be set here.
|
||||
* Currently, just assign each interrupt to a different CPU on
|
||||
* a round-robin basis. Some idea of the packages/cores/thread
|
||||
* topology would be useful here, e.g. to not assign interrupts
|
||||
* to more than one thread in a core, or to use a "noise" core.
|
||||
* But, as usual, Intel make that an onerous task.
|
||||
*/
|
||||
lock(&physidlock);
|
||||
for(;;){
|
||||
i = physid++;
|
||||
if(physid >= nelem(mpapic))
|
||||
physid = 0;
|
||||
if(mpapic[i] == nil)
|
||||
continue;
|
||||
if(mpapic[i]->online)
|
||||
break;
|
||||
}
|
||||
unlock(&physidlock);
|
||||
|
||||
return mpapic[i]->apicno;
|
||||
}
|
||||
|
||||
/*
|
||||
* With the APIC a unique vector can be assigned to each
|
||||
* request to enable an interrupt. There are two reasons this
|
||||
* is a good idea:
|
||||
* 1) to prevent lost interrupts, no more than 2 interrupts
|
||||
* should be assigned per block of 16 vectors (there is an
|
||||
* in-service entry and a holding entry for each priority
|
||||
* level and there is one priority level per block of 16
|
||||
* interrupts).
|
||||
* 2) each input pin on the IOAPIC will receive a different
|
||||
* vector regardless of whether the devices on that pin use
|
||||
* the same IRQ as devices on another pin.
|
||||
*/
|
||||
static int
|
||||
allocvector(void)
|
||||
{
|
||||
static int round = 0, num = 0;
|
||||
static Lock l;
|
||||
int vno;
|
||||
|
||||
lock(&l);
|
||||
vno = VectorAPIC + num;
|
||||
if(vno < MaxVectorAPIC-7)
|
||||
num += 8;
|
||||
else
|
||||
num = ++round % 8;
|
||||
unlock(&l);
|
||||
return vno;
|
||||
}
|
||||
|
||||
static int
|
||||
mpintrenablex(Vctl* v, int tbdf)
|
||||
{
|
||||
Bus *bus;
|
||||
Aintr *aintr;
|
||||
Apic *apic;
|
||||
Pcidev *pcidev;
|
||||
int bno, dno, pin, hi, irq, lo, n, type, vno;
|
||||
|
||||
type = BUSTYPE(tbdf);
|
||||
bno = BUSBNO(tbdf);
|
||||
dno = BUSDNO(tbdf);
|
||||
|
||||
pin = 0;
|
||||
pcidev = nil;
|
||||
if(type == BusPCI){
|
||||
if(pcidev = pcimatchtbdf(tbdf))
|
||||
pin = pcicfgr8(pcidev, PciINTP);
|
||||
} else if(type == BusISA)
|
||||
bno = mpisabus;
|
||||
|
||||
Findbus:
|
||||
for(bus = mpbus; bus != nil; bus = bus->next){
|
||||
if(bus->type != type)
|
||||
continue;
|
||||
if(bus->busno == bno)
|
||||
break;
|
||||
}
|
||||
|
||||
if(bus == nil){
|
||||
/*
|
||||
* if the PCI device is behind a PCI-PCI bridge thats not described
|
||||
* by the MP or ACPI tables then walk up the bus translating interrupt
|
||||
* pin to parent bus.
|
||||
*/
|
||||
if(pcidev && pcidev->parent && pin > 0){
|
||||
pin = ((dno+(pin-1))%4)+1;
|
||||
pcidev = pcidev->parent;
|
||||
bno = BUSBNO(pcidev->tbdf);
|
||||
dno = BUSDNO(pcidev->tbdf);
|
||||
goto Findbus;
|
||||
}
|
||||
print("mpintrenable: can't find bus type %d, number %d\n", type, bno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* For PCI devices the interrupt pin (INT[ABCD]) and device
|
||||
* number are encoded into the entry irq field, so create something
|
||||
* to match on.
|
||||
*/
|
||||
if(bus->type == BusPCI){
|
||||
if(pin > 0)
|
||||
irq = (dno<<2)|(pin-1);
|
||||
else
|
||||
irq = -1;
|
||||
}
|
||||
else
|
||||
irq = v->irq;
|
||||
|
||||
/*
|
||||
* Find a matching interrupt entry from the list of interrupts
|
||||
* attached to this bus.
|
||||
*/
|
||||
for(aintr = bus->aintr; aintr; aintr = aintr->next){
|
||||
if(aintr->intr->irq != irq)
|
||||
continue;
|
||||
if(0){
|
||||
PCMPintr* p = aintr->intr;
|
||||
print("mpintrenablex: bus %d intin %d irq %d\n",
|
||||
p->busno, p->intin, p->irq);
|
||||
}
|
||||
/*
|
||||
* Check if already enabled. Multifunction devices may share
|
||||
* INT[A-D]# so, if already enabled, check the polarity matches
|
||||
* and the trigger is level.
|
||||
*
|
||||
* Should check the devices differ only in the function number,
|
||||
* but that can wait for the planned enable/disable rewrite.
|
||||
* The RDT read here is safe for now as currently interrupts
|
||||
* are never disabled once enabled.
|
||||
*/
|
||||
apic = aintr->apic;
|
||||
ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
|
||||
if(!(lo & ApicIMASK)){
|
||||
vno = lo & 0xFF;
|
||||
if(0) print("%s vector %d (!imask)\n", v->name, vno);
|
||||
n = mpintrinit(bus, aintr->intr, vno, v->irq);
|
||||
n |= ApicPHYSICAL; /* no-op */
|
||||
lo &= ~(ApicRemoteIRR|ApicDELIVS);
|
||||
if(n != lo){
|
||||
print("mpintrenable: multiple botch irq %d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
|
||||
v->irq, tbdf, lo, n);
|
||||
return -1;
|
||||
}
|
||||
v->isr = lapicisr;
|
||||
v->eoi = lapiceoi;
|
||||
return vno;
|
||||
}
|
||||
|
||||
vno = allocvector();
|
||||
hi = mpintrcpu()<<24;
|
||||
lo = mpintrinit(bus, aintr->intr, vno, v->irq);
|
||||
lo |= ApicPHYSICAL; /* no-op */
|
||||
if(lo & ApicIMASK){
|
||||
print("mpintrenable: disabled irq %d, tbdf %uX, lo %8.8uX, hi %8.8uX\n",
|
||||
v->irq, tbdf, lo, hi);
|
||||
return -1;
|
||||
}
|
||||
if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC)
|
||||
ioapicrdtw(apic, aintr->intr->intin, hi, lo);
|
||||
|
||||
v->isr = lapicisr;
|
||||
v->eoi = lapiceoi;
|
||||
return vno;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum {
|
||||
MSICtrl = 0x02, /* message control register (16 bit) */
|
||||
MSIAddr = 0x04, /* message address register (64 bit) */
|
||||
MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
|
||||
MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
|
||||
};
|
||||
|
||||
enum {
|
||||
HTMSIMapping = 0xA8,
|
||||
HTMSIFlags = 0x02,
|
||||
HTMSIFlagsEn = 0x01,
|
||||
};
|
||||
|
||||
static int
|
||||
htmsicapenable(Pcidev *p)
|
||||
{
|
||||
return -1;
|
||||
#if 0
|
||||
int cap, flags;
|
||||
|
||||
if((cap = pcihtcap(p, HTMSIMapping)) <= 0)
|
||||
return -1;
|
||||
flags = pcicfgr8(p, cap + HTMSIFlags);
|
||||
if((flags & HTMSIFlagsEn) == 0)
|
||||
pcicfgw8(p, cap + HTMSIFlags, flags | HTMSIFlagsEn);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
htmsienable(Pcidev *pdev)
|
||||
{
|
||||
Pcidev *p;
|
||||
|
||||
p = nil;
|
||||
while((p = pcimatch(p, 0x1022, 0)) != nil)
|
||||
if(p->did == 0x1103 || p->did == 0x1203)
|
||||
break;
|
||||
|
||||
if(p == nil)
|
||||
return 0; /* not hypertransport platform */
|
||||
|
||||
p = nil;
|
||||
while((p = pcimatch(p, 0x10de, 0)) != nil){
|
||||
switch(p->did){
|
||||
case 0x02f0: /* NVIDIA NFORCE C51 MEMC0 */
|
||||
case 0x02f1: /* NVIDIA NFORCE C51 MEMC1 */
|
||||
case 0x02f2: /* NVIDIA NFORCE C51 MEMC2 */
|
||||
case 0x02f3: /* NVIDIA NFORCE C51 MEMC3 */
|
||||
case 0x02f4: /* NVIDIA NFORCE C51 MEMC4 */
|
||||
case 0x02f5: /* NVIDIA NFORCE C51 MEMC5 */
|
||||
case 0x02f6: /* NVIDIA NFORCE C51 MEMC6 */
|
||||
case 0x02f7: /* NVIDIA NFORCE C51 MEMC7 */
|
||||
case 0x0369: /* NVIDIA NFORCE MCP55 MEMC */
|
||||
htmsicapenable(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(htmsicapenable(pdev) == 0)
|
||||
return 0;
|
||||
|
||||
for(p = pdev->parent; p != nil; p = p->parent)
|
||||
if(htmsicapenable(p) == 0)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
msiintrenable(Vctl *v)
|
||||
{
|
||||
int tbdf, vno, cap, cpu, ok64;
|
||||
Pcidev *pci;
|
||||
|
||||
if(getconf("*nomsi") != nil)
|
||||
return -1;
|
||||
tbdf = v->tbdf;
|
||||
if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
|
||||
return -1;
|
||||
pci = pcimatchtbdf(tbdf);
|
||||
if(pci == nil) {
|
||||
print("msiintrenable: could not find Pcidev for tbdf %uX\n", tbdf);
|
||||
return -1;
|
||||
}
|
||||
if(htmsienable(pci) < 0)
|
||||
return -1;
|
||||
cap = pcicap(pci, PciCapMSI);
|
||||
if(cap < 0)
|
||||
return -1;
|
||||
vno = allocvector();
|
||||
cpu = mpintrcpu();
|
||||
ok64 = (pcicfgr16(pci, cap + MSICtrl) & (1<<7)) != 0;
|
||||
pcicfgw32(pci, cap + MSIAddr, (0xFEE << 20) | (cpu << 12));
|
||||
if(ok64) pcicfgw32(pci, cap + MSIAddr + 4, 0);
|
||||
pcicfgw16(pci, cap + (ok64 ? MSIData64 : MSIData32), vno | (1<<14));
|
||||
pcicfgw16(pci, cap + MSICtrl, 1);
|
||||
v->isr = lapicisr;
|
||||
v->eoi = lapiceoi;
|
||||
return vno;
|
||||
}
|
||||
|
||||
int
|
||||
mpintrenable(Vctl* v)
|
||||
{
|
||||
int irq, tbdf, vno;
|
||||
|
||||
vno = msiintrenable(v);
|
||||
if(vno != -1)
|
||||
return vno;
|
||||
|
||||
/*
|
||||
* If the bus is known, try it.
|
||||
* BUSUNKNOWN is given both by [E]ISA devices and by
|
||||
* interrupts local to the processor (local APIC, coprocessor
|
||||
* breakpoint and page-fault).
|
||||
*/
|
||||
tbdf = v->tbdf;
|
||||
if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
|
||||
return vno;
|
||||
|
||||
irq = v->irq;
|
||||
if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
|
||||
if(irq != IrqSPURIOUS)
|
||||
v->isr = lapiceoi;
|
||||
return VectorPIC+irq;
|
||||
}
|
||||
if(irq < 0 || irq > MaxIrqPIC){
|
||||
print("mpintrenable: irq %d out of range\n", irq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either didn't find it or have to try the default buses
|
||||
* (ISA and EISA). This hack is due to either over-zealousness
|
||||
* or laziness on the part of some manufacturers.
|
||||
*
|
||||
* The MP configuration table on some older systems
|
||||
* (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
|
||||
* but none for ISA. It also has the interrupt type and
|
||||
* polarity set to 'default for this bus' which wouldn't
|
||||
* be compatible with ISA.
|
||||
*/
|
||||
if(mpeisabus != -1){
|
||||
vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
|
||||
if(vno != -1)
|
||||
return vno;
|
||||
}
|
||||
if(mpisabus != -1){
|
||||
vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
|
||||
if(vno != -1)
|
||||
return vno;
|
||||
}
|
||||
print("mpintrenable: out of choices eisa %d isa %d tbdf %uX irq %d\n",
|
||||
mpeisabus, mpisabus, v->tbdf, v->irq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
mpshutdown(void)
|
||||
{
|
||||
/*
|
||||
* Park application processors.
|
||||
*/
|
||||
if(m->machno != 0){
|
||||
splhi();
|
||||
arch->introff();
|
||||
for(;;) idle();
|
||||
}
|
||||
delay(1000);
|
||||
splhi();
|
||||
|
||||
/*
|
||||
* INIT all excluding self.
|
||||
*/
|
||||
lapicicrw(0, 0x000C0000|ApicINIT);
|
||||
|
||||
pcireset();
|
||||
}
|
||||
|
261
sys/src/kern/amd64/mp.h
Normal file
261
sys/src/kern/amd64/mp.h
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* MultiProcessor Specification Version 1.[14].
|
||||
*/
|
||||
typedef struct { /* floating pointer */
|
||||
uint8_t signature[4]; /* "_MP_" */
|
||||
int32_t physaddr; /* physical address of MP configuration table */
|
||||
uint8_t length; /* 1 */
|
||||
uint8_t specrev; /* [14] */
|
||||
uint8_t checksum; /* all bytes must add up to 0 */
|
||||
uint8_t type; /* MP system configuration type */
|
||||
uint8_t imcrp;
|
||||
uint8_t reserved[3];
|
||||
} _MP_;
|
||||
|
||||
#define _MP_sz (4+4+1+1+1+1+1+3)
|
||||
|
||||
typedef struct { /* configuration table header */
|
||||
uint8_t signature[4]; /* "PCMP" */
|
||||
uint16_t length; /* total table length */
|
||||
uint8_t version; /* [14] */
|
||||
uint8_t checksum; /* all bytes must add up to 0 */
|
||||
uint8_t product[20]; /* product id */
|
||||
uint32_t oemtable; /* OEM table pointer */
|
||||
uint16_t oemlength; /* OEM table length */
|
||||
uint16_t entry; /* entry count */
|
||||
uint32_t lapicbase; /* address of local APIC */
|
||||
uint16_t xlength; /* extended table length */
|
||||
uint8_t xchecksum; /* extended table checksum */
|
||||
uint8_t reserved;
|
||||
} PCMP;
|
||||
|
||||
#define PCMPsz (4+2+1+1+20+4+2+2+4+2+1+1)
|
||||
|
||||
typedef struct { /* processor table entry */
|
||||
uint8_t type; /* entry type (0) */
|
||||
uint8_t apicno; /* local APIC id */
|
||||
uint8_t version; /* local APIC verison */
|
||||
uint8_t flags; /* CPU flags */
|
||||
uint8_t signature[4]; /* CPU signature */
|
||||
uint32_t feature; /* feature flags from CPUID instruction */
|
||||
uint8_t reserved[8];
|
||||
} PCMPprocessor;
|
||||
|
||||
#define PCMPprocessorsz (1+1+1+1+4+4+8)
|
||||
|
||||
typedef struct { /* bus table entry */
|
||||
uint8_t type; /* entry type (1) */
|
||||
uint8_t busno; /* bus id */
|
||||
char string[6]; /* bus type string */
|
||||
} PCMPbus;
|
||||
|
||||
#define PCMPbussz (1+1+6)
|
||||
|
||||
typedef struct { /* I/O APIC table entry */
|
||||
uint8_t type; /* entry type (2) */
|
||||
uint8_t apicno; /* I/O APIC id */
|
||||
uint8_t version; /* I/O APIC version */
|
||||
uint8_t flags; /* I/O APIC flags */
|
||||
uint32_t addr; /* I/O APIC address */
|
||||
} PCMPioapic;
|
||||
|
||||
#define PCMPioapicsz (1+1+1+1+4)
|
||||
|
||||
typedef struct { /* interrupt table entry */
|
||||
uint8_t type; /* entry type ([34]) */
|
||||
uint8_t intr; /* interrupt type */
|
||||
uint16_t flags; /* interrupt flag */
|
||||
uint8_t busno; /* source bus id */
|
||||
uint8_t irq; /* source bus irq */
|
||||
uint8_t apicno; /* destination APIC id */
|
||||
uint8_t intin; /* destination APIC [L]INTIN# */
|
||||
} PCMPintr;
|
||||
|
||||
#define PCMPintrsz (1+1+2+1+1+1+1)
|
||||
|
||||
typedef struct { /* system address space mapping entry */
|
||||
uint8_t type; /* entry type (128) */
|
||||
uint8_t length; /* of this entry (20) */
|
||||
uint8_t busno; /* bus id */
|
||||
uint8_t addrtype;
|
||||
uint32_t addrbase[2];
|
||||
uint32_t addrlength[2];
|
||||
} PCMPsasm;
|
||||
|
||||
#define PCMPsasmsz (1+1+1+1+8+8)
|
||||
|
||||
typedef struct { /* bus hierarchy descriptor entry */
|
||||
uint8_t type; /* entry type (129) */
|
||||
uint8_t length; /* of this entry (8) */
|
||||
uint8_t busno; /* bus id */
|
||||
uint8_t info; /* bus info */
|
||||
uint8_t parent; /* parent bus */
|
||||
uint8_t reserved[3];
|
||||
} PCMPhierarchy;
|
||||
|
||||
#define PCMPhirarchysz (1+1+1+1+1+3)
|
||||
|
||||
typedef struct { /* compatibility bus address space modifier entry */
|
||||
uint8_t type; /* entry type (130) */
|
||||
uint8_t length; /* of this entry (8) */
|
||||
uint8_t busno; /* bus id */
|
||||
uint8_t modifier; /* address modifier */
|
||||
uint32_t range; /* predefined range list */
|
||||
} PCMPcbasm;
|
||||
|
||||
#define PCMPcbasmsz (1+1+1+1+4)
|
||||
|
||||
enum { /* table entry types */
|
||||
PcmpPROCESSOR = 0x00, /* one entry per processor */
|
||||
PcmpBUS = 0x01, /* one entry per bus */
|
||||
PcmpIOAPIC = 0x02, /* one entry per I/O APIC */
|
||||
PcmpIOINTR = 0x03, /* one entry per bus interrupt source */
|
||||
PcmpLINTR = 0x04, /* one entry per system interrupt source */
|
||||
|
||||
PcmpSASM = 0x80,
|
||||
PcmpHIERARCHY = 0x81,
|
||||
PcmpCBASM = 0x82,
|
||||
|
||||
/* PCMPprocessor and PCMPioapic flags */
|
||||
PcmpEN = 0x01, /* enabled */
|
||||
PcmpBP = 0x02, /* bootstrap processor */
|
||||
|
||||
/* PCMPiointr and PCMPlintr flags */
|
||||
PcmpPOMASK = 0x03, /* polarity conforms to specifications of bus */
|
||||
PcmpHIGH = 0x01, /* active high */
|
||||
PcmpLOW = 0x03, /* active low */
|
||||
PcmpELMASK = 0x0C, /* trigger mode of APIC input signals */
|
||||
PcmpEDGE = 0x04, /* edge-triggered */
|
||||
PcmpLEVEL = 0x0C, /* level-triggered */
|
||||
|
||||
/* PCMPiointr and PCMPlintr interrupt type */
|
||||
PcmpINT = 0x00, /* vectored interrupt from APIC Rdt */
|
||||
PcmpNMI = 0x01, /* non-maskable interrupt */
|
||||
PcmpSMI = 0x02, /* system management interrupt */
|
||||
PcmpExtINT = 0x03, /* vectored interrupt from external PIC */
|
||||
|
||||
/* PCMPsasm addrtype */
|
||||
PcmpIOADDR = 0x00, /* I/O address */
|
||||
PcmpMADDR = 0x01, /* memory address */
|
||||
PcmpPADDR = 0x02, /* prefetch address */
|
||||
|
||||
/* PCMPhierarchy info */
|
||||
PcmpSD = 0x01, /* subtractive decode bus */
|
||||
|
||||
/* PCMPcbasm modifier */
|
||||
PcmpPR = 0x01, /* predefined range list */
|
||||
};
|
||||
|
||||
/*
|
||||
* Condensed form of the MP Configuration Table.
|
||||
* This is created during a single pass through the MP Configuration
|
||||
* table.
|
||||
*/
|
||||
typedef struct Aintr Aintr;
|
||||
typedef struct Bus Bus;
|
||||
typedef struct Apic Apic;
|
||||
|
||||
typedef struct Bus {
|
||||
uint8_t type;
|
||||
uint8_t busno;
|
||||
uint8_t po;
|
||||
uint8_t el;
|
||||
|
||||
Aintr* aintr; /* interrupts tied to this bus */
|
||||
Bus* next;
|
||||
} Bus;
|
||||
|
||||
typedef struct Aintr {
|
||||
PCMPintr* intr;
|
||||
Apic* apic;
|
||||
Aintr* next;
|
||||
} Aintr;
|
||||
|
||||
typedef struct Apic {
|
||||
int type;
|
||||
int apicno;
|
||||
uint32_t* addr; /* register base address */
|
||||
uint32_t paddr;
|
||||
int flags; /* PcmpBP|PcmpEN */
|
||||
|
||||
Lock; /* I/O APIC: register access */
|
||||
int mre; /* I/O APIC: maximum redirection entry */
|
||||
int gsibase; /* I/O APIC: global system interrupt base (acpi) */
|
||||
|
||||
int lintr[2]; /* Local APIC */
|
||||
int machno;
|
||||
|
||||
int online;
|
||||
} Apic;
|
||||
|
||||
enum {
|
||||
MaxAPICNO = 254, /* 255 is physical broadcast */
|
||||
};
|
||||
|
||||
enum { /* I/O APIC registers */
|
||||
IoapicID = 0x00, /* ID */
|
||||
IoapicVER = 0x01, /* version */
|
||||
IoapicARB = 0x02, /* arbitration ID */
|
||||
IoapicRDT = 0x10, /* redirection table */
|
||||
};
|
||||
|
||||
/*
|
||||
* Common bits for
|
||||
* I/O APIC Redirection Table Entry;
|
||||
* Local APIC Local Interrupt Vector Table;
|
||||
* Local APIC Inter-Processor Interrupt;
|
||||
* Local APIC Timer Vector Table.
|
||||
*/
|
||||
enum {
|
||||
ApicFIXED = 0x00000000, /* [10:8] Delivery Mode */
|
||||
ApicLOWEST = 0x00000100, /* Lowest priority */
|
||||
ApicSMI = 0x00000200, /* System Management Interrupt */
|
||||
ApicRR = 0x00000300, /* Remote Read */
|
||||
ApicNMI = 0x00000400,
|
||||
ApicINIT = 0x00000500, /* INIT/RESET */
|
||||
ApicSTARTUP = 0x00000600, /* Startup IPI */
|
||||
ApicExtINT = 0x00000700,
|
||||
|
||||
ApicPHYSICAL = 0x00000000, /* [11] Destination Mode (RW) */
|
||||
ApicLOGICAL = 0x00000800,
|
||||
|
||||
ApicDELIVS = 0x00001000, /* [12] Delivery Status (RO) */
|
||||
ApicHIGH = 0x00000000, /* [13] Interrupt Input Pin Polarity (RW) */
|
||||
ApicLOW = 0x00002000,
|
||||
ApicRemoteIRR = 0x00004000, /* [14] Remote IRR (RO) */
|
||||
ApicEDGE = 0x00000000, /* [15] Trigger Mode (RW) */
|
||||
ApicLEVEL = 0x00008000,
|
||||
ApicIMASK = 0x00010000, /* [16] Interrupt Mask */
|
||||
};
|
||||
|
||||
extern void ioapicinit(Apic*, int);
|
||||
extern void ioapicrdtr(Apic*, int, int*, int*);
|
||||
extern void ioapicrdtw(Apic*, int, int, int);
|
||||
|
||||
extern void lapicclock(Ureg*, void*);
|
||||
extern int lapiceoi(int);
|
||||
extern void lapicerror(Ureg*, void*);
|
||||
extern void lapicicrw(uint32_t, uint32_t);
|
||||
extern void lapicinit(Apic*);
|
||||
extern void lapicintroff(void);
|
||||
extern void lapicintron(void);
|
||||
extern int lapicisr(int);
|
||||
extern void lapicnmidisable(void);
|
||||
extern void lapicnmienable(void);
|
||||
extern void lapiconline(void);
|
||||
extern void lapicspurious(Ureg*, void*);
|
||||
extern void lapicstartap(Apic*, uintptr_t);
|
||||
extern void lapictimerset(uint64_t);
|
||||
|
||||
extern int mpintrinit(Bus*, PCMPintr*, int, int);
|
||||
extern void mpinit(void);
|
||||
extern int mpintrenable(Vctl*);
|
||||
extern void mpshutdown(void);
|
||||
extern void mpstartap(Apic*);
|
||||
|
||||
extern Bus* mpbus;
|
||||
extern Bus* mpbuslast;
|
||||
extern int mpisabus;
|
||||
extern int mpeisabus;
|
||||
extern Apic *mpioapic[];
|
||||
extern Apic *mpapic[];
|
390
sys/src/kern/amd64/mtrr.c
Normal file
390
sys/src/kern/amd64/mtrr.c
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* memory-type region registers.
|
||||
*
|
||||
* due to the possibility of extended addresses (for PAE)
|
||||
* as large as 36 bits coming from the e820 memory map and the like,
|
||||
* we'll use vlongs to hold addresses and lengths, even though we don't
|
||||
* implement PAE in Plan 9.
|
||||
*/
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
|
||||
enum {
|
||||
/*
|
||||
* MTRR Physical base/mask are indexed by
|
||||
* MTRRPhys{Base|Mask}N = MTRRPhys{Base|Mask}0 + 2*N
|
||||
*/
|
||||
MTRRPhysBase0 = 0x200,
|
||||
MTRRPhysMask0 = 0x201,
|
||||
MTRRDefaultType = 0x2FF,
|
||||
MTRRCap = 0xFE,
|
||||
Nmtrr = 8,
|
||||
|
||||
/* cpuid extended function codes */
|
||||
Exthighfunc = 1ul << 31,
|
||||
Extprocsigamd,
|
||||
Extprocname0,
|
||||
Extprocname1,
|
||||
Extprocname2,
|
||||
Exttlbl1,
|
||||
Extl2,
|
||||
Extapm,
|
||||
Extaddrsz,
|
||||
|
||||
Paerange = 1LL << 36,
|
||||
};
|
||||
|
||||
enum {
|
||||
CR4PageGlobalEnable = 1 << 7,
|
||||
CR0CacheDisable = 1 << 30,
|
||||
};
|
||||
|
||||
enum {
|
||||
Uncacheable = 0,
|
||||
Writecomb = 1,
|
||||
Unknown1 = 2,
|
||||
Unknown2 = 3,
|
||||
Writethru = 4,
|
||||
Writeprot = 5,
|
||||
Writeback = 6,
|
||||
};
|
||||
|
||||
enum {
|
||||
Capvcnt = 0xff, /* mask: # of variable-range MTRRs we have */
|
||||
Capwc = 1<<8, /* flag: have write combining? */
|
||||
Capfix = 1<<10, /* flag: have fixed MTRRs? */
|
||||
Deftype = 0xff, /* default MTRR type */
|
||||
Deffixena = 1<<10, /* fixed-range MTRR enable */
|
||||
Defena = 1<<11, /* MTRR enable */
|
||||
};
|
||||
|
||||
typedef struct Mtrreg Mtrreg;
|
||||
typedef struct Mtrrop Mtrrop;
|
||||
|
||||
struct Mtrreg {
|
||||
long base;
|
||||
long mask;
|
||||
};
|
||||
|
||||
static char *types[] = {
|
||||
[Uncacheable] "uc",
|
||||
[Writecomb] "wc",
|
||||
[Unknown1] "uk1",
|
||||
[Unknown2] "uk2",
|
||||
[Writethru] "wt",
|
||||
[Writeprot] "wp",
|
||||
[Writeback] "wb",
|
||||
nil
|
||||
};
|
||||
|
||||
static int dosync;
|
||||
static Mtrreg mtrreg[Nmtrr];
|
||||
|
||||
static char *
|
||||
type2str(int type)
|
||||
{
|
||||
if(type < 0 || type >= nelem(types))
|
||||
return nil;
|
||||
return types[type];
|
||||
}
|
||||
|
||||
static int
|
||||
str2type(char *str)
|
||||
{
|
||||
char **p;
|
||||
|
||||
for(p = types; *p != nil; p++)
|
||||
if (strcmp(str, *p) == 0)
|
||||
return p - types;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
physmask(void)
|
||||
{
|
||||
uint32_t regs[4];
|
||||
static long mask = -1;
|
||||
|
||||
if (mask != -1)
|
||||
return mask;
|
||||
cpuid(Exthighfunc, regs);
|
||||
if(regs[0] >= Extaddrsz) { /* ax */
|
||||
cpuid(Extaddrsz, regs);
|
||||
mask = (1LL << (regs[0] & 0xFF)) - 1; /* ax */
|
||||
}
|
||||
mask &= Paerange - 1; /* x86 sanity */
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* limit physical addresses to 36 bits on the x86 */
|
||||
static void
|
||||
sanity(Mtrreg *mtrr)
|
||||
{
|
||||
mtrr->base &= Paerange - 1;
|
||||
mtrr->mask &= Paerange - 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ispow2(unsigned long ul)
|
||||
{
|
||||
return (ul & (ul - 1)) == 0;
|
||||
}
|
||||
|
||||
/* true if mtrr is valid */
|
||||
static int
|
||||
mtrrdec(Mtrreg *mtrr, unsigned long *ptr, unsigned long *size, int *type)
|
||||
{
|
||||
sanity(mtrr);
|
||||
*ptr = mtrr->base & ~(BY2PG-1);
|
||||
*type = mtrr->base & 0xff;
|
||||
*size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
|
||||
return (mtrr->mask >> 11) & 1;
|
||||
}
|
||||
|
||||
static void
|
||||
mtrrenc(Mtrreg *mtrr, unsigned long ptr, unsigned long size, int type, int ok)
|
||||
{
|
||||
mtrr->base = ptr | (type & 0xff);
|
||||
mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
|
||||
sanity(mtrr);
|
||||
}
|
||||
|
||||
/*
|
||||
* i is the index of the MTRR, and is multiplied by 2 because
|
||||
* mask and base offsets are interleaved.
|
||||
*/
|
||||
static void
|
||||
mtrrget(Mtrreg *mtrr, uint i)
|
||||
{
|
||||
rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
|
||||
rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
|
||||
sanity(mtrr);
|
||||
}
|
||||
|
||||
static void
|
||||
mtrrput(Mtrreg *mtrr, uint i)
|
||||
{
|
||||
sanity(mtrr);
|
||||
wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
|
||||
wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
|
||||
}
|
||||
|
||||
static int
|
||||
mtrrvcnt(void)
|
||||
{
|
||||
long cap;
|
||||
int vcnt;
|
||||
|
||||
rdmsr(MTRRCap, &cap);
|
||||
vcnt = cap & Capvcnt;
|
||||
if(vcnt > Nmtrr)
|
||||
vcnt = Nmtrr;
|
||||
return vcnt;
|
||||
}
|
||||
|
||||
static int
|
||||
mtrrgetall(void)
|
||||
{
|
||||
int i, vcnt;
|
||||
|
||||
vcnt = mtrrvcnt();
|
||||
for(i = 0; i < vcnt; i++)
|
||||
mtrrget(&mtrreg[i], i);
|
||||
return vcnt;
|
||||
}
|
||||
|
||||
static void
|
||||
mtrrputall(void)
|
||||
{
|
||||
int s, i, vcnt;
|
||||
uint32_t cr0, cr4;
|
||||
long def;
|
||||
|
||||
s = splhi();
|
||||
|
||||
cr4 = cr4get();
|
||||
cr4put(cr4 & ~CR4PageGlobalEnable);
|
||||
cr0 = cr0get();
|
||||
wbinvd();
|
||||
cr0put(cr0 | CR0CacheDisable);
|
||||
wbinvd();
|
||||
rdmsr(MTRRDefaultType, &def);
|
||||
wrmsr(MTRRDefaultType, def & ~(long)Defena);
|
||||
|
||||
vcnt = mtrrvcnt();
|
||||
for(i=0; i<vcnt; i++)
|
||||
mtrrput(&mtrreg[i], i);
|
||||
|
||||
wbinvd();
|
||||
wrmsr(MTRRDefaultType, def);
|
||||
cr0put(cr0);
|
||||
cr4put(cr4);
|
||||
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
mtrrclock(void) /* called from clock interrupt */
|
||||
{
|
||||
static Ref bar1, bar2;
|
||||
int s;
|
||||
|
||||
if(dosync == 0)
|
||||
return;
|
||||
|
||||
s = splhi();
|
||||
|
||||
/*
|
||||
* wait for all CPUs to sync here, so that the MTRR setup gets
|
||||
* done at roughly the same time on all processors.
|
||||
*/
|
||||
incref(&bar1);
|
||||
while(bar1.ref < sys->nmach)
|
||||
microdelay(10);
|
||||
|
||||
mtrrputall();
|
||||
|
||||
/*
|
||||
* wait for all CPUs to sync up again, so that we don't continue
|
||||
* executing while the MTRRs are still being set up.
|
||||
*/
|
||||
incref(&bar2);
|
||||
while(bar2.ref < sys->nmach)
|
||||
microdelay(10);
|
||||
decref(&bar1);
|
||||
while(bar1.ref > 0)
|
||||
microdelay(10);
|
||||
decref(&bar2);
|
||||
|
||||
dosync = 0;
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static char*
|
||||
mtrr0(unsigned long base, unsigned long size, char *tstr)
|
||||
{
|
||||
int i, vcnt, slot, type, mtype, mok;
|
||||
long def, cap;
|
||||
unsigned long mp, msize;
|
||||
|
||||
if(!(m->cpuiddx & Mtrr))
|
||||
return "mtrrs not supported";
|
||||
if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
|
||||
return "mtrr base or size not 4k aligned or zero size";
|
||||
if(base + size >= Paerange)
|
||||
return "mtrr range exceeds 36 bits";
|
||||
if(!ispow2(size))
|
||||
return "mtrr size not power of 2";
|
||||
if(base & (size - 1))
|
||||
return "mtrr base not naturally aligned";
|
||||
|
||||
if((type = str2type(tstr)) == -1)
|
||||
return "mtrr bad type";
|
||||
|
||||
rdmsr(MTRRCap, &cap);
|
||||
rdmsr(MTRRDefaultType, &def);
|
||||
|
||||
switch(type){
|
||||
default:
|
||||
return "mtrr unknown type";
|
||||
case Writecomb:
|
||||
if(!(cap & Capwc))
|
||||
return "mtrr type wc (write combining) unsupported";
|
||||
/* fallthrough */
|
||||
case Uncacheable:
|
||||
case Writethru:
|
||||
case Writeprot:
|
||||
case Writeback:
|
||||
break;
|
||||
}
|
||||
|
||||
vcnt = mtrrgetall();
|
||||
|
||||
slot = -1;
|
||||
for(i = 0; i < vcnt; i++){
|
||||
mok = mtrrdec(&mtrreg[i], &mp, &msize, &mtype);
|
||||
if(slot == -1 && (!mok || mtype == (def & Deftype)))
|
||||
slot = i; /* good, but look further for exact match */
|
||||
if(mok && mp == base && msize == size){
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(slot == -1)
|
||||
return "no free mtrr slots";
|
||||
|
||||
mtrrenc(&mtrreg[slot], base, size, type, 1);
|
||||
|
||||
coherence();
|
||||
|
||||
dosync = 1;
|
||||
mtrrclock();
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
char*
|
||||
mtrr(unsigned long base, unsigned long size, char *tstr)
|
||||
{
|
||||
static QLock mtrrlk;
|
||||
char *err;
|
||||
|
||||
qlock(&mtrrlk);
|
||||
err = mtrr0(base, size, tstr);
|
||||
qunlock(&mtrrlk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
mtrrprint(char *buf, long bufsize)
|
||||
{
|
||||
int i, n, vcnt, type;
|
||||
unsigned long base, size;
|
||||
Mtrreg mtrr;
|
||||
long def;
|
||||
|
||||
if(!(m->cpuiddx & Mtrr))
|
||||
return 0;
|
||||
rdmsr(MTRRDefaultType, &def);
|
||||
n = snprint(buf, bufsize, "cache default %s\n",
|
||||
type2str(def & Deftype));
|
||||
vcnt = mtrrvcnt();
|
||||
for(i = 0; i < vcnt; i++){
|
||||
mtrrget(&mtrr, i);
|
||||
if (mtrrdec(&mtrr, &base, &size, &type))
|
||||
n += snprint(buf+n, bufsize-n,
|
||||
"cache 0x%llux %llud %s\n",
|
||||
base, size, type2str(type));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
mtrrsync(void)
|
||||
{
|
||||
static long cap0, def0;
|
||||
long cap, def;
|
||||
|
||||
rdmsr(MTRRCap, &cap);
|
||||
rdmsr(MTRRDefaultType, &def);
|
||||
|
||||
if(m->machno == 0){
|
||||
cap0 = cap;
|
||||
def0 = def;
|
||||
mtrrgetall();
|
||||
return;
|
||||
}
|
||||
|
||||
if(cap0 != cap)
|
||||
print("mtrrcap%d: %lluX %lluX\n",
|
||||
m->machno, cap0, cap);
|
||||
if(def0 != def)
|
||||
print("mtrrdef%d: %lluX %lluX\n",
|
||||
m->machno, def0, def);
|
||||
mtrrputall();
|
||||
}
|
@ -1,155 +1,126 @@
|
||||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "ureg.h"
|
||||
#include "multiboot.h"
|
||||
|
||||
typedef struct Mbi Mbi;
|
||||
struct Mbi {
|
||||
uint32_t flags;
|
||||
uint32_t memlower;
|
||||
uint32_t memupper;
|
||||
uint32_t bootdevice;
|
||||
uint32_t cmdline;
|
||||
uint32_t modscount;
|
||||
uint32_t modsaddr;
|
||||
uint32_t syms[4];
|
||||
uint32_t mmaplength;
|
||||
uint32_t mmapaddr;
|
||||
uint32_t driveslength;
|
||||
uint32_t drivesaddr;
|
||||
uint32_t configtable;
|
||||
uint32_t bootloadername;
|
||||
uint32_t apmtable;
|
||||
uint32_t vbe[6];
|
||||
};
|
||||
#define AT_KERNEL(p) ((void*)(p+KZERO))
|
||||
|
||||
enum { /* flags */
|
||||
Fmem = 0x00000001, /* mem* valid */
|
||||
Fbootdevice = 0x00000002, /* bootdevice valid */
|
||||
Fcmdline = 0x00000004, /* cmdline valid */
|
||||
Fmods = 0x00000008, /* mod* valid */
|
||||
Fsyms = 0x00000010, /* syms[] has a.out info */
|
||||
Felf = 0x00000020, /* syms[] has ELF info */
|
||||
Fmmap = 0x00000040, /* mmap* valid */
|
||||
Fdrives = 0x00000080, /* drives* valid */
|
||||
Fconfigtable = 0x00000100, /* configtable* valid */
|
||||
Fbootloadername = 0x00000200, /* bootloadername* valid */
|
||||
Fapmtable = 0x00000400, /* apmtable* valid */
|
||||
Fvbe = 0x00000800, /* vbe[] valid */
|
||||
};
|
||||
|
||||
typedef struct Mod Mod;
|
||||
struct Mod {
|
||||
uint32_t modstart;
|
||||
uint32_t modend;
|
||||
uint32_t string;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
typedef struct MMap MMap;
|
||||
struct MMap {
|
||||
uint32_t size;
|
||||
uint32_t base[2];
|
||||
uint32_t length[2];
|
||||
uint32_t type;
|
||||
};
|
||||
extern int e820map(int type, uintptr_t base, uintptr_t top);
|
||||
|
||||
int
|
||||
multiboot(uint32_t magic, uint32_t pmbi, int vflag)
|
||||
multiboot(int just_log)
|
||||
{
|
||||
uint32_t magic;
|
||||
char *p, *modname;
|
||||
int i, n;
|
||||
Mbi *mbi;
|
||||
Mod *mod;
|
||||
MMap *mmap;
|
||||
multiboot_info_t *mbi;
|
||||
multiboot_module_t *mod;
|
||||
multiboot_memory_map_t *mmap;
|
||||
uint64_t addr, len;
|
||||
|
||||
if(vflag)
|
||||
jehanne_print("magic %#ux pmbi %#ux\n", magic, pmbi);
|
||||
if(magic != 0x2badb002){
|
||||
//return -1;
|
||||
jehanne_print("wrong magic in multiboot\n");
|
||||
}
|
||||
magic = (uint32_t)sys->boot_regs->ax;
|
||||
mbi = (multiboot_info_t*)sys->boot_regs->bx;
|
||||
|
||||
mbi = KADDR(pmbi);
|
||||
if(vflag)
|
||||
if(just_log)
|
||||
jehanne_print("magic %#ux infos at %#p\n", magic, mbi);
|
||||
if(magic != 0x2badb002)
|
||||
return -1;
|
||||
|
||||
if(just_log)
|
||||
jehanne_print("flags %#ux\n", mbi->flags);
|
||||
if(mbi->flags & Fcmdline){
|
||||
p = KADDR(mbi->cmdline);
|
||||
if(vflag)
|
||||
jehanne_print("cmdline <%s>\n", p);
|
||||
if(mbi->flags & MULTIBOOT_INFO_CMDLINE){
|
||||
p = AT_KERNEL(mbi->cmdline);
|
||||
if(just_log)
|
||||
jehanne_print("Multiboot Command Line:\n\t%s\n", p);
|
||||
else
|
||||
optionsinit(p);
|
||||
}
|
||||
if(mbi->flags & Fmods){
|
||||
for(i = 0; i < mbi->modscount; i++){
|
||||
mod = KADDR(mbi->modsaddr + i*16);
|
||||
if(mod->string != 0)
|
||||
p = KADDR(mod->string);
|
||||
if(mbi->flags & MULTIBOOT_INFO_MODS){
|
||||
if(just_log)
|
||||
jehanne_print("Multiboot Modules:\n");
|
||||
for(i = 0; i < mbi->mods_count; i++){
|
||||
mod = AT_KERNEL(mbi->mods_addr);
|
||||
if(mod->cmdline != 0)
|
||||
p = AT_KERNEL(mod->cmdline);
|
||||
else
|
||||
p = "";
|
||||
if(vflag)
|
||||
jehanne_print("mod %#ux %#ux <%s>\n",
|
||||
mod->modstart, mod->modend, p);
|
||||
if(just_log)
|
||||
jehanne_print("\tModule <%s> %#ux %#ux\n",
|
||||
mod->mod_start, mod->mod_end, p);
|
||||
else {
|
||||
asmmodinit(mod->modstart, mod->modend, p);
|
||||
asmmodinit(mod->mod_start, mod->mod_end, p);
|
||||
modname = jehanne_strrchr(p, '/');
|
||||
if(modname == nil)
|
||||
modname = p;
|
||||
if(*modname == '/')
|
||||
++modname;
|
||||
addbootfile(modname, KADDR(mod->modstart), mod->modend - mod->modstart);
|
||||
addbootfile(modname, AT_KERNEL(mod->mod_start), mod->mod_end - mod->mod_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mbi->flags & Fmmap){
|
||||
mmap = KADDR(mbi->mmapaddr);
|
||||
if(mbi->flags & MULTIBOOT_INFO_MEM_MAP){
|
||||
if(just_log)
|
||||
jehanne_print("Multiboot Memory Map:\n");
|
||||
mmap = AT_KERNEL(mbi->mmap_addr);
|
||||
n = 0;
|
||||
while(n < mbi->mmaplength){
|
||||
addr = (((uint64_t)mmap->base[1])<<32)|mmap->base[0];
|
||||
len = (((uint64_t)mmap->length[1])<<32)|mmap->length[0];
|
||||
while(n < mbi->mmap_length){
|
||||
addr = mmap->addr;
|
||||
len = mmap->len;
|
||||
switch(mmap->type){
|
||||
default:
|
||||
if(vflag)
|
||||
jehanne_print("type %ud", mmap->type);
|
||||
if(just_log)
|
||||
jehanne_print("\ttype %ud ", mmap->type);
|
||||
break;
|
||||
case 1:
|
||||
if(vflag)
|
||||
jehanne_print("Memory");
|
||||
else
|
||||
asmmapinit(addr, len, mmap->type);
|
||||
case MULTIBOOT_MEMORY_AVAILABLE:
|
||||
if(just_log)
|
||||
jehanne_print("\tMemory");
|
||||
break;
|
||||
case 2:
|
||||
if(vflag)
|
||||
jehanne_print("reserved");
|
||||
else
|
||||
asmmapinit(addr, len, mmap->type);
|
||||
case MULTIBOOT_MEMORY_RESERVED:
|
||||
if(just_log)
|
||||
jehanne_print("\tReserved");
|
||||
break;
|
||||
case 3:
|
||||
if(vflag)
|
||||
jehanne_print("ACPI Reclaim Memory");
|
||||
else
|
||||
asmmapinit(addr, len, mmap->type);
|
||||
if(just_log)
|
||||
jehanne_print("\tACPI Reclaim Memory");
|
||||
break;
|
||||
case 4:
|
||||
if(vflag)
|
||||
jehanne_print("ACPI NVS Memory");
|
||||
else
|
||||
asmmapinit(addr, len, mmap->type);
|
||||
if(just_log)
|
||||
jehanne_print("\tACPI NVS Memory");
|
||||
break;
|
||||
}
|
||||
if(vflag)
|
||||
jehanne_print("\n\t%#16.16llux %#16.16llux (%llud)\n",
|
||||
if(just_log)
|
||||
jehanne_print("\n\t %#16.16llux %#16.16llux (%llud)\n",
|
||||
addr, addr+len, len);
|
||||
else
|
||||
e820map(mmap->type, addr, addr+len);
|
||||
|
||||
n += mmap->size+sizeof(mmap->size);
|
||||
mmap = KADDR(mbi->mmapaddr+n);
|
||||
mmap = AT_KERNEL(mbi->mmap_addr+n);
|
||||
}
|
||||
}
|
||||
if(vflag && (mbi->flags & Fbootloadername)){
|
||||
p = KADDR(mbi->bootloadername);
|
||||
jehanne_print("bootloadername <%s>\n", p);
|
||||
if(just_log && (mbi->flags & MULTIBOOT_INFO_BOOT_LOADER_NAME)){
|
||||
p = AT_KERNEL(mbi->boot_loader_name);
|
||||
jehanne_print("Multiboot: Boot Loader Name <%s>\n", p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
222
sys/src/kern/amd64/multiboot.h
Normal file
222
sys/src/kern/amd64/multiboot.h
Normal file
@ -0,0 +1,222 @@
|
||||
/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
|
||||
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MULTIBOOT_HEADER
|
||||
#define MULTIBOOT_HEADER 1
|
||||
|
||||
/* How many bytes from the start of the file we search for the header. */
|
||||
#define MULTIBOOT_SEARCH 8192
|
||||
|
||||
/* The magic field should contain this. */
|
||||
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
|
||||
|
||||
/* This should be in %eax. */
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||
|
||||
/* The bits in the required part of flags field we don't support. */
|
||||
#define MULTIBOOT_UNSUPPORTED 0x0000fffc
|
||||
|
||||
/* Alignment of multiboot modules. */
|
||||
#define MULTIBOOT_MOD_ALIGN 0x00001000
|
||||
|
||||
/* Alignment of the multiboot info structure. */
|
||||
#define MULTIBOOT_INFO_ALIGN 0x00000004
|
||||
|
||||
/* Flags set in the 'flags' member of the multiboot header. */
|
||||
|
||||
/* Align all boot modules on i386 page (4KB) boundaries. */
|
||||
#define MULTIBOOT_PAGE_ALIGN 0x00000001
|
||||
|
||||
/* Must pass memory information to OS. */
|
||||
#define MULTIBOOT_MEMORY_INFO 0x00000002
|
||||
|
||||
/* Must pass video information to OS. */
|
||||
#define MULTIBOOT_VIDEO_MODE 0x00000004
|
||||
|
||||
/* This flag indicates the use of the address fields in the header. */
|
||||
#define MULTIBOOT_AOUT_KLUDGE 0x00010000
|
||||
|
||||
/* Flags to be set in the 'flags' member of the multiboot info structure. */
|
||||
|
||||
/* is there basic lower/upper memory information? */
|
||||
#define MULTIBOOT_INFO_MEMORY 0x00000001
|
||||
/* is there a boot device set? */
|
||||
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
|
||||
/* is the command-line defined? */
|
||||
#define MULTIBOOT_INFO_CMDLINE 0x00000004
|
||||
/* are there modules to do something with? */
|
||||
#define MULTIBOOT_INFO_MODS 0x00000008
|
||||
|
||||
/* These next two are mutually exclusive */
|
||||
|
||||
/* is there a symbol table loaded? */
|
||||
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
|
||||
/* is there an ELF section header table? */
|
||||
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
|
||||
|
||||
/* is there a full memory map? */
|
||||
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
|
||||
|
||||
/* Is there drive info? */
|
||||
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
|
||||
|
||||
/* Is there a config table? */
|
||||
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
|
||||
|
||||
/* Is there a boot loader name? */
|
||||
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
|
||||
|
||||
/* Is there a APM table? */
|
||||
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
|
||||
|
||||
/* Is there video information? */
|
||||
#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
typedef unsigned short multiboot_uint16_t;
|
||||
typedef unsigned int multiboot_uint32_t;
|
||||
typedef unsigned long long multiboot_uint64_t;
|
||||
|
||||
struct multiboot_header
|
||||
{
|
||||
/* Must be MULTIBOOT_MAGIC - see above. */
|
||||
multiboot_uint32_t magic;
|
||||
|
||||
/* Feature flags. */
|
||||
multiboot_uint32_t flags;
|
||||
|
||||
/* The above fields plus this one must equal 0 mod 2^32. */
|
||||
multiboot_uint32_t checksum;
|
||||
|
||||
/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
|
||||
multiboot_uint32_t header_addr;
|
||||
multiboot_uint32_t load_addr;
|
||||
multiboot_uint32_t load_end_addr;
|
||||
multiboot_uint32_t bss_end_addr;
|
||||
multiboot_uint32_t entry_addr;
|
||||
|
||||
/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
|
||||
multiboot_uint32_t mode_type;
|
||||
multiboot_uint32_t width;
|
||||
multiboot_uint32_t height;
|
||||
multiboot_uint32_t depth;
|
||||
};
|
||||
|
||||
/* The symbol table for a.out. */
|
||||
struct multiboot_aout_symbol_table
|
||||
{
|
||||
multiboot_uint32_t tabsize;
|
||||
multiboot_uint32_t strsize;
|
||||
multiboot_uint32_t addr;
|
||||
multiboot_uint32_t reserved;
|
||||
};
|
||||
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
|
||||
|
||||
/* The section header table for ELF. */
|
||||
struct multiboot_elf_section_header_table
|
||||
{
|
||||
multiboot_uint32_t num;
|
||||
multiboot_uint32_t size;
|
||||
multiboot_uint32_t addr;
|
||||
multiboot_uint32_t shndx;
|
||||
};
|
||||
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
|
||||
|
||||
struct multiboot_info
|
||||
{
|
||||
/* Multiboot info version number */
|
||||
multiboot_uint32_t flags;
|
||||
|
||||
/* Available memory from BIOS */
|
||||
multiboot_uint32_t mem_lower;
|
||||
multiboot_uint32_t mem_upper;
|
||||
|
||||
/* "root" partition */
|
||||
multiboot_uint32_t boot_device;
|
||||
|
||||
/* Kernel command line */
|
||||
multiboot_uint32_t cmdline;
|
||||
|
||||
/* Boot-Module list */
|
||||
multiboot_uint32_t mods_count;
|
||||
multiboot_uint32_t mods_addr;
|
||||
|
||||
union
|
||||
{
|
||||
multiboot_aout_symbol_table_t aout_sym;
|
||||
multiboot_elf_section_header_table_t elf_sec;
|
||||
} u;
|
||||
|
||||
/* Memory Mapping buffer */
|
||||
multiboot_uint32_t mmap_length;
|
||||
multiboot_uint32_t mmap_addr;
|
||||
|
||||
/* Drive Info buffer */
|
||||
multiboot_uint32_t drives_length;
|
||||
multiboot_uint32_t drives_addr;
|
||||
|
||||
/* ROM configuration table */
|
||||
multiboot_uint32_t config_table;
|
||||
|
||||
/* Boot Loader Name */
|
||||
multiboot_uint32_t boot_loader_name;
|
||||
|
||||
/* APM table */
|
||||
multiboot_uint32_t apm_table;
|
||||
|
||||
/* Video */
|
||||
multiboot_uint32_t vbe_control_info;
|
||||
multiboot_uint32_t vbe_mode_info;
|
||||
multiboot_uint16_t vbe_mode;
|
||||
multiboot_uint16_t vbe_interface_seg;
|
||||
multiboot_uint16_t vbe_interface_off;
|
||||
multiboot_uint16_t vbe_interface_len;
|
||||
};
|
||||
typedef struct multiboot_info multiboot_info_t;
|
||||
|
||||
struct multiboot_mmap_entry
|
||||
{
|
||||
multiboot_uint32_t size;
|
||||
multiboot_uint64_t addr;
|
||||
multiboot_uint64_t len;
|
||||
#define MULTIBOOT_MEMORY_AVAILABLE 1
|
||||
#define MULTIBOOT_MEMORY_RESERVED 2
|
||||
multiboot_uint32_t type;
|
||||
} __attribute__((packed));
|
||||
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
|
||||
|
||||
struct multiboot_mod_list
|
||||
{
|
||||
/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
|
||||
multiboot_uint32_t mod_start;
|
||||
multiboot_uint32_t mod_end;
|
||||
|
||||
/* Module command line */
|
||||
multiboot_uint32_t cmdline;
|
||||
|
||||
/* padding to take it to 16 bytes (must be zero) */
|
||||
multiboot_uint32_t pad;
|
||||
};
|
||||
typedef struct multiboot_mod_list multiboot_module_t;
|
||||
|
||||
#endif /* ! __ASSEMBLER__ */
|
||||
|
||||
#endif /* ! MULTIBOOT_HEADER */
|
@ -1,129 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
/*
|
||||
Conf conf;
|
||||
char *confname[1] = {
|
||||
"console",
|
||||
};
|
||||
char *confval[1] = {
|
||||
"0 b115200",
|
||||
};
|
||||
int nconf = nelem(confname);
|
||||
*/
|
||||
|
||||
/*
|
||||
* Where configuration info is left for the loaded programme.
|
||||
* This will turn into a structure as more is done by the boot loader
|
||||
* (e.g. why parse the .ini file twice?).
|
||||
* There are 3584 bytes available at CONFADDR.
|
||||
*/
|
||||
#define CONFADDR PTR2UINT(KADDR(0x0001200))
|
||||
|
||||
#define BOOTLINE ((char*)CONFADDR)
|
||||
#define BOOTLINELEN 64
|
||||
#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN))
|
||||
#define BOOTARGSLEN (4096-0x200-BOOTLINELEN)
|
||||
#define MAXCONF 64
|
||||
|
||||
char *confname[MAXCONF];
|
||||
char *confval[MAXCONF];
|
||||
int nconf;
|
||||
|
||||
void
|
||||
confoptions(void)
|
||||
{
|
||||
long i, n;
|
||||
char *cp, *line[MAXCONF], *p, *q;
|
||||
|
||||
/*
|
||||
* parse configuration args from dos file plan9.ini
|
||||
*/
|
||||
cp = BOOTARGS; /* where b.com leaves its config */
|
||||
cp[BOOTARGSLEN-1] = 0;
|
||||
|
||||
/*
|
||||
* Strip out '\r', change '\t' -> ' '.
|
||||
*/
|
||||
p = cp;
|
||||
for(q = cp; *q; q++){
|
||||
if(*q == '\r')
|
||||
continue;
|
||||
if(*q == '\t')
|
||||
*q = ' ';
|
||||
*p++ = *q;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
n = jehanne_getfields(cp, line, MAXCONF, 1, "\n");
|
||||
for(i = 0; i < n; i++){
|
||||
if(*line[i] == '#')
|
||||
continue;
|
||||
cp = jehanne_strchr(line[i], '=');
|
||||
if(cp == nil)
|
||||
continue;
|
||||
*cp++ = '\0';
|
||||
confname[nconf] = line[i];
|
||||
confval[nconf] = cp;
|
||||
nconf++;
|
||||
}
|
||||
}
|
||||
|
||||
char*
|
||||
getconf(char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nconf; i++)
|
||||
if(jehanne_cistrcmp(confname[i], name) == 0)
|
||||
return confval[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
confsetenv(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nconf; i++){
|
||||
if(confname[i][0] != '*')
|
||||
ksetenv(confname[i], confval[i], 0);
|
||||
ksetenv(confname[i], confval[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
isaconfig(char *class, int ctlrno, ISAConf *isa)
|
||||
{
|
||||
char cc[32], *p;
|
||||
int i;
|
||||
|
||||
jehanne_snprint(cc, sizeof cc, "%s%d", class, ctlrno);
|
||||
p = getconf(cc);
|
||||
if(p == nil)
|
||||
return 0;
|
||||
|
||||
isa->type = "";
|
||||
isa->nopt = jehanne_tokenize(p, isa->opt, NISAOPT);
|
||||
for(i = 0; i < isa->nopt; i++){
|
||||
p = isa->opt[i];
|
||||
if(jehanne_cistrncmp(p, "type=", 5) == 0)
|
||||
isa->type = p + 5;
|
||||
else if(jehanne_cistrncmp(p, "port=", 5) == 0)
|
||||
isa->port = jehanne_strtoul(p+5, &p, 0);
|
||||
else if(jehanne_cistrncmp(p, "irq=", 4) == 0)
|
||||
isa->irq = jehanne_strtoul(p+4, &p, 0);
|
||||
else if(jehanne_cistrncmp(p, "dma=", 4) == 0)
|
||||
isa->dma = jehanne_strtoul(p+4, &p, 0);
|
||||
else if(jehanne_cistrncmp(p, "mem=", 4) == 0)
|
||||
isa->mem = jehanne_strtoul(p+4, &p, 0);
|
||||
else if(jehanne_cistrncmp(p, "size=", 5) == 0)
|
||||
isa->size = jehanne_strtoul(p+5, &p, 0);
|
||||
else if(jehanne_cistrncmp(p, "freq=", 5) == 0)
|
||||
isa->freq = jehanne_strtoul(p+5, &p, 0);
|
||||
}
|
||||
return 1;
|
||||
}
|
@ -572,8 +572,8 @@ optiset(Pcidev *router, uint8_t link, uint8_t irq)
|
||||
uint8_t pirq;
|
||||
|
||||
pirq = pcicfgr8(router, 0xb8 + (link >> 5));
|
||||
pirq &= (link & 0x10)? 0x0f : 0xf0;
|
||||
pirq |= (link & 0x10)? (irq << 4): (irq & 15);
|
||||
pirq &= (link & 0x10)? 0x0f : 0xf0;
|
||||
pirq |= (link & 0x10)? (irq << 4): (irq & 15);
|
||||
pcicfgw8(router, 0xb8 + (link >> 5), pirq);
|
||||
}
|
||||
|
||||
@ -1420,7 +1420,7 @@ enumcaps(Pcidev *p, int (*fmatch)(Pcidev*, int, int, int), int arg)
|
||||
|
||||
/* status register bit 4 has capabilities */
|
||||
if((pcicfgr16(p, PciPSR) & 1<<4) == 0)
|
||||
return -1;
|
||||
return -1;
|
||||
switch(pcicfgr8(p, PciHDT) & 0x7F){
|
||||
default:
|
||||
return -1;
|
||||
@ -1554,7 +1554,7 @@ pcinextcap(Pcidev *pci, int offset)
|
||||
if(offset == 0) {
|
||||
if((pcicfgr16(pci, PciPSR) & (1<<4)) == 0)
|
||||
return 0; /* no capabilities */
|
||||
offset = PciCP-1;
|
||||
offset = PciCAP-1;
|
||||
}
|
||||
return pcicfgr8(pci, offset+1) & ~3;
|
||||
}
|
@ -36,7 +36,7 @@ enum {
|
||||
* section 2.16 and 3.14
|
||||
*/
|
||||
PeK10,
|
||||
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -53,7 +53,7 @@ enum { /* MSRs */
|
||||
};
|
||||
|
||||
enum { /* HW Performance Counters Event Selector */
|
||||
|
||||
|
||||
PeHo = 0x0000020000000000ull,/* Host only */
|
||||
PeGo = 0x0000010000000000ull,/* Guest only */
|
||||
PeEvMskH = 0x0000000f00000000ull,/* Event mask H */
|
||||
@ -101,7 +101,7 @@ pmcnregs(void)
|
||||
nregs = PeNregAmd;
|
||||
break;
|
||||
case PeIntel:
|
||||
cpuid(0xa, 0, info);
|
||||
cpuid(0xa, info);
|
||||
nregs = (info[0]>>8)&0xff;
|
||||
break;
|
||||
default:
|
||||
@ -124,7 +124,7 @@ pmcmsk(void)
|
||||
msk = ~0ULL;
|
||||
break;
|
||||
case PeIntel:
|
||||
cpuid(0xa, 0, info);
|
||||
cpuid(0xa, info);
|
||||
msk = (1<<((info[0]>>16)&0xff)) - 1;
|
||||
break;
|
||||
}
|
||||
@ -232,7 +232,7 @@ pmcconfigure(void)
|
||||
int i, j, isrecog;
|
||||
|
||||
isrecog = 0;
|
||||
|
||||
|
||||
if(jehanne_memcmp(&m->cpuinfo[0][1], "AuthcAMDenti", 12) == 0){
|
||||
isrecog++;
|
||||
cfg.ctrbase = PerfCtrbaseAmd;
|
||||
@ -327,7 +327,7 @@ pmctrans(PmcCtl *p)
|
||||
}
|
||||
|
||||
//PeHo|PeGo
|
||||
#define PeAll (PeOS|PeUsr)
|
||||
#define PeAll (PeOS|PeUsr)
|
||||
#define SetEvMsk(v, e) ((v)|(((e)&PeEvMskL)|(((e)<<(PeEvMsksh-8))&PeEvMskH)))
|
||||
#define SetUMsk(v, u) ((v)|(((u)<<8ull)&PeUnMsk))
|
||||
|
@ -6,7 +6,6 @@
|
||||
* modified, propagated, or distributed except according to the terms contained
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
@ -22,40 +21,25 @@
|
||||
#include <cursor.h>
|
||||
#include "screen.h"
|
||||
|
||||
#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19)
|
||||
|
||||
Point ZP = {0, 0};
|
||||
|
||||
Rectangle physgscreenr;
|
||||
|
||||
Memdata gscreendata;
|
||||
Memimage *gscreen;
|
||||
|
||||
VGAscr vgascreen[1];
|
||||
|
||||
Cursor arrow = {
|
||||
{ -1, -1 },
|
||||
{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
|
||||
0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
|
||||
0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
|
||||
0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
|
||||
},
|
||||
{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
|
||||
0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
|
||||
0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
|
||||
0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
|
||||
},
|
||||
};
|
||||
|
||||
int didswcursorinit;
|
||||
|
||||
static void *softscreen;
|
||||
|
||||
int
|
||||
screensize(int x, int y, int z, uint32_t chan)
|
||||
screensize(int x, int y, int _, uint32_t chan)
|
||||
{
|
||||
VGAscr *scr;
|
||||
void *oldsoft;
|
||||
|
||||
qlock(&drawlock);
|
||||
if(waserror()){
|
||||
qunlock(&drawlock);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
if(memimageinit() < 0)
|
||||
error("memimageinit failed");
|
||||
|
||||
lock(&vgascreenlock);
|
||||
if(waserror()){
|
||||
@ -63,54 +47,54 @@ screensize(int x, int y, int z, uint32_t chan)
|
||||
nexterror();
|
||||
}
|
||||
|
||||
memimageinit();
|
||||
scr = &vgascreen[0];
|
||||
oldsoft = softscreen;
|
||||
scr->gscreendata = nil;
|
||||
scr->gscreen = nil;
|
||||
if(gscreen){
|
||||
freememimage(gscreen);
|
||||
gscreen = nil;
|
||||
}
|
||||
|
||||
if(scr->paddr == 0){
|
||||
int width = (x*z)/BI2WD;
|
||||
void *p;
|
||||
|
||||
p = jehanne_malloc(width*BY2WD*y);
|
||||
if(p == nil)
|
||||
error("no memory for vga soft screen");
|
||||
gscreendata.bdata = softscreen = p;
|
||||
if(scr->dev && scr->dev->page){
|
||||
scr->vaddr = KADDR(VGAMEM());
|
||||
scr->apsize = 1<<16;
|
||||
}
|
||||
scr->useflush = 1;
|
||||
scr->softscreen = 1;
|
||||
}
|
||||
else{
|
||||
gscreendata.bdata = scr->vaddr;
|
||||
if(scr->softscreen){
|
||||
gscreen = allocmemimage(Rect(0,0,x,y), chan);
|
||||
scr->useflush = 1;
|
||||
}else{
|
||||
static Memdata md;
|
||||
|
||||
md.ref = 1;
|
||||
if((md.bdata = scr->vaddr) == 0)
|
||||
error("framebuffer not maped");
|
||||
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
|
||||
scr->useflush = scr->dev && scr->dev->flush;
|
||||
}
|
||||
|
||||
scr->gscreen = nil;
|
||||
if(gscreen)
|
||||
freememimage(gscreen);
|
||||
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
|
||||
if(gscreen == nil)
|
||||
error("no memory for vga memimage");
|
||||
vgaimageinit(chan);
|
||||
|
||||
scr->palettedepth = 6; /* default */
|
||||
scr->gscreendata = &gscreendata;
|
||||
scr->memdefont = getmemdefont();
|
||||
scr->gscreen = gscreen;
|
||||
scr->gscreendata = gscreen->data;
|
||||
|
||||
physgscreenr = gscreen->r;
|
||||
|
||||
vgaimageinit(chan);
|
||||
|
||||
unlock(&vgascreenlock);
|
||||
poperror();
|
||||
if(oldsoft)
|
||||
jehanne_free(oldsoft);
|
||||
|
||||
memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S);
|
||||
flushmemscreen(gscreen->r);
|
||||
|
||||
if(didswcursorinit)
|
||||
swcursorinit();
|
||||
drawcmap();
|
||||
swcursorinit();
|
||||
|
||||
qunlock(&drawlock);
|
||||
poperror();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -136,8 +120,8 @@ screenaperture(int size, int align)
|
||||
* Need to allocate some physical address space.
|
||||
* The driver will tell the card to use it.
|
||||
*/
|
||||
size = ROUNDUP(sizeof(size), 4*KiB);
|
||||
scr->paddr = (uint64_t)jehanne_malloc(size);
|
||||
size = PGROUND(size);
|
||||
scr->paddr = upaalloc(size, align);
|
||||
if(scr->paddr == 0)
|
||||
return -1;
|
||||
scr->vaddr = vmap(scr->paddr, size);
|
||||
@ -148,7 +132,7 @@ screenaperture(int size, int align)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char*
|
||||
uint8_t*
|
||||
attachscreen(Rectangle* r, uint32_t* chan, int* d, int* width, int *softscreen)
|
||||
{
|
||||
VGAscr *scr;
|
||||
@ -161,63 +145,72 @@ attachscreen(Rectangle* r, uint32_t* chan, int* d, int* width, int *softscreen)
|
||||
*chan = scr->gscreen->chan;
|
||||
*d = scr->gscreen->depth;
|
||||
*width = scr->gscreen->width;
|
||||
*softscreen = scr->useflush;
|
||||
|
||||
if(scr->gscreendata->allocd){
|
||||
/*
|
||||
* we use a memimage as softscreen. devdraw will create its own
|
||||
* screen image on the backing store of that image. when our gscreen
|
||||
* and devdraws screenimage gets freed, the imagedata will
|
||||
* be released.
|
||||
*/
|
||||
*softscreen = 0xa110c;
|
||||
scr->gscreendata->ref++;
|
||||
} else
|
||||
*softscreen = scr->useflush ? 1 : 0;
|
||||
return scr->gscreendata->bdata;
|
||||
}
|
||||
|
||||
/*
|
||||
* It would be fair to say that this doesn't work for >8-bit screens.
|
||||
*/
|
||||
void
|
||||
flushmemscreen(Rectangle r)
|
||||
{
|
||||
VGAscr *scr;
|
||||
unsigned char *sp, *disp, *sdisp, *edisp;
|
||||
uint8_t *sp, *disp, *sdisp, *edisp;
|
||||
int y, len, incs, off, page;
|
||||
|
||||
scr = &vgascreen[0];
|
||||
if(scr->gscreen == nil || scr->useflush == 0)
|
||||
return;
|
||||
if(rectclip(&r, scr->gscreen->r) == 0)
|
||||
return;
|
||||
if(scr->dev && scr->dev->flush){
|
||||
scr->dev->flush(scr, r);
|
||||
return;
|
||||
}
|
||||
if(scr->gscreen == nil || scr->useflush == 0)
|
||||
disp = scr->vaddr;
|
||||
incs = scr->gscreen->width*sizeof(uint32_t);
|
||||
off = (r.min.x*scr->gscreen->depth) / 8;
|
||||
len = (r.max.x*scr->gscreen->depth + 7) / 8;
|
||||
len -= off;
|
||||
off += r.min.y*incs;
|
||||
sp = scr->gscreendata->bdata + scr->gscreen->zero + off;
|
||||
|
||||
/*
|
||||
* Linear framebuffer with softscreen.
|
||||
*/
|
||||
if(scr->paddr){
|
||||
sdisp = disp+off;
|
||||
for(y = r.min.y; y < r.max.y; y++) {
|
||||
memmove(sdisp, sp, len);
|
||||
sp += incs;
|
||||
sdisp += incs;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Paged framebuffer window.
|
||||
*/
|
||||
if(scr->dev == nil || scr->dev->page == nil)
|
||||
return;
|
||||
|
||||
if(rectclip(&r, scr->gscreen->r) == 0)
|
||||
return;
|
||||
|
||||
incs = scr->gscreen->width * BY2WD;
|
||||
|
||||
switch(scr->gscreen->depth){
|
||||
default:
|
||||
len = 0;
|
||||
panic("flushmemscreen: depth\n");
|
||||
break;
|
||||
case 8:
|
||||
len = Dx(r);
|
||||
break;
|
||||
}
|
||||
if(len < 1)
|
||||
return;
|
||||
|
||||
off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
|
||||
page = off/scr->apsize;
|
||||
off %= scr->apsize;
|
||||
disp = scr->vaddr;
|
||||
sdisp = disp+off;
|
||||
edisp = disp+scr->apsize;
|
||||
|
||||
off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
|
||||
|
||||
sp = scr->gscreendata->bdata + off;
|
||||
|
||||
scr->dev->page(scr, page);
|
||||
for(y = r.min.y; y < r.max.y; y++) {
|
||||
if(sdisp + incs < edisp) {
|
||||
jehanne_memmove(sdisp, sp, len);
|
||||
memmove(sdisp, sp, len);
|
||||
sp += incs;
|
||||
sdisp += incs;
|
||||
}
|
||||
@ -226,13 +219,13 @@ flushmemscreen(Rectangle r)
|
||||
page++;
|
||||
if(off <= len){
|
||||
if(off > 0)
|
||||
jehanne_memmove(sdisp, sp, off);
|
||||
memmove(sdisp, sp, off);
|
||||
scr->dev->page(scr, page);
|
||||
if(len - off > 0)
|
||||
jehanne_memmove(disp, sp+off, len - off);
|
||||
memmove(disp, sp+off, len - off);
|
||||
}
|
||||
else {
|
||||
jehanne_memmove(sdisp, sp, len);
|
||||
memmove(sdisp, sp, len);
|
||||
scr->dev->page(scr, page);
|
||||
}
|
||||
sp += incs;
|
||||
@ -261,11 +254,11 @@ getcolor(uint32_t p, uint32_t* pr, uint32_t* pg, uint32_t* pb)
|
||||
}
|
||||
p &= x;
|
||||
|
||||
lock(&cursor.l);
|
||||
lock(&cursor);
|
||||
*pr = scr->colormap[p][0];
|
||||
*pg = scr->colormap[p][1];
|
||||
*pb = scr->colormap[p][2];
|
||||
unlock(&cursor.l);
|
||||
unlock(&cursor);
|
||||
}
|
||||
|
||||
int
|
||||
@ -277,7 +270,7 @@ setpalette(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
|
||||
scr = &vgascreen[0];
|
||||
d = scr->palettedepth;
|
||||
|
||||
lock(&cursor.l);
|
||||
lock(&cursor);
|
||||
scr->colormap[p][0] = r;
|
||||
scr->colormap[p][1] = g;
|
||||
scr->colormap[p][2] = b;
|
||||
@ -285,7 +278,7 @@ setpalette(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
|
||||
vgao(Pdata, r>>(32-d));
|
||||
vgao(Pdata, g>>(32-d));
|
||||
vgao(Pdata, b>>(32-d));
|
||||
unlock(&cursor.l);
|
||||
unlock(&cursor);
|
||||
|
||||
return ~0;
|
||||
}
|
||||
@ -322,27 +315,71 @@ setcolor(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
|
||||
return setpalette(p, r, g, b);
|
||||
}
|
||||
|
||||
int
|
||||
cursoron(int dolock)
|
||||
void
|
||||
swenable(VGAscr* _)
|
||||
{
|
||||
VGAscr *scr;
|
||||
int v;
|
||||
|
||||
scr = &vgascreen[0];
|
||||
if(scr->cur == nil || scr->cur->move == nil)
|
||||
return 0;
|
||||
|
||||
if(dolock)
|
||||
lock(&cursor.l);
|
||||
v = scr->cur->move(scr, mousexy());
|
||||
if(dolock)
|
||||
unlock(&cursor.l);
|
||||
|
||||
return v;
|
||||
swcursorload(&arrow);
|
||||
}
|
||||
|
||||
void
|
||||
cursoroff(int i)
|
||||
swdisable(VGAscr* _)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
swload(VGAscr* _, Cursor *curs)
|
||||
{
|
||||
swcursorload(curs);
|
||||
}
|
||||
|
||||
int
|
||||
swmove(VGAscr* _, Point p)
|
||||
{
|
||||
swcursorhide();
|
||||
swcursordraw(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
VGAcur swcursor =
|
||||
{
|
||||
"soft",
|
||||
swenable,
|
||||
swdisable,
|
||||
swload,
|
||||
swmove,
|
||||
};
|
||||
|
||||
VGAcur vgavesacur =
|
||||
{
|
||||
"soft",
|
||||
swenable,
|
||||
swdisable,
|
||||
swload,
|
||||
swmove,
|
||||
};
|
||||
|
||||
void
|
||||
cursoron(void)
|
||||
{
|
||||
VGAscr *scr;
|
||||
VGAcur *cur;
|
||||
|
||||
scr = &vgascreen[0];
|
||||
cur = scr->cur;
|
||||
if(cur == nil || cur->move == nil)
|
||||
return;
|
||||
|
||||
if(cur == &swcursor)
|
||||
qlock(&drawlock);
|
||||
lock(&cursor);
|
||||
cur->move(scr, mousexy());
|
||||
unlock(&cursor);
|
||||
if(cur == &swcursor)
|
||||
qunlock(&drawlock);
|
||||
}
|
||||
|
||||
void
|
||||
cursoroff(void)
|
||||
{
|
||||
}
|
||||
|
||||
@ -358,8 +395,8 @@ setcursor(Cursor* curs)
|
||||
scr->cur->load(scr, curs);
|
||||
}
|
||||
|
||||
int hwaccel = 1;
|
||||
int hwblank = 0; /* turned on by drivers that are known good */
|
||||
int hwaccel = 0;
|
||||
int hwblank = 0;
|
||||
int panning = 0;
|
||||
|
||||
int
|
||||
@ -367,37 +404,30 @@ hwdraw(Memdrawparam *par)
|
||||
{
|
||||
VGAscr *scr;
|
||||
Memimage *dst, *src, *mask;
|
||||
Memdata *scrd;
|
||||
int m;
|
||||
|
||||
if(hwaccel == 0)
|
||||
return 0;
|
||||
|
||||
scr = &vgascreen[0];
|
||||
if((dst=par->dst) == nil || dst->data == nil)
|
||||
scrd = scr->gscreendata;
|
||||
if(scr->gscreen == nil || scrd == nil)
|
||||
return 0;
|
||||
if((src=par->src) == nil || src->data == nil)
|
||||
if((dst = par->dst) == nil || dst->data == nil)
|
||||
return 0;
|
||||
if((mask=par->mask) == nil || mask->data == nil)
|
||||
return 0;
|
||||
|
||||
if((src = par->src) && src->data == nil)
|
||||
src = nil;
|
||||
if((mask = par->mask) && mask->data == nil)
|
||||
mask = nil;
|
||||
if(scr->cur == &swcursor){
|
||||
/*
|
||||
* always calling swcursorhide here doesn't cure
|
||||
* leaving cursor tracks nor failing to refresh menus
|
||||
* with the latest libmemdraw/draw.c.
|
||||
*/
|
||||
if(dst->data->bdata == gscreendata.bdata)
|
||||
if(dst->data->bdata == scrd->bdata)
|
||||
swcursoravoid(par->r);
|
||||
if(src->data->bdata == gscreendata.bdata)
|
||||
if(src && src->data->bdata == scrd->bdata)
|
||||
swcursoravoid(par->sr);
|
||||
if(mask->data->bdata == gscreendata.bdata)
|
||||
if(mask && mask->data->bdata == scrd->bdata)
|
||||
swcursoravoid(par->mr);
|
||||
}
|
||||
|
||||
if(dst->data->bdata != gscreendata.bdata)
|
||||
if(!hwaccel || scr->softscreen)
|
||||
return 0;
|
||||
|
||||
if(scr->fill==nil && scr->scroll==nil)
|
||||
if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -442,36 +472,62 @@ blankscreen(int blank)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vgalinearpciid(VGAscr *scr, int vid, int did)
|
||||
static char*
|
||||
vgalinearaddr0(VGAscr *scr, uint32_t paddr, int size)
|
||||
{
|
||||
Pcidev *p;
|
||||
int x, nsize;
|
||||
uint32_t npaddr;
|
||||
|
||||
p = nil;
|
||||
while((p = pcimatch(p, vid, 0)) != nil){
|
||||
if(p->ccrb != 3) /* video card */
|
||||
continue;
|
||||
if(did != 0 && p->did != did)
|
||||
continue;
|
||||
break;
|
||||
/*
|
||||
* new approach. instead of trying to resize this
|
||||
* later, let's assume that we can just allocate the
|
||||
* entire window to start with.
|
||||
*/
|
||||
if(scr->paddr == paddr && size <= scr->apsize)
|
||||
return nil;
|
||||
|
||||
if(scr->paddr){
|
||||
/*
|
||||
* could call vunmap and vmap,
|
||||
* but worried about dangling pointers in devdraw
|
||||
*/
|
||||
return "cannot grow vga frame buffer";
|
||||
}
|
||||
if(p == nil)
|
||||
error("pci video card not found");
|
||||
|
||||
scr->pci = p;
|
||||
vgalinearpci(scr);
|
||||
/* round to page boundary, just in case */
|
||||
x = paddr&(BY2PG-1);
|
||||
npaddr = paddr-x;
|
||||
nsize = PGROUND(size+x);
|
||||
|
||||
/*
|
||||
* Don't bother trying to map more than 4000x4000x32 = 64MB.
|
||||
* We only have a 256MB window.
|
||||
*/
|
||||
if(nsize > 64*MB)
|
||||
nsize = 64*MB;
|
||||
scr->vaddr = vmap(npaddr, nsize);
|
||||
if(scr->vaddr == 0)
|
||||
return "cannot allocate vga frame buffer";
|
||||
|
||||
patwc(scr->vaddr, nsize);
|
||||
|
||||
scr->vaddr = (char*)scr->vaddr+x;
|
||||
scr->paddr = paddr;
|
||||
scr->apsize = nsize;
|
||||
|
||||
mtrr(npaddr, nsize, "wc");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
vgalinearpci(VGAscr *scr)
|
||||
static char*
|
||||
vgalinearpci0(VGAscr *scr)
|
||||
{
|
||||
uint32_t paddr;
|
||||
int i, size, best;
|
||||
Pcidev *p;
|
||||
|
||||
p = scr->pci;
|
||||
if(p == nil)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Scan for largest memory region on card.
|
||||
@ -500,261 +556,182 @@ vgalinearpci(VGAscr *scr)
|
||||
if(best >= 0){
|
||||
paddr = p->mem[best].bar & ~0x0F;
|
||||
size = p->mem[best].size;
|
||||
vgalinearaddr(scr, paddr, size);
|
||||
return;
|
||||
return vgalinearaddr0(scr, paddr, size);
|
||||
}
|
||||
error("no video memory found on pci card");
|
||||
return "no video memory found on pci card";
|
||||
}
|
||||
|
||||
void
|
||||
vgalinearpci(VGAscr *scr)
|
||||
{
|
||||
char *err;
|
||||
|
||||
if(scr->pci == nil)
|
||||
return;
|
||||
if((err = vgalinearpci0(scr)) != nil)
|
||||
error(err);
|
||||
}
|
||||
|
||||
void
|
||||
vgalinearaddr(VGAscr *scr, uint32_t paddr, int size)
|
||||
{
|
||||
int x, nsize;
|
||||
uint32_t npaddr;
|
||||
char *err;
|
||||
|
||||
/*
|
||||
* new approach. instead of trying to resize this
|
||||
* later, let's assume that we can just allocate the
|
||||
* entire window to start with.
|
||||
*/
|
||||
|
||||
if(scr->paddr == paddr && size <= scr->apsize)
|
||||
return;
|
||||
|
||||
if(scr->paddr){
|
||||
/*
|
||||
* could call vunmap and vmap,
|
||||
* but worried about dangling pointers in devdraw
|
||||
*/
|
||||
error("cannot grow vga frame buffer");
|
||||
}
|
||||
|
||||
/* round to page boundary, just in case */
|
||||
x = paddr&(4*KiB-1);
|
||||
npaddr = paddr-x;
|
||||
nsize = ROUNDUP(size+x, 4*KiB);
|
||||
|
||||
/*
|
||||
* Don't bother trying to map more than 4000x4000x32 = 64MB.
|
||||
* We only have a 256MB window.
|
||||
*/
|
||||
if(nsize > 64*MB)
|
||||
nsize = 64*MB;
|
||||
scr->vaddr = vmap(npaddr, nsize);
|
||||
if(scr->vaddr == 0)
|
||||
error("cannot allocate vga frame buffer");
|
||||
scr->vaddr = (char*)scr->vaddr+x;
|
||||
scr->paddr = paddr;
|
||||
scr->apsize = nsize;
|
||||
if((err = vgalinearaddr0(scr, paddr, size)) != nil)
|
||||
error(err);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Software cursor.
|
||||
*/
|
||||
int swvisible; /* is the cursor visible? */
|
||||
int swenabled; /* is the cursor supposed to be on the screen? */
|
||||
Memimage* swback; /* screen under cursor */
|
||||
Memimage* swimg; /* cursor image */
|
||||
Memimage* swmask; /* cursor mask */
|
||||
Memimage* swimg1;
|
||||
Memimage* swmask1;
|
||||
|
||||
Point swoffset;
|
||||
Rectangle swrect; /* screen rectangle in swback */
|
||||
Point swpt; /* desired cursor location */
|
||||
Point swvispt; /* actual cursor location */
|
||||
int swvers; /* incremented each time cursor image changes */
|
||||
int swvisvers; /* the version on the screen */
|
||||
|
||||
/*
|
||||
* called with drawlock locked for us, most of the time.
|
||||
* kernel prints at inopportune times might mean we don't
|
||||
* hold the lock, but memimagedraw is now reentrant so
|
||||
* that should be okay: worst case we get cursor droppings.
|
||||
*/
|
||||
void
|
||||
swcursorhide(void)
|
||||
static char*
|
||||
bootmapfb(VGAscr *scr, uint32_t pa, uint32_t sz)
|
||||
{
|
||||
if(swvisible == 0)
|
||||
return;
|
||||
if(swback == nil)
|
||||
return;
|
||||
swvisible = 0;
|
||||
memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
|
||||
flushmemscreen(swrect);
|
||||
}
|
||||
uint32_t start, end;
|
||||
Pcidev *p;
|
||||
int i;
|
||||
|
||||
void
|
||||
swcursoravoid(Rectangle r)
|
||||
{
|
||||
if(swvisible && rectXrect(r, swrect))
|
||||
swcursorhide();
|
||||
}
|
||||
|
||||
void
|
||||
swcursordraw(void)
|
||||
{
|
||||
if(swvisible)
|
||||
return;
|
||||
if(swenabled == 0)
|
||||
return;
|
||||
if(swback == nil || swimg1 == nil || swmask1 == nil)
|
||||
return;
|
||||
assert(!canqlock(&drawlock));
|
||||
swvispt = swpt;
|
||||
swvisvers = swvers;
|
||||
swrect = rectaddpt(Rect(0,0,16,16), swvispt);
|
||||
memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
|
||||
memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
|
||||
flushmemscreen(swrect);
|
||||
swvisible = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to lock drawlock for ourselves.
|
||||
*/
|
||||
void
|
||||
swenable(VGAscr *v)
|
||||
{
|
||||
swenabled = 1;
|
||||
if(canqlock(&drawlock)){
|
||||
swcursordraw();
|
||||
qunlock(&drawlock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
swdisable(VGAscr *v)
|
||||
{
|
||||
swenabled = 0;
|
||||
if(canqlock(&drawlock)){
|
||||
swcursorhide();
|
||||
qunlock(&drawlock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
swload(VGAscr *v, Cursor *curs)
|
||||
{
|
||||
unsigned char *ip, *mp;
|
||||
int i, j, set, clr;
|
||||
|
||||
if(!swimg || !swmask || !swimg1 || !swmask1)
|
||||
return;
|
||||
/*
|
||||
* Build cursor image and mask.
|
||||
* Image is just the usual cursor image
|
||||
* but mask is a transparent alpha mask.
|
||||
*
|
||||
* The 16x16x8 memimages do not have
|
||||
* padding at the end of their scan lines.
|
||||
*/
|
||||
ip = byteaddr(swimg, ZP);
|
||||
mp = byteaddr(swmask, ZP);
|
||||
for(i=0; i<32; i++){
|
||||
set = curs->set[i];
|
||||
clr = curs->clr[i];
|
||||
for(j=0x80; j; j>>=1){
|
||||
*ip++ = set&j ? 0x00 : 0xFF;
|
||||
*mp++ = (clr|set)&j ? 0xFF : 0x00;
|
||||
for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
|
||||
for(i=0; i<nelem(p->mem); i++){
|
||||
if(p->mem[i].bar & 1)
|
||||
continue;
|
||||
start = p->mem[i].bar & ~0xF;
|
||||
end = start + p->mem[i].size;
|
||||
if(pa == start && (pa + sz) <= end){
|
||||
scr->pci = p;
|
||||
return vgalinearpci0(scr);
|
||||
}
|
||||
}
|
||||
}
|
||||
swoffset = curs->offset;
|
||||
swvers++;
|
||||
memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
|
||||
memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
|
||||
return vgalinearaddr0(scr, pa, sz);
|
||||
}
|
||||
|
||||
int
|
||||
swmove(VGAscr *v, Point p)
|
||||
char*
|
||||
rgbmask2chan(char *buf, int depth, uint32_t rm, uint32_t gm, uint32_t bm)
|
||||
{
|
||||
swpt = addpt(p, swoffset);
|
||||
return 0;
|
||||
}
|
||||
uint32_t m[4], dm; /* r,g,b,x */
|
||||
char tmp[32];
|
||||
int c, n;
|
||||
|
||||
void
|
||||
swcursorclock(void)
|
||||
{
|
||||
int x;
|
||||
if(!swenabled)
|
||||
return;
|
||||
if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
|
||||
return;
|
||||
dm = 1<<depth-1;
|
||||
dm |= dm-1;
|
||||
|
||||
x = splhi();
|
||||
if(swenabled)
|
||||
if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
|
||||
if(canqlock(&drawlock)){
|
||||
swcursorhide();
|
||||
swcursordraw();
|
||||
qunlock(&drawlock);
|
||||
m[0] = rm & dm;
|
||||
m[1] = gm & dm;
|
||||
m[2] = bm & dm;
|
||||
m[3] = (~(m[0] | m[1] | m[2])) & dm;
|
||||
|
||||
buf[0] = 0;
|
||||
Next:
|
||||
for(c=0; c<4; c++){
|
||||
for(n = 0; m[c] & (1<<n); n++)
|
||||
;
|
||||
if(n){
|
||||
m[0] >>= n, m[1] >>= n, m[2] >>= n, m[3] >>= n;
|
||||
snprint(tmp, sizeof tmp, "%c%d%s", "rgbx"[c], n, buf);
|
||||
strcpy(buf, tmp);
|
||||
goto Next;
|
||||
}
|
||||
}
|
||||
splx(x);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* called early on boot to attach to framebuffer
|
||||
* setup by bootloader/firmware or plan9.
|
||||
*/
|
||||
void
|
||||
swcursorinit(void)
|
||||
bootscreeninit(void)
|
||||
{
|
||||
static int init, warned;
|
||||
static Memdata md;
|
||||
VGAscr *scr;
|
||||
didswcursorinit = 1;
|
||||
int x, y, z;
|
||||
uint32_t chan, pa, sz;
|
||||
char *s, *p, *err;
|
||||
|
||||
/* *bootscreen=WIDTHxHEIGHTxDEPTH CHAN PA [SZ] */
|
||||
s = getconf("*bootscreen");
|
||||
if(s == nil)
|
||||
return;
|
||||
|
||||
x = strtoul(s, &s, 0);
|
||||
if(x == 0 || *s++ != 'x')
|
||||
return;
|
||||
|
||||
y = strtoul(s, &s, 0);
|
||||
if(y == 0 || *s++ != 'x')
|
||||
return;
|
||||
|
||||
z = strtoul(s, &s, 0);
|
||||
if(*s != ' ')
|
||||
return;
|
||||
if((p = strchr(++s, ' ')) == nil)
|
||||
return;
|
||||
*p = 0;
|
||||
chan = strtochan(s);
|
||||
*p = ' ';
|
||||
if(chan == 0 || chantodepth(chan) != z)
|
||||
return;
|
||||
|
||||
sz = 0;
|
||||
pa = strtoul(p+1, &s, 0);
|
||||
if(pa == 0)
|
||||
return;
|
||||
if(*s++ == ' ')
|
||||
sz = strtoul(s, nil, 0);
|
||||
if(sz < x * y * (z+7)/8)
|
||||
sz = x * y * (z+7)/8;
|
||||
|
||||
/* map framebuffer */
|
||||
scr = &vgascreen[0];
|
||||
if(scr==nil || scr->gscreen==nil)
|
||||
return;
|
||||
|
||||
if(scr->dev == nil || scr->dev->linear == nil){
|
||||
if(!warned){
|
||||
jehanne_print("cannot use software cursor on non-linear vga screen\n");
|
||||
warned = 1;
|
||||
}
|
||||
if((err = bootmapfb(scr, pa, sz)) != nil){
|
||||
print("bootmapfb: %s\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(swback){
|
||||
freememimage(swback);
|
||||
freememimage(swmask);
|
||||
freememimage(swmask1);
|
||||
freememimage(swimg);
|
||||
freememimage(swimg1);
|
||||
}
|
||||
|
||||
swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
|
||||
swmask = allocmemimage(Rect(0,0,16,16), GREY8);
|
||||
swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
|
||||
swimg = allocmemimage(Rect(0,0,16,16), GREY8);
|
||||
swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
|
||||
if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
|
||||
jehanne_print("software cursor: allocmemimage fails");
|
||||
if(memimageinit() < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
memfillcolor(swmask, DOpaque);
|
||||
memfillcolor(swmask1, DOpaque);
|
||||
memfillcolor(swimg, DBlack);
|
||||
memfillcolor(swimg1, DBlack);
|
||||
if(!init){
|
||||
init = 1;
|
||||
addclock0link(swcursorclock, 10);
|
||||
}
|
||||
md.ref = 1;
|
||||
md.bdata = scr->vaddr;
|
||||
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
|
||||
if(gscreen == nil)
|
||||
return;
|
||||
|
||||
scr->palettedepth = 6; /* default */
|
||||
scr->memdefont = getmemdefont();
|
||||
scr->gscreen = gscreen;
|
||||
scr->gscreendata = gscreen->data;
|
||||
scr->softscreen = 0;
|
||||
scr->useflush = 0;
|
||||
scr->dev = nil;
|
||||
|
||||
physgscreenr = gscreen->r;
|
||||
|
||||
vgaimageinit(chan);
|
||||
vgascreenwin(scr);
|
||||
|
||||
/* turn mouse cursor on */
|
||||
swcursorinit();
|
||||
scr->cur = &swcursor;
|
||||
scr->cur->enable(scr);
|
||||
cursoron();
|
||||
|
||||
sys->monitor = 1;
|
||||
}
|
||||
|
||||
VGAcur swcursor =
|
||||
/*
|
||||
* called from devvga when the framebuffer is setup
|
||||
* to set *bootscreen= that can be passed on to a
|
||||
* new kernel on reboot.
|
||||
*/
|
||||
void
|
||||
bootscreenconf(VGAscr *scr)
|
||||
{
|
||||
"soft",
|
||||
swenable,
|
||||
swdisable,
|
||||
swload,
|
||||
swmove,
|
||||
};
|
||||
|
||||
// A bit hokey but it saves dumbness in the build tool and other code.
|
||||
VGAcur vgavesacur =
|
||||
{
|
||||
"vesa",
|
||||
swenable,
|
||||
swdisable,
|
||||
swload,
|
||||
swmove,
|
||||
};
|
||||
char conf[100], chan[30];
|
||||
|
||||
conf[0] = '\0';
|
||||
if(scr != nil && scr->paddr != 0 && scr->gscreen != nil)
|
||||
snprint(conf, sizeof(conf), "%dx%dx%d %s %#p %d\n",
|
||||
scr->gscreen->r.max.x, scr->gscreen->r.max.y,
|
||||
scr->gscreen->depth, chantostr(chan, scr->gscreen->chan),
|
||||
scr->paddr, scr->apsize);
|
||||
ksetenv("*bootscreen", conf, 1);
|
||||
}
|
||||
|
@ -1,21 +1,13 @@
|
||||
/*
|
||||
* This file is part of the UCB release of Plan 9. It is subject to the license
|
||||
* terms in the LICENSE file found in the top-level directory of this
|
||||
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
||||
* part of the UCB release of Plan 9, including this file, may be copied,
|
||||
* modified, propagated, or distributed except according to the terms contained
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
|
||||
typedef struct Cursor Cursor;
|
||||
typedef struct Cursorinfo Cursorinfo;
|
||||
struct Cursorinfo {
|
||||
Cursor c;
|
||||
Lock l;
|
||||
Cursor;
|
||||
Lock;
|
||||
};
|
||||
|
||||
/* devmouse.c */
|
||||
extern void mousetrack(int, int, int, int);
|
||||
extern void mousetrack(int, int, int, uint32_t);
|
||||
extern void absmousetrack(int, int, int, uint32_t);
|
||||
extern Point mousexy(void);
|
||||
|
||||
extern void mouseaccelerate(int);
|
||||
@ -61,8 +53,8 @@ enum {
|
||||
#define vgai(port) inb(port)
|
||||
#define vgao(port, data) outb(port, data)
|
||||
|
||||
extern int vgaxi(int32_t, unsigned char);
|
||||
extern int vgaxo(int32_t, unsigned char, unsigned char);
|
||||
extern int vgaxi(int32_t port, uint8_t index);
|
||||
extern int vgaxo(int32_t port, uint8_t index, uint8_t data);
|
||||
|
||||
/*
|
||||
*/
|
||||
@ -103,16 +95,16 @@ struct VGAscr {
|
||||
Pcidev* pci;
|
||||
|
||||
VGAcur* cur;
|
||||
uint32_t storage;
|
||||
Cursor Cursor;
|
||||
uintptr_t storage;
|
||||
Cursor;
|
||||
|
||||
int useflush;
|
||||
|
||||
uint32_t paddr; /* frame buffer */
|
||||
uintptr_t paddr; /* frame buffer */
|
||||
void* vaddr;
|
||||
int apsize;
|
||||
int apsize;
|
||||
|
||||
uint32_t io; /* device specific registers */
|
||||
uint32_t io; /* device specific registers */
|
||||
uint32_t *mmio;
|
||||
|
||||
uint32_t colormap[Pcolours][3];
|
||||
@ -126,8 +118,8 @@ struct VGAscr {
|
||||
int (*scroll)(VGAscr*, Rectangle, Rectangle);
|
||||
void (*blank)(VGAscr*, int);
|
||||
uint32_t id; /* internal identifier for driver use */
|
||||
int isblank;
|
||||
int overlayinit;
|
||||
int overlayinit;
|
||||
int softscreen;
|
||||
};
|
||||
|
||||
extern VGAscr vgascreen[];
|
||||
@ -137,23 +129,28 @@ enum {
|
||||
};
|
||||
|
||||
/* mouse.c */
|
||||
extern void mousectl(Cmdbuf*);
|
||||
extern void mouseresize(void);
|
||||
extern void mousectl(Cmdbuf*);
|
||||
extern void mouseresize(void);
|
||||
extern void mouseredraw(void);
|
||||
|
||||
/* screen.c */
|
||||
extern int hwaccel; /* use hw acceleration; default on */
|
||||
extern int hwblank; /* use hw blanking; default on */
|
||||
extern int panning; /* use virtual screen panning; default off */
|
||||
extern int hwaccel; /* use hw acceleration */
|
||||
extern int hwblank; /* use hw blanking */
|
||||
extern int panning; /* use virtual screen panning */
|
||||
extern void addvgaseg(char*, uint32_t, uint32_t);
|
||||
extern unsigned char* attachscreen(Rectangle*, uint32_t*, int*, int*, int*);
|
||||
extern uint8_t* attachscreen(Rectangle*, uint32_t*, int*, int*, int*);
|
||||
extern void flushmemscreen(Rectangle);
|
||||
extern int cursoron(int);
|
||||
extern void cursoroff(int);
|
||||
extern void cursoron(void);
|
||||
extern void cursoroff(void);
|
||||
extern void setcursor(Cursor*);
|
||||
extern int screensize(int, int, int, uint32_t);
|
||||
extern int screenaperture(int, int);
|
||||
extern Rectangle physgscreenr; /* actual monitor size */
|
||||
extern void blankscreen(int);
|
||||
extern char* rgbmask2chan(char *buf, int depth, uint32_t rm, uint32_t gm, uint32_t bm);
|
||||
|
||||
extern void bootscreeninit(void);
|
||||
extern void bootscreenconf(VGAscr*);
|
||||
|
||||
extern VGAcur swcursor;
|
||||
extern void swcursorinit(void);
|
||||
@ -165,22 +162,25 @@ extern void swcursorunhide(void);
|
||||
extern void deletescreenimage(void);
|
||||
extern void resetscreenimage(void);
|
||||
extern int drawhasclients(void);
|
||||
extern uint32_t blanktime;
|
||||
extern void setscreenimageclipr(Rectangle);
|
||||
extern void drawflush(void);
|
||||
extern int drawidletime(void);
|
||||
extern QLock drawlock;
|
||||
|
||||
/* vga.c */
|
||||
extern void vgascreenwin(VGAscr*);
|
||||
extern void vgaimageinit(uint32_t);
|
||||
extern void vgalinearpciid(VGAscr*, int, int);
|
||||
extern void vgalinearpci(VGAscr*);
|
||||
extern void vgalinearaddr(VGAscr*, uint32_t, int);
|
||||
|
||||
extern void drawblankscreen(int);
|
||||
extern void vgablank(VGAscr*, int);
|
||||
|
||||
extern Lock vgascreenlock;
|
||||
|
||||
#define ishwimage(i) (vgascreen[0].gscreendata && (i)->data->bdata == vgascreen[0].gscreendata->bdata)
|
||||
|
||||
/* swcursor.c */
|
||||
void swcursorhide(void);
|
||||
void swcursoravoid(Rectangle);
|
||||
void swcursordraw(Point);
|
||||
void swcursorload(Cursor *);
|
||||
void swcursorinit(void);
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "io.h"
|
||||
#include "../port/error.h"
|
||||
#include "../port/sd.h"
|
||||
#include "../386/fis.h"
|
||||
#include "fis.h"
|
||||
#include "../port/sdfis.h"
|
||||
#include "ahci.h"
|
||||
#include "../port/led.h"
|
||||
|
@ -2185,7 +2185,7 @@ atadisable(SDev *sdev)
|
||||
if (ctlr->idisable)
|
||||
ctlr->idisable(ctlr);
|
||||
jehanne_snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
|
||||
intrdisable(ctlr->vector);
|
||||
intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
|
||||
if (ctlr->bmiba) {
|
||||
if (ctlr->pcidev)
|
||||
pciclrbme(ctlr->pcidev);
|
||||
|
@ -1471,7 +1471,7 @@ iadisable(SDev *s)
|
||||
ilock(c);
|
||||
ahcidisable(c->hba);
|
||||
jehanne_snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
|
||||
intrdisable(c->vector);
|
||||
intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
|
||||
c->enabled = 0;
|
||||
iunlock(c);
|
||||
return 1;
|
@ -475,4 +475,3 @@ again:
|
||||
|
||||
return rlen;
|
||||
}
|
||||
|
@ -1,122 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "apic.h"
|
||||
#include "sipi.h"
|
||||
|
||||
#define SIPIHANDLER (KZERO+0x3000)
|
||||
|
||||
/*
|
||||
* Parameters are passed to the bootstrap code via a vector
|
||||
* in low memory indexed by the APIC number of the processor.
|
||||
* The layout, size, and location have to be kept in sync
|
||||
* with the handler code in l64sipi.s.
|
||||
*/
|
||||
typedef struct Sipi Sipi;
|
||||
struct Sipi {
|
||||
uint32_t pml4;
|
||||
uint32_t _4_;
|
||||
uintptr_t stack;
|
||||
Mach* mach;
|
||||
uintptr_t pc;
|
||||
};
|
||||
|
||||
void
|
||||
sipi(void)
|
||||
{
|
||||
Lapic *apic;
|
||||
Mach *mach;
|
||||
Sipi *sipi;
|
||||
int apicno, i, nproc;
|
||||
uint8_t *sipiptr;
|
||||
uintmem sipipa;
|
||||
uint8_t *alloc, *p;
|
||||
extern void squidboy(int);
|
||||
|
||||
/*
|
||||
* Move the startup code into place,
|
||||
* must be aligned properly.
|
||||
*/
|
||||
sipipa = mmuphysaddr(SIPIHANDLER);
|
||||
if((sipipa & (4*KiB - 1)) || sipipa > (1*MiB - 2*4*KiB))
|
||||
return;
|
||||
sipiptr = UINT2PTR(SIPIHANDLER);
|
||||
jehanne_memmove(sipiptr, sipihandler, sizeof(sipihandler));
|
||||
jehanne_memset(sipiptr+4*KiB, 0, sizeof(Sipi)*Napic);
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* The Universal Startup Algorithm described in the MP Spec. 1.4.
|
||||
* The data needed per-processor is the sum of the stack, page
|
||||
* table pages, vsvm page and the Mach page. The layout is similar
|
||||
* to that described in data.h for the bootstrap processor, but
|
||||
* with any unused space elided.
|
||||
*/
|
||||
nproc = 0;
|
||||
for(apicno = 0; apicno < Napic; apicno++){
|
||||
apic = lapiclookup(apicno);
|
||||
if(apic == nil || !apic->useable || apic->machno == 0)
|
||||
continue;
|
||||
if(++nproc >= MACHMAX){
|
||||
jehanne_print("sipi: MACHMAX too small, need %d\n", nproc);
|
||||
break;
|
||||
}
|
||||
sipi = &((Sipi*)(sipiptr+4*KiB))[apicno];
|
||||
|
||||
/*
|
||||
* NOTE: for now, share the page tables with the
|
||||
* bootstrap processor, until this code is worked out,
|
||||
* so only the Mach and stack portions are used below.
|
||||
*/
|
||||
alloc = jehanne_mallocalign(MACHSTKSZ+4*PTSZ+4*KiB+MACHSZ, 4096, 0, 0);
|
||||
if(alloc == nil)
|
||||
continue;
|
||||
jehanne_memset(alloc, 0, MACHSTKSZ+4*PTSZ+4*KiB+MACHSZ);
|
||||
p = alloc+MACHSTKSZ;
|
||||
|
||||
sipi->pml4 = cr3get();
|
||||
sipi->stack = PTR2UINT(p);
|
||||
|
||||
p += 4*PTSZ+4*KiB;
|
||||
|
||||
/*
|
||||
* Committed. If the AP startup fails, can't safely
|
||||
* release the resources, who knows what mischief
|
||||
* the AP is up to. Perhaps should try to put it
|
||||
* back into the INIT state?
|
||||
*/
|
||||
mach = (Mach*)p;
|
||||
sipi->mach = mach;
|
||||
mach->machno = apic->machno; /* NOT one-to-one... */
|
||||
mach->splpc = PTR2UINT(squidboy);
|
||||
sipi->pc = mach->splpc;
|
||||
mach->apicno = apicno;
|
||||
mach->stack = PTR2UINT(alloc);
|
||||
mach->vsvm = alloc+MACHSTKSZ+4*PTSZ;
|
||||
mach->pml4 = m->pml4;
|
||||
|
||||
p = KADDR(0x467);
|
||||
*p++ = sipipa;
|
||||
*p++ = sipipa>>8;
|
||||
*p++ = 0;
|
||||
*p = 0;
|
||||
|
||||
nvramwrite(0x0f, 0x0a);
|
||||
lapicsipi(apicno, sipipa);
|
||||
|
||||
for(i = 0; i < 1000; i++){
|
||||
if(mach->splpc == 0)
|
||||
break;
|
||||
millidelay(5);
|
||||
}
|
||||
nvramwrite(0x0f, 0x00);
|
||||
|
||||
DBG("apicno%d: machno %d mach %#p (%#p) %dMHz\n",
|
||||
apicno, mach->machno,
|
||||
mach, sys->machptr[mach->machno],
|
||||
mach->cpumhz);
|
||||
}
|
||||
}
|
107
sys/src/kern/amd64/squidboy.c
Normal file
107
sys/src/kern/amd64/squidboy.c
Normal file
@ -0,0 +1,107 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "ureg.h"
|
||||
|
||||
#include "mp.h"
|
||||
|
||||
extern void initialize_processor(void);
|
||||
|
||||
static void
|
||||
squidboy(Apic* apic)
|
||||
{
|
||||
initialize_processor();
|
||||
mmuinit();
|
||||
cpuidentify();
|
||||
cpuidprint();
|
||||
syncclock();
|
||||
active.machs[m->machno] = 1;
|
||||
apic->online = 1;
|
||||
m->online = 1;
|
||||
lapicinit(apic);
|
||||
lapiconline();
|
||||
timersinit();
|
||||
schedinit();
|
||||
}
|
||||
|
||||
void
|
||||
mpstartap(Apic* apic)
|
||||
{
|
||||
uintptr_t *apbootp, *pml4, *pdp0;
|
||||
Segdesc *gdt;
|
||||
Mach *mach;
|
||||
uint8_t *p;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Initialise the AP page-tables and Mach structure.
|
||||
* Xspanalloc will panic if an allocation can't be made.
|
||||
*/
|
||||
p = xspanalloc(2*PTSZ + BY2PG + MACHSIZE, BY2PG, 0);
|
||||
pml4 = (uintptr*)p;
|
||||
p += PTSZ;
|
||||
pdp0 = (uintptr*)p;
|
||||
p += PTSZ;
|
||||
gdt = (Segdesc*)p;
|
||||
p += BY2PG;
|
||||
mach = (Mach*)p;
|
||||
|
||||
memset(pml4, 0, PTSZ);
|
||||
memset(pdp0, 0, PTSZ);
|
||||
memset(gdt, 0, BY2PG);
|
||||
memset(mach, 0, MACHSIZE);
|
||||
|
||||
mach->machno = apic->machno;
|
||||
mach->pml4 = pml4;
|
||||
mach->gdt = gdt; /* filled by mmuinit */
|
||||
MACHP(mach->machno) = mach;
|
||||
|
||||
/*
|
||||
* map KZERO (note that we share the KZERO (and VMAP)
|
||||
* PDP between processors.
|
||||
*/
|
||||
pml4[PTLX(KZERO, 3)] = MACHP(0)->pml4[PTLX(KZERO, 3)];
|
||||
pml4[PTLX(VMAP, 3)] = MACHP(0)->pml4[PTLX(VMAP, 3)];
|
||||
|
||||
/* double map */
|
||||
pml4[0] = PADDR(pdp0) | PTEWRITE|PTEVALID;
|
||||
pdp0[0] = *mmuwalk(pml4, KZERO, 2, 0);
|
||||
|
||||
/*
|
||||
* Tell the AP where its kernel vector and pdb are.
|
||||
* The offsets are known in the AP bootstrap code.
|
||||
*/
|
||||
apbootp = (uintptr*)(APBOOTSTRAP+0x08);
|
||||
apbootp[0] = (uintptr)squidboy; /* assembler jumps here eventually */
|
||||
apbootp[1] = (uintptr)PADDR(pml4);
|
||||
apbootp[2] = (uintptr)apic;
|
||||
apbootp[3] = (uintptr)mach;
|
||||
|
||||
/*
|
||||
* Universal Startup Algorithm.
|
||||
*/
|
||||
p = KADDR(0x467); /* warm-reset vector */
|
||||
*p++ = PADDR(APBOOTSTRAP);
|
||||
*p++ = PADDR(APBOOTSTRAP)>>8;
|
||||
i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;
|
||||
/* code assumes i==0 */
|
||||
if(i != 0)
|
||||
print("mp: bad APBOOTSTRAP\n");
|
||||
*p++ = i;
|
||||
*p = i>>8;
|
||||
coherence();
|
||||
|
||||
nvramwrite(0x0F, 0x0A); /* shutdown code: warm reset upon init ipi */
|
||||
lapicstartap(apic, PADDR(APBOOTSTRAP));
|
||||
for(i = 0; i < 100000; i++){
|
||||
if(arch->fastclock == tscticks)
|
||||
cycles(&m->tscticks); /* for ap's syncclock(); */
|
||||
if(apic->online)
|
||||
break;
|
||||
delay(1);
|
||||
}
|
||||
nvramwrite(0x0F, 0x00);
|
||||
}
|
@ -46,7 +46,6 @@ static void
|
||||
noted(Ureg* cur, uintptr_t arg0)
|
||||
{
|
||||
NFrame *nf;
|
||||
Note note;
|
||||
Ureg *nur;
|
||||
|
||||
qlock(&up->debug);
|
||||
@ -55,11 +54,13 @@ noted(Ureg* cur, uintptr_t arg0)
|
||||
pprint("suicide: call to noted when not notified\n");
|
||||
pexit("Suicide", 0);
|
||||
}
|
||||
awake_gc_note(up);
|
||||
up->notified = 0;
|
||||
fpunoted();
|
||||
|
||||
nf = up->ureg;
|
||||
|
||||
up->fpstate &= ~FPillegal;
|
||||
|
||||
/* sanity clause */
|
||||
if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){
|
||||
qunlock(&up->debug);
|
||||
@ -115,17 +116,13 @@ noted(Ureg* cur, uintptr_t arg0)
|
||||
cur->sp = PTR2UINT(nf);
|
||||
break;
|
||||
default:
|
||||
jehanne_memmove(¬e, &up->lastnote, sizeof(Note));
|
||||
qunlock(&up->debug);
|
||||
pprint("suicide: bad arg %#p in noted: %s\n", arg0, note.msg);
|
||||
pexit(note.msg, 0);
|
||||
break;
|
||||
up->lastnote.flag = NDebug;
|
||||
/* fall through */
|
||||
case NDFLT:
|
||||
jehanne_memmove(¬e, &up->lastnote, sizeof(Note));
|
||||
qunlock(&up->debug);
|
||||
if(note.flag == NDebug)
|
||||
pprint("suicide: %s\n", note.msg);
|
||||
pexit(note.msg, note.flag != NDebug);
|
||||
if(up->lastnote.flag == NDebug)
|
||||
pprint("suicide: %s\n", up->lastnote.msg);
|
||||
pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -148,7 +145,11 @@ notify(Ureg* ureg)
|
||||
if(up->nnote == 0)
|
||||
return 0;
|
||||
|
||||
fpunotify(ureg);
|
||||
if(up->fpstate == FPactive){
|
||||
fpsave(&up->fpsave);
|
||||
up->fpstate = FPinactive;
|
||||
}
|
||||
up->fpstate |= FPillegal;
|
||||
|
||||
s = spllo();
|
||||
qlock(&up->debug);
|
||||
@ -238,7 +239,6 @@ syscall(Syscalls scallnr, Ureg* ureg)
|
||||
m->syscall++;
|
||||
up->inkernel = 1;
|
||||
up->cursyscall = (Syscalls)scallnr;
|
||||
up->blockingsc = 0;
|
||||
up->pc = ureg->ip;
|
||||
up->dbgreg = ureg;
|
||||
if(up->trace && (pt = proctrace) != nil)
|
||||
@ -250,8 +250,6 @@ syscall(Syscalls scallnr, Ureg* ureg)
|
||||
}
|
||||
|
||||
up->scallnr = scallnr;
|
||||
if(scallnr == SysRfork)
|
||||
fpusysrfork(ureg);
|
||||
spllo();
|
||||
|
||||
startns = 0;
|
||||
@ -355,13 +353,8 @@ syscall(Syscalls scallnr, Ureg* ureg)
|
||||
splhi();
|
||||
if(scallnr != SysRfork && (up->procctl || up->nnote))
|
||||
notify(ureg);
|
||||
else if(up->blockingsc){
|
||||
if(up->blockingsc != scallnr)
|
||||
panic("syscall: up->blockingsc dirty");
|
||||
if(canwakeup(scallnr))
|
||||
awokeproc(up);
|
||||
up->blockingsc = 0;
|
||||
}
|
||||
else
|
||||
awake_awakened(up); // we are not sleeping after all!
|
||||
|
||||
/* if we delayed sched because we held a lock, sched now */
|
||||
if(up->delaysched){
|
||||
@ -412,7 +405,9 @@ sysexecregs(uintptr_t entry, uint32_t ssize)
|
||||
ureg = up->dbgreg;
|
||||
ureg->sp = PTR2UINT(sp);
|
||||
ureg->ip = entry;
|
||||
ureg->type = 64; /* fiction for acid */
|
||||
ureg->cs = UESEL;
|
||||
ureg->ss = UDSEL;
|
||||
ureg->r14 = ureg->r15 = 0; /* extern user registers */
|
||||
ureg->r12 = up->pid;
|
||||
|
||||
/*
|
||||
@ -425,7 +420,9 @@ sysexecregs(uintptr_t entry, uint32_t ssize)
|
||||
void
|
||||
sysprocsetup(Proc* p)
|
||||
{
|
||||
fpusysprocsetup(p);
|
||||
fpprocsetup(p);
|
||||
cycles(&p->kentry);
|
||||
p->pcycles = -p->kentry;
|
||||
}
|
||||
|
||||
void
|
||||
@ -448,9 +445,9 @@ sysrforkchild(Proc* child, Proc* parent)
|
||||
cureg = (Ureg*)(child->sched.sp+STACKPAD*BY2SE);
|
||||
jehanne_memmove(cureg, parent->dbgreg, sizeof(Ureg));
|
||||
|
||||
cureg->ax = 0;
|
||||
|
||||
/* Things from bottom of syscall which were never executed */
|
||||
child->psstate = 0;
|
||||
child->inkernel = 0;
|
||||
|
||||
fpusysrforkchild(child, parent);
|
||||
}
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#include "amd64.h"
|
||||
|
||||
static int trapinited;
|
||||
|
||||
extern int notify(Ureg*);
|
||||
|
||||
static void debugbpt(Ureg*, void*);
|
||||
@ -54,12 +56,19 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
||||
Vctl *v;
|
||||
|
||||
if(f == nil){
|
||||
jehanne_print("intrenable: nil handler for %d, tbdf %#ux for %s\n",
|
||||
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
|
||||
irq, tbdf, name);
|
||||
return nil;
|
||||
}
|
||||
|
||||
v = jehanne_malloc(sizeof(Vctl));
|
||||
if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0)){
|
||||
print("intrenable: got unassigned irq %d, tbdf 0x%uX for %s\n",
|
||||
irq, tbdf, name);
|
||||
irq = -1;
|
||||
}
|
||||
|
||||
if((v = xalloc(sizeof(Vctl))) == nil)
|
||||
panic("intrenable: out of memory");
|
||||
v->isintr = 1;
|
||||
v->irq = irq;
|
||||
v->tbdf = tbdf;
|
||||
@ -69,28 +78,24 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
||||
v->name[KNAMELEN-1] = 0;
|
||||
|
||||
ilock(&vctllock);
|
||||
vno = ioapicintrenable(v);
|
||||
vno = arch->intrenable(v);
|
||||
if(vno == -1){
|
||||
iunlock(&vctllock);
|
||||
jehanne_print("intrenable: couldn't enable irq %d, tbdf %#ux for %s\n",
|
||||
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
|
||||
irq, tbdf, v->name);
|
||||
jehanne_free(v);
|
||||
xfree(v);
|
||||
return nil;
|
||||
}
|
||||
if(vctl[vno]){
|
||||
if(vctl[v->vno]->isr != v->isr || vctl[v->vno]->eoi != v->eoi)
|
||||
if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
|
||||
panic("intrenable: handler: %s %s %#p %#p %#p %#p",
|
||||
vctl[v->vno]->name, v->name,
|
||||
vctl[v->vno]->isr, v->isr, vctl[v->vno]->eoi, v->eoi);
|
||||
vctl[vno]->name, v->name,
|
||||
vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
|
||||
v->next = vctl[vno];
|
||||
}
|
||||
v->vno = vno;
|
||||
v->next = vctl[vno];
|
||||
vctl[vno] = v;
|
||||
iunlock(&vctllock);
|
||||
|
||||
if(v->mask != nil)
|
||||
v->mask(v, 0);
|
||||
|
||||
/*
|
||||
* Return the assigned vector so intrdisable can find
|
||||
* the handler; the IRQ is useless in the wondrefule world
|
||||
@ -100,26 +105,42 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
||||
}
|
||||
|
||||
int
|
||||
intrdisable(void* vector)
|
||||
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
|
||||
{
|
||||
Vctl *v, **vl;
|
||||
Vctl **pv, *v;
|
||||
int vno;
|
||||
|
||||
if(arch->intrvecno == nil || (tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))){
|
||||
/*
|
||||
* on APIC machine, irq is pretty meaningless
|
||||
* and disabling a the vector is not implemented.
|
||||
* however, we still want to remove the matching
|
||||
* Vctl entry to prevent calling Vctl.f() with a
|
||||
* stale Vctl.a pointer.
|
||||
*/
|
||||
irq = -1;
|
||||
vno = VectorPIC;
|
||||
} else {
|
||||
vno = arch->intrvecno(irq);
|
||||
}
|
||||
ilock(&vctllock);
|
||||
v = vector;
|
||||
for(vl = &vctl[v->vno]; *vl != nil; vl = &(*vl)->next)
|
||||
if(*vl == v)
|
||||
do {
|
||||
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
|
||||
if(v->isintr && (v->irq == irq || irq == -1)
|
||||
&& v->tbdf == tbdf && v->f == f && v->a == a
|
||||
&& strcmp(v->name, name) == 0)
|
||||
break;
|
||||
}
|
||||
if(v != nil){
|
||||
*pv = v->next;
|
||||
xfree(v);
|
||||
|
||||
if(irq != -1 && vctl[vno] == nil && arch->intrdisable != nil)
|
||||
arch->intrdisable(irq);
|
||||
break;
|
||||
if(*vl == nil)
|
||||
panic("intrdisable: v %#p", v);
|
||||
if(v->mask != nil)
|
||||
v->mask(v, 1);
|
||||
v->f(nil, v->a);
|
||||
*vl = v->next;
|
||||
ioapicintrdisable(v->vno);
|
||||
}
|
||||
} while(irq == -1 && ++vno <= MaxVectorAPIC);
|
||||
iunlock(&vctllock);
|
||||
|
||||
jehanne_free(v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -167,9 +188,10 @@ trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
|
||||
{
|
||||
Vctl *v;
|
||||
|
||||
if(vno < 0 || vno >= 256)
|
||||
panic("trapenable: vno %d\n", vno);
|
||||
v = jehanne_malloc(sizeof(Vctl));
|
||||
if(vno < 0 || vno >= VectorPIC)
|
||||
panic("trapenable: vno %d", vno);
|
||||
if((v = xalloc(sizeof(Vctl))) == nil)
|
||||
panic("trapenable: out of memory");
|
||||
v->tbdf = BUSUNKNOWN;
|
||||
v->f = f;
|
||||
v->a = a;
|
||||
@ -177,7 +199,8 @@ trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
|
||||
v->name[KNAMELEN-1] = 0;
|
||||
|
||||
ilock(&vctllock);
|
||||
v->next = vctl[vno];
|
||||
if(vctl[vno])
|
||||
v->next = vctl[vno]->next;
|
||||
vctl[vno] = v;
|
||||
iunlock(&vctllock);
|
||||
}
|
||||
@ -194,10 +217,48 @@ nmienable(void)
|
||||
outb(0x70, 0);
|
||||
|
||||
x = inb(0x61) & 0x07; /* Enable NMI */
|
||||
outb(0x61, 0x08|x);
|
||||
outb(0x61, 0x0C|x);
|
||||
outb(0x61, x);
|
||||
}
|
||||
|
||||
void
|
||||
trapinit0(void)
|
||||
{
|
||||
uint32_t d1, v;
|
||||
uintptr_t vaddr;
|
||||
Segdesc *idt;
|
||||
|
||||
idt = (Segdesc*)IDTADDR;
|
||||
vaddr = (uintptr_t)idthandlers;
|
||||
for(v = 0; v < 256; v++){
|
||||
d1 = (vaddr & 0xFFFF0000)|SEGP;
|
||||
switch(v){
|
||||
|
||||
case VectorBPT:
|
||||
d1 |= SEGPL(3)|SEGIG;
|
||||
break;
|
||||
|
||||
case VectorSYSCALL:
|
||||
d1 |= SEGPL(3)|SEGIG;
|
||||
break;
|
||||
|
||||
default:
|
||||
d1 |= SEGPL(0)|SEGIG;
|
||||
break;
|
||||
}
|
||||
|
||||
idt->d0 = (vaddr & 0xFFFF)|(KESEL<<16);
|
||||
idt->d1 = d1;
|
||||
idt++;
|
||||
|
||||
idt->d0 = (vaddr >> 32);
|
||||
idt->d1 = 0;
|
||||
idt++;
|
||||
|
||||
vaddr += 6;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
trapinit(void)
|
||||
{
|
||||
@ -208,48 +269,50 @@ trapinit(void)
|
||||
* Special traps.
|
||||
* Syscall() is called directly without going through trap().
|
||||
*/
|
||||
trapenable(IdtBP, debugbpt, 0, "#BP");
|
||||
trapenable(IdtPF, faultamd64, 0, "#PF");
|
||||
trapenable(IdtDF, doublefault, 0, "#DF");
|
||||
trapenable(Idt0F, unexpected, 0, "#15");
|
||||
// trapenable(VectorDE, debugexc, 0, "debugexc");
|
||||
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
||||
trapenable(VectorPF, faultamd64, 0, "faultamd64");
|
||||
trapenable(Vector2F, doublefault, 0, "doublefault");
|
||||
trapenable(Vector15, unexpected, 0, "unexpected");
|
||||
nmienable();
|
||||
|
||||
addarchfile("irqalloc", 0444, irqallocread, nil);
|
||||
trapinited = 1;
|
||||
}
|
||||
|
||||
static char* excname[32] = {
|
||||
"#DE", /* Divide-by-Zero Error */
|
||||
"#DB", /* Debug */
|
||||
"#NMI", /* Non-Maskable-Interrupt */
|
||||
"#BP", /* Breakpoint */
|
||||
"#OF", /* Overflow */
|
||||
"#BR", /* Bound-Range */
|
||||
"#UD", /* Invalid-Opcode */
|
||||
"#NM", /* Device-Not-Available */
|
||||
"#DF", /* Double-Fault */
|
||||
"#9 (reserved)",
|
||||
"#TS", /* Invalid-TSS */
|
||||
"#NP", /* Segment-Not-Present */
|
||||
"#SS", /* Stack */
|
||||
"#GP", /* General-Protection */
|
||||
"#PF", /* Page-Fault */
|
||||
"#15 (reserved)",
|
||||
"#MF", /* x87 FPE-Pending */
|
||||
"#AC", /* Alignment-Check */
|
||||
"#MC", /* Machine-Check */
|
||||
"#XF", /* SIMD Floating-Point */
|
||||
"#20 (reserved)",
|
||||
"#21 (reserved)",
|
||||
"#22 (reserved)",
|
||||
"#23 (reserved)",
|
||||
"#24 (reserved)",
|
||||
"#25 (reserved)",
|
||||
"#26 (reserved)",
|
||||
"#27 (reserved)",
|
||||
"#28 (reserved)",
|
||||
"#29 (reserved)",
|
||||
"#30 (reserved)",
|
||||
"#31 (reserved)",
|
||||
"divide error",
|
||||
"debug exception",
|
||||
"nonmaskable interrupt",
|
||||
"breakpoint",
|
||||
"overflow",
|
||||
"bounds check",
|
||||
"invalid opcode",
|
||||
"coprocessor not available",
|
||||
"double fault",
|
||||
"coprocessor segment overrun",
|
||||
"invalid TSS",
|
||||
"segment not present",
|
||||
"stack exception",
|
||||
"general protection violation",
|
||||
"page fault",
|
||||
"15 (reserved)",
|
||||
"coprocessor error",
|
||||
"alignment check",
|
||||
"machine check",
|
||||
"simd error",
|
||||
"20 (reserved)",
|
||||
"21 (reserved)",
|
||||
"22 (reserved)",
|
||||
"23 (reserved)",
|
||||
"24 (reserved)",
|
||||
"25 (reserved)",
|
||||
"26 (reserved)",
|
||||
"27 (reserved)",
|
||||
"28 (reserved)",
|
||||
"29 (reserved)",
|
||||
"30 (reserved)",
|
||||
"31 (reserved)",
|
||||
};
|
||||
|
||||
/*
|
||||
@ -295,6 +358,13 @@ trap(Ureg* ureg)
|
||||
char buf[ERRMAX];
|
||||
Vctl *ctl, *v;
|
||||
|
||||
if(!trapinited){
|
||||
/* faultamd64 can give a better error message */
|
||||
if(ureg->type == VectorPF)
|
||||
faultamd64(ureg, nil);
|
||||
panic("trap %llud: not ready", ureg->type);
|
||||
}
|
||||
|
||||
m->perf.intrts = perfticks();
|
||||
user = userureg(ureg);
|
||||
if(user){
|
||||
@ -305,10 +375,11 @@ trap(Ureg* ureg)
|
||||
clockintr = 0;
|
||||
|
||||
vno = ureg->type;
|
||||
|
||||
if(ctl = vctl[vno]){
|
||||
if(ctl->isintr){
|
||||
m->intr++;
|
||||
if(vno >= IdtPIC && vno != IdtSYSCALL)
|
||||
if(vno >= VectorPIC)
|
||||
m->lastintr = ctl->irq;
|
||||
}
|
||||
if(ctl->isr)
|
||||
@ -323,10 +394,8 @@ trap(Ureg* ureg)
|
||||
if(ctl->isintr){
|
||||
intrtime(m, vno);
|
||||
|
||||
if(ctl->irq == IdtPIC+IrqCLOCK || ctl->irq == IdtTIMER){
|
||||
checkflushmmu();
|
||||
if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
|
||||
clockintr = 1;
|
||||
}
|
||||
|
||||
if(up && !clockintr)
|
||||
preempted();
|
||||
@ -373,33 +442,37 @@ trap(Ureg* ureg)
|
||||
return;
|
||||
}
|
||||
else{
|
||||
if(vno == IdtNMI){
|
||||
if(active.ispanic){
|
||||
/*
|
||||
* Use of m->dbgsp avoids stack confusion
|
||||
* caused by writing the address of the SP to
|
||||
* the top of the stack.
|
||||
*/
|
||||
m->dbgreg = ureg;
|
||||
m->dbgsp = &ureg->sp;
|
||||
for(;;)
|
||||
_halt();
|
||||
}
|
||||
if(m->perfintr != nil){
|
||||
m->perfintr(ureg, nil);
|
||||
nmienable();
|
||||
return;
|
||||
}
|
||||
if(vno == VectorNMI){
|
||||
/*
|
||||
* Don't re-enable, it confuses the crash dumps.
|
||||
nmienable();
|
||||
*/
|
||||
iprint("cpu%d: nmi PC %#p, status %ux\n",
|
||||
m->machno, ureg->ip, inb(0x61));
|
||||
while(m->machno != 0)
|
||||
;
|
||||
}
|
||||
if(vno == 39){
|
||||
/* We get this one and didn't track it down yet: it's ok */
|
||||
iprint("vno %d: buggeration @ %#p...\n", vno, ureg->ip);
|
||||
}else if(vno < nelem(excname)){
|
||||
dumpregs(ureg);
|
||||
panic("%s pc %#p", excname[vno], ureg->ip);
|
||||
}else
|
||||
panic("unknown trap/intr: %d pc %#p\n", vno, ureg->ip);
|
||||
|
||||
if(!user){
|
||||
void (*pc)(void);
|
||||
|
||||
extern void _rdmsrinst(void);
|
||||
extern void _wrmsrinst(void);
|
||||
|
||||
pc = (void*)ureg->ip;
|
||||
if(pc == _rdmsrinst || pc == _wrmsrinst){
|
||||
if(vno == VectorGPF){
|
||||
ureg->bp = -1;
|
||||
ureg->ip += 2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dumpregs(ureg);
|
||||
if(vno < nelem(excname))
|
||||
panic("%s", excname[vno]);
|
||||
panic("unknown trap/intr: %d", vno);
|
||||
}
|
||||
splhi();
|
||||
|
||||
@ -581,23 +654,21 @@ faultamd64(Ureg* ureg, void* _1)
|
||||
void (*pt)(Proc*, int, int64_t, int64_t);
|
||||
|
||||
addr = cr2get();
|
||||
user = userureg(ureg);
|
||||
|
||||
/*
|
||||
* There must be a user context.
|
||||
* If not, the usual problem is causing a fault during
|
||||
* initialisation before the system is fully up.
|
||||
*/
|
||||
if(up == nil){
|
||||
panic("fault with up == nil; pc %#llux addr %#llux\n",
|
||||
ureg->ip, addr);
|
||||
}
|
||||
if(ureg->error&2)
|
||||
ftype = FaultWrite;
|
||||
else if(ureg->error&16)
|
||||
ftype = FaultExecute;
|
||||
else
|
||||
ftype = FaultRead;
|
||||
user = userureg(ureg);
|
||||
if(!user){
|
||||
if(addr >= USTKTOP)
|
||||
panic("kernel fault: bad address pc=%#p addr=%#p", ureg->ip, addr);
|
||||
if(up == nil)
|
||||
panic("kernel fault: no user process pc=%#p addr=%#p", ureg->ip, addr);
|
||||
}
|
||||
if(up == nil)
|
||||
panic("user fault: up=0 pc=%#p addr=%#p", ureg->ip, addr);
|
||||
|
||||
if(up->trace && (pt = proctrace) != nil){
|
||||
if(ftype == FaultWrite)
|
||||
@ -617,7 +688,6 @@ if(iskaddr(addr)){
|
||||
dumpregs(ureg);
|
||||
}
|
||||
if(fault(addr, ureg->ip, ftype) < 0){
|
||||
splhi();
|
||||
if(!user){
|
||||
dumpregs(ureg);
|
||||
panic("fault: %#llux pc %#p\n", addr, ureg->ip);
|
||||
|
@ -124,13 +124,11 @@ extern PhysUart i8250physuart;
|
||||
static Ctlr i8250ctlr[2] = {
|
||||
{ .io = Uart0,
|
||||
.irq = Uart0IRQ,
|
||||
.tbdf = -1,
|
||||
.poll = 0, },
|
||||
.tbdf = -1, },
|
||||
|
||||
{ .io = Uart1,
|
||||
.irq = Uart1IRQ,
|
||||
.tbdf = -1,
|
||||
.poll = 0, },
|
||||
.tbdf = -1, },
|
||||
};
|
||||
|
||||
static Uart i8250uart[2] = {
|
||||
@ -161,7 +159,7 @@ i8250status(Uart* uart, void* buf, long n, long offset)
|
||||
uint8_t ier, lcr, mcr, msr;
|
||||
|
||||
ctlr = uart->regs;
|
||||
p = jehanne_malloc(READSTR);
|
||||
p = smalloc(READSTR);
|
||||
mcr = ctlr->sticky[Mcr];
|
||||
msr = csr8r(ctlr, Msr);
|
||||
ier = ctlr->sticky[Ier];
|
||||
@ -482,13 +480,13 @@ i8250interrupt(Ureg* _1, void* arg)
|
||||
uart->ctsbackoff = 2;
|
||||
iunlock(&uart->tlock);
|
||||
}
|
||||
if(r & Ddsr){
|
||||
if(r & Ddsr){
|
||||
old = r & Dsr;
|
||||
if(uart->hup_dsr && uart->dsr && !old)
|
||||
uart->dohup = 1;
|
||||
uart->dsr = old;
|
||||
}
|
||||
if(r & Ddcd){
|
||||
if(r & Ddcd){
|
||||
old = r & Dcd;
|
||||
if(uart->hup_dcd && uart->dcd && !old)
|
||||
uart->dohup = 1;
|
||||
@ -545,8 +543,8 @@ i8250disable(Uart* uart)
|
||||
csr8w(ctlr, Ier, ctlr->sticky[Ier]);
|
||||
|
||||
if(ctlr->iena != 0){
|
||||
if(intrdisable(ctlr->vector) == 0)
|
||||
ctlr->iena = 0;
|
||||
ctlr->iena = 0;
|
||||
intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,27 +686,6 @@ i8250putc(Uart* uart, int c)
|
||||
delay(1);
|
||||
}
|
||||
|
||||
static void
|
||||
i8250poll(Uart* uart)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
|
||||
/*
|
||||
* If PhysUart has a non-nil .poll member, this
|
||||
* routine will be called from the uartclock timer.
|
||||
* If the Ctlr .poll member is non-zero, when the
|
||||
* Uart is enabled interrupts will not be enabled
|
||||
* and the result is polled input and output.
|
||||
* Not very useful here, but ports to new hardware
|
||||
* or simulators can use this to get serial I/O
|
||||
* without setting up the interrupt mechanism.
|
||||
*/
|
||||
ctlr = uart->regs;
|
||||
if(ctlr->iena || !ctlr->poll)
|
||||
return;
|
||||
i8250interrupt(nil, uart);
|
||||
}
|
||||
|
||||
PhysUart i8250physuart = {
|
||||
.name = "i8250",
|
||||
.pnp = i8250pnp,
|
||||
@ -727,73 +704,27 @@ PhysUart i8250physuart = {
|
||||
.fifo = i8250fifo,
|
||||
.getc = i8250getc,
|
||||
.putc = i8250putc,
|
||||
.poll = i8250poll,
|
||||
};
|
||||
|
||||
Uart*
|
||||
i8250console(char* cfg)
|
||||
void
|
||||
i8250console(void)
|
||||
{
|
||||
int i;
|
||||
Uart *uart;
|
||||
Ctlr *ctlr;
|
||||
int n;
|
||||
char *cmd, *p;
|
||||
ISAConf isa;
|
||||
|
||||
/*
|
||||
* Before i8250pnp() is run can only set the console
|
||||
* to 0 or 1 because those are the only uart structs which
|
||||
* will be the same before and after that.
|
||||
*/
|
||||
if((p = getconf("console")) == nil && (p = cfg) == nil)
|
||||
return nil;
|
||||
i = jehanne_strtoul(p, &cmd, 0);
|
||||
if(p == cmd)
|
||||
return nil;
|
||||
//WTF? Something to do with the PCIe-only machine?
|
||||
if((uart = uartconsole(i, cmd)) != nil){
|
||||
consuart = uart;
|
||||
return uart;
|
||||
}
|
||||
switch(i){
|
||||
default:
|
||||
return nil;
|
||||
case 0:
|
||||
uart = &i8250uart[0];
|
||||
break;
|
||||
case 1:
|
||||
uart = &i8250uart[1];
|
||||
break;
|
||||
}
|
||||
|
||||
//Madness. Something to do with the PCIe-only machine?
|
||||
jehanne_memset(&isa, 0, sizeof(isa));
|
||||
ctlr = uart->regs;
|
||||
if(isaconfig("eia", i, &isa) != 0){
|
||||
if(isa.port != 0)
|
||||
ctlr->io = isa.port;
|
||||
if(isa.irq != 0)
|
||||
ctlr->irq = isa.irq;
|
||||
if(isa.freq != 0)
|
||||
uart->freq = isa.freq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does it exist?
|
||||
* Should be able to write/read
|
||||
* the Scratch Pad.
|
||||
*/
|
||||
// ctlr = uart->regs;
|
||||
// csr8o(ctlr, Scr, 0x55);
|
||||
// if(csr8r(ctlr, Scr) != 0x55)
|
||||
// return nil;
|
||||
if((p = getconf("console")) == nil)
|
||||
return;
|
||||
n = strtoul(p, &cmd, 0);
|
||||
if(p == cmd || n < 0 || n >= nelem(i8250uart))
|
||||
return;
|
||||
uart = &i8250uart[n];
|
||||
|
||||
(*uart->phys->enable)(uart, 0);
|
||||
uartctl(uart, "b9600 l8 pn s1 i1");
|
||||
uartctl(uart, "b9600 l8 pn s1");
|
||||
if(*cmd != '\0')
|
||||
uartctl(uart, cmd);
|
||||
|
||||
consuart = uart;
|
||||
uart->console = 1;
|
||||
|
||||
return uart;
|
||||
}
|
@ -1485,7 +1485,7 @@ epio(Ep *ep, Qio *io, void *a, int32_t count, int mustlock)
|
||||
jehanne_print("\t%s\n", buf);
|
||||
}
|
||||
if(mustlock){
|
||||
qlock(&io->ql);
|
||||
eqlock(&io->ql);
|
||||
if(waserror()){
|
||||
qunlock(&io->ql);
|
||||
nexterror();
|
||||
@ -1625,7 +1625,7 @@ epread(Ep *ep, void *a, int32_t count)
|
||||
switch(ep->ttype){
|
||||
case Tctl:
|
||||
cio = ep->aux;
|
||||
qlock(&cio->ql);
|
||||
eqlock(&cio->ql);
|
||||
if(waserror()){
|
||||
qunlock(&cio->ql);
|
||||
nexterror();
|
||||
@ -1698,7 +1698,7 @@ epctlio(Ep *ep, Ctlio *cio, void *a, int32_t count)
|
||||
cio, ep->dev->nb, ep->nb, count);
|
||||
if(count < Rsetuplen)
|
||||
error("short usb command");
|
||||
qlock(&cio->ql);
|
||||
eqlock(&cio->ql);
|
||||
jehanne_free(cio->data);
|
||||
cio->data = nil;
|
||||
cio->ndata = 0;
|
||||
@ -1804,7 +1804,7 @@ episowrite(Ep *ep, void *a, int32_t count)
|
||||
iso->delay = (ep->sampledelay*ep->samplesz + ep->maxpkt-1) / ep->maxpkt;
|
||||
iso->debug = ep->debug;
|
||||
|
||||
qlock(&iso->ql);
|
||||
eqlock(&iso->ql);
|
||||
if(waserror()){
|
||||
qunlock(&iso->ql);
|
||||
nexterror();
|
||||
@ -2193,7 +2193,7 @@ portreset(Hci *hp, int port, int on)
|
||||
return 0;
|
||||
|
||||
ctlr = hp->aux;
|
||||
qlock(&ctlr->resetl);
|
||||
eqlock(&ctlr->resetl);
|
||||
if(waserror()){
|
||||
qunlock(&ctlr->resetl);
|
||||
nexterror();
|
||||
@ -2225,7 +2225,7 @@ portenable(Hci *hp, int port, int on)
|
||||
|
||||
ctlr = hp->aux;
|
||||
dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
|
||||
qlock(&ctlr->resetl);
|
||||
eqlock(&ctlr->resetl);
|
||||
if(waserror()){
|
||||
qunlock(&ctlr->resetl);
|
||||
nexterror();
|
||||
|
@ -1031,7 +1031,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, int32_t count)
|
||||
diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
|
||||
|
||||
ctlr = ep->hp->aux;
|
||||
qlock(&iso->ql);
|
||||
eqlock(&iso->ql);
|
||||
if(waserror()){
|
||||
qunlock(&iso->ql);
|
||||
nexterror();
|
||||
@ -1102,7 +1102,7 @@ episoread(Ep *ep, Isoio *iso, void *a, int count)
|
||||
|
||||
b = a;
|
||||
ctlr = ep->hp->aux;
|
||||
qlock(&iso->ql);
|
||||
eqlock(&iso->ql);
|
||||
if(waserror()){
|
||||
qunlock(&iso->ql);
|
||||
nexterror();
|
||||
@ -1306,7 +1306,7 @@ epio(Ep *ep, Qio *io, void *a, int32_t count, int mustlock)
|
||||
jehanne_print("uchi epio: user data: %s\n", buf);
|
||||
}
|
||||
if(mustlock){
|
||||
qlock(&io->ql);
|
||||
eqlock(&io->ql);
|
||||
if(waserror()){
|
||||
qunlock(&io->ql);
|
||||
nexterror();
|
||||
@ -1452,7 +1452,7 @@ epread(Ep *ep, void *a, int32_t count)
|
||||
switch(ep->ttype){
|
||||
case Tctl:
|
||||
cio = ep->aux;
|
||||
qlock(&cio->ql);
|
||||
eqlock(&cio->ql);
|
||||
if(waserror()){
|
||||
qunlock(&cio->ql);
|
||||
nexterror();
|
||||
@ -1525,7 +1525,7 @@ epctlio(Ep *ep, Ctlio *cio, void *a, int32_t count)
|
||||
cio, ep->dev->nb, ep->nb, count);
|
||||
if(count < Rsetuplen)
|
||||
error("short usb comand");
|
||||
qlock(&cio->ql);
|
||||
eqlock(&cio->ql);
|
||||
jehanne_free(cio->data);
|
||||
cio->data = nil;
|
||||
cio->ndata = 0;
|
||||
@ -2013,7 +2013,7 @@ portenable(Hci *hp, int port, int on)
|
||||
ctlr = hp->aux;
|
||||
dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on);
|
||||
ioport = PORT(port-1);
|
||||
qlock(&ctlr->portlck);
|
||||
eqlock(&ctlr->portlck);
|
||||
if(waserror()){
|
||||
qunlock(&ctlr->portlck);
|
||||
nexterror();
|
||||
@ -2070,7 +2070,7 @@ portstatus(Hci *hp, int port)
|
||||
|
||||
ctlr = hp->aux;
|
||||
ioport = PORT(port-1);
|
||||
qlock(&ctlr->portlck);
|
||||
eqlock(&ctlr->portlck);
|
||||
if(waserror()){
|
||||
iunlock(&ctlr->l);
|
||||
qunlock(&ctlr->portlck);
|
||||
|
@ -225,7 +225,7 @@ vgascreenwin(VGAscr* scr)
|
||||
window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
|
||||
curpos = window.min;
|
||||
|
||||
consputs = vgascreenputs;
|
||||
screenputs = vgascreenputs;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -275,7 +275,7 @@ cornerstring(char *s)
|
||||
Point p;
|
||||
|
||||
scr = &vgascreen[0];
|
||||
if(scr->vaddr == nil || consputs != vgascreenputs)
|
||||
if(scr->vaddr == nil || screenputs != vgascreenputs)
|
||||
return;
|
||||
p = memsubfontwidth(scr->memdefont, s);
|
||||
w = p.x;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* This file is part of the UCB release of Plan 9. It is subject to the license
|
||||
* terms in the LICENSE file found in the top-level directory of this
|
||||
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
||||
@ -78,7 +78,6 @@ vbecall(VGAreg *u)
|
||||
pa = PADDR(RMBUF);
|
||||
cmem->dev->write(cmem, modebuf, sizeof modebuf, pa);
|
||||
u->trap = 0x10;
|
||||
jehanne_print("vbecall: sizeof u is %d\n", sizeof *u);
|
||||
creg->dev->write(creg, u, sizeof *u, 0);
|
||||
|
||||
creg->dev->read(creg, u, sizeof *u, 0);
|
@ -24,7 +24,7 @@
|
||||
static Lock vgaxlock; /* access to index registers */
|
||||
|
||||
int
|
||||
vgaxi(int32_t port, unsigned char index)
|
||||
vgaxi(int32_t port, uint8_t index)
|
||||
{
|
||||
unsigned char data;
|
||||
|
||||
@ -71,7 +71,7 @@ vgaxi(int32_t port, unsigned char index)
|
||||
}
|
||||
|
||||
int
|
||||
vgaxo(int32_t port, unsigned char index, unsigned char data)
|
||||
vgaxo(int32_t port, uint8_t index, uint8_t data)
|
||||
{
|
||||
ilock(&vgaxlock);
|
||||
switch(port){
|
||||
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Vestigial Segmented Virtual Memory.
|
||||
* To do:
|
||||
* dynamic allocation and free of descriptors;
|
||||
* IST should perhaps point to a different handler;
|
||||
* user-level descriptors (if not dynamic).
|
||||
*/
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "amd64.h"
|
||||
#include "ureg.h"
|
||||
|
||||
typedef struct Gd Gd;
|
||||
typedef uint64_t Sd;
|
||||
typedef uint16_t Ss;
|
||||
typedef struct Tss Tss;
|
||||
|
||||
struct Gd {
|
||||
Sd sd;
|
||||
uint64_t hi;
|
||||
};
|
||||
|
||||
struct Tss {
|
||||
uint32_t _0_;
|
||||
uint32_t rsp0[2];
|
||||
uint32_t rsp1[2];
|
||||
uint32_t rsp2[2];
|
||||
uint32_t _28_[2];
|
||||
uint32_t ist[14];
|
||||
uint16_t _92_[5];
|
||||
uint16_t iomap;
|
||||
};
|
||||
|
||||
enum {
|
||||
Ngdt = 16, /* max. entries in gdt */
|
||||
Nidt = 256, /* max. entries in idt */
|
||||
};
|
||||
|
||||
static Sd gdt64[Ngdt] = {
|
||||
0ull, /* NULL descriptor */
|
||||
SdL|SdP|SdDPL0|SdS|SdCODE, /* CS */
|
||||
SdG|SdD|SdP|SdDPL0|SdS|SdW, /* DS */
|
||||
SdG|SdD|SdP|SdDPL3|SdS|SdCODE|SdR|Sd4G, /* User CS 32-bit */
|
||||
SdG|SdD|SdP|SdDPL3|SdS|SdW|Sd4G, /* User DS */
|
||||
SdL|SdP|SdDPL3|SdS|SdCODE, /* User CS 64-bit */
|
||||
|
||||
0ull, /* FS */
|
||||
0ull, /* GS */
|
||||
|
||||
0ull, /* TSS lower */
|
||||
0ull, /* TSS upper */
|
||||
};
|
||||
//static int ngdt64 = 10; // not used
|
||||
|
||||
static Gd idt64[Nidt];
|
||||
|
||||
static Sd
|
||||
mksd(uint64_t base, uint64_t limit, uint64_t bits, uint64_t* upper)
|
||||
{
|
||||
Sd sd;
|
||||
|
||||
sd = bits;
|
||||
sd |= (((limit & 0x00000000000f0000ull)>>16)<<48)
|
||||
|(limit & 0x000000000000ffffull);
|
||||
sd |= (((base & 0x00000000ff000000ull)>>24)<<56)
|
||||
|(((base & 0x0000000000ff0000ull)>>16)<<32)
|
||||
|((base & 0x000000000000ffffull)<<16);
|
||||
if(upper != nil)
|
||||
*upper = base>>32;
|
||||
|
||||
return sd;
|
||||
}
|
||||
|
||||
static void
|
||||
mkgd(Gd* gd, uint64_t offset, Ss ss, uint64_t bits, int ist)
|
||||
{
|
||||
Sd sd;
|
||||
|
||||
sd = bits;
|
||||
sd |= (((offset & 0x00000000ffff0000ull)>>16)<<48)
|
||||
|(offset & 0x000000000000ffffull);
|
||||
sd |= ((ss & 0x000000000000ffffull)<<16);
|
||||
sd |= (ist & (SdISTM>>32))<<32;
|
||||
gd->sd = sd;
|
||||
gd->hi = offset>>32;
|
||||
}
|
||||
|
||||
static void
|
||||
idtinit(void)
|
||||
{
|
||||
Gd *gd;
|
||||
int ist, v;
|
||||
uint64_t dpl;
|
||||
uintptr_t offset;
|
||||
|
||||
gd = idt64;
|
||||
offset = PTR2UINT(idthandlers);
|
||||
|
||||
for(v = 0; v < Nidt; v++){
|
||||
ist = 0;
|
||||
dpl = SdP|SdDPL0|SdIG;
|
||||
switch(v){
|
||||
default:
|
||||
break;
|
||||
case IdtBP: /* #BP */
|
||||
dpl = SdP|SdDPL3|SdIG;
|
||||
break;
|
||||
case IdtUD: /* #UD */
|
||||
case IdtDF: /* #DF */
|
||||
ist = 1;
|
||||
break;
|
||||
}
|
||||
mkgd(gd, offset, SSEL(SiCS, SsTIGDT|SsRPL0), dpl, ist);
|
||||
gd++;
|
||||
offset += 6;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tssrsp0(uintptr_t sp)
|
||||
{
|
||||
Tss *tss;
|
||||
|
||||
tss = m->tss;
|
||||
tss->rsp0[0] = sp;
|
||||
tss->rsp0[1] = sp>>32;
|
||||
}
|
||||
|
||||
static void
|
||||
tssinit(uintptr_t sp)
|
||||
{
|
||||
int ist;
|
||||
Tss *tss;
|
||||
|
||||
tss = m->tss;
|
||||
jehanne_memset(tss, 0, sizeof(Tss));
|
||||
|
||||
tssrsp0(sp);
|
||||
|
||||
sp = PTR2UINT(m->vsvm+PGSZ);
|
||||
for(ist = 0; ist < 14; ist += 2){
|
||||
tss->ist[ist] = sp;
|
||||
tss->ist[ist+1] = sp>>32;
|
||||
}
|
||||
tss->iomap = 0xdfff;
|
||||
}
|
||||
|
||||
void
|
||||
vsvminit(int size)
|
||||
{
|
||||
Sd *sd;
|
||||
uint64_t r;
|
||||
|
||||
if(m->machno == 0)
|
||||
idtinit();
|
||||
|
||||
m->gdt = m->vsvm;
|
||||
jehanne_memmove(m->gdt, gdt64, sizeof(gdt64));
|
||||
m->tss = &m->vsvm[ROUNDUP(sizeof(gdt64), 16)];
|
||||
|
||||
sd = &((Sd*)m->gdt)[SiTSS];
|
||||
*sd = mksd(PTR2UINT(m->tss), sizeof(Tss)-1, SdP|SdDPL0|SdaTSS, sd+1);
|
||||
|
||||
tssinit(m->stack+size);
|
||||
|
||||
gdtput(sizeof(gdt64)-1, PTR2UINT(m->gdt), SSEL(SiCS, SsTIGDT|SsRPL0));
|
||||
idtput(sizeof(idt64)-1, PTR2UINT(idt64));
|
||||
trput(SSEL(SiTSS, SsTIGDT|SsRPL0));
|
||||
|
||||
wrmsr(FSbase, 0ull);
|
||||
wrmsr(GSbase, PTR2UINT(&sys->machptr[m->machno]));
|
||||
wrmsr(KernelGSbase, 0ull);
|
||||
|
||||
r = rdmsr(Efer);
|
||||
r |= Sce;
|
||||
wrmsr(Efer, r);
|
||||
r = ((uint64_t)SSEL(SiU32CS, SsRPL3))<<48;
|
||||
r |= ((uint64_t)SSEL(SiCS, SsRPL0))<<32;
|
||||
wrmsr(Star, r);
|
||||
wrmsr(Lstar, PTR2UINT(syscallentry));
|
||||
wrmsr(Sfmask, If);
|
||||
}
|
||||
|
||||
int
|
||||
userureg(Ureg* ureg)
|
||||
{
|
||||
return ureg->cs == SSEL(SiUCS, SsRPL3);
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
],
|
||||
"Include": [
|
||||
"core.json",
|
||||
"../386/include.json",
|
||||
"../ip/include.json",
|
||||
"../port/include.json"
|
||||
],
|
||||
@ -17,7 +16,6 @@
|
||||
"int printallsyscalls;"
|
||||
],
|
||||
"Dev": [
|
||||
"acpi",
|
||||
"arch",
|
||||
"bridge",
|
||||
"cap",
|
||||
@ -105,7 +103,6 @@
|
||||
"autogenerated.c",
|
||||
"sdata.c",
|
||||
"cga.c",
|
||||
"devacpi.c",
|
||||
"usbehcipc.c",
|
||||
"usbohci.c",
|
||||
"usbuhci.c"
|
||||
|
351
sys/src/kern/port/alloc.c
Normal file
351
sys/src/kern/port/alloc.c
Normal file
@ -0,0 +1,351 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
#include <pool.h>
|
||||
|
||||
static void poolprint(Pool*, char*, ...);
|
||||
static void ppanic(Pool*, char*, ...);
|
||||
static void pool_lock(Pool*);
|
||||
static void pool_unlock(Pool*);
|
||||
|
||||
typedef struct Private Private;
|
||||
struct Private {
|
||||
Lock lk;
|
||||
char msg[256]; /* a rock for messages to be printed at unlock */
|
||||
};
|
||||
|
||||
static Private pmainpriv;
|
||||
static Pool pmainmem = {
|
||||
.name= "Main",
|
||||
.maxsize= 4*1024*1024,
|
||||
.minarena= 128*1024,
|
||||
.quantum= 32,
|
||||
.alloc= xalloc,
|
||||
.merge= xmerge,
|
||||
.flags= POOL_TOLERANCE,
|
||||
|
||||
.lock= pool_lock,
|
||||
.unlock= pool_unlock,
|
||||
.print= poolprint,
|
||||
.panic= ppanic,
|
||||
|
||||
.private= &pmainpriv,
|
||||
};
|
||||
|
||||
static Private pimagpriv;
|
||||
static Pool pimagmem = {
|
||||
.name= "Image",
|
||||
.maxsize= 16*1024*1024,
|
||||
.minarena= 2*1024*1024,
|
||||
.quantum= 32,
|
||||
.alloc= xalloc,
|
||||
.merge= xmerge,
|
||||
.flags= 0,
|
||||
|
||||
.lock= pool_lock,
|
||||
.unlock= pool_unlock,
|
||||
.print= poolprint,
|
||||
.panic= ppanic,
|
||||
|
||||
.private= &pimagpriv,
|
||||
};
|
||||
|
||||
static Private psecrpriv;
|
||||
static Pool psecrmem = {
|
||||
.name= "Secrets",
|
||||
.maxsize= 16*1024*1024,
|
||||
.minarena= 64*1024,
|
||||
.quantum= 32,
|
||||
.alloc= xalloc,
|
||||
.merge= xmerge,
|
||||
.flags= POOL_ANTAGONISM,
|
||||
|
||||
.lock= pool_lock,
|
||||
.unlock= pool_unlock,
|
||||
.print= poolprint,
|
||||
.panic= ppanic,
|
||||
|
||||
.private= &psecrpriv,
|
||||
};
|
||||
|
||||
Pool* mainmem = &pmainmem;
|
||||
Pool* imagmem = &pimagmem;
|
||||
Pool* secrmem = &psecrmem;
|
||||
|
||||
/*
|
||||
* because we can't print while we're holding the locks,
|
||||
* we have the save the message and print it once we let go.
|
||||
*/
|
||||
static void
|
||||
poolprint(Pool *p, char *fmt, ...)
|
||||
{
|
||||
va_list v;
|
||||
Private *pv;
|
||||
|
||||
pv = p->private;
|
||||
va_start(v, fmt);
|
||||
vseprint(pv->msg+strlen(pv->msg), pv->msg+sizeof pv->msg, fmt, v);
|
||||
va_end(v);
|
||||
}
|
||||
|
||||
static void
|
||||
ppanic(Pool *p, char *fmt, ...)
|
||||
{
|
||||
va_list v;
|
||||
Private *pv;
|
||||
char msg[sizeof pv->msg];
|
||||
|
||||
pv = p->private;
|
||||
va_start(v, fmt);
|
||||
vseprint(pv->msg+strlen(pv->msg), pv->msg+sizeof pv->msg, fmt, v);
|
||||
va_end(v);
|
||||
memmove(msg, pv->msg, sizeof msg);
|
||||
iunlock(&pv->lk);
|
||||
panic("%s", msg);
|
||||
}
|
||||
|
||||
static void
|
||||
pool_lock(Pool *p)
|
||||
{
|
||||
Private *pv;
|
||||
|
||||
pv = p->private;
|
||||
ilock(&pv->lk);
|
||||
// pv->lk.pc = getcallerpc();
|
||||
pv->msg[0] = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pool_unlock(Pool *p)
|
||||
{
|
||||
Private *pv;
|
||||
char msg[sizeof pv->msg];
|
||||
|
||||
pv = p->private;
|
||||
if(pv->msg[0] == 0){
|
||||
iunlock(&pv->lk);
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(msg, pv->msg, sizeof msg);
|
||||
iunlock(&pv->lk);
|
||||
iprint("%.*s", sizeof pv->msg, msg);
|
||||
}
|
||||
|
||||
void
|
||||
poolsummary(Pool *p)
|
||||
{
|
||||
print("%s max %llud cur %llud free %llud alloc %llud\n", p->name,
|
||||
(uint64_t)p->maxsize, (uint64_t)p->cursize,
|
||||
(uint64_t)p->curfree, (uint64_t)p->curalloc);
|
||||
}
|
||||
|
||||
void
|
||||
mallocsummary(void)
|
||||
{
|
||||
poolsummary(mainmem);
|
||||
poolsummary(imagmem);
|
||||
poolsummary(secrmem);
|
||||
}
|
||||
|
||||
/* everything from here down should be the same in libc, libdebugmalloc, and the kernel */
|
||||
/* - except the code for malloc(), which alternately doesn't clear or does. */
|
||||
/* - except the code for smalloc(), which lives only in the kernel. */
|
||||
|
||||
/*
|
||||
* Npadlong is the number of uint32_t's to leave at the beginning of
|
||||
* each allocated buffer for our own bookkeeping. We return to the callers
|
||||
* a pointer that points immediately after our bookkeeping area. Incoming pointers
|
||||
* must be decremented by that much, and outgoing pointers incremented.
|
||||
* The malloc tag is stored at MallocOffset from the beginning of the block,
|
||||
* and the realloc tag at ReallocOffset. The offsets are from the true beginning
|
||||
* of the block, not the beginning the caller sees.
|
||||
*
|
||||
* The extra if(Npadlong != 0) in various places is a hint for the compiler to
|
||||
* compile out function calls that would otherwise be no-ops.
|
||||
*/
|
||||
|
||||
/* non tracing
|
||||
*
|
||||
enum {
|
||||
Npadlong = 0,
|
||||
MallocOffset = 0,
|
||||
ReallocOffset = 0,
|
||||
};
|
||||
*
|
||||
*/
|
||||
|
||||
/* tracing */
|
||||
enum {
|
||||
Npadlong = 2,
|
||||
MallocOffset = 0,
|
||||
ReallocOffset = 1
|
||||
};
|
||||
|
||||
|
||||
void*
|
||||
smalloc(uint32_t size)
|
||||
{
|
||||
void *v;
|
||||
|
||||
while((v = poolalloc(mainmem, size+Npadlong*sizeof(uint32_t))) == nil){
|
||||
if(!waserror()){
|
||||
resrcwait(nil, nil);
|
||||
poperror();
|
||||
}
|
||||
}
|
||||
if(Npadlong){
|
||||
v = (uint32_t*)v+Npadlong;
|
||||
setmalloctag(v, getcallerpc());
|
||||
}
|
||||
memset(v, 0, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
void*
|
||||
jehanne_malloc(uint32_t size)
|
||||
{
|
||||
void *v;
|
||||
|
||||
v = poolalloc(mainmem, size+Npadlong*sizeof(uint32_t));
|
||||
if(v == nil)
|
||||
return nil;
|
||||
if(Npadlong){
|
||||
v = (uint32_t*)v+Npadlong;
|
||||
setmalloctag(v, getcallerpc());
|
||||
setrealloctag(v, 0);
|
||||
}
|
||||
memset(v, 0, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
void*
|
||||
jehanne_mallocz(uint32_t size, int clr)
|
||||
{
|
||||
void *v;
|
||||
|
||||
v = poolalloc(mainmem, size+Npadlong*sizeof(uint32_t));
|
||||
if(Npadlong && v != nil){
|
||||
v = (uint32_t*)v+Npadlong;
|
||||
setmalloctag(v, getcallerpc());
|
||||
setrealloctag(v, 0);
|
||||
}
|
||||
if(clr && v != nil)
|
||||
memset(v, 0, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
void*
|
||||
jehanne_mallocalign(uint32_t size, uint32_t align, long offset, uint32_t span)
|
||||
{
|
||||
void *v;
|
||||
|
||||
v = poolallocalign(mainmem, size+Npadlong*sizeof(uint32_t), align, offset-Npadlong*sizeof(uint32_t), span);
|
||||
if(Npadlong && v != nil){
|
||||
v = (uint32_t*)v+Npadlong;
|
||||
setmalloctag(v, getcallerpc());
|
||||
setrealloctag(v, 0);
|
||||
}
|
||||
if(v)
|
||||
memset(v, 0, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
void
|
||||
jehanne_free(void *v)
|
||||
{
|
||||
if(v != nil)
|
||||
poolfree(mainmem, (uint32_t*)v-Npadlong);
|
||||
}
|
||||
|
||||
void*
|
||||
jehanne_realloc(void *v, uint32_t size)
|
||||
{
|
||||
void *nv;
|
||||
|
||||
if(v != nil)
|
||||
v = (uint32_t*)v-Npadlong;
|
||||
if(Npadlong !=0 && size != 0)
|
||||
size += Npadlong*sizeof(uint32_t);
|
||||
|
||||
if(nv = poolrealloc(mainmem, v, size)){
|
||||
nv = (uint32_t*)nv+Npadlong;
|
||||
setrealloctag(nv, getcallerpc());
|
||||
if(v == nil)
|
||||
setmalloctag(nv, getcallerpc());
|
||||
}
|
||||
return nv;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
jehanne_msize(void *v)
|
||||
{
|
||||
return poolmsize(mainmem, (uint32_t*)v-Npadlong)-Npadlong*sizeof(uint32_t);
|
||||
}
|
||||
|
||||
/* secret memory, used to back cryptographic keys and cipher states */
|
||||
void*
|
||||
secalloc(uint32_t size)
|
||||
{
|
||||
void *v;
|
||||
|
||||
while((v = poolalloc(secrmem, size+Npadlong*sizeof(uint32_t))) == nil){
|
||||
if(!waserror()){
|
||||
resrcwait(nil, nil);
|
||||
poperror();
|
||||
}
|
||||
}
|
||||
if(Npadlong){
|
||||
v = (uint32_t*)v+Npadlong;
|
||||
setmalloctag(v, getcallerpc());
|
||||
setrealloctag(v, 0);
|
||||
}
|
||||
memset(v, 0, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
void
|
||||
secfree(void *v)
|
||||
{
|
||||
if(v != nil)
|
||||
poolfree(secrmem, (uint32_t*)v-Npadlong);
|
||||
}
|
||||
|
||||
void
|
||||
jehanne_setmalloctag(void *v, uintptr_t pc)
|
||||
{
|
||||
USED(v, pc);
|
||||
if(Npadlong <= MallocOffset || v == nil)
|
||||
return;
|
||||
((uint32_t*)v)[-Npadlong+MallocOffset] = (uint32_t)pc;
|
||||
}
|
||||
|
||||
void
|
||||
jehanne_setrealloctag(void *v, uintptr_t pc)
|
||||
{
|
||||
USED(v, pc);
|
||||
if(Npadlong <= ReallocOffset || v == nil)
|
||||
return;
|
||||
((uint32_t*)v)[-Npadlong+ReallocOffset] = (uint32_t)pc;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
jehanne_getmalloctag(void *v)
|
||||
{
|
||||
USED(v);
|
||||
if(Npadlong <= MallocOffset)
|
||||
return ~0;
|
||||
return (int)((uint32_t*)v)[-Npadlong+MallocOffset];
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
jehanne_getrealloctag(void *v)
|
||||
{
|
||||
USED(v);
|
||||
if(Npadlong <= ReallocOffset)
|
||||
return ~0;
|
||||
return (int)((uint32_t*)v)[-Npadlong+ReallocOffset];
|
||||
}
|
@ -3,50 +3,46 @@
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
enum
|
||||
{
|
||||
Hdrspc = 64, /* leave room for high-level headers */
|
||||
Tlrspc = 16, /* extra room at the end for pad/crc/mac */
|
||||
Bdead = 0x51494F42, /* "QIOB" */
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
Lock;
|
||||
uint32_t bytes;
|
||||
uint32_t limit;
|
||||
|
||||
} ialloc;
|
||||
|
||||
static Block*
|
||||
_allocb(int size, int align)
|
||||
_allocb(int size)
|
||||
{
|
||||
Block *b;
|
||||
uint8_t *p;
|
||||
int n;
|
||||
uintptr_t addr;
|
||||
|
||||
if(align <= 0)
|
||||
align = BLOCKALIGN;
|
||||
n = align + ROUNDUP(size+Hdrspc, align) + sizeof(Block);
|
||||
if((p = jehanne_malloc(n)) == nil)
|
||||
size += Tlrspc;
|
||||
if((b = mallocz(sizeof(Block)+size+Hdrspc, 0)) == nil)
|
||||
return nil;
|
||||
|
||||
b = (Block*)(p + n - sizeof(Block)); /* block at end of allocated space */
|
||||
b->base = p;
|
||||
b->next = nil;
|
||||
b->list = nil;
|
||||
b->free = 0;
|
||||
b->free = nil;
|
||||
b->flag = 0;
|
||||
|
||||
/* align base and bounds of data */
|
||||
b->lim = (uint8_t*)(PTR2UINT(b) & ~(align-1));
|
||||
/* align start of data portion by rounding up */
|
||||
addr = (uintptr_t)b;
|
||||
addr = ROUND(addr + sizeof(Block), BLOCKALIGN);
|
||||
b->base = (uint8_t*)addr;
|
||||
|
||||
/* align start of writable data, leaving space below for added headers */
|
||||
b->rp = b->lim - ROUNDUP(size, align);
|
||||
b->wp = b->rp;
|
||||
/* align end of data portion by rounding down */
|
||||
b->lim = (uint8_t*)b + msize(b);
|
||||
addr = (uintptr_t)b->lim;
|
||||
addr &= ~(BLOCKALIGN-1);
|
||||
b->lim = (uint8_t*)addr;
|
||||
|
||||
if(b->rp < b->base || b->lim - b->rp < size)
|
||||
/* leave sluff at beginning for added headers */
|
||||
b->rp = b->lim - ROUND(size, BLOCKALIGN);
|
||||
if(b->rp < b->base)
|
||||
panic("_allocb");
|
||||
b->wp = b->rp;
|
||||
|
||||
return b;
|
||||
}
|
||||
@ -58,80 +54,44 @@ allocb(int size)
|
||||
|
||||
/*
|
||||
* Check in a process and wait until successful.
|
||||
* Can still error out of here, though.
|
||||
*/
|
||||
if(up == nil)
|
||||
panic("allocb without up: %#p\n", getcallerpc());
|
||||
if((b = _allocb(size, 0)) == nil){
|
||||
mallocsummary();
|
||||
panic("allocb: no memory for %d bytes\n", size);
|
||||
panic("allocb without up: %#p", getcallerpc());
|
||||
while((b = _allocb(size)) == nil){
|
||||
if(up->nlocks || m->ilockdepth || !islo()){
|
||||
xsummary();
|
||||
mallocsummary();
|
||||
panic("allocb: no memory for %d bytes", size);
|
||||
}
|
||||
if(!waserror()){
|
||||
resrcwait("no memory for allocb", nil);
|
||||
poperror();
|
||||
}
|
||||
}
|
||||
jehanne_setmalloctag(b->base, getcallerpc());
|
||||
setmalloctag(b, getcallerpc());
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
Block*
|
||||
allocbalign(int size, int align)
|
||||
{
|
||||
Block *b;
|
||||
|
||||
/*
|
||||
* Check in a process and wait until successful.
|
||||
* Can still error out of here, though.
|
||||
*/
|
||||
if(up == nil)
|
||||
panic("allocbalign without up: %#p\n", getcallerpc());
|
||||
if((b = _allocb(size, align)) == nil){
|
||||
mallocsummary();
|
||||
panic("allocbalign: no memory for %d bytes\n", size);
|
||||
}
|
||||
jehanne_setmalloctag(b->base, getcallerpc());
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void
|
||||
ialloclimit(uint32_t limit)
|
||||
{
|
||||
ialloc.limit = limit;
|
||||
}
|
||||
|
||||
Block*
|
||||
iallocb(int size)
|
||||
{
|
||||
Block *b;
|
||||
static int m1, m2, mp;
|
||||
|
||||
if(ialloc.bytes > ialloc.limit){
|
||||
if((m1++%10000)==0){
|
||||
if(mp++ > 1000){
|
||||
active.exiting = 1;
|
||||
exit(0);
|
||||
if((b = _allocb(size)) == nil){
|
||||
static uint32_t nerr;
|
||||
if((nerr++%10000)==0){
|
||||
if(nerr > 10000000){
|
||||
xsummary();
|
||||
mallocsummary();
|
||||
panic("iallocb: out of memory");
|
||||
}
|
||||
iprint("iallocb: limited %lud/%lud\n",
|
||||
ialloc.bytes, ialloc.limit);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((b = _allocb(size, 0)) == nil){
|
||||
if((m2++%10000)==0){
|
||||
if(mp++ > 1000){
|
||||
active.exiting = 1;
|
||||
exit(0);
|
||||
}
|
||||
iprint("iallocb: no memory %lud/%lud\n",
|
||||
ialloc.bytes, ialloc.limit);
|
||||
iprint("iallocb: no memory for %d bytes\n", size);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
setmalloctag(b, getcallerpc());
|
||||
b->flag = BINTR;
|
||||
jehanne_setmalloctag(b->base, getcallerpc());
|
||||
|
||||
ilock(&ialloc);
|
||||
ialloc.bytes += b->lim - b->base;
|
||||
iunlock(&ialloc);
|
||||
|
||||
return b;
|
||||
}
|
||||
@ -140,7 +100,6 @@ void
|
||||
freeb(Block *b)
|
||||
{
|
||||
void *dead = (void*)Bdead;
|
||||
uint8_t *p;
|
||||
|
||||
if(b == nil)
|
||||
return;
|
||||
@ -149,17 +108,10 @@ freeb(Block *b)
|
||||
* drivers which perform non cache coherent DMA manage their own buffer
|
||||
* pool of uncached buffers and provide their own free routine.
|
||||
*/
|
||||
if(b->free) {
|
||||
if(b->free != nil) {
|
||||
b->free(b);
|
||||
return;
|
||||
}
|
||||
if(b->flag & BINTR) {
|
||||
ilock(&ialloc);
|
||||
ialloc.bytes -= b->lim - b->base;
|
||||
iunlock(&ialloc);
|
||||
}
|
||||
|
||||
p = b->base;
|
||||
|
||||
/* poison the block in case someone is still holding onto it */
|
||||
b->next = dead;
|
||||
@ -168,7 +120,7 @@ freeb(Block *b)
|
||||
b->lim = dead;
|
||||
b->base = dead;
|
||||
|
||||
jehanne_free(p);
|
||||
free(b);
|
||||
}
|
||||
|
||||
void
|
||||
@ -180,10 +132,10 @@ checkb(Block *b, char *msg)
|
||||
panic("checkb b %s %#p", msg, b);
|
||||
if(b->base == dead || b->lim == dead || b->next == dead
|
||||
|| b->rp == dead || b->wp == dead){
|
||||
jehanne_print("checkb: base %#p lim %#p next %#p\n",
|
||||
print("checkb: base %#p lim %#p next %#p\n",
|
||||
b->base, b->lim, b->next);
|
||||
jehanne_print("checkb: rp %#p wp %#p\n", b->rp, b->wp);
|
||||
panic("checkb dead: %s\n", msg);
|
||||
print("checkb: rp %#p wp %#p\n", b->rp, b->wp);
|
||||
panic("checkb dead: %s", msg);
|
||||
}
|
||||
|
||||
if(b->base > b->lim)
|
||||
@ -197,9 +149,3 @@ checkb(Block *b, char *msg)
|
||||
if(b->wp > b->lim)
|
||||
panic("checkb 4 %s %#p %#p", msg, b->wp, b->lim);
|
||||
}
|
||||
|
||||
void
|
||||
iallocsummary(void)
|
||||
{
|
||||
jehanne_print("ialloc %lud/%lud\n", ialloc.bytes, ialloc.limit);
|
||||
}
|
||||
|
@ -21,333 +21,611 @@
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
typedef struct PendingWakeup PendingWakeup;
|
||||
struct PendingWakeup
|
||||
/* Requirements:
|
||||
* - each process can register several wakeups
|
||||
* - wakeups registered outside a note handler will not happen
|
||||
* in a note handler
|
||||
* - wakeups registered by a note handler will be deleted on noted()
|
||||
* - all pending wakeups of a process will be deleted on pexit()
|
||||
* - future wakeups can be deleted
|
||||
* - each wakeup will interrupt a blocking syscall
|
||||
*/
|
||||
struct AwakeAlarm /* 24 byte */
|
||||
{
|
||||
short notified;
|
||||
uint64_t time;
|
||||
Proc *p;
|
||||
PendingWakeup *next;
|
||||
Proc* p;
|
||||
AwakeAlarm* next;
|
||||
unsigned char notified;
|
||||
unsigned char done;
|
||||
unsigned long time : 48;
|
||||
};
|
||||
typedef struct AlarmPool
|
||||
{
|
||||
AwakeAlarm* alarms;
|
||||
AwakeAlarm* end;
|
||||
int size;
|
||||
int first;
|
||||
int nfree;
|
||||
Lock l;
|
||||
} AlarmPool;
|
||||
static AlarmPool awkpool;
|
||||
#define alarm2wkp(a) ~((a)->time << 16 | (a) - awkpool.alarms)
|
||||
#define wkp2alarm(a) (awkpool.alarms + (~(a) & 0xffff))
|
||||
|
||||
static AwakeAlarm *registry;
|
||||
static Lock rl;
|
||||
|
||||
|
||||
typedef enum ElapsedAlarmFate
|
||||
{
|
||||
TryAgain,
|
||||
Forget,
|
||||
FreeAndForget
|
||||
} ElapsedAlarmFate;
|
||||
static AwakeAlarm *elapsed, **eEnd = &elapsed;
|
||||
static Lock el;
|
||||
|
||||
static Rendez producer;
|
||||
static Rendez consumer;
|
||||
|
||||
static unsigned long next_wakeup;
|
||||
|
||||
static const int awakeable_syscalls[] = {
|
||||
[SysAwait] = 1,
|
||||
[SysAwake] = 0,
|
||||
[SysBind] = 0,
|
||||
[SysClose] = 0,
|
||||
[SysCreate] = 0,
|
||||
[SysErrstr] = 0,
|
||||
[SysExec] = 0,
|
||||
[Sys_exits] = 0,
|
||||
[SysFauth] = 0,
|
||||
[SysFd2path] = 0,
|
||||
[SysFstat] = 1,
|
||||
[SysFversion] = 0,
|
||||
[SysFwstat] = 1,
|
||||
[SysMount] = 0,
|
||||
[SysNoted] = 0,
|
||||
[SysNotify] = 0,
|
||||
[SysOpen] = 1,
|
||||
[SysPread] = 1,
|
||||
[SysPwrite] = 1,
|
||||
[SysRemove] = 0,
|
||||
[SysRendezvous] = 1,
|
||||
[SysRfork] = 0,
|
||||
[SysSeek] = 0,
|
||||
[SysSemacquire] = 1,
|
||||
[SysSemrelease] = 0,
|
||||
[SysUnmount] = 0,
|
||||
[SysAlarm] = 0,
|
||||
};
|
||||
|
||||
/* alarms: linked list, sorted by wakeup time, protected by qlock(&l)
|
||||
* wakeupafter inserts new items, forgivewakeup remove them,
|
||||
* awakekproc consume the expired ones and clearwakeups remove those
|
||||
* survived to their process.
|
||||
*/
|
||||
static PendingWakeup *alarms;
|
||||
static QLock l;
|
||||
#define DEBUG
|
||||
|
||||
static Rendez awaker;
|
||||
#ifdef DEBUG
|
||||
|
||||
int
|
||||
canwakeup(Syscalls scall)
|
||||
void
|
||||
awake_detect_loop(AwakeAlarm *head)
|
||||
{
|
||||
AwakeAlarm *tail;
|
||||
while(head != nil){
|
||||
if(!head->p)
|
||||
panic("awake: free alarm head: %#p, pc %#p", head, getcallerpc());
|
||||
if(head < awkpool.alarms || head > awkpool.end)
|
||||
panic("awake: not an alarm head: %#p, pc %#p", head, getcallerpc());
|
||||
tail = head->next;
|
||||
while(tail != nil){
|
||||
if(!tail->p)
|
||||
panic("awake: free alarm tail: %#p, pc %#p", tail, getcallerpc());
|
||||
if(tail < awkpool.alarms || tail > awkpool.end)
|
||||
panic("awake: not an alarm tail: %#p, pc %#p", tail, getcallerpc());
|
||||
if(head == tail)
|
||||
panic("awake: loop detected");
|
||||
else
|
||||
tail = tail->next;
|
||||
}
|
||||
head = head->next;
|
||||
}
|
||||
}
|
||||
|
||||
AwakeAlarm*
|
||||
awake_find_first_of(Proc* p, AwakeAlarm* head)
|
||||
{
|
||||
AwakeAlarm* a = head;
|
||||
if(head == nil)
|
||||
return nil;
|
||||
while(a < awkpool.end){
|
||||
if(a->p == p)
|
||||
return a;
|
||||
++a;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
awake_can_interrupt(Syscalls scall)
|
||||
{
|
||||
if(scall == 0)
|
||||
panic("canwakeup on page fault");
|
||||
|
||||
switch(scall){
|
||||
default:
|
||||
panic("canwakeup: unknown scall %d\n", scall);
|
||||
case SysFstat:
|
||||
case SysFwstat:
|
||||
case SysOpen:
|
||||
case SysPread:
|
||||
case SysPwrite:
|
||||
case SysRendezvous:
|
||||
case SysSemacquire:
|
||||
case SysSemrelease:
|
||||
case SysAwait:
|
||||
return 1;
|
||||
case SysAwake:
|
||||
case SysBind:
|
||||
case SysClose:
|
||||
case SysCreate:
|
||||
case SysErrstr:
|
||||
case SysExec:
|
||||
case Sys_exits:
|
||||
case SysFauth:
|
||||
case SysFd2path:
|
||||
case SysFversion:
|
||||
case SysMount:
|
||||
case SysNoted:
|
||||
case SysNotify:
|
||||
case SysRemove:
|
||||
case SysRfork:
|
||||
case SysSeek:
|
||||
case SysUnmount:
|
||||
case SysAlarm:
|
||||
return 0;
|
||||
}
|
||||
panic("awake_can_interrupts on page fault");
|
||||
if(scall >= sizeof(awakeable_syscalls) - 1)
|
||||
panic("awake_can_interrupts: unknown syscall %d", scall);
|
||||
return awakeable_syscalls[scall];
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually wakeup a process
|
||||
*/
|
||||
#else
|
||||
# define awake_detect_loop(h)
|
||||
# define awake_can_interrupt(scall) (awakeable_syscalls[scall])
|
||||
//# undef assert
|
||||
//# define assert(a)
|
||||
#endif
|
||||
|
||||
static void
|
||||
wakeupProc(Proc *p, unsigned long t)
|
||||
pool_init(void)
|
||||
{
|
||||
Mpl pl;
|
||||
Rendez *r;
|
||||
Proc *d, **l;
|
||||
awkpool.size = sys->nproc * (sys->nmach + 1);
|
||||
if(awkpool.size >= 1<<16)
|
||||
awkpool.size = 1<<16; /* we have 16 bit in the wakeup token for the index */
|
||||
awkpool.alarms = malloc(sizeof(AwakeAlarm) * awkpool.size);
|
||||
awkpool.end = awkpool.alarms + awkpool.size;
|
||||
awkpool.nfree = awkpool.size;
|
||||
awkpool.first = 0;
|
||||
}
|
||||
|
||||
/* this loop is to avoid lock ordering problems. */
|
||||
for(;;){
|
||||
pl = splhi();
|
||||
lock(&p->rlock);
|
||||
r = p->r;
|
||||
static AwakeAlarm*
|
||||
alarm_new(Proc *p)
|
||||
{
|
||||
AwakeAlarm *a;
|
||||
|
||||
/* waiting for a wakeup? */
|
||||
if(r == nil)
|
||||
break; /* no */
|
||||
|
||||
/* try for the second lock */
|
||||
if(canlock(&r->l)){
|
||||
if(p->state != Wakeme || r->p != p)
|
||||
panic("wakeupProc: state %d %d %d", r->p != p, p->r != r, p->state);
|
||||
p->r = nil;
|
||||
r->p = nil;
|
||||
ready(p);
|
||||
unlock(&r->l);
|
||||
break;
|
||||
}
|
||||
|
||||
/* give other process time to get out of critical section and try again */
|
||||
unlock(&p->rlock);
|
||||
splx(pl);
|
||||
sched();
|
||||
lock(&awkpool.l);
|
||||
while(awkpool.nfree <= 2*p->nwakeups){
|
||||
unlock(&awkpool.l);
|
||||
resrcwait("wait-wkp", nil);
|
||||
lock(&awkpool.l);
|
||||
}
|
||||
unlock(&p->rlock);
|
||||
splx(pl);
|
||||
|
||||
p->pendingWakeup = t;
|
||||
if(p->state != Rendezvous)
|
||||
return;
|
||||
|
||||
/* Try and pull out of a rendezvous */
|
||||
lock(&p->rgrp->l);
|
||||
if(p->state == Rendezvous) {
|
||||
p->rendval = ~0;
|
||||
l = &REND(p->rgrp, p->rendtag);
|
||||
for(d = *l; d; d = d->rendhash) {
|
||||
if(d == p) {
|
||||
*l = p->rendhash;
|
||||
break;
|
||||
}
|
||||
l = &d->rendhash;
|
||||
}
|
||||
ready(p);
|
||||
/* here we know that nfree > 0... */
|
||||
if(awkpool.first == awkpool.size){
|
||||
/* but we don't now where the first free is */
|
||||
awkpool.first = -1;
|
||||
while(++awkpool.first < awkpool.size && awkpool.alarms[awkpool.first].p)
|
||||
;
|
||||
}
|
||||
unlock(&p->rgrp->l);
|
||||
assert(awkpool.first < awkpool.size);
|
||||
|
||||
a = awkpool.alarms + awkpool.first;
|
||||
a->p = p;
|
||||
|
||||
if(adec(&awkpool.nfree) > 0){
|
||||
UpdateFirst:
|
||||
while(++awkpool.first < awkpool.size && awkpool.alarms[awkpool.first].p)
|
||||
;
|
||||
if(awkpool.first == awkpool.size){
|
||||
awkpool.first = -1;
|
||||
goto UpdateFirst;
|
||||
}
|
||||
} else {
|
||||
awkpool.first = awkpool.size;
|
||||
}
|
||||
unlock(&awkpool.l);
|
||||
|
||||
ainc(&p->nwakeups);
|
||||
a->done = 0;
|
||||
a->notified = p->notified ? 1 : 0;
|
||||
a->time = 0;
|
||||
a->next = nil;
|
||||
|
||||
return a;
|
||||
}
|
||||
static void
|
||||
alarm_free(AwakeAlarm* a)
|
||||
{
|
||||
Proc *p = a->p;
|
||||
#ifdef DEBUG
|
||||
a->next = (void*)getcallerpc();
|
||||
#endif
|
||||
a->p = nil;
|
||||
ainc(&awkpool.nfree);
|
||||
adec(&p->nwakeups);
|
||||
}
|
||||
|
||||
void
|
||||
awakekproc(void* v)
|
||||
awake_fell_asleep(Proc *p)
|
||||
{
|
||||
Proc *p;
|
||||
PendingWakeup *tail, *toAwake, **toAwakeEnd, *toDefer, **toDeferEnd;
|
||||
uint64_t now;
|
||||
|
||||
for(;;){
|
||||
now = sys->ticks;
|
||||
toAwake = nil;
|
||||
toAwakeEnd = &toAwake;
|
||||
toDefer = nil;
|
||||
toDeferEnd = &toDefer;
|
||||
|
||||
/* search for processes to wakeup */
|
||||
qlock(&l);
|
||||
tail = alarms;
|
||||
while(tail && tail->time <= now){
|
||||
if(tail->p->pendingWakeup > tail->p->lastWakeup
|
||||
&& tail->p->state >= Ready){
|
||||
*toDeferEnd = tail;
|
||||
toDeferEnd = &tail->next;
|
||||
} else if (!tail->notified && tail->p->notified){
|
||||
/* If an awake was requested outside of
|
||||
* a note handler, it cannot expire
|
||||
* while executing a note handler.
|
||||
* On the other hand if an awake was
|
||||
* requeted while executing a note handler,
|
||||
* it's up to the note handler to
|
||||
* forgive it if it's not needed anymore.
|
||||
*/
|
||||
*toDeferEnd = tail;
|
||||
toDeferEnd = &tail->next;
|
||||
} else {
|
||||
*toAwakeEnd = tail;
|
||||
toAwakeEnd = &tail->next;
|
||||
--tail->p->wakeups;
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
if(toAwake != nil){
|
||||
*toAwakeEnd = nil;
|
||||
if(toDefer != nil){
|
||||
*toDeferEnd = tail;
|
||||
alarms = toDefer;
|
||||
} else {
|
||||
alarms = tail;
|
||||
}
|
||||
}
|
||||
qunlock(&l);
|
||||
|
||||
/* wakeup sleeping processes */
|
||||
while(toAwake != nil){
|
||||
p = toAwake->p;
|
||||
if(p->lastWakeup < toAwake->time && p->state > Ready) {
|
||||
/* debugged processes will miss wakeups,
|
||||
* but the alternatives seem even worse
|
||||
*/
|
||||
if(canqlock(&p->debug)){
|
||||
if(!waserror()){
|
||||
wakeupProc(p, toAwake->time);
|
||||
poperror();
|
||||
}
|
||||
qunlock(&p->debug);
|
||||
}
|
||||
}
|
||||
tail = toAwake->next;
|
||||
jehanne_free(toAwake);
|
||||
toAwake = tail;
|
||||
}
|
||||
|
||||
sleep(&awaker, return0, 0);
|
||||
Syscalls cs = p->cursyscall;
|
||||
if(cs != 0 && cs != SysAwake){
|
||||
/* awake_register might sleep() on alarm_new and we
|
||||
* don't want this sleep to be interrupted.
|
||||
*/
|
||||
p->wakeups[p->notified].blockingsc = cs;
|
||||
p->wakeups[p->notified].fell_asleep = sys->ticks;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
awake_should_wake_up(Proc *p)
|
||||
{
|
||||
AwakeAlarm *a = p->wakeups[p->notified].elapsed;
|
||||
Syscalls blockingsc = p->wakeups[p->notified].blockingsc;
|
||||
return a != nil
|
||||
&& blockingsc
|
||||
&& awake_can_interrupt(blockingsc)
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
awake_awakened(Proc *p)
|
||||
{
|
||||
AwakeAlarm *a;
|
||||
Syscalls blockingsc = p->wakeups[p->notified].blockingsc;
|
||||
if(blockingsc && awake_can_interrupt(blockingsc))
|
||||
if(a = xchgm(&p->wakeups[p->notified].elapsed, nil)){
|
||||
p->wakeups[p->notified].awakened = sys->ticks;
|
||||
p->wakeups[p->notified].last = alarm2wkp(a);
|
||||
p->wakeups[p->notified].blockingsc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* called from noted() to remove all pending alarms */
|
||||
void
|
||||
awake_gc_note(Proc *p)
|
||||
{
|
||||
AwakeAlarm *a, **last;
|
||||
|
||||
assert(p->notified != 0);
|
||||
|
||||
lock(&rl);
|
||||
awake_detect_loop(registry);
|
||||
|
||||
/* first pending alarm */
|
||||
a = xchgm(&p->wakeups[1].elapsed, nil);
|
||||
p->wakeups[1].blockingsc = 0;
|
||||
|
||||
/* then clear the registry */
|
||||
last = ®istry;
|
||||
for(a = *last; a != nil && p->wakeups[1].count > 0; a = *last) {
|
||||
if(!a->p)
|
||||
panic("awake_reset: free alarm in registry");
|
||||
if(a->p == p && a->notified){
|
||||
adec(&p->wakeups[a->notified].count);
|
||||
*last = a->next;
|
||||
alarm_free(a);
|
||||
} else {
|
||||
last = &a->next;
|
||||
}
|
||||
}
|
||||
awake_detect_loop(registry);
|
||||
if(registry)
|
||||
next_wakeup = registry->time;
|
||||
unlock(&rl);
|
||||
}
|
||||
|
||||
/*
|
||||
* called on pexit
|
||||
* called from pexit() and sysexec() to clear all pending
|
||||
*/
|
||||
void
|
||||
clearwakeups(Proc *p)
|
||||
awake_gc_proc(Proc *p)
|
||||
{
|
||||
PendingWakeup *w, **next, *freelist, **fend;
|
||||
AwakeAlarm *a, **last;
|
||||
|
||||
freelist = nil;
|
||||
/* clear all wakeups (process is exiting) */
|
||||
lock(&rl);
|
||||
awake_detect_loop(registry);
|
||||
|
||||
/* find all PendingWakeup* to free and remove them from alarms */
|
||||
qlock(&l);
|
||||
if(p->wakeups){
|
||||
/* note: qlock(&l) && p->wakeups > 0 => alarms != nil */
|
||||
next = &alarms;
|
||||
fend = &freelist;
|
||||
clearnext:
|
||||
w = *next;
|
||||
while (w != nil && w->p == p) {
|
||||
/* found one to remove */
|
||||
*fend = w; /* append to freelist */
|
||||
*next = w->next; /* remove from alarms */
|
||||
fend = &w->next; /* move fend to end of freelist */
|
||||
*fend = nil; /* clean the end of freelist */
|
||||
--p->wakeups;
|
||||
w = *next; /* next to analyze */
|
||||
}
|
||||
/* while exited => w == nil || w->p != p */
|
||||
if(p->wakeups){
|
||||
/* note: p->wakeups > 0 => w != nil
|
||||
* p->wakeups > 0 && w->p != p => w->next != nil
|
||||
*/
|
||||
next = &w->next;
|
||||
goto clearnext;
|
||||
/* first pending alarm */
|
||||
p->wakeups[0].elapsed = nil;
|
||||
p->wakeups[0].blockingsc = 0;
|
||||
p->wakeups[1].elapsed = nil;
|
||||
p->wakeups[1].blockingsc = 0;
|
||||
|
||||
/* then clear the registry */
|
||||
last = ®istry;
|
||||
for(a = *last; a != nil && (p->wakeups[0].count > 0 || p->wakeups[1].count > 0); a = *last) {
|
||||
if(!a->p)
|
||||
panic("awake_reset: free alarm in registry");
|
||||
if(a->p == p){
|
||||
adec(&p->wakeups[a->notified].count);
|
||||
*last = a->next;
|
||||
alarm_free(a);
|
||||
} else {
|
||||
last = &a->next;
|
||||
}
|
||||
}
|
||||
qunlock(&l);
|
||||
awake_detect_loop(registry);
|
||||
if(registry)
|
||||
next_wakeup = registry->time;
|
||||
unlock(&rl);
|
||||
|
||||
/* free the found PendingWakeup* (out of the lock) */
|
||||
w = freelist;
|
||||
while(w != nil) {
|
||||
freelist = w->next;
|
||||
jehanne_free(w);
|
||||
w = freelist;
|
||||
assert(p->wakeups[0].count == 0);
|
||||
assert(p->wakeups[1].count == 0);
|
||||
|
||||
while(p->nwakeups > 0)
|
||||
resrcwait(nil, nil);
|
||||
}
|
||||
|
||||
static long
|
||||
awake_register(long ms)
|
||||
{
|
||||
AwakeAlarm *a, *new, **last;
|
||||
|
||||
new = alarm_new(up);
|
||||
|
||||
ilock(&rl);
|
||||
last = ®istry;
|
||||
awake_detect_loop(registry);
|
||||
|
||||
new->time = (sys->ticks + ms2tk(ms) + sys->nmach) & 0xffffffffffff;
|
||||
for(a = *last; a != nil && a->time <= new->time; a = *last) {
|
||||
if(!a->p)
|
||||
panic("awake_register: free alarm in registry");
|
||||
if(a->done){
|
||||
adec(&a->p->wakeups[a->notified].count);
|
||||
*last = a->next;
|
||||
alarm_free(a);
|
||||
continue;
|
||||
}
|
||||
if(a->time == new->time && a->p == up){
|
||||
/* avoid two alarms at the same tick for a process */
|
||||
++new->time;
|
||||
}
|
||||
last = &a->next;
|
||||
}
|
||||
*last = new;
|
||||
new->next = a;
|
||||
awake_detect_loop(registry);
|
||||
ainc(&up->wakeups[new->notified].count);
|
||||
assert(registry != nil);
|
||||
next_wakeup = registry->time;
|
||||
iunlock(&rl);
|
||||
|
||||
return alarm2wkp(new);
|
||||
}
|
||||
|
||||
static long
|
||||
awake_remove(long request)
|
||||
{
|
||||
AwakeAlarm *a;
|
||||
|
||||
if(request >= up->wakeups[up->notified].last)
|
||||
return 0; /* already free */
|
||||
|
||||
a = wkp2alarm(request);
|
||||
if(a >= awkpool.end)
|
||||
return 0; /* should we send a note to up? */
|
||||
|
||||
if(a->p != up)
|
||||
return 0; /* should we send a note to up? */
|
||||
|
||||
if(a->time != (~request)>>16)
|
||||
return 0; /* should we send a note to up? */
|
||||
|
||||
lock(&rl); /* sync with awake_timer */
|
||||
if(!CASV(&up->wakeups[up->notified].elapsed, a, nil)){
|
||||
a->done = 1;
|
||||
}
|
||||
unlock(&rl);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
long
|
||||
sysawake(long request)
|
||||
{
|
||||
if(request == 0)
|
||||
return up->wakeups[up->notified].last;
|
||||
if(request < 0)
|
||||
return awake_remove(request);
|
||||
return awake_register(request);
|
||||
}
|
||||
|
||||
/*
|
||||
* called every clock tick
|
||||
*/
|
||||
void
|
||||
checkwakeups(void)
|
||||
awake_tick(unsigned long now)
|
||||
{
|
||||
PendingWakeup *p;
|
||||
uint64_t now;
|
||||
if(next_wakeup < now && (now&7) == 0)
|
||||
wakeup(&producer);
|
||||
}
|
||||
|
||||
p = alarms;
|
||||
static void
|
||||
try_wire_process(void)
|
||||
{
|
||||
int i;
|
||||
/* wire to an online processor to reduce context switches
|
||||
* but try to avoid boot processor as it runs awake_tick
|
||||
*/
|
||||
if(up->mach->machno > 0)
|
||||
procwired(up, up->mach->machno);
|
||||
else if(sys->nmach > 1){
|
||||
i = sys->nmach;
|
||||
while(up->wired != nil && i > 1)
|
||||
if(sys->machptr[--i]->online)
|
||||
procwired(up, i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
awake_timer(void* v)
|
||||
{
|
||||
long now;
|
||||
AwakeAlarm **next, *tmp, *toAwake, **toAwakeEnd;
|
||||
|
||||
try_wire_process();
|
||||
|
||||
/* initialize wakeups wkppool */
|
||||
pool_init();
|
||||
|
||||
CheckWakeups:
|
||||
next_wakeup = ~0;
|
||||
|
||||
/* we fix time to preserve wakeup order */
|
||||
now = sys->ticks;
|
||||
|
||||
if(p && p->time <= now)
|
||||
wakeup(&awaker);
|
||||
toAwake = nil;
|
||||
toAwakeEnd = &toAwake;
|
||||
|
||||
/* search for processes to wakeup */
|
||||
ilock(&rl);
|
||||
next = ®istry;
|
||||
while((tmp = *next) != nil && tmp->time <= now){
|
||||
if(tmp->p == nil)
|
||||
panic("awake_timer: free alarm in registry");
|
||||
if(tmp->done){
|
||||
*next = tmp->next;
|
||||
adec(&tmp->p->wakeups[tmp->notified].count);
|
||||
alarm_free(tmp);
|
||||
} else {
|
||||
if(!CASV(&tmp->p->wakeups[tmp->notified].elapsed, nil, tmp)){
|
||||
/* each wakeup must have a chance */
|
||||
next = &tmp->next;
|
||||
} else {
|
||||
adec(&tmp->p->wakeups[tmp->notified].count);
|
||||
*toAwakeEnd = tmp;
|
||||
toAwakeEnd = &tmp->next;
|
||||
*next = tmp->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(toAwake != nil)
|
||||
*toAwakeEnd = nil;
|
||||
|
||||
awake_detect_loop(registry);
|
||||
iunlock(&rl);
|
||||
|
||||
if(toAwake != nil){
|
||||
/* pass the elapsed wakeups to awake_ringer preserving order */
|
||||
lock(&el);
|
||||
*eEnd = toAwake;
|
||||
eEnd = toAwakeEnd;
|
||||
unlock(&el);
|
||||
wakeup(&consumer);
|
||||
}
|
||||
|
||||
if(registry)
|
||||
next_wakeup = registry->time;
|
||||
|
||||
sleep(&producer, return0, 0);
|
||||
goto CheckWakeups;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
wakeupafter(int64_t ms)
|
||||
/* Try to wake up a process
|
||||
* Returns:
|
||||
* - 0 if the alarm must be preserved in the elapsed list
|
||||
* - 1 if the alarm must be removed from the elapsed list and freed
|
||||
*/
|
||||
static int
|
||||
awake_dispatch(AwakeAlarm *a)
|
||||
{
|
||||
PendingWakeup *w, *new, **last;
|
||||
int64_t when;
|
||||
int canfree;
|
||||
Rendez *r;
|
||||
Proc *p;
|
||||
Syscalls bs;
|
||||
|
||||
when = ms2tk(ms) + sys->ticks + 2; /* +2 against round errors and cpu's clocks misalignment */
|
||||
new = jehanne_mallocz(sizeof(PendingWakeup), 1);
|
||||
if(new == nil)
|
||||
p = a->p;
|
||||
if(p == nil)
|
||||
panic("awake_dispatch: free alarm in elapsed list");
|
||||
|
||||
if(p < procalloc.arena || p > procalloc.arena + procalloc.nproc)
|
||||
panic("awake_dispatch: dirty alarm");
|
||||
if(!canlock(&p->rlock))
|
||||
return 0;
|
||||
new->p = up;
|
||||
new->notified = up->notified;
|
||||
new->time = when;
|
||||
|
||||
qlock(&l);
|
||||
last = &alarms;
|
||||
for(w = *last; w != nil && w->time <= when; w = w->next) {
|
||||
last = &w->next;
|
||||
/* sched() locks p->rlock before setting p->mach and p->state
|
||||
*/
|
||||
if(p->mach != nil && p->state <= Running){
|
||||
canfree = p->state < Ready;
|
||||
goto Done;
|
||||
}
|
||||
new->next = w;
|
||||
*last = new;
|
||||
++up->wakeups;
|
||||
qunlock(&l);
|
||||
|
||||
return -when;
|
||||
if(p->wakeups[a->notified].elapsed != a){
|
||||
/* cleared by awake_awakened */
|
||||
canfree = 1;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
canfree = 0;
|
||||
|
||||
if(a->done){
|
||||
/* already signaled, we have to wait for awake_awakened */
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if(a->notified && !p->notified){
|
||||
/* this should never happen because p is not Running:
|
||||
* either noted as been called previously (and thus awake_reset)
|
||||
* or it has not
|
||||
*
|
||||
* so if this happens it's probably a but in awake_reset
|
||||
*/
|
||||
unlock(&p->rlock);
|
||||
panic("awake_dispatch: notified alarm for not notified process");
|
||||
}
|
||||
|
||||
if(p->notepending && !p->notedeferred){
|
||||
/* notes take precedence */
|
||||
goto Done;
|
||||
}
|
||||
|
||||
bs = p->wakeups[a->notified].blockingsc;
|
||||
if(bs == 0 || !awake_can_interrupt(bs)){
|
||||
/* wait for a chance */
|
||||
goto Done;
|
||||
}
|
||||
|
||||
r = p->r;
|
||||
if(r != nil){
|
||||
if(canlock(&r->l)){
|
||||
if(p->state != Wakeme || r->p != p)
|
||||
panic("awake_dispatch: state %d %d %d", r->p != p, p->r != r, p->state);
|
||||
a->done = 1;
|
||||
p->r = nil;
|
||||
r->p = nil;
|
||||
ready(p);
|
||||
unlock(&r->l);
|
||||
}
|
||||
} else {
|
||||
if(p->state == Rendezvous || p->state == Queueing)
|
||||
if(proc_interrupt_finalize(p))
|
||||
a->done = 1;
|
||||
}
|
||||
|
||||
Done:
|
||||
unlock(&p->rlock);
|
||||
return canfree;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
forgivewakeup(int64_t time)
|
||||
void
|
||||
awake_ringer(void* v)
|
||||
{
|
||||
PendingWakeup *w, **last;
|
||||
AwakeAlarm *pending, **pLast, **next, *a;
|
||||
|
||||
if(up->lastWakeup >= time || up->pendingWakeup >= time)
|
||||
return 0;
|
||||
qlock(&l);
|
||||
if(alarms == nil || up->wakeups == 0){
|
||||
qunlock(&l);
|
||||
return 0; // nothing to do
|
||||
pending = nil;
|
||||
pLast = &pending;
|
||||
|
||||
CheckElapsed:
|
||||
lock(&el);
|
||||
*pLast = elapsed;
|
||||
elapsed = nil;
|
||||
eEnd = &elapsed;
|
||||
unlock(&el);
|
||||
|
||||
pLast = &pending;
|
||||
next = pLast;
|
||||
while(*next != nil){
|
||||
a = *next;
|
||||
if(awake_dispatch(a)){
|
||||
*next = a->next;
|
||||
alarm_free(a);
|
||||
} else {
|
||||
*pLast = a;
|
||||
next = &a->next;
|
||||
pLast = next;
|
||||
}
|
||||
}
|
||||
|
||||
last = &alarms;
|
||||
for(w = *last; w != nil && w->time < time; w = w->next) {
|
||||
last = &w->next;
|
||||
if(pending == nil){
|
||||
assert(pLast == &pending);
|
||||
sleep(&consumer, return0, 0);
|
||||
} else if(elapsed == nil){
|
||||
tsleep(&up->sleep, return0, 0, 30);
|
||||
}
|
||||
while(w != nil && w->time == time && w->p != up){
|
||||
last = &w->next;
|
||||
w = w->next;
|
||||
}
|
||||
if(w == nil || w->time > time || w->p != up){
|
||||
/* wakeup not found */
|
||||
qunlock(&l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*last = w->next;
|
||||
--up->wakeups;
|
||||
qunlock(&l);
|
||||
|
||||
jehanne_free(w);
|
||||
|
||||
return -time;
|
||||
}
|
||||
|
||||
int64_t
|
||||
procawake(int64_t ms)
|
||||
{
|
||||
if(ms == 0)
|
||||
return -up->lastWakeup; // nothing to do
|
||||
if(ms < 0)
|
||||
return forgivewakeup(-ms);
|
||||
return wakeupafter(ms);
|
||||
goto CheckElapsed;
|
||||
}
|
||||
|
@ -1,360 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
/*
|
||||
* - locks
|
||||
* - could instead coalesce free items on demand (cf. Wulf)
|
||||
* - or lazy buddy (cf. Barkley)
|
||||
*/
|
||||
|
||||
enum{
|
||||
MinK= PGSHFT, /* default minimum size (one page) */
|
||||
Nbits= sizeof(uintmem)*8,
|
||||
MaxK= Nbits-1, /* last usable k (largest block is 2^k) */
|
||||
|
||||
Busy= 0x80, /* bit set in byte map if block busy (low order bits are block size, 0=unavailable) */
|
||||
};
|
||||
|
||||
//#define usize uintmem
|
||||
|
||||
typedef struct Blk Blk;
|
||||
struct Blk{
|
||||
Blk* forw; /* free list */
|
||||
Blk* back;
|
||||
};
|
||||
|
||||
typedef struct Bfree Bfree;
|
||||
struct Bfree{
|
||||
Blk h; /* header */
|
||||
Lock;
|
||||
uint32_t avail;
|
||||
};
|
||||
|
||||
struct Bpool{
|
||||
Lock lk; /* TO DO: localise lock (need CAS update of pool->kofb) (also see Johnson & Davis 1992) */
|
||||
Bfree blist[Nbits]; /* increasing powers of two */
|
||||
uint8_t* kofb; /* k(block_index) with top bit set if busy */
|
||||
uint32_t mink;
|
||||
uint32_t maxk;
|
||||
uint32_t maxb; /* limit to block index, in minbsize blocks (pool size) */
|
||||
Blk* blocks; /* free list pointers */
|
||||
uintmem base;
|
||||
uintmem minbsize;
|
||||
uintmem limit;
|
||||
};
|
||||
|
||||
static uint8_t lg2table[256] = {
|
||||
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
};
|
||||
|
||||
#define BI(a) ((a)>>pool->mink)
|
||||
#define IB(x) ((uintmem)(x)<<pool->mink)
|
||||
|
||||
static int
|
||||
lg2ceil(uintmem m)
|
||||
{
|
||||
uint32_t n, h;
|
||||
int r;
|
||||
|
||||
r = (m & (m-1)) != 0; /* not a power of two => round up */
|
||||
n = (uint32_t)m;
|
||||
if(sizeof(uintmem)>sizeof(uint32_t)){
|
||||
h = (uint64_t)m>>32;
|
||||
if(h != 0){
|
||||
n = h;
|
||||
r += 32;
|
||||
}
|
||||
}
|
||||
if((n>>8) == 0)
|
||||
return lg2table[n] + r;
|
||||
if((n>>16) == 0)
|
||||
return 8 + lg2table[n>>8] + r;
|
||||
if((n>>24) == 0)
|
||||
return 16 + lg2table[n>>16] + r;
|
||||
return 24 + lg2table[n>>24] + r;
|
||||
}
|
||||
|
||||
Bpool*
|
||||
bpoolcreate(uint32_t mink, uint32_t maxk, uintmem base, uintmem top, void* (*alloc)(usize, int))
|
||||
{
|
||||
int k;
|
||||
Blk *b;
|
||||
Bpool *pool;
|
||||
|
||||
if(mink == 0)
|
||||
mink = MinK;
|
||||
if(maxk > MaxK)
|
||||
panic("bpoolcreate");
|
||||
pool = alloc(sizeof(Bpool), 1);
|
||||
if(pool == nil)
|
||||
panic("bpoolcreate alloc");
|
||||
pool->mink = mink;
|
||||
pool->maxk = maxk;
|
||||
pool->base = base;
|
||||
pool->limit = top;
|
||||
pool->maxb = BI(top-base);
|
||||
pool->minbsize = (uintmem)1<<mink;
|
||||
pool->blocks = alloc((pool->maxb+1)*sizeof(*pool->blocks), 0);
|
||||
if(pool->blocks == nil)
|
||||
panic("bpoolinit: can't allocate %ud blocks", pool->maxb+1);
|
||||
for(k = 0; k < nelem(pool->blist); k++){
|
||||
b = &pool->blist[k].h;
|
||||
b->forw = b->back = b;
|
||||
}
|
||||
pool->kofb = alloc((pool->maxb+1)*sizeof(*pool->kofb), 1);
|
||||
if(pool->kofb == nil)
|
||||
panic("physinit: can't allocate %ud kofb", pool->maxb+1);
|
||||
jehanne_print("pool %#p space base %#P top=%#P maxb=%#ux (%d)\n", pool, base, top, pool->maxb, pool->maxb);
|
||||
return pool;
|
||||
}
|
||||
|
||||
uintmem
|
||||
bpoolalloc(Bpool *pool, usize size)
|
||||
{
|
||||
int j, k;
|
||||
Blk *b, *b2;
|
||||
uintmem a, a2;
|
||||
uint32_t bi;
|
||||
|
||||
k = lg2ceil(size);
|
||||
if(k < pool->mink)
|
||||
k = pool->mink;
|
||||
if(k > pool->maxk)
|
||||
return 0;
|
||||
DBG("%#p size=%#P k=%d\n", pool, (uintmem)size, k);
|
||||
lock(&pool->lk);
|
||||
for(j = k;;){
|
||||
b = pool->blist[j].h.forw;
|
||||
if(b != &pool->blist[j].h)
|
||||
break;
|
||||
if(++j > pool->maxk){
|
||||
unlock(&pool->lk);
|
||||
return 0; /* out of space */
|
||||
}
|
||||
}
|
||||
if(b == nil)
|
||||
panic("physalloc: nil");
|
||||
/* set busy state */
|
||||
bi = b - pool->blocks;
|
||||
if(pool->kofb[bi] & Busy || b->forw == nil || b->back == nil)
|
||||
panic("physalloc: inval k=%d j=%d %#p %d %#ux %#p %#p", k, j, b, bi, pool->kofb[bi], b->forw, b->back);
|
||||
pool->kofb[bi] = k | Busy;
|
||||
pool->blist[j].avail--;
|
||||
b->forw->back = b->back;
|
||||
b->back->forw = b->forw;
|
||||
a = IB(bi);
|
||||
while(j != k){
|
||||
/* split */
|
||||
j--;
|
||||
a2 = a+((uintmem)1<<j);
|
||||
bi = BI(a2);
|
||||
DBG("split %#llux %#llux k=%d %#llux pool->kofb=%#ux\n", a, a2, j, (uintmem)1<<j, pool->kofb[bi]);
|
||||
if(pool->kofb[bi] & Busy){
|
||||
if(pool->kofb[bi] & ~Busy)
|
||||
panic("bal: busy block %#llux k=%d\n", a, pool->kofb[bi] & ~Busy);
|
||||
}
|
||||
pool->kofb[bi] = j; /* new size, not busy */
|
||||
b2 = &pool->blocks[bi];
|
||||
b2->forw = &pool->blist[j].h;
|
||||
b2->back = pool->blist[j].h.back;
|
||||
pool->blist[j].h.back = b2;
|
||||
b2->back->forw = b2;
|
||||
pool->blist[j].avail++;
|
||||
}
|
||||
unlock(&pool->lk);
|
||||
return a + pool->base;
|
||||
}
|
||||
|
||||
void
|
||||
bpoolfree(Bpool *pool, uintmem a, usize size)
|
||||
{
|
||||
int k;
|
||||
Blk *b, *b2;
|
||||
uintmem a2;
|
||||
uint32_t bi, bi2;
|
||||
|
||||
k = lg2ceil(size); /* could look it up in pool->kofb */
|
||||
if(k < pool->mink)
|
||||
return;
|
||||
if(k > pool->maxk)
|
||||
k = pool->maxk;
|
||||
DBG("%#p free %#llux %#P k%d\n", pool, a, (uintmem)size, k);
|
||||
if(a < pool->base)
|
||||
panic("bpoolfree");
|
||||
a -= pool->base;
|
||||
bi = BI(a);
|
||||
lock(&pool->lk);
|
||||
if(pool->kofb[bi] != 0 && pool->kofb[bi] != (Busy|k)){
|
||||
unlock(&pool->lk);
|
||||
panic("balfree: busy %#llux odd k k=%d kofb=%#ux\n", a, k, pool->kofb[bi]);
|
||||
}
|
||||
for(; k != pool->maxk; k++){
|
||||
pool->kofb[bi] = Busy;
|
||||
a2 = a ^ ((uintmem)1<<k); /* buddy */
|
||||
bi2 = BI(a2);
|
||||
b2 = &pool->blocks[bi2];
|
||||
if(bi2 >= pool->maxb || pool->kofb[bi2] != k)
|
||||
break;
|
||||
/* valid, not busy or empty, size k */
|
||||
DBG("combine %#llux %#llux %d %#llux\n", a, a2, k, (uintmem)1<<k);
|
||||
b2->back->forw = b2->forw;
|
||||
b2->forw->back = b2->back;
|
||||
pool->kofb[bi2] = Busy;
|
||||
pool->blist[k].avail--;
|
||||
if(a2 < a){
|
||||
a = a2;
|
||||
bi = bi2;
|
||||
}
|
||||
}
|
||||
pool->kofb[bi] = k; /* sets size and resets Busy */
|
||||
b = &pool->blocks[bi];
|
||||
b->forw = &pool->blist[k].h;
|
||||
b->back = pool->blist[k].h.back;
|
||||
pool->blist[k].h.back = b;
|
||||
b->back->forw = b;
|
||||
pool->blist[k].avail++;
|
||||
unlock(&pool->lk);
|
||||
}
|
||||
|
||||
void
|
||||
bpoolallocrange(Bpool *pool, usize *low, usize *high)
|
||||
{
|
||||
*low = (usize)1<<pool->mink;
|
||||
*high = (usize)1<<pool->maxk;
|
||||
}
|
||||
|
||||
static void
|
||||
ibpoolfree(Bpool *pool, uintmem base, usize size)
|
||||
{
|
||||
bpoolfree(pool, base+pool->base, size);
|
||||
}
|
||||
|
||||
void
|
||||
bpoolinitfree(Bpool *pool, uintmem base, uintmem lim)
|
||||
{
|
||||
uintmem m, size;
|
||||
int i;
|
||||
|
||||
/* chop limit to min block alignment */
|
||||
if(base >= pool->limit)
|
||||
return;
|
||||
if(pool->base > base)
|
||||
base = pool->base;
|
||||
if(lim > pool->limit)
|
||||
lim = pool->limit;
|
||||
base -= pool->base;
|
||||
lim -= pool->base;
|
||||
lim &= ~(pool->minbsize-1);
|
||||
if(BI(lim) > pool->maxb){
|
||||
jehanne_print("physinitfree: address space too large");
|
||||
lim = IB(pool->maxb);
|
||||
}
|
||||
|
||||
/* round base to min block alignment */
|
||||
base = (base + pool->minbsize-1) & ~(pool->minbsize-1);
|
||||
|
||||
size = lim - base;
|
||||
if(size < pool->minbsize)
|
||||
return;
|
||||
DBG("bpoolinitfree %#p %#P-%#P [%#P]\n", pool, pool->base+base, pool->base+lim, size);
|
||||
|
||||
/* move up from base in largest blocks that remain aligned */
|
||||
for(i=pool->mink; i<pool->maxk; i++){
|
||||
m = (uintmem)1 << i;
|
||||
if(base & m){
|
||||
if(size < m)
|
||||
break;
|
||||
if(base & (m-1)){
|
||||
jehanne_print(" ** error: %#P %#P\n", base, m);
|
||||
return;
|
||||
}
|
||||
ibpoolfree(pool, base, m);
|
||||
base += m;
|
||||
size -= m;
|
||||
}
|
||||
}
|
||||
|
||||
/* largest chunks, aligned */
|
||||
m = (uintmem)1<<pool->maxk;
|
||||
while(size >= m){
|
||||
if(base & (m-1)){
|
||||
jehanne_print(" ** error: %#P %#P\n", base, m);
|
||||
return;
|
||||
}
|
||||
ibpoolfree(pool, base, m);
|
||||
base += m;
|
||||
size -= m;
|
||||
}
|
||||
|
||||
/* free remaining chunks, decreasing alignment */
|
||||
for(; size >= pool->minbsize; m >>= 1){
|
||||
if(size & m){
|
||||
DBG("\t%#P %#P\n", base, m);
|
||||
if(base & (m-1)){
|
||||
jehanne_print(" ** error: %#P %#P\n", base, m);
|
||||
return;
|
||||
}
|
||||
ibpoolfree(pool, base, m);
|
||||
base += m;
|
||||
size &= ~m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char*
|
||||
seprintbpoolstats(Bpool *pool, char *s, char *e)
|
||||
{
|
||||
Bfree *b;
|
||||
int i;
|
||||
|
||||
lock(&pool->lk);
|
||||
for(i = 0; i < nelem(pool->blist); i++){
|
||||
b = &pool->blist[i];
|
||||
if(b->avail != 0)
|
||||
s = jehanne_seprint(s, e, "%ud %ulldK blocks avail\n",
|
||||
b->avail, (1ull<<i)/KiB);
|
||||
}
|
||||
unlock(&pool->lk);
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
bpooldump(Bpool *pool)
|
||||
{
|
||||
uintmem a;
|
||||
uint32_t bi;
|
||||
int i, k;
|
||||
Blk *b;
|
||||
|
||||
for(i=0; i<nelem(pool->blist); i++){
|
||||
b = pool->blist[i].h.forw;
|
||||
if(b != &pool->blist[i].h){
|
||||
jehanne_print("%d ", i);
|
||||
for(; b != &pool->blist[i].h; b = b->forw){
|
||||
bi = b-pool->blocks;
|
||||
a = IB(bi);
|
||||
k = pool->kofb[bi];
|
||||
jehanne_print(" [%#llux %d %#ux b=%#llux]", a, k, 1<<k, a^((uintmem)1<<k));
|
||||
}
|
||||
jehanne_print("\n");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,543 +0,0 @@
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
enum
|
||||
{
|
||||
NHASH = 128,
|
||||
MAXCACHE = 1024*1024,
|
||||
NFILE = 4096,
|
||||
NEXTENT = 200, /* extent allocation size */
|
||||
};
|
||||
|
||||
typedef struct Extent Extent;
|
||||
struct Extent
|
||||
{
|
||||
uint32_t start;
|
||||
int len;
|
||||
char *cache;
|
||||
Extent *next;
|
||||
};
|
||||
|
||||
typedef struct Mntcache Mntcache;
|
||||
struct Mntcache
|
||||
{
|
||||
Qid qid;
|
||||
uint32_t devno;
|
||||
Dev* dev;
|
||||
QLock;
|
||||
Extent *list;
|
||||
Mntcache *hash;
|
||||
Mntcache *prev;
|
||||
Mntcache *next;
|
||||
};
|
||||
|
||||
typedef struct Cache Cache;
|
||||
struct Cache
|
||||
{
|
||||
Lock l;
|
||||
int pgno;
|
||||
Mntcache *head;
|
||||
Mntcache *tail;
|
||||
Mntcache *hash[NHASH];
|
||||
};
|
||||
|
||||
typedef struct Ecache Ecache;
|
||||
struct Ecache
|
||||
{
|
||||
Lock l;
|
||||
int total;
|
||||
int free;
|
||||
Extent* head;
|
||||
};
|
||||
|
||||
//static Image fscache; // not used
|
||||
static Cache cache;
|
||||
static Ecache ecache;
|
||||
static int maxcache = MAXCACHE;
|
||||
|
||||
static void
|
||||
extentfree(Extent* e)
|
||||
{
|
||||
jehanne_free(e->cache);
|
||||
e->cache = nil;
|
||||
lock(&ecache.l);
|
||||
e->next = ecache.head;
|
||||
ecache.head = e;
|
||||
ecache.free++;
|
||||
unlock(&ecache.l);
|
||||
}
|
||||
|
||||
static Extent*
|
||||
extentalloc(void)
|
||||
{
|
||||
Extent *e;
|
||||
int i;
|
||||
|
||||
lock(&ecache.l);
|
||||
if(ecache.head == nil){
|
||||
e = jehanne_malloc(NEXTENT*sizeof(Extent));
|
||||
if(e == nil){
|
||||
unlock(&ecache.l);
|
||||
return nil;
|
||||
}
|
||||
for(i = 0; i < NEXTENT; i++){
|
||||
e->next = ecache.head;
|
||||
ecache.head = e;
|
||||
e++;
|
||||
}
|
||||
ecache.free += NEXTENT;
|
||||
ecache.total += NEXTENT;
|
||||
}
|
||||
|
||||
e = ecache.head;
|
||||
ecache.head = e->next;
|
||||
jehanne_memset(e, 0, sizeof(Extent));
|
||||
ecache.free--;
|
||||
unlock(&ecache.l);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void
|
||||
cinit(void)
|
||||
{
|
||||
int i;
|
||||
Mntcache *mc;
|
||||
|
||||
if((cache.head = jehanne_malloc(sizeof(Mntcache)*NFILE)) == nil)
|
||||
panic("cinit: no memory");
|
||||
mc = cache.head;
|
||||
|
||||
/* a good algorithm to set maxcache would be nice */
|
||||
|
||||
for(i = 0; i < NFILE-1; i++) {
|
||||
mc->next = mc+1;
|
||||
mc->prev = mc-1;
|
||||
mc++;
|
||||
}
|
||||
|
||||
cache.tail = mc;
|
||||
cache.tail->next = 0;
|
||||
cache.head->prev = 0;
|
||||
}
|
||||
|
||||
void
|
||||
cnodata(Mntcache *mc)
|
||||
{
|
||||
Extent *e, *n;
|
||||
|
||||
/*
|
||||
* Invalidate all extent data
|
||||
* Image lru will waste the pages
|
||||
*/
|
||||
for(e = mc->list; e; e = n) {
|
||||
n = e->next;
|
||||
extentfree(e);
|
||||
}
|
||||
mc->list = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ctail(Mntcache *mc)
|
||||
{
|
||||
/* Unlink and send to the tail */
|
||||
if(mc->prev)
|
||||
mc->prev->next = mc->next;
|
||||
else
|
||||
cache.head = mc->next;
|
||||
if(mc->next)
|
||||
mc->next->prev = mc->prev;
|
||||
else
|
||||
cache.tail = mc->prev;
|
||||
|
||||
if(cache.tail) {
|
||||
mc->prev = cache.tail;
|
||||
cache.tail->next = mc;
|
||||
mc->next = 0;
|
||||
cache.tail = mc;
|
||||
}
|
||||
else {
|
||||
cache.head = mc;
|
||||
cache.tail = mc;
|
||||
mc->prev = 0;
|
||||
mc->next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
copen(Chan *c)
|
||||
{
|
||||
int h;
|
||||
Extent *e, *next;
|
||||
Mntcache *mc, *f, **l;
|
||||
|
||||
/* directories aren't cacheable and append-only files confuse us */
|
||||
if(c->qid.type&(QTDIR|QTAPPEND))
|
||||
return;
|
||||
|
||||
h = c->qid.path%NHASH;
|
||||
lock(&cache.l);
|
||||
for(mc = cache.hash[h]; mc != nil; mc = mc->hash) {
|
||||
if(mc->qid.path == c->qid.path)
|
||||
if(mc->qid.type == c->qid.type)
|
||||
if(mc->devno == c->devno && mc->dev == c->dev) {
|
||||
c->mc = mc;
|
||||
ctail(mc);
|
||||
unlock(&cache.l);
|
||||
|
||||
/* File was updated, invalidate cache */
|
||||
if(mc->qid.vers != c->qid.vers) {
|
||||
mc->qid.vers = c->qid.vers;
|
||||
qlock(mc);
|
||||
cnodata(mc);
|
||||
qunlock(mc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* LRU the cache headers */
|
||||
mc = cache.head;
|
||||
l = &cache.hash[mc->qid.path%NHASH];
|
||||
for(f = *l; f; f = f->hash) {
|
||||
if(f == mc) {
|
||||
*l = mc->hash;
|
||||
break;
|
||||
}
|
||||
l = &f->hash;
|
||||
}
|
||||
|
||||
mc->qid = c->qid;
|
||||
mc->devno = c->devno;
|
||||
mc->dev = c->dev;
|
||||
|
||||
l = &cache.hash[h];
|
||||
mc->hash = *l;
|
||||
*l = mc;
|
||||
ctail(mc);
|
||||
|
||||
qlock(mc);
|
||||
c->mc = mc;
|
||||
e = mc->list;
|
||||
mc->list = 0;
|
||||
unlock(&cache.l);
|
||||
|
||||
while(e) {
|
||||
next = e->next;
|
||||
extentfree(e);
|
||||
e = next;
|
||||
}
|
||||
qunlock(mc);
|
||||
}
|
||||
|
||||
static int
|
||||
cdev(Mntcache *mc, Chan *c)
|
||||
{
|
||||
if(mc->qid.path != c->qid.path)
|
||||
return 0;
|
||||
if(mc->qid.type != c->qid.type)
|
||||
return 0;
|
||||
if(mc->devno != c->devno)
|
||||
return 0;
|
||||
if(mc->dev != c->dev)
|
||||
return 0;
|
||||
if(mc->qid.vers != c->qid.vers)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
cread(Chan *c, uint8_t *buf, int len, int64_t off)
|
||||
{
|
||||
char *p;
|
||||
Mntcache *mc;
|
||||
Extent *e, **t;
|
||||
int o, l, total;
|
||||
uint32_t offset;
|
||||
|
||||
if(off+len > maxcache)
|
||||
return 0;
|
||||
|
||||
mc = c->mc;
|
||||
if(mc == nil)
|
||||
return 0;
|
||||
|
||||
qlock(mc);
|
||||
if(cdev(mc, c) == 0) {
|
||||
qunlock(mc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = off;
|
||||
t = &mc->list;
|
||||
for(e = *t; e; e = e->next) {
|
||||
if(offset >= e->start && offset < e->start+e->len)
|
||||
break;
|
||||
t = &e->next;
|
||||
}
|
||||
|
||||
if(e == 0) {
|
||||
qunlock(mc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
while(len) {
|
||||
p = e->cache;
|
||||
if(p == nil){
|
||||
*t = e->next;
|
||||
extentfree(e);
|
||||
qunlock(mc);
|
||||
return total;
|
||||
}
|
||||
|
||||
o = offset - e->start;
|
||||
l = len;
|
||||
if(l > e->len-o)
|
||||
l = e->len-o;
|
||||
|
||||
if(waserror()) {
|
||||
qunlock(mc);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
jehanne_memmove(buf, p+o, l);
|
||||
|
||||
poperror();
|
||||
|
||||
buf += l;
|
||||
len -= l;
|
||||
offset += l;
|
||||
total += l;
|
||||
t = &e->next;
|
||||
e = e->next;
|
||||
if(e == 0 || e->start != offset)
|
||||
break;
|
||||
}
|
||||
|
||||
qunlock(mc);
|
||||
return total;
|
||||
}
|
||||
|
||||
Extent*
|
||||
cchain(uint8_t *buf, uint32_t offset, int len, Extent **tail)
|
||||
{
|
||||
int l;
|
||||
char *p;
|
||||
Extent *e, *start, **t;
|
||||
|
||||
start = 0;
|
||||
*tail = 0;
|
||||
t = &start;
|
||||
while(len) {
|
||||
e = extentalloc();
|
||||
if(e == 0)
|
||||
break;
|
||||
|
||||
p = jehanne_mallocz(PGSZ, 0);
|
||||
if(p == nil){
|
||||
extentfree(e);
|
||||
break;
|
||||
}
|
||||
l = len;
|
||||
if(l > PGSZ)
|
||||
l = PGSZ;
|
||||
|
||||
e->cache = p;
|
||||
e->start = offset;
|
||||
e->len = l;
|
||||
|
||||
jehanne_memmove(p, buf, l);
|
||||
|
||||
buf += l;
|
||||
offset += l;
|
||||
len -= l;
|
||||
|
||||
*t = e;
|
||||
*tail = e;
|
||||
t = &e->next;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
int
|
||||
cpgmove(Extent *e, uint8_t *buf, int boff, int len)
|
||||
{
|
||||
if(e->cache == nil){
|
||||
/* shouldn't happen */
|
||||
jehanne_print("CACHE: cpgmove %#p %d %d nil\n", e, boff, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
jehanne_memmove(e->cache+boff, buf, len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
cupdate(Chan *c, uint8_t *buf, int len, int64_t off)
|
||||
{
|
||||
Mntcache *mc;
|
||||
Extent *tail;
|
||||
Extent *e, *f, *p;
|
||||
int o, ee, eblock;
|
||||
uint32_t offset;
|
||||
|
||||
if(off > maxcache || len == 0)
|
||||
return;
|
||||
|
||||
mc = c->mc;
|
||||
if(mc == nil)
|
||||
return;
|
||||
qlock(mc);
|
||||
if(cdev(mc, c) == 0) {
|
||||
qunlock(mc);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the insertion point
|
||||
*/
|
||||
offset = off;
|
||||
p = 0;
|
||||
for(f = mc->list; f; f = f->next) {
|
||||
if(f->start > offset)
|
||||
break;
|
||||
p = f;
|
||||
}
|
||||
|
||||
/* trim if there is a successor */
|
||||
eblock = offset+len;
|
||||
if(f != 0 && eblock > f->start) {
|
||||
len -= (eblock - f->start);
|
||||
if(len <= 0) {
|
||||
qunlock(mc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(p == 0) { /* at the head */
|
||||
e = cchain(buf, offset, len, &tail);
|
||||
if(e != 0) {
|
||||
mc->list = e;
|
||||
tail->next = f;
|
||||
}
|
||||
qunlock(mc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* trim to the predecessor */
|
||||
ee = p->start+p->len;
|
||||
if(offset < ee) {
|
||||
o = ee - offset;
|
||||
len -= o;
|
||||
if(len <= 0) {
|
||||
qunlock(mc);
|
||||
return;
|
||||
}
|
||||
buf += o;
|
||||
offset += o;
|
||||
}
|
||||
|
||||
/* try and pack data into the predecessor */
|
||||
if(offset == ee && p->len < PGSZ) {
|
||||
o = len;
|
||||
if(o > PGSZ - p->len)
|
||||
o = PGSZ - p->len;
|
||||
if(cpgmove(p, buf, p->len, o)) {
|
||||
p->len += o;
|
||||
buf += o;
|
||||
len -= o;
|
||||
offset += o;
|
||||
if(len <= 0) {
|
||||
if(f && p->start + p->len > f->start) jehanne_print("CACHE: p->start=%uld p->len=%d f->start=%uld\n", p->start, p->len, f->start);
|
||||
qunlock(mc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e = cchain(buf, offset, len, &tail);
|
||||
if(e != 0) {
|
||||
p->next = e;
|
||||
tail->next = f;
|
||||
}
|
||||
qunlock(mc);
|
||||
}
|
||||
|
||||
void
|
||||
cwrite(Chan* c, uint8_t *buf, int len, int64_t off)
|
||||
{
|
||||
int o, eo;
|
||||
Mntcache *mc;
|
||||
uint32_t eblock, ee;
|
||||
Extent *p, *f, *e, *tail;
|
||||
uint32_t offset;
|
||||
|
||||
if(off > maxcache || len == 0)
|
||||
return;
|
||||
|
||||
mc = c->mc;
|
||||
if(mc == nil)
|
||||
return;
|
||||
|
||||
qlock(mc);
|
||||
if(cdev(mc, c) == 0) {
|
||||
qunlock(mc);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = off;
|
||||
mc->qid.vers++;
|
||||
c->qid.vers++;
|
||||
|
||||
p = 0;
|
||||
for(f = mc->list; f; f = f->next) {
|
||||
if(f->start >= offset)
|
||||
break;
|
||||
p = f;
|
||||
}
|
||||
|
||||
if(p != 0) {
|
||||
ee = p->start+p->len;
|
||||
eo = offset - p->start;
|
||||
/* pack in predecessor if there is space */
|
||||
if(offset <= ee && eo < PGSZ) {
|
||||
o = len;
|
||||
if(o > PGSZ - eo)
|
||||
o = PGSZ - eo;
|
||||
if(cpgmove(p, buf, eo, o)) {
|
||||
if(eo+o > p->len)
|
||||
p->len = eo+o;
|
||||
buf += o;
|
||||
len -= o;
|
||||
offset += o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free the overlap -- it's a rare case */
|
||||
eblock = offset+len;
|
||||
while(f && f->start < eblock) {
|
||||
e = f->next;
|
||||
extentfree(f);
|
||||
f = e;
|
||||
}
|
||||
|
||||
/* link the block (if any) into the middle */
|
||||
e = cchain(buf, offset, len, &tail);
|
||||
if(e != 0) {
|
||||
tail->next = f;
|
||||
f = e;
|
||||
}
|
||||
|
||||
if(p == 0)
|
||||
mc->list = f;
|
||||
else
|
||||
p->next = f;
|
||||
qunlock(mc);
|
||||
}
|
@ -151,7 +151,6 @@ newchan(void)
|
||||
c->dri = 0;
|
||||
c->aux = 0;
|
||||
c->mchan = 0;
|
||||
c->mc = 0;
|
||||
c->mux = 0;
|
||||
jehanne_memset(&c->mqid, 0, sizeof(c->mqid));
|
||||
c->path = 0;
|
||||
@ -1442,9 +1441,6 @@ namec(char *aname, int amode, long omode, long perm)
|
||||
/* save registers else error() in open has wrong value of c saved */
|
||||
saveregisters();
|
||||
|
||||
if(omode == OEXEC)
|
||||
c->flag &= ~CCACHE;
|
||||
|
||||
c = c->dev->open(c, omode&~OCEXEC);
|
||||
|
||||
if(omode & OCEXEC)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user