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 |