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:
Giacomo Tesio 2017-08-11 01:47:15 +02:00
parent 1bc08b7631
commit 93dde48355
145 changed files with 34164 additions and 33891 deletions

View File

@ -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

View File

@ -10,16 +10,20 @@
typedef struct Pool Pool;
struct Pool {
char* name;
uint32_t maxsize;
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 */
int flags;
int nfree;
int lastcompact;
void* freeroot; /* actually Free* */
void* arenalist; /* actually Arena* */
@ -27,10 +31,6 @@ struct Pool {
int (*merge)(void*, void*);
void (*move)(void* from, void* to);
int flags;
int nfree;
int lastcompact;
void (*lock)(Pool*);
void (*unlock)(Pool*);
void (*print)(Pool*, char*, ...);
@ -41,8 +41,7 @@ struct Pool {
};
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);

View File

@ -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);

View File

@ -1,7 +1,7 @@
{
"realemu": {
"Cflags": [
"-I", "/sys/src/kern/386/"
"-I", "/sys/src/kern/$ARCH/"
],
"Include": [
"/sys/src/cmd/cmd.json"

View File

@ -1,7 +1,7 @@
{
"vga": {
"Cflags": [
"-I", "/sys/src/kern/386/"
"-I", "/sys/src/kern/$ARCH/"
],
"Include": [
"/sys/src/cmd/cmd.json"

View File

@ -120,9 +120,6 @@ main(int argc, char **argv)
case 'c':
mntflags |= MCREATE;
break;
case 'C':
mntflags |= MCACHE;
break;
case 'd':
debug++;
break;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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
View 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;
}

View File

@ -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"

View File

@ -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);
}

View File

@ -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"
]
}
}

View File

@ -6,22 +6,15 @@ 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
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
/*

View File

@ -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

View File

@ -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 */
};
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

View File

@ -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",

View File

@ -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
View 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,
};

View File

@ -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)
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
View 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;
}

View File

@ -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:
#define MBFLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_PAGE_ALIGN)
_multibootheader:
.long MULTIBOOT_HEADER_MAGIC
.long MULTIBOOT_HEADER_FLAGS
.long CHECKSUM
.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, %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

View File

@ -2038,4 +2038,3 @@ etherigbelink(void)
addethercard("i82543", igbepnp);
addethercard("igbe", igbepnp);
}

View File

@ -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)
{

View File

@ -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)
{
uint8_t *p;
/*
* 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...
*/
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);
break;
}
return n;
}
void
fpunotify(Ureg* u)
fpclear(void)
{
/*
* 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;
}
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;
/*
* Some attention should probably be paid here to the
* exception masks and error summary.
*/
msg = "unknown exception";
for(i = 1; i <= 5; i++){
if(!((1<<i) & status))
continue;
msg = mathmsg[i];
break;
}
if(status & 0x01){
if(status & 0x40){
if(status & 0x200)
msg = "stack overflow";
else
msg = "stack underflow";
}else
msg = "invalid operation";
}
snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=0x%lux",
msg, pc, status);
postnote(up, 1, note, NDebug);
}
/*
* math coprocessor error
*/
static void
matherror(Ureg* _, void* __)
{
/*
* Save FPU state to check out the error.
*/
fpsave(&up->fpsave);
up->fpstate = FPinactive;
mathnote(up->fpsave.fsw, up->fpsave.rip);
}
/*
* SIMD error
*/
static void
simderror(Ureg *ureg, void* _)
{
fpsave(&up->fpsave);
up->fpstate = FPinactive;
mathnote(up->fpsave.mxcsr & 0x3f, ureg->ip);
}
/*
* math coprocessor emulation fault
*/
static void
mathemu(Ureg *ureg, void* _)
{
unsigned int status, control;
if(up->fpstate & FPillegal){
/* someone did floating point in a note handler */
postnote(up, 1, "sys: floating point in note handler", NDebug);
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";
else
cm = "Stack Underflow";
}
else
cm = "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;
}
char*
xfpuxf(Ureg* ureg, void* v)
{
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;
}
void
fpuxf(Ureg *ureg, void *p)
{
char *n;
n = xfpuxf(ureg, p);
if(n != nil)
postnote(up, 1, n, NDebug);
}
char*
acfpuxf(Ureg *ureg, void *p)
{
return xfpuxf(ureg, p);
}
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:
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;
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);
}
/*
* Set up the exception handlers.
* math coprocessor segment overrun
*/
trapenable(IdtNM, fpunm, 0, "#NM");
trapenable(IdtMF, fpumf, 0, "#MF");
trapenable(IdtXF, fpuxf, 0, "#XF");
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
View 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;
}

View File

@ -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);
}

View File

@ -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,
@ -34,12 +44,12 @@ enum {
MaxIrqPIC = 15,
VectorLAPIC = VectorPIC+16, /* local APIC interrupts */
IrqLINT0 = VectorLAPIC+0,
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))
/* 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

View File

@ -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;
}

View File

@ -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
{

View File

@ -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

View File

@ -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

View File

@ -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 */
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

View File

@ -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

View File

@ -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 */
LapicID = 0x0020, /* ID */
LapicVER = 0x0030, /* Version */
LapicTPR = 0x0080, /* Task Priority */
LapicAPR = 0x0090, /* Arbitration Priority */
LapicPPR = 0x00A0, /* Processor Priority */
LapicEOI = 0x00B0, /* EOI */
LapicLDR = 0x00D0, /* Logical Destination */
LapicDFR = 0x00E0, /* Destination Format */
LapicSVR = 0x00F0, /* Spurious Interrupt Vector */
LapicISR = 0x0100, /* Interrupt Status (8 registers) */
LapicTMR = 0x0180, /* Trigger Mode (8 registers) */
LapicIRR = 0x0200, /* Interrupt Request (8 registers) */
LapicESR = 0x0280, /* Error Status */
LapicICRLO = 0x0300, /* Interrupt Command */
LapicICRHI = 0x0310, /* Interrupt Command [63:32] */
LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */
LapicPCINT = 0x0340, /* Performance Counter LVT */
LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */
LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */
LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */
LapicTICR = 0x0380, /* Timer Initial Count */
LapicTCCR = 0x0390, /* Timer Current Count */
LapicTDCR = 0x03E0, /* Timer Divide Configuration */
};
enum { /* Siv */
Swen = 0x00000100, /* Software Enable */
Fdis = 0x00000200, /* Focus Disable */
enum { /* LapicSVR */
LapicENABLE = 0x00000100, /* Unit Enable */
LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */
};
enum { /* Iclo */
Lassert = 0x00004000, /* Assert level */
enum { /* LapicICRLO */
/* [14] IPI Trigger Mode Level (RW) */
LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */
LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */
DSnone = 0x00000000, /* Use Destination Field */
DSself = 0x00040000, /* Self is only destination */
DSallinc = 0x00080000, /* All including self */
DSallexc = 0x000c0000, /* All Excluding self */
/* [17:16] Remote Read Status */
LapicINVALID = 0x00000000, /* Invalid */
LapicWAIT = 0x00010000, /* In-Progress */
LapicVALID = 0x00020000, /* Valid */
/* [19:18] Destination Shorthand */
LapicFIELD = 0x00000000, /* No shorthand */
LapicSELF = 0x00040000, /* Self is single destination */
LapicALLINC = 0x00080000, /* All including self */
LapicALLEXC = 0x000C0000, /* All Excluding self */
};
enum { /* Tlvt */
Periodic = 0x00020000, /* Periodic Timer Mode */
enum { /* LapicESR */
LapicSENDCS = 0x00000001, /* Send CS Error */
LapicRCVCS = 0x00000002, /* Receive CS Error */
LapicSENDACCEPT = 0x00000004, /* Send Accept Error */
LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */
LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */
LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */
LapicREGISTER = 0x00000080, /* Illegal Register Address */
};
enum { /* Tdc */
DivX2 = 0x00000000, /* Divide by 2 */
DivX4 = 0x00000001, /* Divide by 4 */
DivX8 = 0x00000002, /* Divide by 8 */
DivX16 = 0x00000003, /* Divide by 16 */
DivX32 = 0x00000008, /* Divide by 32 */
DivX64 = 0x00000009, /* Divide by 64 */
DivX128 = 0x0000000a, /* Divide by 128 */
DivX1 = 0x0000000b, /* Divide by 1 */
enum { /* LapicTIMER */
/* [17] Timer Mode (RW) */
LapicONESHOT = 0x00000000, /* One-shot */
LapicPERIODIC = 0x00020000, /* Periodic */
/* [19:18] Timer Base (RW) */
LapicCLKIN = 0x00000000, /* use CLKIN as input */
LapicTMBASE = 0x00040000, /* use TMBASE */
LapicDIVIDER = 0x00080000, /* use output of the divider */
};
static uint8_t lapictdxtab[] = { /* LapicTDCR */
0x0B, /* divide by 1 */
0x00, /* divide by 2 */
0x01, /* divide by 4 */
0x02, /* divide by 8 */
0x03, /* divide by 16 */
0x08, /* divide by 32 */
0x09, /* divide by 64 */
0x0A, /* divide by 128 */
};
static uint32_t* lapicbase;
static Lapic xlapic[Napic];
Lapic*
lapiclookup(uint32_t id)
typedef struct Apictimer Apictimer;
struct Apictimer
{
Lapic *a;
uint64_t hz;
uint32_t max;
uint32_t min;
uint32_t div;
int tdx;
};
if(id >= nelem(xlapic))
return nil;
a = xlapic + id;
if(a->useable)
return a;
return nil;
}
static Apictimer lapictimer[MACHMAX];
static uint32_t
lapicrget(int r)
lapicr(int r)
{
return lapicbase[r/4];
return *(lapicbase+(r/sizeof(*lapicbase)));
}
static void
lapicrput(int r, uint32_t data)
lapicw(int r, uint32_t data)
{
if(lapicbase != nil){
/* early panics can occur with lapicbase uninitialized
* but if we let it fault, we loose the actual panic PC
*/
lapicbase[r/4] = data;
}
}
int
lapiceoi(int vecno)
{
lapicrput(Eoi, 0);
return vecno;
}
int
lapicisr(int vecno)
{
int isr;
isr = lapicrget(Is + (vecno/32)*16);
return isr & (1<<(vecno%32));
}
static char*
lapicprint(char *p, char *e, Lapic *a, int i)
{
char *s;
s = "proc";
p = jehanne_seprint(p, e, "%-8s ", s);
p = jehanne_seprint(p, e, "%8ux ", i);
// p = jehanne_seprint(p, e, "%.8ux ", a->dest);
// p = jehanne_seprint(p, e, "%.8ux ", a->mask);
// p = jehanne_seprint(p, e, "%c", a->flags & PcmpBP? 'b': ' ');
// p = jehanne_seprint(p, e, "%c ", a->flags & PcmpEN? 'e': ' ');
// p = jehanne_seprint(p, e, "%8ux %8ux", a->lintr[0], a->lintr[1]);
p = jehanne_seprint(p, e, "%12d\n", a->machno);
return p;
}
static long
lapicread(Chan* _1, void *a, long n, int64_t off)
{
char *s, *e, *p;
long i, r;
s = jehanne_malloc(READSTR);
e = s+READSTR;
p = s;
for(i = 0; i < nelem(xlapic); i++)
if(xlapic[i].useable)
p = lapicprint(p, e, xlapic + i, i);
r = -1;
if(!waserror()){
r = readstr(off, a, n, s);
poperror();
}
jehanne_free(s);
return r;
*(lapicbase+(r/sizeof(*lapicbase))) = data;
data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
USED(data);
}
void
lapicinit(int lapicno, uintmem pa, int isbp)
{
Lapic *apic;
/*
* Mark the LAPIC useable if it has a good ID, and the registers can
* be mapped. There is x2LAPIC to be dealt with at some point.
*/
DBG("lapicinit: lapicno %d pa %#P isbp %d caller %#p\n", lapicno, pa, isbp, getcallerpc());
addarchfile("lapic", 0444, lapicread, nil);
if(lapicno >= Napic){
panic("lapicinit%d: out of range", lapicno);
return;
}
if((apic = &xlapic[lapicno])->useable){
jehanne_print("lapicinit%d: already initialised\n", lapicno);
return;
}
if(lapicbase == nil){
//adrmapck(pa, 1024, Ammio, Mfree, Cnone);
if((lapicbase = vmap(pa, 1024)) == nil){
panic("lapicinit%d: can't map lapicbase %#P", lapicno, pa);
return;
}
DBG("lapicinit%d: lapicbase %#P -> %#p\n", lapicno, pa, lapicbase);
}
apic->useable = 1;
/*
* Assign a machno to the processor associated with this
* LAPIC, it may not be an identity map.
* Machno 0 is always the bootstrap processor.
*/
if(isbp){
apic->machno = 0;
m->apicno = lapicno;
}
else
apic->machno = sys->nmach++;
}
void
lapicsetdom(int lapicno, int dom)
{
Lapic *apic;
DBG("lapic%d: setdom: %d\n", lapicno, dom);
if(lapicno >= Napic){
panic("lapic%d: lapicsetdom: apic out of range", lapicno);
return;
}
if((apic = &xlapic[lapicno])->useable)
apic->dom = dom;
else
jehanne_print("lapic%d: lapicsetdom: apic not usable\n", lapicno);
}
int
machdom(Mach *mp)
{
return xlapic[mp->apicno].dom;
}
static void
lapicdump0(Lapic *apic, int i)
{
if(!apic->useable)
return;
DBG("lapic%d: machno %d lint0 %#8.8ux lint1 %#8.8ux\n",
i, apic->machno, apic->lvt[0], apic->lvt[1]);
DBG(" tslvt %#8.8ux pclvt %#8.8ux elvt %#8.8ux\n",
lapicrget(Tslvt), lapicrget(Pclvt), lapicrget(Elvt));
DBG(" tlvt %#8.8ux lint0 %#8.8ux lint1 %#8.8ux siv %#8.8ux\n",
lapicrget(Tlvt), lapicrget(Lint0),
lapicrget(Lint1), lapicrget(Siv));
}
void
lapicdump(void)
{
int i;
if(!DBGFLG)
return;
DBG("lapicbase %#p\n", lapicbase);
for(i = 0; i < Napic; i++)
lapicdump0(xlapic + i, i);
}
int
lapiconline(void)
{
Lapic *apic;
uint64_t tsc;
uint32_t dfr, ver;
int apicno, nlvt;
Apictimer *a;
if(lapicbase == nil)
panic("lapiconline: no lapic base");
if((apicno = ((lapicrget(Id)>>24) & 0xff)) >= Napic)
panic("lapic: id too large %d", apicno);
if(apicno != m->apicno){
panic("lapic: %d != %d", m->apicno, apicno);
dfr = lapicrget(Id) & ~(0xff<<24);
dfr |= m->apicno<<24;
lapicrput(Id, dfr);
apicno = m->apicno;
}
apic = &xlapic[apicno];
if(!apic->useable)
panic("lapiconline: lapic%d: unusable %d", apicno, apic->useable);
a = &lapictimer[m->machno];
/*
* Things that can only be done when on the processor
* owning the APIC, apicinit above runs on the bootstrap
* processor.
* Reload the timer to de-synchronise the processors,
* then lower the task priority to allow interrupts to be
* accepted by the APIC.
*/
ver = lapicrget(Ver);
nlvt = ((ver>>16) & 0xff) + 1;
if(nlvt > nelem(apic->lvt)){
jehanne_print("lapiconline%d: nlvt %d > max (%d)\n",
apicno, nlvt, nelem(apic->lvt));
nlvt = nelem(apic->lvt);
microdelay((TK2MS(1)*1000/sys->nmach) * m->machno);
lapicw(LapicTICR, a->max);
lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
/*
* not strickly neccesary, but reported (osdev.org) to be
* required for some machines.
*/
lapicw(LapicTDCR, lapictdxtab[a->tdx]);
lapicw(LapicTPR, 0);
}
apic->nlvt = nlvt;
apic->ver = ver & 0xff;
/*
* use the i8253/tsc clock to figure out our lapic timer rate.
*/
static void
lapictimerinit(void)
{
uint64_t x, v, hz;
Apictimer *a;
int s;
if(m->machno != 0){
lapictimer[m->machno] = lapictimer[0];
return;
}
s = splhi();
a = &lapictimer[m->machno];
a->tdx = 0;
Retry:
lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
lapicw(LapicTDCR, lapictdxtab[a->tdx]);
x = fastticks(&hz);
x += hz/10;
lapicw(LapicTICR, 0xffffffff);
do{
v = fastticks(nil);
}while(v < x);
v = (0xffffffffUL-lapicr(LapicTCCR))*10;
if(v > hz-(hz/10)){
if(v > hz+(hz/10) && a->tdx < nelem(lapictdxtab)-1){
a->tdx++;
goto Retry;
}
v = hz;
}
assert(v >= (100*HZ));
a->hz = v;
a->div = hz/a->hz;
a->max = a->hz/HZ;
a->min = a->hz/(100*HZ);
splx(s);
v = (v+500000LL)/1000000LL;
print("cpu%d: lapic clock at %lludMHz\n", m->machno, v);
}
void
lapicinit(Apic* apic)
{
uint32_t dfr, ldr, lvt;
if(lapicbase == 0)
lapicbase = apic->addr;
/*
* These don't really matter in Physical mode;
* set the defaults anyway.
*/
// if(jehanne_memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
// dfr = 0xf0000000;
// else
if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
dfr = 0xf0000000;
else
dfr = 0xffffffff;
lapicrput(Df, dfr);
lapicrput(Ld, 0x00000000);
ldr = 0x00000000;
lapicw(LapicDFR, dfr);
lapicw(LapicLDR, ldr);
lapicw(LapicTPR, 0xff);
lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
lapictimerinit();
/*
* Disable interrupts until ready by setting the Task Priority
* register to 0xff.
* Some Pentium revisions have a bug whereby spurious
* interrupts are generated in the through-local mode.
*/
lapicrput(Tp, 0xff);
/*
* Software-enable the APIC in the Spurious Interrupt Vector
* register and set the vector number. The vector number must have
* bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
* is set in the HyperTransport Transaction Control register.
*/
lapicrput(Siv, Swen|IdtSPURIOUS);
/*
* Acknowledge any outstanding interrupts.
*/
lapicrput(Eoi, 0);
/*
* Use the TSC to determine the lapic timer frequency.
* It might be possible to snarf this from a chipset
* register instead.
*/
lapicrput(Tdc, DivX1);
lapicrput(Tlvt, Im);
tsc = rdtsc() + m->cpuhz/10;
lapicrput(Tic, 0xffffffff);
while(rdtsc() < tsc)
;
apic->hz = (0xffffffff-lapicrget(Tcc))*10;
apic->max = apic->hz/HZ;
apic->min = apic->hz/(100*HZ);
apic->div = ((m->cpuhz/apic->max)+HZ/2)/HZ;
if(m->machno == 0 || DBGFLG){
jehanne_print("lapic%d: hz %lld max %lld min %lld div %lld\n", apicno,
apic->hz, apic->max, apic->min, apic->div);
}
/*
* Mask interrupts on Performance Counter overflow and
* Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
* and Lintr1 (Legacy NMI).
* Clear any Error Status (write followed by read) and enable
* the Error interrupt.
*/
switch(apic->nlvt){
case 7:
case 6:
lapicrput(Tslvt, Im);
/*FALLTHROUGH*/
case 5:
lapicrput(Pclvt, Im);
/*FALLTHROUGH*/
default:
switch(m->cpuidax & 0xFFF){
case 0x526: /* stepping cB1 */
case 0x52B: /* stepping E0 */
case 0x52C: /* stepping cC0 */
wrmsr(0x0E, 1<<14); /* TR12 */
break;
}
lapicrput(Lint1, apic->lvt[1]|Im|IdtLINT1);
lapicrput(Lint0, apic->lvt[0]|Im|IdtLINT0);
lapicrput(Es, 0);
lapicrget(Es);
lapicrput(Elvt, IdtERROR);
/*
* Reload the timer to de-synchronise the processors.
* When the caller is ready for the APIC to accept interrupts,
* it should call lapicpri to lower the task priority.
*
* The timer is enabled later by the core-specific startup
* i.e. don't start the timer unless the core needs it,
* to reduce the likelihood of at least one (spurious) interrupt
* from the timer when priority is lowered.
* Set the local interrupts. It's likely these should just be
* masked off for SMP mode as some Pentium Pros have problems if
* LINT[01] are set to ExtINT.
* Acknowledge any outstanding interrupts.
lapicw(LapicLINT0, apic->lintr[0]);
lapicw(LapicLINT1, apic->lintr[1]);
*/
lapiceoi(0);
lvt = (lapicr(LapicVER)>>16) & 0xFF;
if(lvt >= 4)
lapicw(LapicPCINT, ApicIMASK);
lapicw(LapicERROR, VectorPIC+IrqERROR);
lapicw(LapicESR, 0);
lapicr(LapicESR);
/*
* Issue an INIT Level De-Assert to synchronise arbitration ID's.
*/
lapicw(LapicICRHI, 0);
lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
while(lapicr(LapicICRLO) & ApicDELIVS)
;
/*
* Do not allow acceptance of interrupts until all initialisation
* for this processor is done. For the bootstrap processor this can be
* early duing initialisation. For the application processors this should
* be after the bootstrap processor has lowered priority and is accepting
* interrupts.
lapicw(LapicTPR, 0);
*/
microdelay((TK2MS(1)*1000/sys->nmach) * m->machno);
lapicrput(Tic, apic->max);
return 1;
}
void
lapictimerenable(void)
lapicstartap(Apic* apic, uintptr_t pa)
{
/*
* Perhaps apictimerenable/apictimerdisable should just
* clear/set Im in the existing settings of Tlvt, there may
* be a time when the timer is used in a different mode;
* if so will need to ensure the mode is set when the timer
* is initialised.
*/
lapicrput(Tlvt, Periodic|IdtTIMER);
int i;
uint32_t crhi, crlo;
/* make apic's processor do a warm reset */
crhi = apic->apicno<<24;
lapicw(LapicICRHI, crhi);
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
microdelay(200);
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
delay(10);
/* assumes apic is not an 82489dx */
crlo = LapicFIELD|ApicEDGE|ApicSTARTUP|((uint32_t)pa/BY2PG);
for(i = 0; i < 2; i++){
lapicw(LapicICRHI, crhi);
/* make apic's processor start at v in real mode */
lapicw(LapicICRLO, crlo);
microdelay(200);
}
}
void
lapictimerdisable(void)
lapicerror(Ureg* _, void* __)
{
lapicrput(Tlvt, Im|IdtTIMER);
uint32_t esr;
lapicw(LapicESR, 0);
esr = lapicr(LapicESR);
switch(m->cpuidax & 0xFFF){
case 0x526: /* stepping cB1 */
case 0x52B: /* stepping E0 */
case 0x52C: /* stepping cC0 */
return;
}
print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
}
void
lapicspurious(Ureg* _, void* __)
{
print("cpu%d: lapicspurious\n", m->machno);
}
int
lapicisr(int v)
{
uint32_t isr;
isr = lapicr(LapicISR + (v/32));
return isr & (1<<(v%32));
}
int
lapiceoi(int v)
{
lapicw(LapicEOI, 0);
return v;
}
void
lapicicrw(uint32_t hi, uint32_t lo)
{
lapicw(LapicICRHI, hi);
lapicw(LapicICRLO, lo);
}
void
ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
{
uint32_t *iowin;
iowin = apic->addr+(0x10/sizeof(uint32_t));
sel = IoapicRDT + 2*sel;
lock(apic);
*apic->addr = sel+1;
if(hi)
*hi = *iowin;
*apic->addr = sel;
if(lo)
*lo = *iowin;
unlock(apic);
}
void
ioapicrdtw(Apic* apic, int sel, int hi, int lo)
{
uint32_t *iowin;
iowin = apic->addr+(0x10/sizeof(uint32_t));
sel = IoapicRDT + 2*sel;
lock(apic);
*apic->addr = sel+1;
*iowin = hi;
*apic->addr = sel;
*iowin = lo;
unlock(apic);
}
void
ioapicinit(Apic* apic, int apicno)
{
int hi, lo, v;
uint32_t *iowin;
/*
* Initialise the I/O APIC.
* The MultiProcessor Specification says it is the responsibility
* of the O/S to set the APIC id.
* Make sure interrupts are all masked off for now.
*/
iowin = apic->addr+(0x10/sizeof(uint32_t));
lock(apic);
*apic->addr = IoapicVER;
apic->mre = (*iowin>>16) & 0xFF;
*apic->addr = IoapicID;
*iowin = apicno<<24;
unlock(apic);
hi = 0;
lo = ApicIMASK;
for(v = 0; v <= apic->mre; v++)
ioapicrdtw(apic, v, hi, lo);
}
void
lapictimerset(uint64_t next)
{
Lapic *apic;
int64_t period;
Apictimer *a;
apic = &xlapic[(lapicrget(Id)>>24) & 0xff];
ilock(&m->apictimerlock);
period = apic->max;
if(next != 0){
if(apic->div == 0){
jehanne_print("lapictimerset: apic not ready, wait for gdb\n");
waitdebugger();
}
period = next - fastticks(nil); /* fastticks is just rdtsc() */
period /= apic->div;
if(period < apic->min)
period = apic->min;
else if(period > apic->max - apic->min)
period = apic->max;
}
lapicrput(Tic, period);
iunlock(&m->apictimerlock);
a = &lapictimer[m->machno];
period = next - fastticks(nil);
period /= a->div;
if(period < a->min)
period = a->min;
else if(period > a->max - a->min)
period = a->max;
lapicw(LapicTICR, period);
}
void
lapicsipi(int lapicno, uintmem pa)
lapicclock(Ureg *u, void* _)
{
int i;
uint32_t crhi, crlo;
/*
* SIPI - Start-up IPI.
* To do: checks on lapic validity.
* since the MTRR updates need to be synchronized across processors,
* we want to do this within the clock tick.
*/
crhi = lapicno<<24;
lapicrput(Ichi, crhi);
lapicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
microdelay(200);
lapicrput(Iclo, DSnone|TMlevel|MTir);
delay(10);
crlo = DSnone|TMedge|MTsipi|((uint32_t)pa/(4*KiB));
for(i = 0; i < 2; i++){
lapicrput(Ichi, crhi);
lapicrput(Iclo, crlo);
microdelay(200);
}
mtrrclock();
timerintr(u, 0);
}
void
lapicipi(int lapicno)
lapicintron(void)
{
lapicrput(Ichi, lapicno<<24);
lapicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
while(lapicrget(Iclo) & Ds)
;
lapicw(LapicTPR, 0);
}
void
lapicpri(int pri)
lapicintroff(void)
{
lapicrput(Tp, pri);
lapicw(LapicTPR, 0xFF);
}
void
lapicnmienable(void)
{
lapicw(LapicPCINT, ApicNMI);
}
void
lapicnmidisable(void)
{
lapicw(LapicPCINT, ApicIMASK);
}

View File

@ -6,7 +6,6 @@
"Include": [
"core.json",
"devdraw.json",
"../386/include.json",
"../ip/include.json",
"../port/include.json"
],
@ -18,7 +17,6 @@
"int printallsyscalls;"
],
"Dev": [
"acpi",
"arch",
"bridge",
"cap",
@ -85,7 +83,6 @@
"SourceFiles": [
"autogenerated.c",
"cga.c",
"devacpi.c",
"usbehcipc.c",
"usbohci.c",
"usbuhci.c"

View File

@ -1,7 +1,7 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2016 Giacomo Tesio <giacomo@tesio.it>
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,19 +20,16 @@
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "ureg.h"
#include "pool.h"
#include "io.h"
#include "apic.h"
extern void confoptions(void); /* XXX - must go */
extern void confsetenv(void); /* XXX - must go */
static uintptr_t sp; /* XXX - must go - user stack of init proc */
Sys* sys = (Sys*)0x1; /* dummy value to put it in data section
* - entry.S set it correctly
* - main memset to zero the bss section
*/
Sys system;
Sys* sys;
usize sizeofSys = sizeof(Sys);
/*
@ -42,7 +39,7 @@ usize sizeofSys = sizeof(Sys);
* set it all up.
*/
static int64_t oargc;
static char* oargv[20];
static char* oargv[64];
static char oargb[1024];
static int oargblen;
@ -51,7 +48,6 @@ static int numtcs = 32; /* initial # of TCs */
IOConf ioconf;
int procmax;
char *cputype = "amd64";
char dbgflg[256];
static int vflag = 1;
@ -63,6 +59,30 @@ optionsinit(char* s)
oargv[oargc] = nil;
}
char*
getconf(char *name)
{
int i;
char *a;
for(i = 0; i < oargc; i++){
a = strstr(oargv[i], name);
if(a == oargv[i]){
a += strlen(name);
if(a[0] == '=')
return ++a;
}
}
return nil;
}
int
isaconfig(char *class, int ctlrno, ISAConf *isa)
{
return 0;
}
static void
options(int argc, char* argv[])
{
@ -129,251 +149,204 @@ loadenv(int argc, char* argv[])
}
void
squidboy(int apicno)
initialize_processor(void)
{
int64_t hz;
int machno;
Segdesc *gdt;
uintptr_t *pml4;
sys->machptr[m->machno] = m;
/*
* Need something for initial delays
* until a timebase is worked out.
*/
m->cpuhz = sys->machptr[0]->cpuhz;
m->cyclefreq = m->cpuhz;
m->cpumhz = sys->machptr[0]->cpumhz;
machno = m->machno;
pml4 = m->pml4;
gdt = m->gdt;
memset(m, 0, sizeof(Mach));
m->machno = machno;
m->pml4 = pml4;
m->gdt = gdt;
m->perf.period = 1;
DBG("Hello Squidboy %d %d\n", apicno, m->machno);
vsvminit(MACHSTKSZ);
/*
* Beware the Curse of The Non-Interruptable Were-Temporary.
* For polled uart output at boot, need
* a default delay constant. 100000 should
* be enough for a while. Cpuidentify will
* calculate the real value later.
*/
hz = archhz();
if(hz == 0)
ndnr();
m->cpuhz = hz;
m->cpumhz = hz/1000000ll;
archenable();
mmuinit();
if(!lapiconline())
ndnr();
fpuinit();
/*
* Handshake with sipi to let it
* know the Startup IPI succeeded.
*/
m->splpc = 0;
/*
* Handshake with main to proceed with initialisation.
*/
while(sys->epoch == 0)
;
wrmsr(0x10, sys->epoch);
m->rdtsc = rdtsc();
DBG("mach %d is go %#p %#p %3p\n", m->machno, m, m->pml4->pte, &apicno);
timersinit();
/*
* Cannot allow interrupts while waiting for online.
* However, by taking the lowering of the APIC task priority
* out of apiconline something could be done here with
* MONITOR/MWAIT perhaps to drop the energy used by the
* idle core.
*/
while(!m->online)
pause();
lapictimerenable();
lapicpri(0);
jehanne_print("mach%d: online color %d\n", m->machno, m->color);
schedinit();
panic("squidboy returns (type %d)", m->mode);
m->loopconst = 100000;
}
#define D(c) if(0)outb(0x3f8, (c))
void
main(uint32_t ax, uint32_t bx)
static void
initialize_boot_processor(void)
{
int i;
int64_t hz;
char *p;
system.nmach = 1;
jehanne_memset(edata, 0, end - edata);
system.machptr[0] = m; /* m was set by entry.S */
/*
* ilock via i8250enable via i8250console
* needs m->machno, sys->machptr[] set, and
* also 'up' set to nil.
*/
cgapost(sizeof(uintptr_t)*8);
jehanne_memset(m, 0, sizeof(Mach));
m->machno = 0;
m->pml4 = (uint64_t*)CPU0PML4;
m->gdt = (Segdesc*)CPU0GDT;
initialize_processor();
active.machs[0] = 1;
m->online = 1;
sys->machptr[m->machno] = &sys->mach;
m->stack = PTR2UINT(sys->machstk);
m->vsvm = sys->vsvmpage;
sys->nmach = 1;
sys->nonline = 1;
sys->copymode = 0; /* copy on write */
up = nil;
confoptions();
asminit();
multiboot(ax, bx, 0);
options(oargc, oargv);
p = getconf("*dbflags");
if(p != nil){
for(; *p != 0; p++)
if(*p >= 'a' && *p <= 'z' || *p >= 'A' && *p <= 'Z')
dbgflg[(uint8_t)*p] = 1;
}
/*
* Need something for initial delays
* until a timebase is worked out.
*/
m->cpuhz = 2000000000ll;
m->cpumhz = 2000;
cgainit();
i8250console("0");
consputs = cgaconsputs;
vsvminit(MACHSTKSZ);
active.exiting = 0;
fmtinit();
jehanne_print("\nJehanne Operating System\n");
if(vflag){
jehanne_print("&ax = %#p, ax = %#ux, bx = %#ux\n", &ax, ax, bx);
multiboot(ax, bx, vflag);
}
e820();
m->perf.period = 1;
if((hz = archhz()) != 0ll){
m->cpuhz = hz;
m->cpumhz = hz/1000000ll;
}
archenable();
static void
intialize_system(void)
{
extern Ureg _boot_registers;
uintptr_t p;
/*
* Mmuinit before meminit because it
* makes mappings and
* flushes the TLB via m->pml4->pa.
*/
mmuinit();
p = (uintptr_t)&_boot_registers;
p += KZERO;
sys = &system;
sys->boot_regs = (void*)p;
sys->architecture = "amd64";
}
ioinit();
keybinit();
static void
configure_kernel(void)
{
char *p;
int i, userpcnt;
unsigned int kpages;
meminit();
archinit();
physallocinit();
D('a');
mallocinit();
D('b');
memdebug();
trapinit();
D('c');
if(p = getconf("service")){
if(strcmp(p, "cpu") == 0)
cpuserver = 1;
else if(strcmp(p,"terminal") == 0)
cpuserver = 0;
}
/*
* Printinit will cause the first malloc
* call to happen (printinit->qopen->malloc).
* If the system dies here it's probably due
* to malloc not being initialised
* correctly, or the data segment is misaligned
* (it's amazing how far you can get with
* things like that completely broken).
*/
printinit();
D('d');
/*
* This is necessary with GRUB and QEMU.
* Without it an interrupt can occur at a weird vector,
* because the vector base is likely different, causing
* havoc. Do it before any APIC initialisation.
*/
i8259init(IdtPIC);
D('e');
/* memory */
if(p = getconf("*kernelpercent"))
userpcnt = 100 - strtol(p, 0, 0);
else
userpcnt = 0;
acpiinit(MACHMAX);
D('f');
// mpsinit();
D('g');
lapiconline();
ioapiconline();
D('h');
intrenable(IdtTIMER, timerintr, 0, -1, "APIC timer");
lapictimerenable();
lapicpri(0);
D('i');
sys->npages = 0;
for(i=0; i<nelem(sys->mem); i++)
sys->npages += sys->mem[i].npage;
timersinit();
D('j');
keybenable();
mouseenable();
D('k');
fpuinit();
D('l');
/* processes */
p = getconf("*procmax");
if(p != nil)
procmax = jehanne_strtoull(p, nil, 0);
if(procmax == 0)
procmax = 2000;
psinit(procmax);
D('m');
// imagepool_init();
D('n');
links();
D('o');
devtabreset();
D('p');
umem_init();
D('r');
userinit();
D('s');
if(!dbgflg['S'])
sipi();
D('t');
sys->epoch = rdtsc();
wrmsr(0x10, sys->epoch);
m->rdtsc = rdtsc();
D('u');
/*
* Release the hounds.
*/
for(i = 1; i < MACHMAX; i++){
if(sys->machptr[i] == nil)
continue;
ainc(&sys->nonline);
sys->machptr[i]->color = corecolor(i);
if(sys->machptr[i]->color < 0)
sys->machptr[i]->color = 0;
sys->machptr[i]->online = 1;
sys->nproc = jehanne_strtoull(p, nil, 0);
if(sys->nproc == 0){
sys->nproc = 100 + ((sys->npages*PGSZ)/MiB)*5;
if(cpuserver)
sys->nproc *= 3;
}
D('v');
prflush();
if(sys->nproc > 2046){
/* devproc supports at most (2^11)-2 processes */
sys->nproc = 2046;
}
sys->nimage = 256;
if(cpuserver) {
if(userpcnt < 10)
userpcnt = 70;
kpages = sys->npages - (sys->npages*userpcnt)/100;
sys->nimage = sys->nproc;
} else {
if(userpcnt < 10) {
if(sys->npages*PGSZ < 16*MiB)
userpcnt = 50;
else
userpcnt = 60;
}
kpages = sys->npages - (sys->npages*userpcnt)/100;
/*
* Make sure terminals with low memory get at least
* 4MB on the first Image chunk allocation.
*/
if(sys->npages*PGSZ < 16*MiB)
imagmem->minarena = 4*MiB;
}
/*
* can't go past the end of virtual memory.
*/
if(kpages > ((uintptr_t)-KZERO)/PGSZ)
kpages = ((uintptr_t)-KZERO)/PGSZ;
sys->upages = sys->npages - kpages;
sys->ialloc = (kpages/2)*PGSZ;
/*
* Guess how much is taken by the large permanent
* datastructures. Mntcache and Mntrpc are not accounted for.
*/
kpages *= PGSZ;
kpages -= sys->nproc*sizeof(Proc);
mainmem->maxsize = kpages;
/*
* the dynamic allocation will balance the load properly,
* hopefully. be careful with 32-bit overflow.
*/
imagmem->maxsize = kpages - (kpages/10);
if(p = getconf("*imagemaxmb")){
imagmem->maxsize = strtol(p, nil, 0)*MiB;
if(imagmem->maxsize > mainmem->maxsize)
imagmem->maxsize = mainmem->maxsize;
}
}
void
main(void)
{
intialize_system();
initialize_boot_processor();
multiboot(0);
options(oargc, oargv);
ioinit();
i8250console();
fmtinit();
screen_init();
jehanne_print("\nJehanne Operating System\n");
trapinit0();
// i8259init();
i8253init();
cpuidentify();
meminit();
configure_kernel();
xinit();
archinit();
// bootscreeninit();
// if(i8237alloc != nil)
// i8237alloc();
trapinit();
printinit();
cpuidprint();
mmuinit();
if(arch->intrinit)
arch->intrinit();
timersinit();
// keybinit();
// keybenable();
// mouseenable();
mathinit();
if(arch->clockenable)
arch->clockenable();
psinit(sys->nproc);
links();
devtabreset();
umem_init();
userinit();
schedinit();
}
@ -384,8 +357,6 @@ init0(void)
up->nerrlab = 0;
// if(consuart == nil)
// i8250console("0");
spllo();
/*
@ -408,11 +379,11 @@ init0(void)
ksetenv("service", "cpu", 0);
else
ksetenv("service", "terminal", 0);
confsetenv();
poperror();
}
kproc("alarm", alarmkproc, 0);
kproc("awake", awakekproc, 0);
kproc("atimer", awake_timer, 0);
kproc("aringer", awake_ringer, 0);
touser(sp);
}
@ -461,17 +432,23 @@ userinit(void)
char *k;
uintptr_t va, mmuphys;
p = newproc();
p->pgrp = newpgrp();
p->egrp = smalloc(sizeof(Egrp));
p->egrp->r.ref = 1;
p->fgrp = dupfgrp(nil);
p->rgrp = newrgrp();
p->procmode = 0640;
/* NOTE: we use the global pointer up so that the kaddr()
* (called by segment_fault) can find it
*/
up = newproc();
up->pgrp = newpgrp();
up->egrp = smalloc(sizeof(Egrp));
up->egrp->r.ref = 1;
up->fgrp = dupfgrp(nil);
up->rgrp = newrgrp();
up->procmode = 0640;
kstrdup(&eve, "");
kstrdup(&p->text, "*init*");
kstrdup(&p->user, eve);
kstrdup(&up->text, "*init*");
kstrdup(&up->user, eve);
sysprocsetup(up);
/*
* Kernel Stack
*
@ -480,23 +457,23 @@ userinit(void)
* space for gotolabel's return PC
* AMD64 stack must be quad-aligned.
*/
p->sched.pc = PTR2UINT(init0);
p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t));
p->sched.sp = STACKALIGN(p->sched.sp);
D('0');
up->sched.pc = PTR2UINT(init0);
up->sched.sp = PTR2UINT(up->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t));
up->sched.sp = STACKALIGN(up->sched.sp);
/*
* User Stack
*/
s = nil;
if(!segment_virtual(&s, SgStack, SgRead|SgWrite, 0, USTKTOP-USTKSIZE, USTKTOP))
panic("userinit: cannot create stack segment");
D('1');
p->seg[SSEG] = s;
up->seg[SSEG] = s;
va = USTKTOP-USTKSIZE;
mmuphys = 0;
if(!segment_fault(&mmuphys, &va, s, FaultWrite))
panic("userinit: cannot allocate first stack page");
D('2');
page = segment_page(s, va);
if(page == 0)
panic("userinit: cannot find first stack page (previously faulted)");
@ -510,7 +487,7 @@ D('2');
s = nil;
if(!segment_userinit(&s, 0))
panic("userinit: cannot create text segment");
p->seg[TSEG] = s;
up->seg[TSEG] = s;
/*
* Data
@ -518,8 +495,11 @@ D('2');
s = nil;
if(!segment_userinit(&s, 1))
panic("userinit: cannot create data segment");
p->seg[DSEG] = s;
up->seg[DSEG] = s;
/* reset global pointer up */
p = up;
up = nil;
ready(p);
}
@ -527,7 +507,7 @@ static void
fullstop(void)
{
splhi();
lapicpri(0xff);
// lapicpri(0xff);
/* i8259 was initialised as disabled */
for(;;)
_halt();
@ -544,7 +524,7 @@ shutdown(int ispanic)
active.ispanic = ispanic;
m->online = 0;
active.exiting = 1;
adec(&sys->nonline);
adec((int*)&sys->nmach);
iprint("cpu%d: exiting\n", m->machno);
/* wait for any other processors to shutdown */
@ -552,7 +532,7 @@ shutdown(int ispanic)
prflush();
for(ms = 10*1000; ms > 0; ms -= 2){
delay(2);
if(sys->nonline == 0 && consactive() == 0)
if(sys->nmach == 0 && consactive() == 0)
break;
}

View File

@ -1,42 +0,0 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
/*
* Before mmuinit is done, we can't rely on sys->vmunmapped
* being set, so we use the static limit TMFM
*/
void*
KADDR(uintmem pa)
{
uint8_t* va;
va = UINT2PTR(pa);
if(sys->vmunmapped != 0){
if(pa < sys->vmunmapped-KSEG0)
return KSEG0+va;
}else if(pa < TMFM)
return KSEG0+va;
return KSEG2+va;
}
uintmem
PADDR(void* va)
{
uintmem pa;
pa = PTR2UINT(va);
if(pa >= KSEG2 && pa < KSEG1)
return pa-KSEG2;
if(pa >= KSEG0 && pa < KSEG0+TMFM)
return pa-KSEG0;
if(pa > KSEG2)
return pa-KSEG2;
panic("PADDR: va %#p pa #%p @ %#p\n", va, mmuphysaddr(PTR2UINT(va)), getcallerpc());
return 0;
}

View File

@ -33,7 +33,7 @@
#define EiB gULL(1152921504606846976)
#define HOWMANY(x, y) (((x)+((y)-1))/(y))
#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1))
#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
#define ROUNDDN(x, y) (((x)/(y))*(y))
#define MIN(a, b) ((a) < (b)? (a): (b))
@ -44,21 +44,25 @@
*/
#define BI2BY 8 /* bits per byte */
#define BI2WD 32 /* bits per word */
#define BY2WD 4 /* bytes per word */
#define BY2WD 8 /* bytes per word */
#define BY2V 8 /* bytes per double word */
#define BY2SE 8 /* bytes per stack element */
#define BLOCKALIGN 8
#define PGSZ (4*KiB) /* page size */
#define BY2PG PGSZ
#define WD2PG (BY2PG/BY2WD)
#define PGSHFT 12 /* log(PGSZ) */
#define PTSZ (4*KiB) /* page table page size */
#define PTSHFT 9 /* */
#define PGSHIFT PGSHFT
#define PGROUND(s) ROUNDUP(s, PGSZ)
#define BLOCKALIGN 8
#define FPalign 16
#define MACHSZ (4*KiB) /* Mach+stack size */
#define MACHSZ (2*PGSZ) /* Mach+stack size */
#define MACHMAX 32 /* max. number of cpus */
#define MACHSTKSZ (6*(4*KiB)) /* Mach stack size */
#define KSTACK (16*1024) /* Size of Proc kernel stack */
#define KSTACK (16*KiB) /* Size of Proc kernel stack */
#define STACKALIGN(sp) ((sp) & ~(BY2SE-1)) /* bug: assure with alloc */
/*
@ -82,18 +86,46 @@
#define UTZERO (0+2*MiB) /* first address in user text */
#define UTROUND(t) ROUNDUP((t), 2*MiB) /* first address beyond text for user data */
#define USTKTOP gULL(0x00007ffffffff000)
#define USTKSIZE (16*1024*1024) /* size of user stack */
#define TSTKTOP (USTKTOP-USTKSIZE) /* end of new stack in sysexec */
#define UADDRMASK gULL(0x00007fffffffffff) /* canonical address mask */
#define TSTKTOP (0x00007ffffffff000ull)
#define USTKSIZE (16*MiB) /* size of user stack */
#define USTKTOP (TSTKTOP-USTKSIZE) /* end of new stack in sysexec */
#define KSEG0 gULL(0xfffffffff0000000) /* 256MB - this is confused */
#define KSEG1 gULL(0xffffff0000000000) /* 512GB - embedded PML4 */
#define KSEG2 gULL(0xfffffe0000000000) /* 1TB - KMAP */
//#define KSEG0 gULL(0xffffffff80000000) /* 256MB - this is confused */
//#define KSEG1 gULL(0xffffff0000000000) /* 512GB - embedded PML4 */
//#define KSEG2 gULL(0xfffffe8000000000) /* 1TB - KMAP */
#define PMAPADDR gULL(0xffffffffffe00000) /* unused as of yet (KMAP?) */
#define KZERO gULL(0xfffffffff0000000)
#define KZERO gULL(0xffffffff80000000)
#define KTZERO (KZERO+1*MiB+64*KiB)
#define VMAP gULL(0xffffff0000000000)
#define VMAPSIZE (512*GiB)
#define KMAP gULL(0xfffffe8000000000)
#define KMAPSIZE (2*MiB)
/*
* Fundamental addresses - bottom 64kB saved for return to real mode
*/
#define CONFADDR (KZERO+0x1200) /* info passed from boot loader */
#define APBOOTSTRAP (KZERO+0x3000) /* AP bootstrap code */
#define IDTADDR (KZERO+0x10000) /* idt */
#define REBOOTADDR (0x11000) /* reboot code - physical address */
#define CPU0PML4 (KZERO+0x13000)
#define CPU0PDP (KZERO+0x14000)
#define CPU0PD0 (KZERO+0x15000) /* KZERO */
#define CPU0PD1 (KZERO+0x16000) /* KZERO+1GB */
#define CPU0GDT (KZERO+0x17000) /* bootstrap processor GDT */
#define CPU0MACH (KZERO+0x18000) /* Mach for bootstrap processor */
#define CPU0END (CPU0MACH+MACHSIZE)
#define MACHSIZE (2*KSTACK)
/*
* virtual MMU
*/
@ -104,14 +136,17 @@
#define SEGMAXPG (SEGMAPSIZE)
/*
* This is the interface between fixfault and mmuput.
* Should be in port.
* physical MMU
*/
#define PTEVALID (1<<0)
#define PTEWRITE (1<<1)
#define PTERONLY (0<<1)
#define PTEUSER (1<<2)
#define PTEUNCACHED (1<<4)
#define PTEVALID (gULL(1)<<0)
#define PTEWT (gULL(1)<<3)
#define PTEUNCACHED (gULL(1)<<4)
#define PTEWRITE (gULL(1)<<1)
#define PTERONLY (gULL(0)<<1)
#define PTEKERNEL (gULL(0)<<2)
#define PTEUSER (gULL(1)<<2)
#define PTESIZE (gULL(1)<<7)
#define PTEGLOBAL (gULL(1)<<8)
#define getpgcolor(a) 0
@ -123,6 +158,9 @@
* and level 0 the PT pages. The PTLX macro gives an index into the
* page-table page at level 'l' for the virtual address 'v'.
*/
#define PTSZ (4*KiB) /* page table page size */
#define PTSHFT 9 /* */
#define PTLX(v, l) (((v)>>(((l)*PTSHFT)+PGSHFT)) & ((1<<PTSHFT)-1))
#define PGLSZ(l) (1<<(((l)*PTSHFT)+PGSHFT))
@ -133,26 +171,21 @@
#define CACHELINESZ 64
#define KVATOP (KSEG0&KSEG1&KSEG2)
#define iskaddr(a) (((uintptr_t)(a)&KVATOP) == KVATOP)
//#define KVATOP (KSEG0&KSEG1&KSEG2)
#define iskaddr(a) (((uintptr_t)(a)) > KZERO)
/*
* known x86 segments (in GDT) and their selectors
*/
#define NULLSEG 0 /* null segment */
#define KDSEG 1 /* kernel data/stack */
#define KESEG 2 /* kernel executable */
#define UDSEG 3 /* user data/stack */
#define UESEG 4 /* user executable */
#define TSSSEG 5 /* task segment */
#define APMCSEG 6 /* APM code segment */
#define APMCSEG16 7 /* APM 16-bit code segment */
#define APMDSEG 8 /* APM data segment */
#define KESEG16 9 /* kernel executable 16-bit */
#define LDTSEG 10 /* local descriptor table */
#define PROCSEG0 11 /* per process descriptor0 */
#define NPROCSEG 3 /* number of per process descriptors */
#define NGDT 14 /* number of GDT entries required */
#define KESEG 1 /* kernel executable */
#define KDSEG 2 /* kernel data */
#define UE32SEG 3 /* user executable 32bit */
#define UDSEG 4 /* user data/stack */
#define UESEG 5 /* user executable 64bit */
#define TSSSEG 8 /* task segment (two descriptors) */
#define NGDT 10 /* number of GDT entries required */
#define SELGDT (0<<2) /* selector is in gdt */
#define SELLDT (1<<2) /* selector is in ldt */
@ -160,12 +193,33 @@
#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p))
#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0)
#define KDSEL SELECTOR(KDSEG, SELGDT, 0)
#define KESEL SELECTOR(KESEG, SELGDT, 0)
#define UESEL SELECTOR(UESEG, SELGDT, 3)
#define UE32SEL SELECTOR(UE32SEG, SELGDT, 3)
#define UDSEL SELECTOR(UDSEG, SELGDT, 3)
#define UESEL SELECTOR(UESEG, SELGDT, 3)
#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0)
#define APMCSEL SELECTOR(APMCSEG, SELGDT, 0)
#define APMCSEL16 SELECTOR(APMCSEG16, SELGDT, 0)
#define APMDSEL SELECTOR(APMDSEG, SELGDT, 0)
#define LDTSEL SELECTOR(LDTSEG, SELGDT, 0)
/*
* fields in segment descriptors
*/
#define SEGDATA (0x10<<8) /* data/stack segment */
#define SEGEXEC (0x18<<8) /* executable segment */
#define SEGTSS (0x9<<8) /* TSS segment */
#define SEGCG (0x0C<<8) /* call gate */
#define SEGIG (0x0E<<8) /* interrupt gate */
#define SEGTG (0x0F<<8) /* trap gate */
#define SEGLDT (0x02<<8) /* local descriptor table */
#define SEGTYPE (0x1F<<8)
#define SEGP (1<<15) /* segment present */
#define SEGPL(x) ((x)<<13) /* priority level */
#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */
#define SEGD (1<<22) /* default 1==32bit (for code) */
#define SEGE (1<<10) /* expand down */
#define SEGW (1<<9) /* writable (for data/stack) */
#define SEGR (1<<9) /* readable (for code) */
#define SEGL (1<<21) /* 64 bit */
#define SEGG (1<<23) /* granularity 1==4k (for other) */
#define getpgcolor(a) 0
#define MBPGMASK (~(PGSZ-1))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,6 @@
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
@ -31,8 +30,6 @@ enum
MousePS2= 2,
};
extern int mouseshifted;
static QLock mousectlqlock;
static int mousetype;
static int intellimouse;
@ -40,6 +37,7 @@ static int packetsize;
static int resolution;
static int accelerated;
static int mousehwaccel;
static char mouseport[5];
enum
{
@ -93,6 +91,76 @@ static Cmdtab mousectlmsg[] =
* To resynchronize, if we get a byte more than two seconds
* after the previous byte, we assume it's the first in a packet.
*/
static void
ps2mouseputc(int c, int shift)
{
static short msg[4];
static int nb;
static uint8_t b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
static uint32_t lasttick;
uint32_t m;
int buttons, dx, dy;
/*
* Resynchronize in stream with timing; see comment above.
*/
m = MACHP(0)->ticks;
if(TK2SEC(m - lasttick) > 2)
nb = 0;
lasttick = m;
/*
* check byte 0 for consistency
*/
if(nb==0 && (c&0xc8)!=0x08){
if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){
/* last byte of 4-byte packet */
packetsize = 4;
}
return;
}
msg[nb] = c;
if(++nb >= packetsize){
nb = 0;
if(msg[0] & 0x10)
msg[1] |= 0xFF00;
if(msg[0] & 0x20)
msg[2] |= 0xFF00;
buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
if(intellimouse && packetsize==4){
if((msg[3]&0xc8) == 0x08){
/* first byte of 3-byte packet */
packetsize = 3;
msg[0] = msg[3];
nb = 1;
/* fall through to emit previous packet */
}else{
/* The AccuPoint on the Toshiba 34[48]0CT
* encodes extra buttons as 4 and 5. They repeat
* and don't release, however, so user-level
* timing code is required. Furthermore,
* intellimice with 3buttons + scroll give a
* two's complement number in the lower 4 bits
* (bit 4 is sign extension) that describes
* the amount the scroll wheel has moved during
* the last sample. Here we use only the sign to
* decide whether the wheel is moving up or down
* and generate a single button 4 or 5 click
* accordingly.
*/
if((msg[3] >> 3) & 1)
buttons |= 1<<3;
else if(msg[3] & 0x7)
buttons |= 1<<4;
}
}
dx = msg[1];
dy = -msg[2];
mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks));
}
}
/*
* set up a ps2 mouse
@ -103,14 +171,12 @@ ps2mouse(void)
if(mousetype == MousePS2)
return;
mouseenable();
/* make mouse streaming, enabled */
mousecmd(0xEA);
mousecmd(0xF4);
mousetype = MousePS2;
packetsize = 3;
mousehwaccel = 1;
mousehwaccel = 0;
i8042auxenable(ps2mouseputc);
i8042auxcmd(0xEA); /* set stream mode */
}
/*
@ -131,7 +197,7 @@ setaccelerated(int x)
if(mousehwaccel){
switch(mousetype){
case MousePS2:
mousecmd(0xE7);
i8042auxcmd(0xE7);
return;
}
}
@ -145,7 +211,7 @@ setlinear(void)
if(mousehwaccel){
switch(mousetype){
case MousePS2:
mousecmd(0xE6);
i8042auxcmd(0xE6);
return;
}
}
@ -158,8 +224,8 @@ setres(int n)
resolution = n;
switch(mousetype){
case MousePS2:
mousecmd(0xE8);
mousecmd(n);
i8042auxcmd(0xE8);
i8042auxcmd(n);
break;
}
}
@ -171,12 +237,15 @@ setintellimouse(void)
packetsize = 4;
switch(mousetype){
case MousePS2:
mousecmd(0xF3); /* set sample */
mousecmd(0xC8);
mousecmd(0xF3); /* set sample */
mousecmd(0x64);
mousecmd(0xF3); /* set sample */
mousecmd(0x50);
i8042auxcmd(0xF3); /* set sample */
i8042auxcmd(0xC8);
i8042auxcmd(0xF3); /* set sample */
i8042auxcmd(0x64);
i8042auxcmd(0xF3); /* set sample */
i8042auxcmd(0x50);
break;
case Mouseserial:
uartsetmouseputc(mouseport, m5mouseputc);
break;
}
}
@ -187,11 +256,30 @@ resetmouse(void)
packetsize = 3;
switch(mousetype){
case MousePS2:
mousecmd(0xF6);
mousecmd(0xEA); /* streaming */
mousecmd(0xE8); /* set resolution */
mousecmd(3);
mousecmd(0xF4); /* enabled */
i8042auxcmd(0xF6);
i8042auxcmd(0xEA); /* streaming */
i8042auxcmd(0xE8); /* set resolution */
i8042auxcmd(3);
break;
}
}
static void
setstream(int on)
{
int i;
switch(mousetype){
case MousePS2:
/*
* disabling streaming can fail when
* a packet is currently transmitted.
*/
for(i=0; i<4; i++){
if(i8042auxcmd(on ? 0xF4 : 0xF5) != -1)
break;
delay(50);
}
break;
}
}
@ -210,27 +298,37 @@ mousectl(Cmdbuf *cb)
ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
switch(ct->index){
case CMaccelerated:
setaccelerated(cb->nf == 1 ? 1 : jehanne_atoi(cb->f[1]));
setstream(0);
setaccelerated(cb->nf == 1 ? 1 : atoi(cb->f[1]));
setstream(1);
break;
case CMintellimouse:
setstream(0);
setintellimouse();
setstream(1);
break;
case CMlinear:
setstream(0);
setlinear();
setstream(1);
break;
case CMps2:
intellimouse = 0;
ps2mouse();
setstream(1);
break;
case CMps2intellimouse:
ps2mouse();
setintellimouse();
setstream(1);
break;
case CMres:
setstream(0);
if(cb->nf >= 2)
setres(jehanne_atoi(cb->f[1]));
setres(atoi(cb->f[1]));
else
setres(1);
setstream(1);
break;
case CMreset:
resetmouse();
@ -240,11 +338,31 @@ mousectl(Cmdbuf *cb)
setres(resolution);
if(intellimouse)
setintellimouse();
setstream(1);
break;
case CMserial:
if(mousetype == Mouseserial)
error(Emouseset);
if(cb->nf > 2){
if(strcmp(cb->f[2], "M") == 0)
uartmouse(cb->f[1], m3mouseputc, 0);
else if(strcmp(cb->f[2], "MI") == 0)
uartmouse(cb->f[1], m5mouseputc, 0);
else
uartmouse(cb->f[1], mouseputc, cb->nf == 1);
} else
uartmouse(cb->f[1], mouseputc, cb->nf == 1);
mousetype = Mouseserial;
strncpy(mouseport, cb->f[1], sizeof(mouseport)-1);
mouseport[sizeof(mouseport)-1] = 0;
packetsize = 3;
break;
case CMhwaccel:
if(jehanne_strcmp(cb->f[1], "on")==0)
if(strcmp(cb->f[1], "on")==0)
mousehwaccel = 1;
else if(jehanne_strcmp(cb->f[1], "off")==0)
else if(strcmp(cb->f[1], "off")==0)
mousehwaccel = 0;
else
cmderror(cb, "bad mouse control message");

View File

@ -3,426 +3,588 @@
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "apic.h"
#include "ureg.h"
#undef DBG
#define DBG jehanne_print
/*
* MultiProcessor Specification Version 1.[14].
*/
typedef struct { /* MP Floating Pointer */
uint8_t signature[4]; /* "_MP_" */
uint8_t addr[4]; /* PCMP */
uint8_t length; /* 1 */
uint8_t revision; /* [14] */
uint8_t checksum;
uint8_t feature[5];
} _MP_;
#include "mp.h"
#include "sipi.h"
typedef struct { /* MP Configuration Table */
uint8_t signature[4]; /* "PCMP" */
uint8_t length[2];
uint8_t revision; /* [14] */
uint8_t checksum;
uint8_t string[20]; /* OEM + Product ID */
uint8_t oaddr[4]; /* OEM table pointer */
uint8_t olength[2]; /* OEM table length */
uint8_t entry[2]; /* entry count */
uint8_t apicpa[4]; /* local APIC address */
uint8_t xlength[2]; /* extended table length */
uint8_t xchecksum; /* extended table checksum */
uint8_t reserved;
/* filled in by pcmpinit or acpiinit */
Bus* mpbus;
Bus* mpbuslast;
int mpisabus = -1;
int mpeisabus = -1;
Apic *mpioapic[MaxAPICNO+1];
Apic *mpapic[MaxAPICNO+1];
uint8_t entries[];
} PCMP;
typedef struct {
char type[6];
int polarity; /* default for this bus */
int trigger; /* default for this bus */
} Mpbus;
static Mpbus mpbusdef[] = {
{ "PCI ", IPlow, TMlevel, },
{ "ISA ", IPhigh, TMedge, },
};
static Mpbus* mpbus[Nbus];
int mpisabusno = -1;
static void
mpintrprint(char* s, uint8_t* p)
int
mpintrinit(Bus* bus, PCMPintr* intr, int vno, int irq)
{
char buf[128], *b, *e;
char format[] = " type %d flags %#ux bus %d IRQ %d APIC %d INTIN %d\n";
b = buf;
e = b + sizeof(buf);
b = jehanne_seprint(b, e, "mpparse: intr:");
if(s != nil)
b = jehanne_seprint(b, e, " %s:", s);
jehanne_seprint(b, e, format, p[1], l16get(p+2), p[4], p[5], p[6], p[7]);
jehanne_print(buf);
}
static uint32_t
mpmkintr(uint8_t* p)
{
uint32_t v;
Lapic *apic;
IOapic *ioapic;
int n, polarity, trigger;
int el, po, v;
/*
* Check valid bus, interrupt input pin polarity
* and trigger mode. If the APIC ID is 0xff it means
* all APICs of this type so those checks for useable
* APIC and valid INTIN must also be done later in
* the appropriate init routine in that case. It's hard
* to imagine routing a signal to all IOAPICs, the
* usual case is routing NMI and ExtINT to all LAPICs.
* Parse an I/O or Local APIC interrupt table entry and
* return the encoded vector.
*/
if(mpbus[p[4]] == nil){
mpintrprint("no source bus", p);
return 0;
}
if(p[6] != 0xff){
if(Napic < 256 && p[6] >= Napic){
mpintrprint("APIC ID out of range", p);
return 0;
}
switch(p[0]){
default:
mpintrprint("INTIN botch", p);
return 0;
case 3: /* IOINTR */
ioapic = ioapiclookup(p[6]);
if(ioapic == nil){
mpintrprint("unuseable IO APIC", p);
return 0;
}
if(p[7] >= ioapic->nrdt){
mpintrprint("IO INTIN out of range", p);
return 0;
}
v = vno;
po = intr->flags & PcmpPOMASK;
el = intr->flags & PcmpELMASK;
switch(intr->intr){
default: /* PcmpINT */
v |= ApicFIXED; /* no-op */
break;
case 4: /* LINTR */
apic = lapiclookup(p[6]);
if(apic == nil){
mpintrprint("unuseable local APIC", p);
return 0;
}
if(p[7] >= nelem(apic->lvt)){
mpintrprint("LOCAL INTIN out of range", p);
return 0;
}
case PcmpNMI:
v |= ApicNMI;
po = PcmpHIGH;
el = PcmpEDGE;
break;
case PcmpSMI:
v |= ApicSMI;
break;
case PcmpExtINT:
v |= ApicExtINT;
/*
* The AMI Goliath doesn't boot successfully with it's LINTR0
* entry which decodes to low+level. The PPro manual says ExtINT
* should be level, whereas the Pentium is edge. Setting the
* Goliath to edge+high seems to cure the problem. Other PPro
* MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes
* to edge+high, so who knows.
* Perhaps it would be best just to not set an ExtINT entry at
* all, it shouldn't be needed for SMP mode.
*/
po = PcmpHIGH;
el = PcmpEDGE;
break;
}
}
n = l16get(p+2);
if((polarity = (n & 0x03)) == 2 || (trigger = ((n>>2) & 0x03)) == 2){
mpintrprint("invalid polarity/trigger", p);
return 0;
}
/*
* Create the low half of the vector table entry (LVT or RDT).
* For the NMI, SMI and ExtINT cases, the polarity and trigger
* are fixed (but are not always consistent over IA-32 generations).
* For the INT case, either the polarity/trigger are given or
* it defaults to that of the source bus;
* whether INT is Fixed or Lowest Priority is left until later.
*/
v = Im;
switch(p[1]){
default:
mpintrprint("invalid type", p);
return 0;
case 0: /* INT */
switch(polarity){
case 0:
v |= mpbus[p[4]]->polarity;
break;
case 1:
v |= IPhigh;
break;
case 3:
v |= IPlow;
break;
if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
po = PcmpHIGH;
el = PcmpEDGE;
}
switch(trigger){
case 0:
v |= mpbus[p[4]]->trigger;
break;
case 1:
v |= TMedge;
break;
case 3:
v |= TMlevel;
break;
if(!po)
po = bus->po;
if(po == PcmpLOW)
v |= ApicLOW;
else if(po != PcmpHIGH){
print("mpintrinit: bad polarity 0x%uX\n", po);
return ApicIMASK;
}
break;
case 1: /* NMI */
v |= TMedge|IPhigh|MTnmi;
break;
case 2: /* SMI */
v |= TMedge|IPhigh|MTsmi;
break;
case 3: /* ExtINT */
v |= TMedge|IPhigh|MTei;
break;
if(!el)
el = bus->el;
if(el == PcmpLEVEL)
v |= ApicLEVEL;
else if(el != PcmpEDGE){
print("mpintrinit: bad trigger 0x%uX\n", el);
return ApicIMASK;
}
return v;
}
static void
mpparse(PCMP* pcmp)
uint64_t
tscticks(uint64_t *hz)
{
uint32_t lo;
uint8_t *e, *p;
int i, n, bustype;
Lapic *apic;
if(hz != nil)
*hz = m->cpuhz;
p = pcmp->entries;
e = ((uint8_t*)pcmp)+l16get(pcmp->length);
while(p < e) switch(*p){
default:
jehanne_print("mpparse: unknown PCMP type %d (e-p %#ld)\n", *p, e-p);
for(i = 0; p < e; i++){
if(i && ((i & 0x0f) == 0))
jehanne_print("\n");
jehanne_print(" %#2.2ux", *p);
p++;
}
jehanne_print("\n");
break;
case 0: /* processor */
/*
* Initialise the APIC if it is enabled (p[3] & 0x01).
* p[1] is the APIC ID, the memory mapped address comes
* from the PCMP structure as the addess is local to the
* CPU and identical for all. Indicate whether this is
* the bootstrap processor (p[3] & 0x02).
*/
DBG("mpparse: APIC %d pa %#ux useable %d\n",
p[1], l32get(pcmp->apicpa), p[3] & 0x01);
if(p[3] & 0x01)
lapicinit(p[1], l32get(pcmp->apicpa), p[3] & 0x02);
p += 20;
break;
case 1: /* bus */
DBG("mpparse: bus: %d type %6.6s\n", p[1], (char*)p+2);
if(mpbus[p[1]] != nil){
jehanne_print("mpparse: bus %d already allocated\n", p[1]);
p += 8;
break;
}
for(i = 0; i < nelem(mpbusdef); i++){
if(jehanne_memcmp(p+2, mpbusdef[i].type, 6) != 0)
continue;
if(jehanne_memcmp(p+2, "ISA ", 6) == 0){
if(mpisabusno != -1){
jehanne_print("mpparse: bus %d already have ISA bus %d\n",
p[1], mpisabusno);
continue;
}
mpisabusno = p[1];
}
mpbus[p[1]] = &mpbusdef[i];
break;
}
if(mpbus[p[1]] == nil)
jehanne_print("mpparse: bus %d type %6.6s unknown\n",
p[1], (char*)p+2);
p += 8;
break;
case 2: /* IOAPIC */
/*
* Initialise the IOAPIC if it is enabled (p[3] & 0x01).
* p[1] is the APIC ID, p[4-7] is the memory mapped address.
*/
DBG("mpparse: IOAPIC %d pa %#ux useable %d\n",
p[1], l32get(p+4), p[3] & 0x01);
if(p[3] & 0x01)
ioapicinit(p[1], -1, l32get(p+4));
p += 8;
break;
case 3: /* IOINTR */
/*
* p[1] is the interrupt type;
* p[2-3] contains the polarity and trigger mode;
* p[4] is the source bus;
* p[5] is the IRQ on the source bus;
* p[6] is the destination APIC;
* p[7] is the INITIN pin on the destination APIC.
*/
if(p[6] == 0xff){
mpintrprint("routed to all IOAPICs", p);
p += 8;
break;
}
if((lo = mpmkintr(p)) == 0){
p += 8;
break;
}
if(DBGFLG)
mpintrprint(nil, p);
/*
* Always present the device number in the style
* of a PCI Interrupt Assignment Entry. For the ISA
* bus the IRQ is the device number but unencoded.
* May need to handle other buses here in the future
* (but unlikely).
*/
bustype = -1;
if(jehanne_memcmp(mpbus[p[4]]->type, "PCI ", 6) == 0)
bustype = BusPCI; /* had devno = p[5]<<2 */
else if(jehanne_memcmp(mpbus[p[4]]->type, "ISA ", 6) == 0)
bustype = BusISA;
if(bustype != -1)
ioapicintrinit(bustype, p[4], p[6], p[7], p[5], lo);
p += 8;
break;
case 4: /* LINTR */
/*
* Format is the same as IOINTR above.
*/
if((lo = mpmkintr(p)) == 0){
p += 8;
break;
}
if(DBGFLG)
mpintrprint(nil, p);
/*
* Everything was checked in mpmkintr above.
*/
if(p[6] == 0xff){
for(i = 0; i < Napic; i++){
apic = lapiclookup(i);
if(apic != nil)
apic->lvt[p[7]] = lo;
}
}
else{
apic = lapiclookup(p[6]);
if(apic != nil)
apic->lvt[p[7]] = lo;
}
p += 8;
break;
cycles(&m->tscticks); /* Uses the rdtsc instruction */
return m->tscticks;
}
/*
* There's nothing of real interest in the extended table,
* should just move along, but check it for consistency.
*/
p = e;
e = p + l16get(pcmp->xlength);
while(p < e) switch(*p){
default:
n = p[1];
jehanne_print("mpparse: unknown extended entry %d length %d\n", *p, n);
for(i = 0; i < n; i++){
if(i && ((i & 0x0f) == 0))
jehanne_print("\n");
jehanne_print(" %#2.2ux", *p);
p++;
}
jehanne_print("\n");
break;
case 128:
DBG("address space mapping\n");
DBG(" bus %d type %d base %#llux length %#llux\n",
p[2], p[3], l64get(p+4), l64get(p+12));
p += p[1];
break;
case 129:
DBG("bus hierarchy descriptor\n");
DBG(" bus %d sd %d parent bus %d\n",
p[2], p[3], p[4]);
p += p[1];
break;
case 130:
DBG("compatibility bus address space modifier\n");
DBG(" bus %d pr %d range list %d\n",
p[2], p[3], l32get(p+4));
p += p[1];
break;
void
syncclock(void)
{
uint64_t x;
if(arch->fastclock != tscticks)
return;
if(m->machno == 0){
wrmsr(0x10, 0);
m->tscticks = 0;
} else {
x = MACHP(0)->tscticks;
while(x == MACHP(0)->tscticks)
;
wrmsr(0x10, MACHP(0)->tscticks);
cycles(&m->tscticks);
}
}
void
mpsinit(void)
mpinit(void)
{
uint8_t *p;
int i, n;
_MP_ *mp;
PCMP *pcmp;
int ncpu, i;
Apic *apic;
char *cp;
if((mp = sigsearch("_MP_")) == nil)
return;
if(DBGFLG){
DBG("_MP_ @ %#p, addr %#ux length %ud rev %d",
mp, l32get(mp->addr), mp->length, mp->revision);
for(i = 0; i < sizeof(mp->feature); i++)
DBG(" %2.2#ux", mp->feature[i]);
DBG("\n");
}
if(mp->revision != 1 && mp->revision != 4)
return;
if(checksum(mp, mp->length*16) != 0)
return;
i8259init();
syncclock();
if((pcmp = vmap(l32get(mp->addr), sizeof(PCMP))) == nil)
return;
if(pcmp->revision != 1 && pcmp->revision != 4){
vunmap(pcmp, sizeof(PCMP));
return;
}
n = l16get(pcmp->length) + l16get(pcmp->xlength);
vunmap(pcmp, sizeof(PCMP));
if((pcmp = vmap(l32get(mp->addr), n)) == nil)
return;
if(checksum(pcmp, l16get(pcmp->length)) != 0){
vunmap(pcmp, n);
return;
}
if(DBGFLG){
DBG("PCMP @ %#p length %#ux revision %d\n",
pcmp, l16get(pcmp->length), pcmp->revision);
DBG(" %20.20s oaddr %#ux olength %#ux\n",
(char*)pcmp->string, l32get(pcmp->oaddr),
l16get(pcmp->olength));
DBG(" entry %d apicpa %#ux\n",
l16get(pcmp->entry), l32get(pcmp->apicpa));
if(getconf("*apicdebug")){
Bus *b;
Aintr *ai;
PCMPintr *pi;
DBG(" xlength %#ux xchecksum %#ux\n",
l16get(pcmp->xlength), pcmp->xchecksum);
for(i=0; i<=MaxAPICNO; i++){
if(apic = mpapic[i])
print("LAPIC%d: pa=%lux va=%#p flags=%x\n",
i, apic->paddr, apic->addr, apic->flags);
if(apic = mpioapic[i])
print("IOAPIC%d: pa=%lux va=%#p flags=%x gsibase=%d mre=%d\n",
i, apic->paddr, apic->addr, apic->flags, apic->gsibase, apic->mre);
}
if(pcmp->xchecksum != 0){
p = ((uint8_t*)pcmp) + l16get(pcmp->length);
i = checksum(p, l16get(pcmp->xlength));
if(((i+pcmp->xchecksum) & 0xff) != 0){
jehanne_print("extended table checksums to %#ux\n", i);
vunmap(pcmp, n);
for(b = mpbus; b; b = b->next){
print("BUS%d type=%d flags=%x\n", b->busno, b->type, b->po|b->el);
for(ai = b->aintr; ai; ai = ai->next){
if(pi = ai->intr)
print("\ttype=%d irq=%d (%d [%c]) apic=%d intin=%d flags=%x\n",
pi->type, pi->irq, pi->irq>>2, "ABCD"[pi->irq&3],
pi->apicno, pi->intin, pi->flags);
}
}
}
apic = nil;
for(i=0; i<=MaxAPICNO; i++){
if(mpapic[i] == nil)
continue;
if(mpapic[i]->flags & PcmpBP){
apic = mpapic[i];
break;
}
}
if(apic == nil){
panic("mpinit: no bootstrap processor");
return;
}
apic->online = 1;
lapicinit(apic);
/*
* These interrupts are local to the processor
* and do not appear in the I/O APIC so it is OK
* to set them now.
*/
intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
lapiconline();
/*
* Initialise the application processors.
*/
if(cp = getconf("*ncpu")){
ncpu = strtol(cp, 0, 0);
if(ncpu < 1)
ncpu = 1;
else if(ncpu > MACHMAX)
ncpu = MACHMAX;
}
else
ncpu = MACHMAX;
memmove((void*)APBOOTSTRAP, sipihandler, sizeof(sipihandler));
for(i=0; i<nelem(mpapic); i++){
if((apic = mpapic[i]) == nil)
continue;
if(apic->machno >= MACHMAX)
continue;
if(ncpu <= 1)
break;
if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN){
mpstartap(apic);
sys->nmach++;
ncpu--;
}
}
/*
* Parse the PCMP table and set up the datastructures
* for later interrupt enabling and application processor
* startup.
* we don't really know the number of processors till
* here.
*
* set sys->copymode here if nmach > 1.
* Should look for an ExtINT line and enable it.
*/
mpparse(pcmp);
lapicdump();
iordtdump();
if(X86FAMILY(m->cpuidax) == 3 || sys->nmach > 1)
sys->copymode = 1;
}
static int
mpintrcpu(void)
{
static Lock physidlock;
static int physid;
int i;
/*
* The bulk of this code was written ~1995, when there was
* one architecture and one generation of hardware, the number
* of CPUs was up to 4(8) and the choices for interrupt routing
* were physical, or flat logical (optionally with lowest
* priority interrupt). Logical mode hasn't scaled well with
* the increasing number of packages/cores/threads, so the
* fall-back is to physical mode, which works across all processor
* generations, both AMD and Intel, using the APIC and xAPIC.
*
* Interrupt routing policy can be set here.
* Currently, just assign each interrupt to a different CPU on
* a round-robin basis. Some idea of the packages/cores/thread
* topology would be useful here, e.g. to not assign interrupts
* to more than one thread in a core, or to use a "noise" core.
* But, as usual, Intel make that an onerous task.
*/
lock(&physidlock);
for(;;){
i = physid++;
if(physid >= nelem(mpapic))
physid = 0;
if(mpapic[i] == nil)
continue;
if(mpapic[i]->online)
break;
}
unlock(&physidlock);
return mpapic[i]->apicno;
}
/*
* With the APIC a unique vector can be assigned to each
* request to enable an interrupt. There are two reasons this
* is a good idea:
* 1) to prevent lost interrupts, no more than 2 interrupts
* should be assigned per block of 16 vectors (there is an
* in-service entry and a holding entry for each priority
* level and there is one priority level per block of 16
* interrupts).
* 2) each input pin on the IOAPIC will receive a different
* vector regardless of whether the devices on that pin use
* the same IRQ as devices on another pin.
*/
static int
allocvector(void)
{
static int round = 0, num = 0;
static Lock l;
int vno;
lock(&l);
vno = VectorAPIC + num;
if(vno < MaxVectorAPIC-7)
num += 8;
else
num = ++round % 8;
unlock(&l);
return vno;
}
static int
mpintrenablex(Vctl* v, int tbdf)
{
Bus *bus;
Aintr *aintr;
Apic *apic;
Pcidev *pcidev;
int bno, dno, pin, hi, irq, lo, n, type, vno;
type = BUSTYPE(tbdf);
bno = BUSBNO(tbdf);
dno = BUSDNO(tbdf);
pin = 0;
pcidev = nil;
if(type == BusPCI){
if(pcidev = pcimatchtbdf(tbdf))
pin = pcicfgr8(pcidev, PciINTP);
} else if(type == BusISA)
bno = mpisabus;
Findbus:
for(bus = mpbus; bus != nil; bus = bus->next){
if(bus->type != type)
continue;
if(bus->busno == bno)
break;
}
if(bus == nil){
/*
* if the PCI device is behind a PCI-PCI bridge thats not described
* by the MP or ACPI tables then walk up the bus translating interrupt
* pin to parent bus.
*/
if(pcidev && pcidev->parent && pin > 0){
pin = ((dno+(pin-1))%4)+1;
pcidev = pcidev->parent;
bno = BUSBNO(pcidev->tbdf);
dno = BUSDNO(pcidev->tbdf);
goto Findbus;
}
print("mpintrenable: can't find bus type %d, number %d\n", type, bno);
return -1;
}
/*
* For PCI devices the interrupt pin (INT[ABCD]) and device
* number are encoded into the entry irq field, so create something
* to match on.
*/
if(bus->type == BusPCI){
if(pin > 0)
irq = (dno<<2)|(pin-1);
else
irq = -1;
}
else
irq = v->irq;
/*
* Find a matching interrupt entry from the list of interrupts
* attached to this bus.
*/
for(aintr = bus->aintr; aintr; aintr = aintr->next){
if(aintr->intr->irq != irq)
continue;
if(0){
PCMPintr* p = aintr->intr;
print("mpintrenablex: bus %d intin %d irq %d\n",
p->busno, p->intin, p->irq);
}
/*
* Check if already enabled. Multifunction devices may share
* INT[A-D]# so, if already enabled, check the polarity matches
* and the trigger is level.
*
* Should check the devices differ only in the function number,
* but that can wait for the planned enable/disable rewrite.
* The RDT read here is safe for now as currently interrupts
* are never disabled once enabled.
*/
apic = aintr->apic;
ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
if(!(lo & ApicIMASK)){
vno = lo & 0xFF;
if(0) print("%s vector %d (!imask)\n", v->name, vno);
n = mpintrinit(bus, aintr->intr, vno, v->irq);
n |= ApicPHYSICAL; /* no-op */
lo &= ~(ApicRemoteIRR|ApicDELIVS);
if(n != lo){
print("mpintrenable: multiple botch irq %d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
v->irq, tbdf, lo, n);
return -1;
}
v->isr = lapicisr;
v->eoi = lapiceoi;
return vno;
}
vno = allocvector();
hi = mpintrcpu()<<24;
lo = mpintrinit(bus, aintr->intr, vno, v->irq);
lo |= ApicPHYSICAL; /* no-op */
if(lo & ApicIMASK){
print("mpintrenable: disabled irq %d, tbdf %uX, lo %8.8uX, hi %8.8uX\n",
v->irq, tbdf, lo, hi);
return -1;
}
if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC)
ioapicrdtw(apic, aintr->intr->intin, hi, lo);
v->isr = lapicisr;
v->eoi = lapiceoi;
return vno;
}
return -1;
}
enum {
MSICtrl = 0x02, /* message control register (16 bit) */
MSIAddr = 0x04, /* message address register (64 bit) */
MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
};
enum {
HTMSIMapping = 0xA8,
HTMSIFlags = 0x02,
HTMSIFlagsEn = 0x01,
};
static int
htmsicapenable(Pcidev *p)
{
return -1;
#if 0
int cap, flags;
if((cap = pcihtcap(p, HTMSIMapping)) <= 0)
return -1;
flags = pcicfgr8(p, cap + HTMSIFlags);
if((flags & HTMSIFlagsEn) == 0)
pcicfgw8(p, cap + HTMSIFlags, flags | HTMSIFlagsEn);
return 0;
#endif
}
static int
htmsienable(Pcidev *pdev)
{
Pcidev *p;
p = nil;
while((p = pcimatch(p, 0x1022, 0)) != nil)
if(p->did == 0x1103 || p->did == 0x1203)
break;
if(p == nil)
return 0; /* not hypertransport platform */
p = nil;
while((p = pcimatch(p, 0x10de, 0)) != nil){
switch(p->did){
case 0x02f0: /* NVIDIA NFORCE C51 MEMC0 */
case 0x02f1: /* NVIDIA NFORCE C51 MEMC1 */
case 0x02f2: /* NVIDIA NFORCE C51 MEMC2 */
case 0x02f3: /* NVIDIA NFORCE C51 MEMC3 */
case 0x02f4: /* NVIDIA NFORCE C51 MEMC4 */
case 0x02f5: /* NVIDIA NFORCE C51 MEMC5 */
case 0x02f6: /* NVIDIA NFORCE C51 MEMC6 */
case 0x02f7: /* NVIDIA NFORCE C51 MEMC7 */
case 0x0369: /* NVIDIA NFORCE MCP55 MEMC */
htmsicapenable(p);
break;
}
}
if(htmsicapenable(pdev) == 0)
return 0;
for(p = pdev->parent; p != nil; p = p->parent)
if(htmsicapenable(p) == 0)
return 0;
return -1;
}
static int
msiintrenable(Vctl *v)
{
int tbdf, vno, cap, cpu, ok64;
Pcidev *pci;
if(getconf("*nomsi") != nil)
return -1;
tbdf = v->tbdf;
if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
return -1;
pci = pcimatchtbdf(tbdf);
if(pci == nil) {
print("msiintrenable: could not find Pcidev for tbdf %uX\n", tbdf);
return -1;
}
if(htmsienable(pci) < 0)
return -1;
cap = pcicap(pci, PciCapMSI);
if(cap < 0)
return -1;
vno = allocvector();
cpu = mpintrcpu();
ok64 = (pcicfgr16(pci, cap + MSICtrl) & (1<<7)) != 0;
pcicfgw32(pci, cap + MSIAddr, (0xFEE << 20) | (cpu << 12));
if(ok64) pcicfgw32(pci, cap + MSIAddr + 4, 0);
pcicfgw16(pci, cap + (ok64 ? MSIData64 : MSIData32), vno | (1<<14));
pcicfgw16(pci, cap + MSICtrl, 1);
v->isr = lapicisr;
v->eoi = lapiceoi;
return vno;
}
int
mpintrenable(Vctl* v)
{
int irq, tbdf, vno;
vno = msiintrenable(v);
if(vno != -1)
return vno;
/*
* If the bus is known, try it.
* BUSUNKNOWN is given both by [E]ISA devices and by
* interrupts local to the processor (local APIC, coprocessor
* breakpoint and page-fault).
*/
tbdf = v->tbdf;
if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
return vno;
irq = v->irq;
if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
if(irq != IrqSPURIOUS)
v->isr = lapiceoi;
return VectorPIC+irq;
}
if(irq < 0 || irq > MaxIrqPIC){
print("mpintrenable: irq %d out of range\n", irq);
return -1;
}
/*
* Either didn't find it or have to try the default buses
* (ISA and EISA). This hack is due to either over-zealousness
* or laziness on the part of some manufacturers.
*
* The MP configuration table on some older systems
* (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
* but none for ISA. It also has the interrupt type and
* polarity set to 'default for this bus' which wouldn't
* be compatible with ISA.
*/
if(mpeisabus != -1){
vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
if(vno != -1)
return vno;
}
if(mpisabus != -1){
vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
if(vno != -1)
return vno;
}
print("mpintrenable: out of choices eisa %d isa %d tbdf %uX irq %d\n",
mpeisabus, mpisabus, v->tbdf, v->irq);
return -1;
}
void
mpshutdown(void)
{
/*
* Park application processors.
*/
if(m->machno != 0){
splhi();
arch->introff();
for(;;) idle();
}
delay(1000);
splhi();
/*
* INIT all excluding self.
*/
lapicicrw(0, 0x000C0000|ApicINIT);
pcireset();
}

261
sys/src/kern/amd64/mp.h Normal file
View File

@ -0,0 +1,261 @@
/*
* MultiProcessor Specification Version 1.[14].
*/
typedef struct { /* floating pointer */
uint8_t signature[4]; /* "_MP_" */
int32_t physaddr; /* physical address of MP configuration table */
uint8_t length; /* 1 */
uint8_t specrev; /* [14] */
uint8_t checksum; /* all bytes must add up to 0 */
uint8_t type; /* MP system configuration type */
uint8_t imcrp;
uint8_t reserved[3];
} _MP_;
#define _MP_sz (4+4+1+1+1+1+1+3)
typedef struct { /* configuration table header */
uint8_t signature[4]; /* "PCMP" */
uint16_t length; /* total table length */
uint8_t version; /* [14] */
uint8_t checksum; /* all bytes must add up to 0 */
uint8_t product[20]; /* product id */
uint32_t oemtable; /* OEM table pointer */
uint16_t oemlength; /* OEM table length */
uint16_t entry; /* entry count */
uint32_t lapicbase; /* address of local APIC */
uint16_t xlength; /* extended table length */
uint8_t xchecksum; /* extended table checksum */
uint8_t reserved;
} PCMP;
#define PCMPsz (4+2+1+1+20+4+2+2+4+2+1+1)
typedef struct { /* processor table entry */
uint8_t type; /* entry type (0) */
uint8_t apicno; /* local APIC id */
uint8_t version; /* local APIC verison */
uint8_t flags; /* CPU flags */
uint8_t signature[4]; /* CPU signature */
uint32_t feature; /* feature flags from CPUID instruction */
uint8_t reserved[8];
} PCMPprocessor;
#define PCMPprocessorsz (1+1+1+1+4+4+8)
typedef struct { /* bus table entry */
uint8_t type; /* entry type (1) */
uint8_t busno; /* bus id */
char string[6]; /* bus type string */
} PCMPbus;
#define PCMPbussz (1+1+6)
typedef struct { /* I/O APIC table entry */
uint8_t type; /* entry type (2) */
uint8_t apicno; /* I/O APIC id */
uint8_t version; /* I/O APIC version */
uint8_t flags; /* I/O APIC flags */
uint32_t addr; /* I/O APIC address */
} PCMPioapic;
#define PCMPioapicsz (1+1+1+1+4)
typedef struct { /* interrupt table entry */
uint8_t type; /* entry type ([34]) */
uint8_t intr; /* interrupt type */
uint16_t flags; /* interrupt flag */
uint8_t busno; /* source bus id */
uint8_t irq; /* source bus irq */
uint8_t apicno; /* destination APIC id */
uint8_t intin; /* destination APIC [L]INTIN# */
} PCMPintr;
#define PCMPintrsz (1+1+2+1+1+1+1)
typedef struct { /* system address space mapping entry */
uint8_t type; /* entry type (128) */
uint8_t length; /* of this entry (20) */
uint8_t busno; /* bus id */
uint8_t addrtype;
uint32_t addrbase[2];
uint32_t addrlength[2];
} PCMPsasm;
#define PCMPsasmsz (1+1+1+1+8+8)
typedef struct { /* bus hierarchy descriptor entry */
uint8_t type; /* entry type (129) */
uint8_t length; /* of this entry (8) */
uint8_t busno; /* bus id */
uint8_t info; /* bus info */
uint8_t parent; /* parent bus */
uint8_t reserved[3];
} PCMPhierarchy;
#define PCMPhirarchysz (1+1+1+1+1+3)
typedef struct { /* compatibility bus address space modifier entry */
uint8_t type; /* entry type (130) */
uint8_t length; /* of this entry (8) */
uint8_t busno; /* bus id */
uint8_t modifier; /* address modifier */
uint32_t range; /* predefined range list */
} PCMPcbasm;
#define PCMPcbasmsz (1+1+1+1+4)
enum { /* table entry types */
PcmpPROCESSOR = 0x00, /* one entry per processor */
PcmpBUS = 0x01, /* one entry per bus */
PcmpIOAPIC = 0x02, /* one entry per I/O APIC */
PcmpIOINTR = 0x03, /* one entry per bus interrupt source */
PcmpLINTR = 0x04, /* one entry per system interrupt source */
PcmpSASM = 0x80,
PcmpHIERARCHY = 0x81,
PcmpCBASM = 0x82,
/* PCMPprocessor and PCMPioapic flags */
PcmpEN = 0x01, /* enabled */
PcmpBP = 0x02, /* bootstrap processor */
/* PCMPiointr and PCMPlintr flags */
PcmpPOMASK = 0x03, /* polarity conforms to specifications of bus */
PcmpHIGH = 0x01, /* active high */
PcmpLOW = 0x03, /* active low */
PcmpELMASK = 0x0C, /* trigger mode of APIC input signals */
PcmpEDGE = 0x04, /* edge-triggered */
PcmpLEVEL = 0x0C, /* level-triggered */
/* PCMPiointr and PCMPlintr interrupt type */
PcmpINT = 0x00, /* vectored interrupt from APIC Rdt */
PcmpNMI = 0x01, /* non-maskable interrupt */
PcmpSMI = 0x02, /* system management interrupt */
PcmpExtINT = 0x03, /* vectored interrupt from external PIC */
/* PCMPsasm addrtype */
PcmpIOADDR = 0x00, /* I/O address */
PcmpMADDR = 0x01, /* memory address */
PcmpPADDR = 0x02, /* prefetch address */
/* PCMPhierarchy info */
PcmpSD = 0x01, /* subtractive decode bus */
/* PCMPcbasm modifier */
PcmpPR = 0x01, /* predefined range list */
};
/*
* Condensed form of the MP Configuration Table.
* This is created during a single pass through the MP Configuration
* table.
*/
typedef struct Aintr Aintr;
typedef struct Bus Bus;
typedef struct Apic Apic;
typedef struct Bus {
uint8_t type;
uint8_t busno;
uint8_t po;
uint8_t el;
Aintr* aintr; /* interrupts tied to this bus */
Bus* next;
} Bus;
typedef struct Aintr {
PCMPintr* intr;
Apic* apic;
Aintr* next;
} Aintr;
typedef struct Apic {
int type;
int apicno;
uint32_t* addr; /* register base address */
uint32_t paddr;
int flags; /* PcmpBP|PcmpEN */
Lock; /* I/O APIC: register access */
int mre; /* I/O APIC: maximum redirection entry */
int gsibase; /* I/O APIC: global system interrupt base (acpi) */
int lintr[2]; /* Local APIC */
int machno;
int online;
} Apic;
enum {
MaxAPICNO = 254, /* 255 is physical broadcast */
};
enum { /* I/O APIC registers */
IoapicID = 0x00, /* ID */
IoapicVER = 0x01, /* version */
IoapicARB = 0x02, /* arbitration ID */
IoapicRDT = 0x10, /* redirection table */
};
/*
* Common bits for
* I/O APIC Redirection Table Entry;
* Local APIC Local Interrupt Vector Table;
* Local APIC Inter-Processor Interrupt;
* Local APIC Timer Vector Table.
*/
enum {
ApicFIXED = 0x00000000, /* [10:8] Delivery Mode */
ApicLOWEST = 0x00000100, /* Lowest priority */
ApicSMI = 0x00000200, /* System Management Interrupt */
ApicRR = 0x00000300, /* Remote Read */
ApicNMI = 0x00000400,
ApicINIT = 0x00000500, /* INIT/RESET */
ApicSTARTUP = 0x00000600, /* Startup IPI */
ApicExtINT = 0x00000700,
ApicPHYSICAL = 0x00000000, /* [11] Destination Mode (RW) */
ApicLOGICAL = 0x00000800,
ApicDELIVS = 0x00001000, /* [12] Delivery Status (RO) */
ApicHIGH = 0x00000000, /* [13] Interrupt Input Pin Polarity (RW) */
ApicLOW = 0x00002000,
ApicRemoteIRR = 0x00004000, /* [14] Remote IRR (RO) */
ApicEDGE = 0x00000000, /* [15] Trigger Mode (RW) */
ApicLEVEL = 0x00008000,
ApicIMASK = 0x00010000, /* [16] Interrupt Mask */
};
extern void ioapicinit(Apic*, int);
extern void ioapicrdtr(Apic*, int, int*, int*);
extern void ioapicrdtw(Apic*, int, int, int);
extern void lapicclock(Ureg*, void*);
extern int lapiceoi(int);
extern void lapicerror(Ureg*, void*);
extern void lapicicrw(uint32_t, uint32_t);
extern void lapicinit(Apic*);
extern void lapicintroff(void);
extern void lapicintron(void);
extern int lapicisr(int);
extern void lapicnmidisable(void);
extern void lapicnmienable(void);
extern void lapiconline(void);
extern void lapicspurious(Ureg*, void*);
extern void lapicstartap(Apic*, uintptr_t);
extern void lapictimerset(uint64_t);
extern int mpintrinit(Bus*, PCMPintr*, int, int);
extern void mpinit(void);
extern int mpintrenable(Vctl*);
extern void mpshutdown(void);
extern void mpstartap(Apic*);
extern Bus* mpbus;
extern Bus* mpbuslast;
extern int mpisabus;
extern int mpeisabus;
extern Apic *mpioapic[];
extern Apic *mpapic[];

390
sys/src/kern/amd64/mtrr.c Normal file
View File

@ -0,0 +1,390 @@
/*
* memory-type region registers.
*
* due to the possibility of extended addresses (for PAE)
* as large as 36 bits coming from the e820 memory map and the like,
* we'll use vlongs to hold addresses and lengths, even though we don't
* implement PAE in Plan 9.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
enum {
/*
* MTRR Physical base/mask are indexed by
* MTRRPhys{Base|Mask}N = MTRRPhys{Base|Mask}0 + 2*N
*/
MTRRPhysBase0 = 0x200,
MTRRPhysMask0 = 0x201,
MTRRDefaultType = 0x2FF,
MTRRCap = 0xFE,
Nmtrr = 8,
/* cpuid extended function codes */
Exthighfunc = 1ul << 31,
Extprocsigamd,
Extprocname0,
Extprocname1,
Extprocname2,
Exttlbl1,
Extl2,
Extapm,
Extaddrsz,
Paerange = 1LL << 36,
};
enum {
CR4PageGlobalEnable = 1 << 7,
CR0CacheDisable = 1 << 30,
};
enum {
Uncacheable = 0,
Writecomb = 1,
Unknown1 = 2,
Unknown2 = 3,
Writethru = 4,
Writeprot = 5,
Writeback = 6,
};
enum {
Capvcnt = 0xff, /* mask: # of variable-range MTRRs we have */
Capwc = 1<<8, /* flag: have write combining? */
Capfix = 1<<10, /* flag: have fixed MTRRs? */
Deftype = 0xff, /* default MTRR type */
Deffixena = 1<<10, /* fixed-range MTRR enable */
Defena = 1<<11, /* MTRR enable */
};
typedef struct Mtrreg Mtrreg;
typedef struct Mtrrop Mtrrop;
struct Mtrreg {
long base;
long mask;
};
static char *types[] = {
[Uncacheable] "uc",
[Writecomb] "wc",
[Unknown1] "uk1",
[Unknown2] "uk2",
[Writethru] "wt",
[Writeprot] "wp",
[Writeback] "wb",
nil
};
static int dosync;
static Mtrreg mtrreg[Nmtrr];
static char *
type2str(int type)
{
if(type < 0 || type >= nelem(types))
return nil;
return types[type];
}
static int
str2type(char *str)
{
char **p;
for(p = types; *p != nil; p++)
if (strcmp(str, *p) == 0)
return p - types;
return -1;
}
static unsigned long
physmask(void)
{
uint32_t regs[4];
static long mask = -1;
if (mask != -1)
return mask;
cpuid(Exthighfunc, regs);
if(regs[0] >= Extaddrsz) { /* ax */
cpuid(Extaddrsz, regs);
mask = (1LL << (regs[0] & 0xFF)) - 1; /* ax */
}
mask &= Paerange - 1; /* x86 sanity */
return mask;
}
/* limit physical addresses to 36 bits on the x86 */
static void
sanity(Mtrreg *mtrr)
{
mtrr->base &= Paerange - 1;
mtrr->mask &= Paerange - 1;
}
static int
ispow2(unsigned long ul)
{
return (ul & (ul - 1)) == 0;
}
/* true if mtrr is valid */
static int
mtrrdec(Mtrreg *mtrr, unsigned long *ptr, unsigned long *size, int *type)
{
sanity(mtrr);
*ptr = mtrr->base & ~(BY2PG-1);
*type = mtrr->base & 0xff;
*size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
return (mtrr->mask >> 11) & 1;
}
static void
mtrrenc(Mtrreg *mtrr, unsigned long ptr, unsigned long size, int type, int ok)
{
mtrr->base = ptr | (type & 0xff);
mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
sanity(mtrr);
}
/*
* i is the index of the MTRR, and is multiplied by 2 because
* mask and base offsets are interleaved.
*/
static void
mtrrget(Mtrreg *mtrr, uint i)
{
rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
sanity(mtrr);
}
static void
mtrrput(Mtrreg *mtrr, uint i)
{
sanity(mtrr);
wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
}
static int
mtrrvcnt(void)
{
long cap;
int vcnt;
rdmsr(MTRRCap, &cap);
vcnt = cap & Capvcnt;
if(vcnt > Nmtrr)
vcnt = Nmtrr;
return vcnt;
}
static int
mtrrgetall(void)
{
int i, vcnt;
vcnt = mtrrvcnt();
for(i = 0; i < vcnt; i++)
mtrrget(&mtrreg[i], i);
return vcnt;
}
static void
mtrrputall(void)
{
int s, i, vcnt;
uint32_t cr0, cr4;
long def;
s = splhi();
cr4 = cr4get();
cr4put(cr4 & ~CR4PageGlobalEnable);
cr0 = cr0get();
wbinvd();
cr0put(cr0 | CR0CacheDisable);
wbinvd();
rdmsr(MTRRDefaultType, &def);
wrmsr(MTRRDefaultType, def & ~(long)Defena);
vcnt = mtrrvcnt();
for(i=0; i<vcnt; i++)
mtrrput(&mtrreg[i], i);
wbinvd();
wrmsr(MTRRDefaultType, def);
cr0put(cr0);
cr4put(cr4);
splx(s);
}
void
mtrrclock(void) /* called from clock interrupt */
{
static Ref bar1, bar2;
int s;
if(dosync == 0)
return;
s = splhi();
/*
* wait for all CPUs to sync here, so that the MTRR setup gets
* done at roughly the same time on all processors.
*/
incref(&bar1);
while(bar1.ref < sys->nmach)
microdelay(10);
mtrrputall();
/*
* wait for all CPUs to sync up again, so that we don't continue
* executing while the MTRRs are still being set up.
*/
incref(&bar2);
while(bar2.ref < sys->nmach)
microdelay(10);
decref(&bar1);
while(bar1.ref > 0)
microdelay(10);
decref(&bar2);
dosync = 0;
splx(s);
}
static char*
mtrr0(unsigned long base, unsigned long size, char *tstr)
{
int i, vcnt, slot, type, mtype, mok;
long def, cap;
unsigned long mp, msize;
if(!(m->cpuiddx & Mtrr))
return "mtrrs not supported";
if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
return "mtrr base or size not 4k aligned or zero size";
if(base + size >= Paerange)
return "mtrr range exceeds 36 bits";
if(!ispow2(size))
return "mtrr size not power of 2";
if(base & (size - 1))
return "mtrr base not naturally aligned";
if((type = str2type(tstr)) == -1)
return "mtrr bad type";
rdmsr(MTRRCap, &cap);
rdmsr(MTRRDefaultType, &def);
switch(type){
default:
return "mtrr unknown type";
case Writecomb:
if(!(cap & Capwc))
return "mtrr type wc (write combining) unsupported";
/* fallthrough */
case Uncacheable:
case Writethru:
case Writeprot:
case Writeback:
break;
}
vcnt = mtrrgetall();
slot = -1;
for(i = 0; i < vcnt; i++){
mok = mtrrdec(&mtrreg[i], &mp, &msize, &mtype);
if(slot == -1 && (!mok || mtype == (def & Deftype)))
slot = i; /* good, but look further for exact match */
if(mok && mp == base && msize == size){
slot = i;
break;
}
}
if(slot == -1)
return "no free mtrr slots";
mtrrenc(&mtrreg[slot], base, size, type, 1);
coherence();
dosync = 1;
mtrrclock();
return nil;
}
char*
mtrr(unsigned long base, unsigned long size, char *tstr)
{
static QLock mtrrlk;
char *err;
qlock(&mtrrlk);
err = mtrr0(base, size, tstr);
qunlock(&mtrrlk);
return err;
}
int
mtrrprint(char *buf, long bufsize)
{
int i, n, vcnt, type;
unsigned long base, size;
Mtrreg mtrr;
long def;
if(!(m->cpuiddx & Mtrr))
return 0;
rdmsr(MTRRDefaultType, &def);
n = snprint(buf, bufsize, "cache default %s\n",
type2str(def & Deftype));
vcnt = mtrrvcnt();
for(i = 0; i < vcnt; i++){
mtrrget(&mtrr, i);
if (mtrrdec(&mtrr, &base, &size, &type))
n += snprint(buf+n, bufsize-n,
"cache 0x%llux %llud %s\n",
base, size, type2str(type));
}
return n;
}
void
mtrrsync(void)
{
static long cap0, def0;
long cap, def;
rdmsr(MTRRCap, &cap);
rdmsr(MTRRDefaultType, &def);
if(m->machno == 0){
cap0 = cap;
def0 = def;
mtrrgetall();
return;
}
if(cap0 != cap)
print("mtrrcap%d: %lluX %lluX\n",
m->machno, cap0, cap);
if(def0 != def)
print("mtrrdef%d: %lluX %lluX\n",
m->machno, def0, def);
mtrrputall();
}

View File

@ -1,155 +1,126 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "ureg.h"
#include "multiboot.h"
typedef struct Mbi Mbi;
struct Mbi {
uint32_t flags;
uint32_t memlower;
uint32_t memupper;
uint32_t bootdevice;
uint32_t cmdline;
uint32_t modscount;
uint32_t modsaddr;
uint32_t syms[4];
uint32_t mmaplength;
uint32_t mmapaddr;
uint32_t driveslength;
uint32_t drivesaddr;
uint32_t configtable;
uint32_t bootloadername;
uint32_t apmtable;
uint32_t vbe[6];
};
#define AT_KERNEL(p) ((void*)(p+KZERO))
enum { /* flags */
Fmem = 0x00000001, /* mem* valid */
Fbootdevice = 0x00000002, /* bootdevice valid */
Fcmdline = 0x00000004, /* cmdline valid */
Fmods = 0x00000008, /* mod* valid */
Fsyms = 0x00000010, /* syms[] has a.out info */
Felf = 0x00000020, /* syms[] has ELF info */
Fmmap = 0x00000040, /* mmap* valid */
Fdrives = 0x00000080, /* drives* valid */
Fconfigtable = 0x00000100, /* configtable* valid */
Fbootloadername = 0x00000200, /* bootloadername* valid */
Fapmtable = 0x00000400, /* apmtable* valid */
Fvbe = 0x00000800, /* vbe[] valid */
};
typedef struct Mod Mod;
struct Mod {
uint32_t modstart;
uint32_t modend;
uint32_t string;
uint32_t reserved;
};
typedef struct MMap MMap;
struct MMap {
uint32_t size;
uint32_t base[2];
uint32_t length[2];
uint32_t type;
};
extern int e820map(int type, uintptr_t base, uintptr_t top);
int
multiboot(uint32_t magic, uint32_t pmbi, int vflag)
multiboot(int just_log)
{
uint32_t magic;
char *p, *modname;
int i, n;
Mbi *mbi;
Mod *mod;
MMap *mmap;
multiboot_info_t *mbi;
multiboot_module_t *mod;
multiboot_memory_map_t *mmap;
uint64_t addr, len;
if(vflag)
jehanne_print("magic %#ux pmbi %#ux\n", magic, pmbi);
if(magic != 0x2badb002){
//return -1;
jehanne_print("wrong magic in multiboot\n");
}
magic = (uint32_t)sys->boot_regs->ax;
mbi = (multiboot_info_t*)sys->boot_regs->bx;
mbi = KADDR(pmbi);
if(vflag)
if(just_log)
jehanne_print("magic %#ux infos at %#p\n", magic, mbi);
if(magic != 0x2badb002)
return -1;
if(just_log)
jehanne_print("flags %#ux\n", mbi->flags);
if(mbi->flags & Fcmdline){
p = KADDR(mbi->cmdline);
if(vflag)
jehanne_print("cmdline <%s>\n", p);
if(mbi->flags & MULTIBOOT_INFO_CMDLINE){
p = AT_KERNEL(mbi->cmdline);
if(just_log)
jehanne_print("Multiboot Command Line:\n\t%s\n", p);
else
optionsinit(p);
}
if(mbi->flags & Fmods){
for(i = 0; i < mbi->modscount; i++){
mod = KADDR(mbi->modsaddr + i*16);
if(mod->string != 0)
p = KADDR(mod->string);
if(mbi->flags & MULTIBOOT_INFO_MODS){
if(just_log)
jehanne_print("Multiboot Modules:\n");
for(i = 0; i < mbi->mods_count; i++){
mod = AT_KERNEL(mbi->mods_addr);
if(mod->cmdline != 0)
p = AT_KERNEL(mod->cmdline);
else
p = "";
if(vflag)
jehanne_print("mod %#ux %#ux <%s>\n",
mod->modstart, mod->modend, p);
if(just_log)
jehanne_print("\tModule <%s> %#ux %#ux\n",
mod->mod_start, mod->mod_end, p);
else {
asmmodinit(mod->modstart, mod->modend, p);
asmmodinit(mod->mod_start, mod->mod_end, p);
modname = jehanne_strrchr(p, '/');
if(modname == nil)
modname = p;
if(*modname == '/')
++modname;
addbootfile(modname, KADDR(mod->modstart), mod->modend - mod->modstart);
addbootfile(modname, AT_KERNEL(mod->mod_start), mod->mod_end - mod->mod_start);
}
}
}
if(mbi->flags & Fmmap){
mmap = KADDR(mbi->mmapaddr);
if(mbi->flags & MULTIBOOT_INFO_MEM_MAP){
if(just_log)
jehanne_print("Multiboot Memory Map:\n");
mmap = AT_KERNEL(mbi->mmap_addr);
n = 0;
while(n < mbi->mmaplength){
addr = (((uint64_t)mmap->base[1])<<32)|mmap->base[0];
len = (((uint64_t)mmap->length[1])<<32)|mmap->length[0];
while(n < mbi->mmap_length){
addr = mmap->addr;
len = mmap->len;
switch(mmap->type){
default:
if(vflag)
jehanne_print("type %ud", mmap->type);
if(just_log)
jehanne_print("\ttype %ud ", mmap->type);
break;
case 1:
if(vflag)
jehanne_print("Memory");
else
asmmapinit(addr, len, mmap->type);
case MULTIBOOT_MEMORY_AVAILABLE:
if(just_log)
jehanne_print("\tMemory");
break;
case 2:
if(vflag)
jehanne_print("reserved");
else
asmmapinit(addr, len, mmap->type);
case MULTIBOOT_MEMORY_RESERVED:
if(just_log)
jehanne_print("\tReserved");
break;
case 3:
if(vflag)
jehanne_print("ACPI Reclaim Memory");
else
asmmapinit(addr, len, mmap->type);
if(just_log)
jehanne_print("\tACPI Reclaim Memory");
break;
case 4:
if(vflag)
jehanne_print("ACPI NVS Memory");
else
asmmapinit(addr, len, mmap->type);
if(just_log)
jehanne_print("\tACPI NVS Memory");
break;
}
if(vflag)
if(just_log)
jehanne_print("\n\t %#16.16llux %#16.16llux (%llud)\n",
addr, addr+len, len);
else
e820map(mmap->type, addr, addr+len);
n += mmap->size+sizeof(mmap->size);
mmap = KADDR(mbi->mmapaddr+n);
mmap = AT_KERNEL(mbi->mmap_addr+n);
}
}
if(vflag && (mbi->flags & Fbootloadername)){
p = KADDR(mbi->bootloadername);
jehanne_print("bootloadername <%s>\n", p);
if(just_log && (mbi->flags & MULTIBOOT_INFO_BOOT_LOADER_NAME)){
p = AT_KERNEL(mbi->boot_loader_name);
jehanne_print("Multiboot: Boot Loader Name <%s>\n", p);
}
return 0;

View File

@ -0,0 +1,222 @@
/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 8192
/* The magic field should contain this. */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/* This should be in %eax. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/* The bits in the required part of flags field we don't support. */
#define MULTIBOOT_UNSUPPORTED 0x0000fffc
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000004
/* Flags set in the 'flags' member of the multiboot header. */
/* Align all boot modules on i386 page (4KB) boundaries. */
#define MULTIBOOT_PAGE_ALIGN 0x00000001
/* Must pass memory information to OS. */
#define MULTIBOOT_MEMORY_INFO 0x00000002
/* Must pass video information to OS. */
#define MULTIBOOT_VIDEO_MODE 0x00000004
/* This flag indicates the use of the address fields in the header. */
#define MULTIBOOT_AOUT_KLUDGE 0x00010000
/* Flags to be set in the 'flags' member of the multiboot info structure. */
/* is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY 0x00000001
/* is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
/* is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE 0x00000004
/* are there modules to do something with? */
#define MULTIBOOT_INFO_MODS 0x00000008
/* These next two are mutually exclusive */
/* is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
/* is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
/* is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
/* Is there drive info? */
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
/* Is there a config table? */
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
/* Is there a boot loader name? */
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
/* Is there a APM table? */
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
/* Is there video information? */
#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800
#ifndef __ASSEMBLER__
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT_MAGIC - see above. */
multiboot_uint32_t magic;
/* Feature flags. */
multiboot_uint32_t flags;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
multiboot_uint32_t entry_addr;
/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
multiboot_uint32_t mode_type;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
/* The symbol table for a.out. */
struct multiboot_aout_symbol_table
{
multiboot_uint32_t tabsize;
multiboot_uint32_t strsize;
multiboot_uint32_t addr;
multiboot_uint32_t reserved;
};
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
/* The section header table for ELF. */
struct multiboot_elf_section_header_table
{
multiboot_uint32_t num;
multiboot_uint32_t size;
multiboot_uint32_t addr;
multiboot_uint32_t shndx;
};
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
struct multiboot_info
{
/* Multiboot info version number */
multiboot_uint32_t flags;
/* Available memory from BIOS */
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
/* "root" partition */
multiboot_uint32_t boot_device;
/* Kernel command line */
multiboot_uint32_t cmdline;
/* Boot-Module list */
multiboot_uint32_t mods_count;
multiboot_uint32_t mods_addr;
union
{
multiboot_aout_symbol_table_t aout_sym;
multiboot_elf_section_header_table_t elf_sec;
} u;
/* Memory Mapping buffer */
multiboot_uint32_t mmap_length;
multiboot_uint32_t mmap_addr;
/* Drive Info buffer */
multiboot_uint32_t drives_length;
multiboot_uint32_t drives_addr;
/* ROM configuration table */
multiboot_uint32_t config_table;
/* Boot Loader Name */
multiboot_uint32_t boot_loader_name;
/* APM table */
multiboot_uint32_t apm_table;
/* Video */
multiboot_uint32_t vbe_control_info;
multiboot_uint32_t vbe_mode_info;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
};
typedef struct multiboot_info multiboot_info_t;
struct multiboot_mmap_entry
{
multiboot_uint32_t size;
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
multiboot_uint32_t type;
} __attribute__((packed));
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_mod_list
{
/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
/* Module command line */
multiboot_uint32_t cmdline;
/* padding to take it to 16 bytes (must be zero) */
multiboot_uint32_t pad;
};
typedef struct multiboot_mod_list multiboot_module_t;
#endif /* ! __ASSEMBLER__ */
#endif /* ! MULTIBOOT_HEADER */

View File

@ -1,129 +0,0 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
/*
Conf conf;
char *confname[1] = {
"console",
};
char *confval[1] = {
"0 b115200",
};
int nconf = nelem(confname);
*/
/*
* Where configuration info is left for the loaded programme.
* This will turn into a structure as more is done by the boot loader
* (e.g. why parse the .ini file twice?).
* There are 3584 bytes available at CONFADDR.
*/
#define CONFADDR PTR2UINT(KADDR(0x0001200))
#define BOOTLINE ((char*)CONFADDR)
#define BOOTLINELEN 64
#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN))
#define BOOTARGSLEN (4096-0x200-BOOTLINELEN)
#define MAXCONF 64
char *confname[MAXCONF];
char *confval[MAXCONF];
int nconf;
void
confoptions(void)
{
long i, n;
char *cp, *line[MAXCONF], *p, *q;
/*
* parse configuration args from dos file plan9.ini
*/
cp = BOOTARGS; /* where b.com leaves its config */
cp[BOOTARGSLEN-1] = 0;
/*
* Strip out '\r', change '\t' -> ' '.
*/
p = cp;
for(q = cp; *q; q++){
if(*q == '\r')
continue;
if(*q == '\t')
*q = ' ';
*p++ = *q;
}
*p = 0;
n = jehanne_getfields(cp, line, MAXCONF, 1, "\n");
for(i = 0; i < n; i++){
if(*line[i] == '#')
continue;
cp = jehanne_strchr(line[i], '=');
if(cp == nil)
continue;
*cp++ = '\0';
confname[nconf] = line[i];
confval[nconf] = cp;
nconf++;
}
}
char*
getconf(char *name)
{
int i;
for(i = 0; i < nconf; i++)
if(jehanne_cistrcmp(confname[i], name) == 0)
return confval[i];
return 0;
}
void
confsetenv(void)
{
int i;
for(i = 0; i < nconf; i++){
if(confname[i][0] != '*')
ksetenv(confname[i], confval[i], 0);
ksetenv(confname[i], confval[i], 1);
}
}
int
isaconfig(char *class, int ctlrno, ISAConf *isa)
{
char cc[32], *p;
int i;
jehanne_snprint(cc, sizeof cc, "%s%d", class, ctlrno);
p = getconf(cc);
if(p == nil)
return 0;
isa->type = "";
isa->nopt = jehanne_tokenize(p, isa->opt, NISAOPT);
for(i = 0; i < isa->nopt; i++){
p = isa->opt[i];
if(jehanne_cistrncmp(p, "type=", 5) == 0)
isa->type = p + 5;
else if(jehanne_cistrncmp(p, "port=", 5) == 0)
isa->port = jehanne_strtoul(p+5, &p, 0);
else if(jehanne_cistrncmp(p, "irq=", 4) == 0)
isa->irq = jehanne_strtoul(p+4, &p, 0);
else if(jehanne_cistrncmp(p, "dma=", 4) == 0)
isa->dma = jehanne_strtoul(p+4, &p, 0);
else if(jehanne_cistrncmp(p, "mem=", 4) == 0)
isa->mem = jehanne_strtoul(p+4, &p, 0);
else if(jehanne_cistrncmp(p, "size=", 5) == 0)
isa->size = jehanne_strtoul(p+5, &p, 0);
else if(jehanne_cistrncmp(p, "freq=", 5) == 0)
isa->freq = jehanne_strtoul(p+5, &p, 0);
}
return 1;
}

View File

@ -1554,7 +1554,7 @@ pcinextcap(Pcidev *pci, int offset)
if(offset == 0) {
if((pcicfgr16(pci, PciPSR) & (1<<4)) == 0)
return 0; /* no capabilities */
offset = PciCP-1;
offset = PciCAP-1;
}
return pcicfgr8(pci, offset+1) & ~3;
}

View File

@ -101,7 +101,7 @@ pmcnregs(void)
nregs = PeNregAmd;
break;
case PeIntel:
cpuid(0xa, 0, info);
cpuid(0xa, info);
nregs = (info[0]>>8)&0xff;
break;
default:
@ -124,7 +124,7 @@ pmcmsk(void)
msk = ~0ULL;
break;
case PeIntel:
cpuid(0xa, 0, info);
cpuid(0xa, info);
msk = (1<<((info[0]>>16)&0xff)) - 1;
break;
}

View File

@ -6,7 +6,6 @@
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
@ -22,40 +21,25 @@
#include <cursor.h>
#include "screen.h"
#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19)
Point ZP = {0, 0};
Rectangle physgscreenr;
Memdata gscreendata;
Memimage *gscreen;
VGAscr vgascreen[1];
Cursor arrow = {
{ -1, -1 },
{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
},
{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
},
};
int didswcursorinit;
static void *softscreen;
int
screensize(int x, int y, int z, uint32_t chan)
screensize(int x, int y, int _, uint32_t chan)
{
VGAscr *scr;
void *oldsoft;
qlock(&drawlock);
if(waserror()){
qunlock(&drawlock);
nexterror();
}
if(memimageinit() < 0)
error("memimageinit failed");
lock(&vgascreenlock);
if(waserror()){
@ -63,54 +47,54 @@ screensize(int x, int y, int z, uint32_t chan)
nexterror();
}
memimageinit();
scr = &vgascreen[0];
oldsoft = softscreen;
scr->gscreendata = nil;
scr->gscreen = nil;
if(gscreen){
freememimage(gscreen);
gscreen = nil;
}
if(scr->paddr == 0){
int width = (x*z)/BI2WD;
void *p;
p = jehanne_malloc(width*BY2WD*y);
if(p == nil)
error("no memory for vga soft screen");
gscreendata.bdata = softscreen = p;
if(scr->dev && scr->dev->page){
scr->vaddr = KADDR(VGAMEM());
scr->apsize = 1<<16;
}
scr->useflush = 1;
scr->softscreen = 1;
}
else{
gscreendata.bdata = scr->vaddr;
if(scr->softscreen){
gscreen = allocmemimage(Rect(0,0,x,y), chan);
scr->useflush = 1;
}else{
static Memdata md;
md.ref = 1;
if((md.bdata = scr->vaddr) == 0)
error("framebuffer not maped");
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
scr->useflush = scr->dev && scr->dev->flush;
}
scr->gscreen = nil;
if(gscreen)
freememimage(gscreen);
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
if(gscreen == nil)
error("no memory for vga memimage");
vgaimageinit(chan);
scr->palettedepth = 6; /* default */
scr->gscreendata = &gscreendata;
scr->memdefont = getmemdefont();
scr->gscreen = gscreen;
scr->gscreendata = gscreen->data;
physgscreenr = gscreen->r;
vgaimageinit(chan);
unlock(&vgascreenlock);
poperror();
if(oldsoft)
jehanne_free(oldsoft);
memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S);
flushmemscreen(gscreen->r);
if(didswcursorinit)
swcursorinit();
drawcmap();
swcursorinit();
qunlock(&drawlock);
poperror();
return 0;
}
@ -136,8 +120,8 @@ screenaperture(int size, int align)
* Need to allocate some physical address space.
* The driver will tell the card to use it.
*/
size = ROUNDUP(sizeof(size), 4*KiB);
scr->paddr = (uint64_t)jehanne_malloc(size);
size = PGROUND(size);
scr->paddr = upaalloc(size, align);
if(scr->paddr == 0)
return -1;
scr->vaddr = vmap(scr->paddr, size);
@ -148,7 +132,7 @@ screenaperture(int size, int align)
return 0;
}
unsigned char*
uint8_t*
attachscreen(Rectangle* r, uint32_t* chan, int* d, int* width, int *softscreen)
{
VGAscr *scr;
@ -161,63 +145,72 @@ attachscreen(Rectangle* r, uint32_t* chan, int* d, int* width, int *softscreen)
*chan = scr->gscreen->chan;
*d = scr->gscreen->depth;
*width = scr->gscreen->width;
*softscreen = scr->useflush;
if(scr->gscreendata->allocd){
/*
* we use a memimage as softscreen. devdraw will create its own
* screen image on the backing store of that image. when our gscreen
* and devdraws screenimage gets freed, the imagedata will
* be released.
*/
*softscreen = 0xa110c;
scr->gscreendata->ref++;
} else
*softscreen = scr->useflush ? 1 : 0;
return scr->gscreendata->bdata;
}
/*
* It would be fair to say that this doesn't work for >8-bit screens.
*/
void
flushmemscreen(Rectangle r)
{
VGAscr *scr;
unsigned char *sp, *disp, *sdisp, *edisp;
uint8_t *sp, *disp, *sdisp, *edisp;
int y, len, incs, off, page;
scr = &vgascreen[0];
if(scr->gscreen == nil || scr->useflush == 0)
return;
if(rectclip(&r, scr->gscreen->r) == 0)
return;
if(scr->dev && scr->dev->flush){
scr->dev->flush(scr, r);
return;
}
if(scr->gscreen == nil || scr->useflush == 0)
disp = scr->vaddr;
incs = scr->gscreen->width*sizeof(uint32_t);
off = (r.min.x*scr->gscreen->depth) / 8;
len = (r.max.x*scr->gscreen->depth + 7) / 8;
len -= off;
off += r.min.y*incs;
sp = scr->gscreendata->bdata + scr->gscreen->zero + off;
/*
* Linear framebuffer with softscreen.
*/
if(scr->paddr){
sdisp = disp+off;
for(y = r.min.y; y < r.max.y; y++) {
memmove(sdisp, sp, len);
sp += incs;
sdisp += incs;
}
return;
}
/*
* Paged framebuffer window.
*/
if(scr->dev == nil || scr->dev->page == nil)
return;
if(rectclip(&r, scr->gscreen->r) == 0)
return;
incs = scr->gscreen->width * BY2WD;
switch(scr->gscreen->depth){
default:
len = 0;
panic("flushmemscreen: depth\n");
break;
case 8:
len = Dx(r);
break;
}
if(len < 1)
return;
off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
page = off/scr->apsize;
off %= scr->apsize;
disp = scr->vaddr;
sdisp = disp+off;
edisp = disp+scr->apsize;
off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
sp = scr->gscreendata->bdata + off;
scr->dev->page(scr, page);
for(y = r.min.y; y < r.max.y; y++) {
if(sdisp + incs < edisp) {
jehanne_memmove(sdisp, sp, len);
memmove(sdisp, sp, len);
sp += incs;
sdisp += incs;
}
@ -226,13 +219,13 @@ flushmemscreen(Rectangle r)
page++;
if(off <= len){
if(off > 0)
jehanne_memmove(sdisp, sp, off);
memmove(sdisp, sp, off);
scr->dev->page(scr, page);
if(len - off > 0)
jehanne_memmove(disp, sp+off, len - off);
memmove(disp, sp+off, len - off);
}
else {
jehanne_memmove(sdisp, sp, len);
memmove(sdisp, sp, len);
scr->dev->page(scr, page);
}
sp += incs;
@ -261,11 +254,11 @@ getcolor(uint32_t p, uint32_t* pr, uint32_t* pg, uint32_t* pb)
}
p &= x;
lock(&cursor.l);
lock(&cursor);
*pr = scr->colormap[p][0];
*pg = scr->colormap[p][1];
*pb = scr->colormap[p][2];
unlock(&cursor.l);
unlock(&cursor);
}
int
@ -277,7 +270,7 @@ setpalette(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
scr = &vgascreen[0];
d = scr->palettedepth;
lock(&cursor.l);
lock(&cursor);
scr->colormap[p][0] = r;
scr->colormap[p][1] = g;
scr->colormap[p][2] = b;
@ -285,7 +278,7 @@ setpalette(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
vgao(Pdata, r>>(32-d));
vgao(Pdata, g>>(32-d));
vgao(Pdata, b>>(32-d));
unlock(&cursor.l);
unlock(&cursor);
return ~0;
}
@ -322,27 +315,71 @@ setcolor(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
return setpalette(p, r, g, b);
}
int
cursoron(int dolock)
void
swenable(VGAscr* _)
{
VGAscr *scr;
int v;
scr = &vgascreen[0];
if(scr->cur == nil || scr->cur->move == nil)
return 0;
if(dolock)
lock(&cursor.l);
v = scr->cur->move(scr, mousexy());
if(dolock)
unlock(&cursor.l);
return v;
swcursorload(&arrow);
}
void
cursoroff(int i)
swdisable(VGAscr* _)
{
}
void
swload(VGAscr* _, Cursor *curs)
{
swcursorload(curs);
}
int
swmove(VGAscr* _, Point p)
{
swcursorhide();
swcursordraw(p);
return 0;
}
VGAcur swcursor =
{
"soft",
swenable,
swdisable,
swload,
swmove,
};
VGAcur vgavesacur =
{
"soft",
swenable,
swdisable,
swload,
swmove,
};
void
cursoron(void)
{
VGAscr *scr;
VGAcur *cur;
scr = &vgascreen[0];
cur = scr->cur;
if(cur == nil || cur->move == nil)
return;
if(cur == &swcursor)
qlock(&drawlock);
lock(&cursor);
cur->move(scr, mousexy());
unlock(&cursor);
if(cur == &swcursor)
qunlock(&drawlock);
}
void
cursoroff(void)
{
}
@ -358,8 +395,8 @@ setcursor(Cursor* curs)
scr->cur->load(scr, curs);
}
int hwaccel = 1;
int hwblank = 0; /* turned on by drivers that are known good */
int hwaccel = 0;
int hwblank = 0;
int panning = 0;
int
@ -367,37 +404,30 @@ hwdraw(Memdrawparam *par)
{
VGAscr *scr;
Memimage *dst, *src, *mask;
Memdata *scrd;
int m;
if(hwaccel == 0)
return 0;
scr = &vgascreen[0];
scrd = scr->gscreendata;
if(scr->gscreen == nil || scrd == nil)
return 0;
if((dst = par->dst) == nil || dst->data == nil)
return 0;
if((src=par->src) == nil || src->data == nil)
return 0;
if((mask=par->mask) == nil || mask->data == nil)
return 0;
if((src = par->src) && src->data == nil)
src = nil;
if((mask = par->mask) && mask->data == nil)
mask = nil;
if(scr->cur == &swcursor){
/*
* always calling swcursorhide here doesn't cure
* leaving cursor tracks nor failing to refresh menus
* with the latest libmemdraw/draw.c.
*/
if(dst->data->bdata == gscreendata.bdata)
if(dst->data->bdata == scrd->bdata)
swcursoravoid(par->r);
if(src->data->bdata == gscreendata.bdata)
if(src && src->data->bdata == scrd->bdata)
swcursoravoid(par->sr);
if(mask->data->bdata == gscreendata.bdata)
if(mask && mask->data->bdata == scrd->bdata)
swcursoravoid(par->mr);
}
if(dst->data->bdata != gscreendata.bdata)
if(!hwaccel || scr->softscreen)
return 0;
if(scr->fill==nil && scr->scroll==nil)
if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
return 0;
/*
@ -442,36 +472,62 @@ blankscreen(int blank)
}
}
void
vgalinearpciid(VGAscr *scr, int vid, int did)
static char*
vgalinearaddr0(VGAscr *scr, uint32_t paddr, int size)
{
Pcidev *p;
int x, nsize;
uint32_t npaddr;
p = nil;
while((p = pcimatch(p, vid, 0)) != nil){
if(p->ccrb != 3) /* video card */
continue;
if(did != 0 && p->did != did)
continue;
break;
}
if(p == nil)
error("pci video card not found");
/*
* new approach. instead of trying to resize this
* later, let's assume that we can just allocate the
* entire window to start with.
*/
if(scr->paddr == paddr && size <= scr->apsize)
return nil;
scr->pci = p;
vgalinearpci(scr);
if(scr->paddr){
/*
* could call vunmap and vmap,
* but worried about dangling pointers in devdraw
*/
return "cannot grow vga frame buffer";
}
void
vgalinearpci(VGAscr *scr)
/* round to page boundary, just in case */
x = paddr&(BY2PG-1);
npaddr = paddr-x;
nsize = PGROUND(size+x);
/*
* Don't bother trying to map more than 4000x4000x32 = 64MB.
* We only have a 256MB window.
*/
if(nsize > 64*MB)
nsize = 64*MB;
scr->vaddr = vmap(npaddr, nsize);
if(scr->vaddr == 0)
return "cannot allocate vga frame buffer";
patwc(scr->vaddr, nsize);
scr->vaddr = (char*)scr->vaddr+x;
scr->paddr = paddr;
scr->apsize = nsize;
mtrr(npaddr, nsize, "wc");
return nil;
}
static char*
vgalinearpci0(VGAscr *scr)
{
uint32_t paddr;
int i, size, best;
Pcidev *p;
p = scr->pci;
if(p == nil)
return;
/*
* Scan for largest memory region on card.
@ -500,261 +556,182 @@ vgalinearpci(VGAscr *scr)
if(best >= 0){
paddr = p->mem[best].bar & ~0x0F;
size = p->mem[best].size;
vgalinearaddr(scr, paddr, size);
return;
return vgalinearaddr0(scr, paddr, size);
}
error("no video memory found on pci card");
return "no video memory found on pci card";
}
void
vgalinearpci(VGAscr *scr)
{
char *err;
if(scr->pci == nil)
return;
if((err = vgalinearpci0(scr)) != nil)
error(err);
}
void
vgalinearaddr(VGAscr *scr, uint32_t paddr, int size)
{
int x, nsize;
uint32_t npaddr;
char *err;
/*
* new approach. instead of trying to resize this
* later, let's assume that we can just allocate the
* entire window to start with.
*/
if(scr->paddr == paddr && size <= scr->apsize)
return;
if(scr->paddr){
/*
* could call vunmap and vmap,
* but worried about dangling pointers in devdraw
*/
error("cannot grow vga frame buffer");
if((err = vgalinearaddr0(scr, paddr, size)) != nil)
error(err);
}
/* round to page boundary, just in case */
x = paddr&(4*KiB-1);
npaddr = paddr-x;
nsize = ROUNDUP(size+x, 4*KiB);
/*
* Don't bother trying to map more than 4000x4000x32 = 64MB.
* We only have a 256MB window.
*/
if(nsize > 64*MB)
nsize = 64*MB;
scr->vaddr = vmap(npaddr, nsize);
if(scr->vaddr == 0)
error("cannot allocate vga frame buffer");
scr->vaddr = (char*)scr->vaddr+x;
scr->paddr = paddr;
scr->apsize = nsize;
}
/*
* Software cursor.
*/
int swvisible; /* is the cursor visible? */
int swenabled; /* is the cursor supposed to be on the screen? */
Memimage* swback; /* screen under cursor */
Memimage* swimg; /* cursor image */
Memimage* swmask; /* cursor mask */
Memimage* swimg1;
Memimage* swmask1;
Point swoffset;
Rectangle swrect; /* screen rectangle in swback */
Point swpt; /* desired cursor location */
Point swvispt; /* actual cursor location */
int swvers; /* incremented each time cursor image changes */
int swvisvers; /* the version on the screen */
/*
* called with drawlock locked for us, most of the time.
* kernel prints at inopportune times might mean we don't
* hold the lock, but memimagedraw is now reentrant so
* that should be okay: worst case we get cursor droppings.
*/
void
swcursorhide(void)
static char*
bootmapfb(VGAscr *scr, uint32_t pa, uint32_t sz)
{
if(swvisible == 0)
return;
if(swback == nil)
return;
swvisible = 0;
memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
flushmemscreen(swrect);
uint32_t start, end;
Pcidev *p;
int i;
for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
for(i=0; i<nelem(p->mem); i++){
if(p->mem[i].bar & 1)
continue;
start = p->mem[i].bar & ~0xF;
end = start + p->mem[i].size;
if(pa == start && (pa + sz) <= end){
scr->pci = p;
return vgalinearpci0(scr);
}
}
}
return vgalinearaddr0(scr, pa, sz);
}
void
swcursoravoid(Rectangle r)
char*
rgbmask2chan(char *buf, int depth, uint32_t rm, uint32_t gm, uint32_t bm)
{
if(swvisible && rectXrect(r, swrect))
swcursorhide();
}
uint32_t m[4], dm; /* r,g,b,x */
char tmp[32];
int c, n;
void
swcursordraw(void)
{
if(swvisible)
return;
if(swenabled == 0)
return;
if(swback == nil || swimg1 == nil || swmask1 == nil)
return;
assert(!canqlock(&drawlock));
swvispt = swpt;
swvisvers = swvers;
swrect = rectaddpt(Rect(0,0,16,16), swvispt);
memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
flushmemscreen(swrect);
swvisible = 1;
dm = 1<<depth-1;
dm |= dm-1;
m[0] = rm & dm;
m[1] = gm & dm;
m[2] = bm & dm;
m[3] = (~(m[0] | m[1] | m[2])) & dm;
buf[0] = 0;
Next:
for(c=0; c<4; c++){
for(n = 0; m[c] & (1<<n); n++)
;
if(n){
m[0] >>= n, m[1] >>= n, m[2] >>= n, m[3] >>= n;
snprint(tmp, sizeof tmp, "%c%d%s", "rgbx"[c], n, buf);
strcpy(buf, tmp);
goto Next;
}
}
return buf;
}
/*
* Need to lock drawlock for ourselves.
* called early on boot to attach to framebuffer
* setup by bootloader/firmware or plan9.
*/
void
swenable(VGAscr *v)
bootscreeninit(void)
{
swenabled = 1;
if(canqlock(&drawlock)){
swcursordraw();
qunlock(&drawlock);
}
}
void
swdisable(VGAscr *v)
{
swenabled = 0;
if(canqlock(&drawlock)){
swcursorhide();
qunlock(&drawlock);
}
}
void
swload(VGAscr *v, Cursor *curs)
{
unsigned char *ip, *mp;
int i, j, set, clr;
if(!swimg || !swmask || !swimg1 || !swmask1)
return;
/*
* Build cursor image and mask.
* Image is just the usual cursor image
* but mask is a transparent alpha mask.
*
* The 16x16x8 memimages do not have
* padding at the end of their scan lines.
*/
ip = byteaddr(swimg, ZP);
mp = byteaddr(swmask, ZP);
for(i=0; i<32; i++){
set = curs->set[i];
clr = curs->clr[i];
for(j=0x80; j; j>>=1){
*ip++ = set&j ? 0x00 : 0xFF;
*mp++ = (clr|set)&j ? 0xFF : 0x00;
}
}
swoffset = curs->offset;
swvers++;
memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
}
int
swmove(VGAscr *v, Point p)
{
swpt = addpt(p, swoffset);
return 0;
}
void
swcursorclock(void)
{
int x;
if(!swenabled)
return;
if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
return;
x = splhi();
if(swenabled)
if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
if(canqlock(&drawlock)){
swcursorhide();
swcursordraw();
qunlock(&drawlock);
}
splx(x);
}
void
swcursorinit(void)
{
static int init, warned;
static Memdata md;
VGAscr *scr;
didswcursorinit = 1;
int x, y, z;
uint32_t chan, pa, sz;
char *s, *p, *err;
/* *bootscreen=WIDTHxHEIGHTxDEPTH CHAN PA [SZ] */
s = getconf("*bootscreen");
if(s == nil)
return;
x = strtoul(s, &s, 0);
if(x == 0 || *s++ != 'x')
return;
y = strtoul(s, &s, 0);
if(y == 0 || *s++ != 'x')
return;
z = strtoul(s, &s, 0);
if(*s != ' ')
return;
if((p = strchr(++s, ' ')) == nil)
return;
*p = 0;
chan = strtochan(s);
*p = ' ';
if(chan == 0 || chantodepth(chan) != z)
return;
sz = 0;
pa = strtoul(p+1, &s, 0);
if(pa == 0)
return;
if(*s++ == ' ')
sz = strtoul(s, nil, 0);
if(sz < x * y * (z+7)/8)
sz = x * y * (z+7)/8;
/* map framebuffer */
scr = &vgascreen[0];
if(scr==nil || scr->gscreen==nil)
return;
if(scr->dev == nil || scr->dev->linear == nil){
if(!warned){
jehanne_print("cannot use software cursor on non-linear vga screen\n");
warned = 1;
}
if((err = bootmapfb(scr, pa, sz)) != nil){
print("bootmapfb: %s\n", err);
return;
}
if(swback){
freememimage(swback);
freememimage(swmask);
freememimage(swmask1);
freememimage(swimg);
freememimage(swimg1);
}
swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
swmask = allocmemimage(Rect(0,0,16,16), GREY8);
swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
swimg = allocmemimage(Rect(0,0,16,16), GREY8);
swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
jehanne_print("software cursor: allocmemimage fails");
if(memimageinit() < 0)
return;
md.ref = 1;
md.bdata = scr->vaddr;
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
if(gscreen == nil)
return;
scr->palettedepth = 6; /* default */
scr->memdefont = getmemdefont();
scr->gscreen = gscreen;
scr->gscreendata = gscreen->data;
scr->softscreen = 0;
scr->useflush = 0;
scr->dev = nil;
physgscreenr = gscreen->r;
vgaimageinit(chan);
vgascreenwin(scr);
/* turn mouse cursor on */
swcursorinit();
scr->cur = &swcursor;
scr->cur->enable(scr);
cursoron();
sys->monitor = 1;
}
memfillcolor(swmask, DOpaque);
memfillcolor(swmask1, DOpaque);
memfillcolor(swimg, DBlack);
memfillcolor(swimg1, DBlack);
if(!init){
init = 1;
addclock0link(swcursorclock, 10);
}
}
VGAcur swcursor =
/*
* called from devvga when the framebuffer is setup
* to set *bootscreen= that can be passed on to a
* new kernel on reboot.
*/
void
bootscreenconf(VGAscr *scr)
{
"soft",
swenable,
swdisable,
swload,
swmove,
};
// A bit hokey but it saves dumbness in the build tool and other code.
VGAcur vgavesacur =
{
"vesa",
swenable,
swdisable,
swload,
swmove,
};
char conf[100], chan[30];
conf[0] = '\0';
if(scr != nil && scr->paddr != 0 && scr->gscreen != nil)
snprint(conf, sizeof(conf), "%dx%dx%d %s %#p %d\n",
scr->gscreen->r.max.x, scr->gscreen->r.max.y,
scr->gscreen->depth, chantostr(chan, scr->gscreen->chan),
scr->paddr, scr->apsize);
ksetenv("*bootscreen", conf, 1);
}

View File

@ -1,21 +1,13 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
typedef struct Cursor Cursor;
typedef struct Cursorinfo Cursorinfo;
struct Cursorinfo {
Cursor c;
Lock l;
Cursor;
Lock;
};
/* devmouse.c */
extern void mousetrack(int, int, int, int);
extern void mousetrack(int, int, int, uint32_t);
extern void absmousetrack(int, int, int, uint32_t);
extern Point mousexy(void);
extern void mouseaccelerate(int);
@ -61,8 +53,8 @@ enum {
#define vgai(port) inb(port)
#define vgao(port, data) outb(port, data)
extern int vgaxi(int32_t, unsigned char);
extern int vgaxo(int32_t, unsigned char, unsigned char);
extern int vgaxi(int32_t port, uint8_t index);
extern int vgaxo(int32_t port, uint8_t index, uint8_t data);
/*
*/
@ -103,12 +95,12 @@ struct VGAscr {
Pcidev* pci;
VGAcur* cur;
uint32_t storage;
Cursor Cursor;
uintptr_t storage;
Cursor;
int useflush;
uint32_t paddr; /* frame buffer */
uintptr_t paddr; /* frame buffer */
void* vaddr;
int apsize;
@ -126,8 +118,8 @@ struct VGAscr {
int (*scroll)(VGAscr*, Rectangle, Rectangle);
void (*blank)(VGAscr*, int);
uint32_t id; /* internal identifier for driver use */
int isblank;
int overlayinit;
int softscreen;
};
extern VGAscr vgascreen[];
@ -139,21 +131,26 @@ enum {
/* mouse.c */
extern void mousectl(Cmdbuf*);
extern void mouseresize(void);
extern void mouseredraw(void);
/* screen.c */
extern int hwaccel; /* use hw acceleration; default on */
extern int hwblank; /* use hw blanking; default on */
extern int panning; /* use virtual screen panning; default off */
extern int hwaccel; /* use hw acceleration */
extern int hwblank; /* use hw blanking */
extern int panning; /* use virtual screen panning */
extern void addvgaseg(char*, uint32_t, uint32_t);
extern unsigned char* attachscreen(Rectangle*, uint32_t*, int*, int*, int*);
extern uint8_t* attachscreen(Rectangle*, uint32_t*, int*, int*, int*);
extern void flushmemscreen(Rectangle);
extern int cursoron(int);
extern void cursoroff(int);
extern void cursoron(void);
extern void cursoroff(void);
extern void setcursor(Cursor*);
extern int screensize(int, int, int, uint32_t);
extern int screenaperture(int, int);
extern Rectangle physgscreenr; /* actual monitor size */
extern void blankscreen(int);
extern char* rgbmask2chan(char *buf, int depth, uint32_t rm, uint32_t gm, uint32_t bm);
extern void bootscreeninit(void);
extern void bootscreenconf(VGAscr*);
extern VGAcur swcursor;
extern void swcursorinit(void);
@ -165,22 +162,25 @@ extern void swcursorunhide(void);
extern void deletescreenimage(void);
extern void resetscreenimage(void);
extern int drawhasclients(void);
extern uint32_t blanktime;
extern void setscreenimageclipr(Rectangle);
extern void drawflush(void);
extern int drawidletime(void);
extern QLock drawlock;
/* vga.c */
extern void vgascreenwin(VGAscr*);
extern void vgaimageinit(uint32_t);
extern void vgalinearpciid(VGAscr*, int, int);
extern void vgalinearpci(VGAscr*);
extern void vgalinearaddr(VGAscr*, uint32_t, int);
extern void drawblankscreen(int);
extern void vgablank(VGAscr*, int);
extern Lock vgascreenlock;
#define ishwimage(i) (vgascreen[0].gscreendata && (i)->data->bdata == vgascreen[0].gscreendata->bdata)
/* swcursor.c */
void swcursorhide(void);
void swcursoravoid(Rectangle);
void swcursordraw(Point);
void swcursorload(Cursor *);
void swcursorinit(void);

View File

@ -11,7 +11,7 @@
#include "io.h"
#include "../port/error.h"
#include "../port/sd.h"
#include "../386/fis.h"
#include "fis.h"
#include "../port/sdfis.h"
#include "ahci.h"
#include "../port/led.h"

View File

@ -2185,7 +2185,7 @@ atadisable(SDev *sdev)
if (ctlr->idisable)
ctlr->idisable(ctlr);
jehanne_snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
intrdisable(ctlr->vector);
intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
if (ctlr->bmiba) {
if (ctlr->pcidev)
pciclrbme(ctlr->pcidev);

View File

@ -1471,7 +1471,7 @@ iadisable(SDev *s)
ilock(c);
ahcidisable(c->hba);
jehanne_snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
intrdisable(c->vector);
intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
c->enabled = 0;
iunlock(c);
return 1;

View File

@ -475,4 +475,3 @@ again:
return rlen;
}

View File

@ -1,122 +0,0 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "apic.h"
#include "sipi.h"
#define SIPIHANDLER (KZERO+0x3000)
/*
* Parameters are passed to the bootstrap code via a vector
* in low memory indexed by the APIC number of the processor.
* The layout, size, and location have to be kept in sync
* with the handler code in l64sipi.s.
*/
typedef struct Sipi Sipi;
struct Sipi {
uint32_t pml4;
uint32_t _4_;
uintptr_t stack;
Mach* mach;
uintptr_t pc;
};
void
sipi(void)
{
Lapic *apic;
Mach *mach;
Sipi *sipi;
int apicno, i, nproc;
uint8_t *sipiptr;
uintmem sipipa;
uint8_t *alloc, *p;
extern void squidboy(int);
/*
* Move the startup code into place,
* must be aligned properly.
*/
sipipa = mmuphysaddr(SIPIHANDLER);
if((sipipa & (4*KiB - 1)) || sipipa > (1*MiB - 2*4*KiB))
return;
sipiptr = UINT2PTR(SIPIHANDLER);
jehanne_memmove(sipiptr, sipihandler, sizeof(sipihandler));
jehanne_memset(sipiptr+4*KiB, 0, sizeof(Sipi)*Napic);
/*
* Notes:
* The Universal Startup Algorithm described in the MP Spec. 1.4.
* The data needed per-processor is the sum of the stack, page
* table pages, vsvm page and the Mach page. The layout is similar
* to that described in data.h for the bootstrap processor, but
* with any unused space elided.
*/
nproc = 0;
for(apicno = 0; apicno < Napic; apicno++){
apic = lapiclookup(apicno);
if(apic == nil || !apic->useable || apic->machno == 0)
continue;
if(++nproc >= MACHMAX){
jehanne_print("sipi: MACHMAX too small, need %d\n", nproc);
break;
}
sipi = &((Sipi*)(sipiptr+4*KiB))[apicno];
/*
* NOTE: for now, share the page tables with the
* bootstrap processor, until this code is worked out,
* so only the Mach and stack portions are used below.
*/
alloc = jehanne_mallocalign(MACHSTKSZ+4*PTSZ+4*KiB+MACHSZ, 4096, 0, 0);
if(alloc == nil)
continue;
jehanne_memset(alloc, 0, MACHSTKSZ+4*PTSZ+4*KiB+MACHSZ);
p = alloc+MACHSTKSZ;
sipi->pml4 = cr3get();
sipi->stack = PTR2UINT(p);
p += 4*PTSZ+4*KiB;
/*
* Committed. If the AP startup fails, can't safely
* release the resources, who knows what mischief
* the AP is up to. Perhaps should try to put it
* back into the INIT state?
*/
mach = (Mach*)p;
sipi->mach = mach;
mach->machno = apic->machno; /* NOT one-to-one... */
mach->splpc = PTR2UINT(squidboy);
sipi->pc = mach->splpc;
mach->apicno = apicno;
mach->stack = PTR2UINT(alloc);
mach->vsvm = alloc+MACHSTKSZ+4*PTSZ;
mach->pml4 = m->pml4;
p = KADDR(0x467);
*p++ = sipipa;
*p++ = sipipa>>8;
*p++ = 0;
*p = 0;
nvramwrite(0x0f, 0x0a);
lapicsipi(apicno, sipipa);
for(i = 0; i < 1000; i++){
if(mach->splpc == 0)
break;
millidelay(5);
}
nvramwrite(0x0f, 0x00);
DBG("apicno%d: machno %d mach %#p (%#p) %dMHz\n",
apicno, mach->machno,
mach, sys->machptr[mach->machno],
mach->cpumhz);
}
}

View File

@ -0,0 +1,107 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "mp.h"
extern void initialize_processor(void);
static void
squidboy(Apic* apic)
{
initialize_processor();
mmuinit();
cpuidentify();
cpuidprint();
syncclock();
active.machs[m->machno] = 1;
apic->online = 1;
m->online = 1;
lapicinit(apic);
lapiconline();
timersinit();
schedinit();
}
void
mpstartap(Apic* apic)
{
uintptr_t *apbootp, *pml4, *pdp0;
Segdesc *gdt;
Mach *mach;
uint8_t *p;
int i;
/*
* Initialise the AP page-tables and Mach structure.
* Xspanalloc will panic if an allocation can't be made.
*/
p = xspanalloc(2*PTSZ + BY2PG + MACHSIZE, BY2PG, 0);
pml4 = (uintptr*)p;
p += PTSZ;
pdp0 = (uintptr*)p;
p += PTSZ;
gdt = (Segdesc*)p;
p += BY2PG;
mach = (Mach*)p;
memset(pml4, 0, PTSZ);
memset(pdp0, 0, PTSZ);
memset(gdt, 0, BY2PG);
memset(mach, 0, MACHSIZE);
mach->machno = apic->machno;
mach->pml4 = pml4;
mach->gdt = gdt; /* filled by mmuinit */
MACHP(mach->machno) = mach;
/*
* map KZERO (note that we share the KZERO (and VMAP)
* PDP between processors.
*/
pml4[PTLX(KZERO, 3)] = MACHP(0)->pml4[PTLX(KZERO, 3)];
pml4[PTLX(VMAP, 3)] = MACHP(0)->pml4[PTLX(VMAP, 3)];
/* double map */
pml4[0] = PADDR(pdp0) | PTEWRITE|PTEVALID;
pdp0[0] = *mmuwalk(pml4, KZERO, 2, 0);
/*
* Tell the AP where its kernel vector and pdb are.
* The offsets are known in the AP bootstrap code.
*/
apbootp = (uintptr*)(APBOOTSTRAP+0x08);
apbootp[0] = (uintptr)squidboy; /* assembler jumps here eventually */
apbootp[1] = (uintptr)PADDR(pml4);
apbootp[2] = (uintptr)apic;
apbootp[3] = (uintptr)mach;
/*
* Universal Startup Algorithm.
*/
p = KADDR(0x467); /* warm-reset vector */
*p++ = PADDR(APBOOTSTRAP);
*p++ = PADDR(APBOOTSTRAP)>>8;
i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;
/* code assumes i==0 */
if(i != 0)
print("mp: bad APBOOTSTRAP\n");
*p++ = i;
*p = i>>8;
coherence();
nvramwrite(0x0F, 0x0A); /* shutdown code: warm reset upon init ipi */
lapicstartap(apic, PADDR(APBOOTSTRAP));
for(i = 0; i < 100000; i++){
if(arch->fastclock == tscticks)
cycles(&m->tscticks); /* for ap's syncclock(); */
if(apic->online)
break;
delay(1);
}
nvramwrite(0x0F, 0x00);
}

View File

@ -46,7 +46,6 @@ static void
noted(Ureg* cur, uintptr_t arg0)
{
NFrame *nf;
Note note;
Ureg *nur;
qlock(&up->debug);
@ -55,11 +54,13 @@ noted(Ureg* cur, uintptr_t arg0)
pprint("suicide: call to noted when not notified\n");
pexit("Suicide", 0);
}
awake_gc_note(up);
up->notified = 0;
fpunoted();
nf = up->ureg;
up->fpstate &= ~FPillegal;
/* sanity clause */
if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){
qunlock(&up->debug);
@ -115,17 +116,13 @@ noted(Ureg* cur, uintptr_t arg0)
cur->sp = PTR2UINT(nf);
break;
default:
jehanne_memmove(&note, &up->lastnote, sizeof(Note));
qunlock(&up->debug);
pprint("suicide: bad arg %#p in noted: %s\n", arg0, note.msg);
pexit(note.msg, 0);
break;
up->lastnote.flag = NDebug;
/* fall through */
case NDFLT:
jehanne_memmove(&note, &up->lastnote, sizeof(Note));
qunlock(&up->debug);
if(note.flag == NDebug)
pprint("suicide: %s\n", note.msg);
pexit(note.msg, note.flag != NDebug);
if(up->lastnote.flag == NDebug)
pprint("suicide: %s\n", up->lastnote.msg);
pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
break;
}
}
@ -148,7 +145,11 @@ notify(Ureg* ureg)
if(up->nnote == 0)
return 0;
fpunotify(ureg);
if(up->fpstate == FPactive){
fpsave(&up->fpsave);
up->fpstate = FPinactive;
}
up->fpstate |= FPillegal;
s = spllo();
qlock(&up->debug);
@ -238,7 +239,6 @@ syscall(Syscalls scallnr, Ureg* ureg)
m->syscall++;
up->inkernel = 1;
up->cursyscall = (Syscalls)scallnr;
up->blockingsc = 0;
up->pc = ureg->ip;
up->dbgreg = ureg;
if(up->trace && (pt = proctrace) != nil)
@ -250,8 +250,6 @@ syscall(Syscalls scallnr, Ureg* ureg)
}
up->scallnr = scallnr;
if(scallnr == SysRfork)
fpusysrfork(ureg);
spllo();
startns = 0;
@ -355,13 +353,8 @@ syscall(Syscalls scallnr, Ureg* ureg)
splhi();
if(scallnr != SysRfork && (up->procctl || up->nnote))
notify(ureg);
else if(up->blockingsc){
if(up->blockingsc != scallnr)
panic("syscall: up->blockingsc dirty");
if(canwakeup(scallnr))
awokeproc(up);
up->blockingsc = 0;
}
else
awake_awakened(up); // we are not sleeping after all!
/* if we delayed sched because we held a lock, sched now */
if(up->delaysched){
@ -412,7 +405,9 @@ sysexecregs(uintptr_t entry, uint32_t ssize)
ureg = up->dbgreg;
ureg->sp = PTR2UINT(sp);
ureg->ip = entry;
ureg->type = 64; /* fiction for acid */
ureg->cs = UESEL;
ureg->ss = UDSEL;
ureg->r14 = ureg->r15 = 0; /* extern user registers */
ureg->r12 = up->pid;
/*
@ -425,7 +420,9 @@ sysexecregs(uintptr_t entry, uint32_t ssize)
void
sysprocsetup(Proc* p)
{
fpusysprocsetup(p);
fpprocsetup(p);
cycles(&p->kentry);
p->pcycles = -p->kentry;
}
void
@ -448,9 +445,9 @@ sysrforkchild(Proc* child, Proc* parent)
cureg = (Ureg*)(child->sched.sp+STACKPAD*BY2SE);
jehanne_memmove(cureg, parent->dbgreg, sizeof(Ureg));
cureg->ax = 0;
/* Things from bottom of syscall which were never executed */
child->psstate = 0;
child->inkernel = 0;
fpusysrforkchild(child, parent);
}

View File

@ -30,6 +30,8 @@
#include "amd64.h"
static int trapinited;
extern int notify(Ureg*);
static void debugbpt(Ureg*, void*);
@ -54,12 +56,19 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
Vctl *v;
if(f == nil){
jehanne_print("intrenable: nil handler for %d, tbdf %#ux for %s\n",
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
irq, tbdf, name);
return nil;
}
v = jehanne_malloc(sizeof(Vctl));
if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0)){
print("intrenable: got unassigned irq %d, tbdf 0x%uX for %s\n",
irq, tbdf, name);
irq = -1;
}
if((v = xalloc(sizeof(Vctl))) == nil)
panic("intrenable: out of memory");
v->isintr = 1;
v->irq = irq;
v->tbdf = tbdf;
@ -69,28 +78,24 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
v->name[KNAMELEN-1] = 0;
ilock(&vctllock);
vno = ioapicintrenable(v);
vno = arch->intrenable(v);
if(vno == -1){
iunlock(&vctllock);
jehanne_print("intrenable: couldn't enable irq %d, tbdf %#ux for %s\n",
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
irq, tbdf, v->name);
jehanne_free(v);
xfree(v);
return nil;
}
if(vctl[vno]){
if(vctl[v->vno]->isr != v->isr || vctl[v->vno]->eoi != v->eoi)
if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
panic("intrenable: handler: %s %s %#p %#p %#p %#p",
vctl[v->vno]->name, v->name,
vctl[v->vno]->isr, v->isr, vctl[v->vno]->eoi, v->eoi);
}
v->vno = vno;
vctl[vno]->name, v->name,
vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
v->next = vctl[vno];
}
vctl[vno] = v;
iunlock(&vctllock);
if(v->mask != nil)
v->mask(v, 0);
/*
* Return the assigned vector so intrdisable can find
* the handler; the IRQ is useless in the wondrefule world
@ -100,26 +105,42 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
}
int
intrdisable(void* vector)
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
{
Vctl *v, **vl;
Vctl **pv, *v;
int vno;
if(arch->intrvecno == nil || (tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))){
/*
* on APIC machine, irq is pretty meaningless
* and disabling a the vector is not implemented.
* however, we still want to remove the matching
* Vctl entry to prevent calling Vctl.f() with a
* stale Vctl.a pointer.
*/
irq = -1;
vno = VectorPIC;
} else {
vno = arch->intrvecno(irq);
}
ilock(&vctllock);
v = vector;
for(vl = &vctl[v->vno]; *vl != nil; vl = &(*vl)->next)
if(*vl == v)
do {
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
if(v->isintr && (v->irq == irq || irq == -1)
&& v->tbdf == tbdf && v->f == f && v->a == a
&& strcmp(v->name, name) == 0)
break;
if(*vl == nil)
panic("intrdisable: v %#p", v);
if(v->mask != nil)
v->mask(v, 1);
v->f(nil, v->a);
*vl = v->next;
ioapicintrdisable(v->vno);
}
if(v != nil){
*pv = v->next;
xfree(v);
if(irq != -1 && vctl[vno] == nil && arch->intrdisable != nil)
arch->intrdisable(irq);
break;
}
} while(irq == -1 && ++vno <= MaxVectorAPIC);
iunlock(&vctllock);
jehanne_free(v);
return 0;
}
@ -167,9 +188,10 @@ trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
{
Vctl *v;
if(vno < 0 || vno >= 256)
panic("trapenable: vno %d\n", vno);
v = jehanne_malloc(sizeof(Vctl));
if(vno < 0 || vno >= VectorPIC)
panic("trapenable: vno %d", vno);
if((v = xalloc(sizeof(Vctl))) == nil)
panic("trapenable: out of memory");
v->tbdf = BUSUNKNOWN;
v->f = f;
v->a = a;
@ -177,7 +199,8 @@ trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
v->name[KNAMELEN-1] = 0;
ilock(&vctllock);
v->next = vctl[vno];
if(vctl[vno])
v->next = vctl[vno]->next;
vctl[vno] = v;
iunlock(&vctllock);
}
@ -194,10 +217,48 @@ nmienable(void)
outb(0x70, 0);
x = inb(0x61) & 0x07; /* Enable NMI */
outb(0x61, 0x08|x);
outb(0x61, 0x0C|x);
outb(0x61, x);
}
void
trapinit0(void)
{
uint32_t d1, v;
uintptr_t vaddr;
Segdesc *idt;
idt = (Segdesc*)IDTADDR;
vaddr = (uintptr_t)idthandlers;
for(v = 0; v < 256; v++){
d1 = (vaddr & 0xFFFF0000)|SEGP;
switch(v){
case VectorBPT:
d1 |= SEGPL(3)|SEGIG;
break;
case VectorSYSCALL:
d1 |= SEGPL(3)|SEGIG;
break;
default:
d1 |= SEGPL(0)|SEGIG;
break;
}
idt->d0 = (vaddr & 0xFFFF)|(KESEL<<16);
idt->d1 = d1;
idt++;
idt->d0 = (vaddr >> 32);
idt->d1 = 0;
idt++;
vaddr += 6;
}
}
void
trapinit(void)
{
@ -208,48 +269,50 @@ trapinit(void)
* Special traps.
* Syscall() is called directly without going through trap().
*/
trapenable(IdtBP, debugbpt, 0, "#BP");
trapenable(IdtPF, faultamd64, 0, "#PF");
trapenable(IdtDF, doublefault, 0, "#DF");
trapenable(Idt0F, unexpected, 0, "#15");
// trapenable(VectorDE, debugexc, 0, "debugexc");
trapenable(VectorBPT, debugbpt, 0, "debugpt");
trapenable(VectorPF, faultamd64, 0, "faultamd64");
trapenable(Vector2F, doublefault, 0, "doublefault");
trapenable(Vector15, unexpected, 0, "unexpected");
nmienable();
addarchfile("irqalloc", 0444, irqallocread, nil);
trapinited = 1;
}
static char* excname[32] = {
"#DE", /* Divide-by-Zero Error */
"#DB", /* Debug */
"#NMI", /* Non-Maskable-Interrupt */
"#BP", /* Breakpoint */
"#OF", /* Overflow */
"#BR", /* Bound-Range */
"#UD", /* Invalid-Opcode */
"#NM", /* Device-Not-Available */
"#DF", /* Double-Fault */
"#9 (reserved)",
"#TS", /* Invalid-TSS */
"#NP", /* Segment-Not-Present */
"#SS", /* Stack */
"#GP", /* General-Protection */
"#PF", /* Page-Fault */
"#15 (reserved)",
"#MF", /* x87 FPE-Pending */
"#AC", /* Alignment-Check */
"#MC", /* Machine-Check */
"#XF", /* SIMD Floating-Point */
"#20 (reserved)",
"#21 (reserved)",
"#22 (reserved)",
"#23 (reserved)",
"#24 (reserved)",
"#25 (reserved)",
"#26 (reserved)",
"#27 (reserved)",
"#28 (reserved)",
"#29 (reserved)",
"#30 (reserved)",
"#31 (reserved)",
"divide error",
"debug exception",
"nonmaskable interrupt",
"breakpoint",
"overflow",
"bounds check",
"invalid opcode",
"coprocessor not available",
"double fault",
"coprocessor segment overrun",
"invalid TSS",
"segment not present",
"stack exception",
"general protection violation",
"page fault",
"15 (reserved)",
"coprocessor error",
"alignment check",
"machine check",
"simd error",
"20 (reserved)",
"21 (reserved)",
"22 (reserved)",
"23 (reserved)",
"24 (reserved)",
"25 (reserved)",
"26 (reserved)",
"27 (reserved)",
"28 (reserved)",
"29 (reserved)",
"30 (reserved)",
"31 (reserved)",
};
/*
@ -295,6 +358,13 @@ trap(Ureg* ureg)
char buf[ERRMAX];
Vctl *ctl, *v;
if(!trapinited){
/* faultamd64 can give a better error message */
if(ureg->type == VectorPF)
faultamd64(ureg, nil);
panic("trap %llud: not ready", ureg->type);
}
m->perf.intrts = perfticks();
user = userureg(ureg);
if(user){
@ -305,10 +375,11 @@ trap(Ureg* ureg)
clockintr = 0;
vno = ureg->type;
if(ctl = vctl[vno]){
if(ctl->isintr){
m->intr++;
if(vno >= IdtPIC && vno != IdtSYSCALL)
if(vno >= VectorPIC)
m->lastintr = ctl->irq;
}
if(ctl->isr)
@ -323,10 +394,8 @@ trap(Ureg* ureg)
if(ctl->isintr){
intrtime(m, vno);
if(ctl->irq == IdtPIC+IrqCLOCK || ctl->irq == IdtTIMER){
checkflushmmu();
if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
clockintr = 1;
}
if(up && !clockintr)
preempted();
@ -373,33 +442,37 @@ trap(Ureg* ureg)
return;
}
else{
if(vno == IdtNMI){
if(active.ispanic){
if(vno == VectorNMI){
/*
* Use of m->dbgsp avoids stack confusion
* caused by writing the address of the SP to
* the top of the stack.
*/
m->dbgreg = ureg;
m->dbgsp = &ureg->sp;
for(;;)
_halt();
}
if(m->perfintr != nil){
m->perfintr(ureg, nil);
* Don't re-enable, it confuses the crash dumps.
nmienable();
*/
iprint("cpu%d: nmi PC %#p, status %ux\n",
m->machno, ureg->ip, inb(0x61));
while(m->machno != 0)
;
}
if(!user){
void (*pc)(void);
extern void _rdmsrinst(void);
extern void _wrmsrinst(void);
pc = (void*)ureg->ip;
if(pc == _rdmsrinst || pc == _wrmsrinst){
if(vno == VectorGPF){
ureg->bp = -1;
ureg->ip += 2;
return;
}
nmienable();
}
if(vno == 39){
/* We get this one and didn't track it down yet: it's ok */
iprint("vno %d: buggeration @ %#p...\n", vno, ureg->ip);
}else if(vno < nelem(excname)){
}
dumpregs(ureg);
panic("%s pc %#p", excname[vno], ureg->ip);
}else
panic("unknown trap/intr: %d pc %#p\n", vno, ureg->ip);
if(vno < nelem(excname))
panic("%s", excname[vno]);
panic("unknown trap/intr: %d", vno);
}
splhi();
@ -581,23 +654,21 @@ faultamd64(Ureg* ureg, void* _1)
void (*pt)(Proc*, int, int64_t, int64_t);
addr = cr2get();
user = userureg(ureg);
/*
* There must be a user context.
* If not, the usual problem is causing a fault during
* initialisation before the system is fully up.
*/
if(up == nil){
panic("fault with up == nil; pc %#llux addr %#llux\n",
ureg->ip, addr);
}
if(ureg->error&2)
ftype = FaultWrite;
else if(ureg->error&16)
ftype = FaultExecute;
else
ftype = FaultRead;
user = userureg(ureg);
if(!user){
if(addr >= USTKTOP)
panic("kernel fault: bad address pc=%#p addr=%#p", ureg->ip, addr);
if(up == nil)
panic("kernel fault: no user process pc=%#p addr=%#p", ureg->ip, addr);
}
if(up == nil)
panic("user fault: up=0 pc=%#p addr=%#p", ureg->ip, addr);
if(up->trace && (pt = proctrace) != nil){
if(ftype == FaultWrite)
@ -617,7 +688,6 @@ if(iskaddr(addr)){
dumpregs(ureg);
}
if(fault(addr, ureg->ip, ftype) < 0){
splhi();
if(!user){
dumpregs(ureg);
panic("fault: %#llux pc %#p\n", addr, ureg->ip);

View File

@ -124,13 +124,11 @@ extern PhysUart i8250physuart;
static Ctlr i8250ctlr[2] = {
{ .io = Uart0,
.irq = Uart0IRQ,
.tbdf = -1,
.poll = 0, },
.tbdf = -1, },
{ .io = Uart1,
.irq = Uart1IRQ,
.tbdf = -1,
.poll = 0, },
.tbdf = -1, },
};
static Uart i8250uart[2] = {
@ -161,7 +159,7 @@ i8250status(Uart* uart, void* buf, long n, long offset)
uint8_t ier, lcr, mcr, msr;
ctlr = uart->regs;
p = jehanne_malloc(READSTR);
p = smalloc(READSTR);
mcr = ctlr->sticky[Mcr];
msr = csr8r(ctlr, Msr);
ier = ctlr->sticky[Ier];
@ -545,8 +543,8 @@ i8250disable(Uart* uart)
csr8w(ctlr, Ier, ctlr->sticky[Ier]);
if(ctlr->iena != 0){
if(intrdisable(ctlr->vector) == 0)
ctlr->iena = 0;
intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
}
}
@ -688,27 +686,6 @@ i8250putc(Uart* uart, int c)
delay(1);
}
static void
i8250poll(Uart* uart)
{
Ctlr *ctlr;
/*
* If PhysUart has a non-nil .poll member, this
* routine will be called from the uartclock timer.
* If the Ctlr .poll member is non-zero, when the
* Uart is enabled interrupts will not be enabled
* and the result is polled input and output.
* Not very useful here, but ports to new hardware
* or simulators can use this to get serial I/O
* without setting up the interrupt mechanism.
*/
ctlr = uart->regs;
if(ctlr->iena || !ctlr->poll)
return;
i8250interrupt(nil, uart);
}
PhysUart i8250physuart = {
.name = "i8250",
.pnp = i8250pnp,
@ -727,73 +704,27 @@ PhysUart i8250physuart = {
.fifo = i8250fifo,
.getc = i8250getc,
.putc = i8250putc,
.poll = i8250poll,
};
Uart*
i8250console(char* cfg)
void
i8250console(void)
{
int i;
Uart *uart;
Ctlr *ctlr;
int n;
char *cmd, *p;
ISAConf isa;
/*
* Before i8250pnp() is run can only set the console
* to 0 or 1 because those are the only uart structs which
* will be the same before and after that.
*/
if((p = getconf("console")) == nil && (p = cfg) == nil)
return nil;
i = jehanne_strtoul(p, &cmd, 0);
if(p == cmd)
return nil;
//WTF? Something to do with the PCIe-only machine?
if((uart = uartconsole(i, cmd)) != nil){
consuart = uart;
return uart;
}
switch(i){
default:
return nil;
case 0:
uart = &i8250uart[0];
break;
case 1:
uart = &i8250uart[1];
break;
}
//Madness. Something to do with the PCIe-only machine?
jehanne_memset(&isa, 0, sizeof(isa));
ctlr = uart->regs;
if(isaconfig("eia", i, &isa) != 0){
if(isa.port != 0)
ctlr->io = isa.port;
if(isa.irq != 0)
ctlr->irq = isa.irq;
if(isa.freq != 0)
uart->freq = isa.freq;
}
/*
* Does it exist?
* Should be able to write/read
* the Scratch Pad.
*/
// ctlr = uart->regs;
// csr8o(ctlr, Scr, 0x55);
// if(csr8r(ctlr, Scr) != 0x55)
// return nil;
if((p = getconf("console")) == nil)
return;
n = strtoul(p, &cmd, 0);
if(p == cmd || n < 0 || n >= nelem(i8250uart))
return;
uart = &i8250uart[n];
(*uart->phys->enable)(uart, 0);
uartctl(uart, "b9600 l8 pn s1 i1");
uartctl(uart, "b9600 l8 pn s1");
if(*cmd != '\0')
uartctl(uart, cmd);
consuart = uart;
uart->console = 1;
return uart;
}

View File

@ -1485,7 +1485,7 @@ epio(Ep *ep, Qio *io, void *a, int32_t count, int mustlock)
jehanne_print("\t%s\n", buf);
}
if(mustlock){
qlock(&io->ql);
eqlock(&io->ql);
if(waserror()){
qunlock(&io->ql);
nexterror();
@ -1625,7 +1625,7 @@ epread(Ep *ep, void *a, int32_t count)
switch(ep->ttype){
case Tctl:
cio = ep->aux;
qlock(&cio->ql);
eqlock(&cio->ql);
if(waserror()){
qunlock(&cio->ql);
nexterror();
@ -1698,7 +1698,7 @@ epctlio(Ep *ep, Ctlio *cio, void *a, int32_t count)
cio, ep->dev->nb, ep->nb, count);
if(count < Rsetuplen)
error("short usb command");
qlock(&cio->ql);
eqlock(&cio->ql);
jehanne_free(cio->data);
cio->data = nil;
cio->ndata = 0;
@ -1804,7 +1804,7 @@ episowrite(Ep *ep, void *a, int32_t count)
iso->delay = (ep->sampledelay*ep->samplesz + ep->maxpkt-1) / ep->maxpkt;
iso->debug = ep->debug;
qlock(&iso->ql);
eqlock(&iso->ql);
if(waserror()){
qunlock(&iso->ql);
nexterror();
@ -2193,7 +2193,7 @@ portreset(Hci *hp, int port, int on)
return 0;
ctlr = hp->aux;
qlock(&ctlr->resetl);
eqlock(&ctlr->resetl);
if(waserror()){
qunlock(&ctlr->resetl);
nexterror();
@ -2225,7 +2225,7 @@ portenable(Hci *hp, int port, int on)
ctlr = hp->aux;
dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
qlock(&ctlr->resetl);
eqlock(&ctlr->resetl);
if(waserror()){
qunlock(&ctlr->resetl);
nexterror();

View File

@ -1031,7 +1031,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, int32_t count)
diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
ctlr = ep->hp->aux;
qlock(&iso->ql);
eqlock(&iso->ql);
if(waserror()){
qunlock(&iso->ql);
nexterror();
@ -1102,7 +1102,7 @@ episoread(Ep *ep, Isoio *iso, void *a, int count)
b = a;
ctlr = ep->hp->aux;
qlock(&iso->ql);
eqlock(&iso->ql);
if(waserror()){
qunlock(&iso->ql);
nexterror();
@ -1306,7 +1306,7 @@ epio(Ep *ep, Qio *io, void *a, int32_t count, int mustlock)
jehanne_print("uchi epio: user data: %s\n", buf);
}
if(mustlock){
qlock(&io->ql);
eqlock(&io->ql);
if(waserror()){
qunlock(&io->ql);
nexterror();
@ -1452,7 +1452,7 @@ epread(Ep *ep, void *a, int32_t count)
switch(ep->ttype){
case Tctl:
cio = ep->aux;
qlock(&cio->ql);
eqlock(&cio->ql);
if(waserror()){
qunlock(&cio->ql);
nexterror();
@ -1525,7 +1525,7 @@ epctlio(Ep *ep, Ctlio *cio, void *a, int32_t count)
cio, ep->dev->nb, ep->nb, count);
if(count < Rsetuplen)
error("short usb comand");
qlock(&cio->ql);
eqlock(&cio->ql);
jehanne_free(cio->data);
cio->data = nil;
cio->ndata = 0;
@ -2013,7 +2013,7 @@ portenable(Hci *hp, int port, int on)
ctlr = hp->aux;
dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on);
ioport = PORT(port-1);
qlock(&ctlr->portlck);
eqlock(&ctlr->portlck);
if(waserror()){
qunlock(&ctlr->portlck);
nexterror();
@ -2070,7 +2070,7 @@ portstatus(Hci *hp, int port)
ctlr = hp->aux;
ioport = PORT(port-1);
qlock(&ctlr->portlck);
eqlock(&ctlr->portlck);
if(waserror()){
iunlock(&ctlr->l);
qunlock(&ctlr->portlck);

View File

@ -225,7 +225,7 @@ vgascreenwin(VGAscr* scr)
window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
curpos = window.min;
consputs = vgascreenputs;
screenputs = vgascreenputs;
}
/*
@ -275,7 +275,7 @@ cornerstring(char *s)
Point p;
scr = &vgascreen[0];
if(scr->vaddr == nil || consputs != vgascreenputs)
if(scr->vaddr == nil || screenputs != vgascreenputs)
return;
p = memsubfontwidth(scr->memdefont, s);
w = p.x;

View File

@ -78,7 +78,6 @@ vbecall(VGAreg *u)
pa = PADDR(RMBUF);
cmem->dev->write(cmem, modebuf, sizeof modebuf, pa);
u->trap = 0x10;
jehanne_print("vbecall: sizeof u is %d\n", sizeof *u);
creg->dev->write(creg, u, sizeof *u, 0);
creg->dev->read(creg, u, sizeof *u, 0);

View File

@ -24,7 +24,7 @@
static Lock vgaxlock; /* access to index registers */
int
vgaxi(int32_t port, unsigned char index)
vgaxi(int32_t port, uint8_t index)
{
unsigned char data;
@ -71,7 +71,7 @@ vgaxi(int32_t port, unsigned char index)
}
int
vgaxo(int32_t port, unsigned char index, unsigned char data)
vgaxo(int32_t port, uint8_t index, uint8_t data)
{
ilock(&vgaxlock);
switch(port){

View File

@ -1,192 +0,0 @@
/*
* Vestigial Segmented Virtual Memory.
* To do:
* dynamic allocation and free of descriptors;
* IST should perhaps point to a different handler;
* user-level descriptors (if not dynamic).
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "amd64.h"
#include "ureg.h"
typedef struct Gd Gd;
typedef uint64_t Sd;
typedef uint16_t Ss;
typedef struct Tss Tss;
struct Gd {
Sd sd;
uint64_t hi;
};
struct Tss {
uint32_t _0_;
uint32_t rsp0[2];
uint32_t rsp1[2];
uint32_t rsp2[2];
uint32_t _28_[2];
uint32_t ist[14];
uint16_t _92_[5];
uint16_t iomap;
};
enum {
Ngdt = 16, /* max. entries in gdt */
Nidt = 256, /* max. entries in idt */
};
static Sd gdt64[Ngdt] = {
0ull, /* NULL descriptor */
SdL|SdP|SdDPL0|SdS|SdCODE, /* CS */
SdG|SdD|SdP|SdDPL0|SdS|SdW, /* DS */
SdG|SdD|SdP|SdDPL3|SdS|SdCODE|SdR|Sd4G, /* User CS 32-bit */
SdG|SdD|SdP|SdDPL3|SdS|SdW|Sd4G, /* User DS */
SdL|SdP|SdDPL3|SdS|SdCODE, /* User CS 64-bit */
0ull, /* FS */
0ull, /* GS */
0ull, /* TSS lower */
0ull, /* TSS upper */
};
//static int ngdt64 = 10; // not used
static Gd idt64[Nidt];
static Sd
mksd(uint64_t base, uint64_t limit, uint64_t bits, uint64_t* upper)
{
Sd sd;
sd = bits;
sd |= (((limit & 0x00000000000f0000ull)>>16)<<48)
|(limit & 0x000000000000ffffull);
sd |= (((base & 0x00000000ff000000ull)>>24)<<56)
|(((base & 0x0000000000ff0000ull)>>16)<<32)
|((base & 0x000000000000ffffull)<<16);
if(upper != nil)
*upper = base>>32;
return sd;
}
static void
mkgd(Gd* gd, uint64_t offset, Ss ss, uint64_t bits, int ist)
{
Sd sd;
sd = bits;
sd |= (((offset & 0x00000000ffff0000ull)>>16)<<48)
|(offset & 0x000000000000ffffull);
sd |= ((ss & 0x000000000000ffffull)<<16);
sd |= (ist & (SdISTM>>32))<<32;
gd->sd = sd;
gd->hi = offset>>32;
}
static void
idtinit(void)
{
Gd *gd;
int ist, v;
uint64_t dpl;
uintptr_t offset;
gd = idt64;
offset = PTR2UINT(idthandlers);
for(v = 0; v < Nidt; v++){
ist = 0;
dpl = SdP|SdDPL0|SdIG;
switch(v){
default:
break;
case IdtBP: /* #BP */
dpl = SdP|SdDPL3|SdIG;
break;
case IdtUD: /* #UD */
case IdtDF: /* #DF */
ist = 1;
break;
}
mkgd(gd, offset, SSEL(SiCS, SsTIGDT|SsRPL0), dpl, ist);
gd++;
offset += 6;
}
}
void
tssrsp0(uintptr_t sp)
{
Tss *tss;
tss = m->tss;
tss->rsp0[0] = sp;
tss->rsp0[1] = sp>>32;
}
static void
tssinit(uintptr_t sp)
{
int ist;
Tss *tss;
tss = m->tss;
jehanne_memset(tss, 0, sizeof(Tss));
tssrsp0(sp);
sp = PTR2UINT(m->vsvm+PGSZ);
for(ist = 0; ist < 14; ist += 2){
tss->ist[ist] = sp;
tss->ist[ist+1] = sp>>32;
}
tss->iomap = 0xdfff;
}
void
vsvminit(int size)
{
Sd *sd;
uint64_t r;
if(m->machno == 0)
idtinit();
m->gdt = m->vsvm;
jehanne_memmove(m->gdt, gdt64, sizeof(gdt64));
m->tss = &m->vsvm[ROUNDUP(sizeof(gdt64), 16)];
sd = &((Sd*)m->gdt)[SiTSS];
*sd = mksd(PTR2UINT(m->tss), sizeof(Tss)-1, SdP|SdDPL0|SdaTSS, sd+1);
tssinit(m->stack+size);
gdtput(sizeof(gdt64)-1, PTR2UINT(m->gdt), SSEL(SiCS, SsTIGDT|SsRPL0));
idtput(sizeof(idt64)-1, PTR2UINT(idt64));
trput(SSEL(SiTSS, SsTIGDT|SsRPL0));
wrmsr(FSbase, 0ull);
wrmsr(GSbase, PTR2UINT(&sys->machptr[m->machno]));
wrmsr(KernelGSbase, 0ull);
r = rdmsr(Efer);
r |= Sce;
wrmsr(Efer, r);
r = ((uint64_t)SSEL(SiU32CS, SsRPL3))<<48;
r |= ((uint64_t)SSEL(SiCS, SsRPL0))<<32;
wrmsr(Star, r);
wrmsr(Lstar, PTR2UINT(syscallentry));
wrmsr(Sfmask, If);
}
int
userureg(Ureg* ureg)
{
return ureg->cs == SSEL(SiUCS, SsRPL3);
}

View File

@ -5,7 +5,6 @@
],
"Include": [
"core.json",
"../386/include.json",
"../ip/include.json",
"../port/include.json"
],
@ -17,7 +16,6 @@
"int printallsyscalls;"
],
"Dev": [
"acpi",
"arch",
"bridge",
"cap",
@ -105,7 +103,6 @@
"autogenerated.c",
"sdata.c",
"cga.c",
"devacpi.c",
"usbehcipc.c",
"usbohci.c",
"usbuhci.c"

351
sys/src/kern/port/alloc.c Normal file
View File

@ -0,0 +1,351 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
#include <pool.h>
static void poolprint(Pool*, char*, ...);
static void ppanic(Pool*, char*, ...);
static void pool_lock(Pool*);
static void pool_unlock(Pool*);
typedef struct Private Private;
struct Private {
Lock lk;
char msg[256]; /* a rock for messages to be printed at unlock */
};
static Private pmainpriv;
static Pool pmainmem = {
.name= "Main",
.maxsize= 4*1024*1024,
.minarena= 128*1024,
.quantum= 32,
.alloc= xalloc,
.merge= xmerge,
.flags= POOL_TOLERANCE,
.lock= pool_lock,
.unlock= pool_unlock,
.print= poolprint,
.panic= ppanic,
.private= &pmainpriv,
};
static Private pimagpriv;
static Pool pimagmem = {
.name= "Image",
.maxsize= 16*1024*1024,
.minarena= 2*1024*1024,
.quantum= 32,
.alloc= xalloc,
.merge= xmerge,
.flags= 0,
.lock= pool_lock,
.unlock= pool_unlock,
.print= poolprint,
.panic= ppanic,
.private= &pimagpriv,
};
static Private psecrpriv;
static Pool psecrmem = {
.name= "Secrets",
.maxsize= 16*1024*1024,
.minarena= 64*1024,
.quantum= 32,
.alloc= xalloc,
.merge= xmerge,
.flags= POOL_ANTAGONISM,
.lock= pool_lock,
.unlock= pool_unlock,
.print= poolprint,
.panic= ppanic,
.private= &psecrpriv,
};
Pool* mainmem = &pmainmem;
Pool* imagmem = &pimagmem;
Pool* secrmem = &psecrmem;
/*
* because we can't print while we're holding the locks,
* we have the save the message and print it once we let go.
*/
static void
poolprint(Pool *p, char *fmt, ...)
{
va_list v;
Private *pv;
pv = p->private;
va_start(v, fmt);
vseprint(pv->msg+strlen(pv->msg), pv->msg+sizeof pv->msg, fmt, v);
va_end(v);
}
static void
ppanic(Pool *p, char *fmt, ...)
{
va_list v;
Private *pv;
char msg[sizeof pv->msg];
pv = p->private;
va_start(v, fmt);
vseprint(pv->msg+strlen(pv->msg), pv->msg+sizeof pv->msg, fmt, v);
va_end(v);
memmove(msg, pv->msg, sizeof msg);
iunlock(&pv->lk);
panic("%s", msg);
}
static void
pool_lock(Pool *p)
{
Private *pv;
pv = p->private;
ilock(&pv->lk);
// pv->lk.pc = getcallerpc();
pv->msg[0] = 0;
}
static void
pool_unlock(Pool *p)
{
Private *pv;
char msg[sizeof pv->msg];
pv = p->private;
if(pv->msg[0] == 0){
iunlock(&pv->lk);
return;
}
memmove(msg, pv->msg, sizeof msg);
iunlock(&pv->lk);
iprint("%.*s", sizeof pv->msg, msg);
}
void
poolsummary(Pool *p)
{
print("%s max %llud cur %llud free %llud alloc %llud\n", p->name,
(uint64_t)p->maxsize, (uint64_t)p->cursize,
(uint64_t)p->curfree, (uint64_t)p->curalloc);
}
void
mallocsummary(void)
{
poolsummary(mainmem);
poolsummary(imagmem);
poolsummary(secrmem);
}
/* everything from here down should be the same in libc, libdebugmalloc, and the kernel */
/* - except the code for malloc(), which alternately doesn't clear or does. */
/* - except the code for smalloc(), which lives only in the kernel. */
/*
* Npadlong is the number of uint32_t's to leave at the beginning of
* each allocated buffer for our own bookkeeping. We return to the callers
* a pointer that points immediately after our bookkeeping area. Incoming pointers
* must be decremented by that much, and outgoing pointers incremented.
* The malloc tag is stored at MallocOffset from the beginning of the block,
* and the realloc tag at ReallocOffset. The offsets are from the true beginning
* of the block, not the beginning the caller sees.
*
* The extra if(Npadlong != 0) in various places is a hint for the compiler to
* compile out function calls that would otherwise be no-ops.
*/
/* non tracing
*
enum {
Npadlong = 0,
MallocOffset = 0,
ReallocOffset = 0,
};
*
*/
/* tracing */
enum {
Npadlong = 2,
MallocOffset = 0,
ReallocOffset = 1
};
void*
smalloc(uint32_t size)
{
void *v;
while((v = poolalloc(mainmem, size+Npadlong*sizeof(uint32_t))) == nil){
if(!waserror()){
resrcwait(nil, nil);
poperror();
}
}
if(Npadlong){
v = (uint32_t*)v+Npadlong;
setmalloctag(v, getcallerpc());
}
memset(v, 0, size);
return v;
}
void*
jehanne_malloc(uint32_t size)
{
void *v;
v = poolalloc(mainmem, size+Npadlong*sizeof(uint32_t));
if(v == nil)
return nil;
if(Npadlong){
v = (uint32_t*)v+Npadlong;
setmalloctag(v, getcallerpc());
setrealloctag(v, 0);
}
memset(v, 0, size);
return v;
}
void*
jehanne_mallocz(uint32_t size, int clr)
{
void *v;
v = poolalloc(mainmem, size+Npadlong*sizeof(uint32_t));
if(Npadlong && v != nil){
v = (uint32_t*)v+Npadlong;
setmalloctag(v, getcallerpc());
setrealloctag(v, 0);
}
if(clr && v != nil)
memset(v, 0, size);
return v;
}
void*
jehanne_mallocalign(uint32_t size, uint32_t align, long offset, uint32_t span)
{
void *v;
v = poolallocalign(mainmem, size+Npadlong*sizeof(uint32_t), align, offset-Npadlong*sizeof(uint32_t), span);
if(Npadlong && v != nil){
v = (uint32_t*)v+Npadlong;
setmalloctag(v, getcallerpc());
setrealloctag(v, 0);
}
if(v)
memset(v, 0, size);
return v;
}
void
jehanne_free(void *v)
{
if(v != nil)
poolfree(mainmem, (uint32_t*)v-Npadlong);
}
void*
jehanne_realloc(void *v, uint32_t size)
{
void *nv;
if(v != nil)
v = (uint32_t*)v-Npadlong;
if(Npadlong !=0 && size != 0)
size += Npadlong*sizeof(uint32_t);
if(nv = poolrealloc(mainmem, v, size)){
nv = (uint32_t*)nv+Npadlong;
setrealloctag(nv, getcallerpc());
if(v == nil)
setmalloctag(nv, getcallerpc());
}
return nv;
}
uint32_t
jehanne_msize(void *v)
{
return poolmsize(mainmem, (uint32_t*)v-Npadlong)-Npadlong*sizeof(uint32_t);
}
/* secret memory, used to back cryptographic keys and cipher states */
void*
secalloc(uint32_t size)
{
void *v;
while((v = poolalloc(secrmem, size+Npadlong*sizeof(uint32_t))) == nil){
if(!waserror()){
resrcwait(nil, nil);
poperror();
}
}
if(Npadlong){
v = (uint32_t*)v+Npadlong;
setmalloctag(v, getcallerpc());
setrealloctag(v, 0);
}
memset(v, 0, size);
return v;
}
void
secfree(void *v)
{
if(v != nil)
poolfree(secrmem, (uint32_t*)v-Npadlong);
}
void
jehanne_setmalloctag(void *v, uintptr_t pc)
{
USED(v, pc);
if(Npadlong <= MallocOffset || v == nil)
return;
((uint32_t*)v)[-Npadlong+MallocOffset] = (uint32_t)pc;
}
void
jehanne_setrealloctag(void *v, uintptr_t pc)
{
USED(v, pc);
if(Npadlong <= ReallocOffset || v == nil)
return;
((uint32_t*)v)[-Npadlong+ReallocOffset] = (uint32_t)pc;
}
uintptr_t
jehanne_getmalloctag(void *v)
{
USED(v);
if(Npadlong <= MallocOffset)
return ~0;
return (int)((uint32_t*)v)[-Npadlong+MallocOffset];
}
uintptr_t
jehanne_getrealloctag(void *v)
{
USED(v);
if(Npadlong <= ReallocOffset)
return ~0;
return (int)((uint32_t*)v)[-Npadlong+ReallocOffset];
}

View File

@ -3,50 +3,46 @@
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
enum
{
Hdrspc = 64, /* leave room for high-level headers */
Tlrspc = 16, /* extra room at the end for pad/crc/mac */
Bdead = 0x51494F42, /* "QIOB" */
};
struct
{
Lock;
uint32_t bytes;
uint32_t limit;
} ialloc;
static Block*
_allocb(int size, int align)
_allocb(int size)
{
Block *b;
uint8_t *p;
int n;
uintptr_t addr;
if(align <= 0)
align = BLOCKALIGN;
n = align + ROUNDUP(size+Hdrspc, align) + sizeof(Block);
if((p = jehanne_malloc(n)) == nil)
size += Tlrspc;
if((b = mallocz(sizeof(Block)+size+Hdrspc, 0)) == nil)
return nil;
b = (Block*)(p + n - sizeof(Block)); /* block at end of allocated space */
b->base = p;
b->next = nil;
b->list = nil;
b->free = 0;
b->free = nil;
b->flag = 0;
/* align base and bounds of data */
b->lim = (uint8_t*)(PTR2UINT(b) & ~(align-1));
/* align start of data portion by rounding up */
addr = (uintptr_t)b;
addr = ROUND(addr + sizeof(Block), BLOCKALIGN);
b->base = (uint8_t*)addr;
/* align start of writable data, leaving space below for added headers */
b->rp = b->lim - ROUNDUP(size, align);
b->wp = b->rp;
/* align end of data portion by rounding down */
b->lim = (uint8_t*)b + msize(b);
addr = (uintptr_t)b->lim;
addr &= ~(BLOCKALIGN-1);
b->lim = (uint8_t*)addr;
if(b->rp < b->base || b->lim - b->rp < size)
/* leave sluff at beginning for added headers */
b->rp = b->lim - ROUND(size, BLOCKALIGN);
if(b->rp < b->base)
panic("_allocb");
b->wp = b->rp;
return b;
}
@ -58,80 +54,44 @@ allocb(int size)
/*
* Check in a process and wait until successful.
* Can still error out of here, though.
*/
if(up == nil)
panic("allocb without up: %#p\n", getcallerpc());
if((b = _allocb(size, 0)) == nil){
panic("allocb without up: %#p", getcallerpc());
while((b = _allocb(size)) == nil){
if(up->nlocks || m->ilockdepth || !islo()){
xsummary();
mallocsummary();
panic("allocb: no memory for %d bytes\n", size);
panic("allocb: no memory for %d bytes", size);
}
jehanne_setmalloctag(b->base, getcallerpc());
if(!waserror()){
resrcwait("no memory for allocb", nil);
poperror();
}
}
setmalloctag(b, getcallerpc());
return b;
}
Block*
allocbalign(int size, int align)
{
Block *b;
/*
* Check in a process and wait until successful.
* Can still error out of here, though.
*/
if(up == nil)
panic("allocbalign without up: %#p\n", getcallerpc());
if((b = _allocb(size, align)) == nil){
mallocsummary();
panic("allocbalign: no memory for %d bytes\n", size);
}
jehanne_setmalloctag(b->base, getcallerpc());
return b;
}
void
ialloclimit(uint32_t limit)
{
ialloc.limit = limit;
}
Block*
iallocb(int size)
{
Block *b;
static int m1, m2, mp;
if(ialloc.bytes > ialloc.limit){
if((m1++%10000)==0){
if(mp++ > 1000){
active.exiting = 1;
exit(0);
if((b = _allocb(size)) == nil){
static uint32_t nerr;
if((nerr++%10000)==0){
if(nerr > 10000000){
xsummary();
mallocsummary();
panic("iallocb: out of memory");
}
iprint("iallocb: limited %lud/%lud\n",
ialloc.bytes, ialloc.limit);
}
return nil;
}
if((b = _allocb(size, 0)) == nil){
if((m2++%10000)==0){
if(mp++ > 1000){
active.exiting = 1;
exit(0);
}
iprint("iallocb: no memory %lud/%lud\n",
ialloc.bytes, ialloc.limit);
iprint("iallocb: no memory for %d bytes\n", size);
}
return nil;
}
setmalloctag(b, getcallerpc());
b->flag = BINTR;
jehanne_setmalloctag(b->base, getcallerpc());
ilock(&ialloc);
ialloc.bytes += b->lim - b->base;
iunlock(&ialloc);
return b;
}
@ -140,7 +100,6 @@ void
freeb(Block *b)
{
void *dead = (void*)Bdead;
uint8_t *p;
if(b == nil)
return;
@ -149,17 +108,10 @@ freeb(Block *b)
* drivers which perform non cache coherent DMA manage their own buffer
* pool of uncached buffers and provide their own free routine.
*/
if(b->free) {
if(b->free != nil) {
b->free(b);
return;
}
if(b->flag & BINTR) {
ilock(&ialloc);
ialloc.bytes -= b->lim - b->base;
iunlock(&ialloc);
}
p = b->base;
/* poison the block in case someone is still holding onto it */
b->next = dead;
@ -168,7 +120,7 @@ freeb(Block *b)
b->lim = dead;
b->base = dead;
jehanne_free(p);
free(b);
}
void
@ -180,10 +132,10 @@ checkb(Block *b, char *msg)
panic("checkb b %s %#p", msg, b);
if(b->base == dead || b->lim == dead || b->next == dead
|| b->rp == dead || b->wp == dead){
jehanne_print("checkb: base %#p lim %#p next %#p\n",
print("checkb: base %#p lim %#p next %#p\n",
b->base, b->lim, b->next);
jehanne_print("checkb: rp %#p wp %#p\n", b->rp, b->wp);
panic("checkb dead: %s\n", msg);
print("checkb: rp %#p wp %#p\n", b->rp, b->wp);
panic("checkb dead: %s", msg);
}
if(b->base > b->lim)
@ -197,9 +149,3 @@ checkb(Block *b, char *msg)
if(b->wp > b->lim)
panic("checkb 4 %s %#p %#p", msg, b->wp, b->lim);
}
void
iallocsummary(void)
{
jehanne_print("ialloc %lud/%lud\n", ialloc.bytes, ialloc.limit);
}

View File

@ -21,333 +21,611 @@
#include "dat.h"
#include "fns.h"
typedef struct PendingWakeup PendingWakeup;
struct PendingWakeup
/* Requirements:
* - each process can register several wakeups
* - wakeups registered outside a note handler will not happen
* in a note handler
* - wakeups registered by a note handler will be deleted on noted()
* - all pending wakeups of a process will be deleted on pexit()
* - future wakeups can be deleted
* - each wakeup will interrupt a blocking syscall
*/
struct AwakeAlarm /* 24 byte */
{
short notified;
uint64_t time;
Proc* p;
PendingWakeup *next;
AwakeAlarm* next;
unsigned char notified;
unsigned char done;
unsigned long time : 48;
};
typedef struct AlarmPool
{
AwakeAlarm* alarms;
AwakeAlarm* end;
int size;
int first;
int nfree;
Lock l;
} AlarmPool;
static AlarmPool awkpool;
#define alarm2wkp(a) ~((a)->time << 16 | (a) - awkpool.alarms)
#define wkp2alarm(a) (awkpool.alarms + (~(a) & 0xffff))
static AwakeAlarm *registry;
static Lock rl;
typedef enum ElapsedAlarmFate
{
TryAgain,
Forget,
FreeAndForget
} ElapsedAlarmFate;
static AwakeAlarm *elapsed, **eEnd = &elapsed;
static Lock el;
static Rendez producer;
static Rendez consumer;
static unsigned long next_wakeup;
static const int awakeable_syscalls[] = {
[SysAwait] = 1,
[SysAwake] = 0,
[SysBind] = 0,
[SysClose] = 0,
[SysCreate] = 0,
[SysErrstr] = 0,
[SysExec] = 0,
[Sys_exits] = 0,
[SysFauth] = 0,
[SysFd2path] = 0,
[SysFstat] = 1,
[SysFversion] = 0,
[SysFwstat] = 1,
[SysMount] = 0,
[SysNoted] = 0,
[SysNotify] = 0,
[SysOpen] = 1,
[SysPread] = 1,
[SysPwrite] = 1,
[SysRemove] = 0,
[SysRendezvous] = 1,
[SysRfork] = 0,
[SysSeek] = 0,
[SysSemacquire] = 1,
[SysSemrelease] = 0,
[SysUnmount] = 0,
[SysAlarm] = 0,
};
/* alarms: linked list, sorted by wakeup time, protected by qlock(&l)
* wakeupafter inserts new items, forgivewakeup remove them,
* awakekproc consume the expired ones and clearwakeups remove those
* survived to their process.
*/
static PendingWakeup *alarms;
static QLock l;
#define DEBUG
static Rendez awaker;
int
canwakeup(Syscalls scall)
{
if(scall == 0)
panic("canwakeup on page fault");
switch(scall){
default:
panic("canwakeup: unknown scall %d\n", scall);
case SysFstat:
case SysFwstat:
case SysOpen:
case SysPread:
case SysPwrite:
case SysRendezvous:
case SysSemacquire:
case SysSemrelease:
case SysAwait:
return 1;
case SysAwake:
case SysBind:
case SysClose:
case SysCreate:
case SysErrstr:
case SysExec:
case Sys_exits:
case SysFauth:
case SysFd2path:
case SysFversion:
case SysMount:
case SysNoted:
case SysNotify:
case SysRemove:
case SysRfork:
case SysSeek:
case SysUnmount:
case SysAlarm:
return 0;
}
}
/*
* Actually wakeup a process
*/
static void
wakeupProc(Proc *p, unsigned long t)
{
Mpl pl;
Rendez *r;
Proc *d, **l;
/* this loop is to avoid lock ordering problems. */
for(;;){
pl = splhi();
lock(&p->rlock);
r = p->r;
/* waiting for a wakeup? */
if(r == nil)
break; /* no */
/* try for the second lock */
if(canlock(&r->l)){
if(p->state != Wakeme || r->p != p)
panic("wakeupProc: state %d %d %d", r->p != p, p->r != r, p->state);
p->r = nil;
r->p = nil;
ready(p);
unlock(&r->l);
break;
}
/* give other process time to get out of critical section and try again */
unlock(&p->rlock);
splx(pl);
sched();
}
unlock(&p->rlock);
splx(pl);
p->pendingWakeup = t;
if(p->state != Rendezvous)
return;
/* Try and pull out of a rendezvous */
lock(&p->rgrp->l);
if(p->state == Rendezvous) {
p->rendval = ~0;
l = &REND(p->rgrp, p->rendtag);
for(d = *l; d; d = d->rendhash) {
if(d == p) {
*l = p->rendhash;
break;
}
l = &d->rendhash;
}
ready(p);
}
unlock(&p->rgrp->l);
}
#ifdef DEBUG
void
awakekproc(void* v)
awake_detect_loop(AwakeAlarm *head)
{
Proc *p;
PendingWakeup *tail, *toAwake, **toAwakeEnd, *toDefer, **toDeferEnd;
uint64_t now;
for(;;){
now = sys->ticks;
toAwake = nil;
toAwakeEnd = &toAwake;
toDefer = nil;
toDeferEnd = &toDefer;
/* search for processes to wakeup */
qlock(&l);
tail = alarms;
while(tail && tail->time <= now){
if(tail->p->pendingWakeup > tail->p->lastWakeup
&& tail->p->state >= Ready){
*toDeferEnd = tail;
toDeferEnd = &tail->next;
} else if (!tail->notified && tail->p->notified){
/* If an awake was requested outside of
* a note handler, it cannot expire
* while executing a note handler.
* On the other hand if an awake was
* requeted while executing a note handler,
* it's up to the note handler to
* forgive it if it's not needed anymore.
*/
*toDeferEnd = tail;
toDeferEnd = &tail->next;
} else {
*toAwakeEnd = tail;
toAwakeEnd = &tail->next;
--tail->p->wakeups;
}
AwakeAlarm *tail;
while(head != nil){
if(!head->p)
panic("awake: free alarm head: %#p, pc %#p", head, getcallerpc());
if(head < awkpool.alarms || head > awkpool.end)
panic("awake: not an alarm head: %#p, pc %#p", head, getcallerpc());
tail = head->next;
while(tail != nil){
if(!tail->p)
panic("awake: free alarm tail: %#p, pc %#p", tail, getcallerpc());
if(tail < awkpool.alarms || tail > awkpool.end)
panic("awake: not an alarm tail: %#p, pc %#p", tail, getcallerpc());
if(head == tail)
panic("awake: loop detected");
else
tail = tail->next;
}
if(toAwake != nil){
*toAwakeEnd = nil;
if(toDefer != nil){
*toDeferEnd = tail;
alarms = toDefer;
head = head->next;
}
}
AwakeAlarm*
awake_find_first_of(Proc* p, AwakeAlarm* head)
{
AwakeAlarm* a = head;
if(head == nil)
return nil;
while(a < awkpool.end){
if(a->p == p)
return a;
++a;
}
return nil;
}
static int
awake_can_interrupt(Syscalls scall)
{
if(scall == 0)
panic("awake_can_interrupts on page fault");
if(scall >= sizeof(awakeable_syscalls) - 1)
panic("awake_can_interrupts: unknown syscall %d", scall);
return awakeable_syscalls[scall];
}
#else
# define awake_detect_loop(h)
# define awake_can_interrupt(scall) (awakeable_syscalls[scall])
//# undef assert
//# define assert(a)
#endif
static void
pool_init(void)
{
awkpool.size = sys->nproc * (sys->nmach + 1);
if(awkpool.size >= 1<<16)
awkpool.size = 1<<16; /* we have 16 bit in the wakeup token for the index */
awkpool.alarms = malloc(sizeof(AwakeAlarm) * awkpool.size);
awkpool.end = awkpool.alarms + awkpool.size;
awkpool.nfree = awkpool.size;
awkpool.first = 0;
}
static AwakeAlarm*
alarm_new(Proc *p)
{
AwakeAlarm *a;
lock(&awkpool.l);
while(awkpool.nfree <= 2*p->nwakeups){
unlock(&awkpool.l);
resrcwait("wait-wkp", nil);
lock(&awkpool.l);
}
/* here we know that nfree > 0... */
if(awkpool.first == awkpool.size){
/* but we don't now where the first free is */
awkpool.first = -1;
while(++awkpool.first < awkpool.size && awkpool.alarms[awkpool.first].p)
;
}
assert(awkpool.first < awkpool.size);
a = awkpool.alarms + awkpool.first;
a->p = p;
if(adec(&awkpool.nfree) > 0){
UpdateFirst:
while(++awkpool.first < awkpool.size && awkpool.alarms[awkpool.first].p)
;
if(awkpool.first == awkpool.size){
awkpool.first = -1;
goto UpdateFirst;
}
} else {
alarms = tail;
awkpool.first = awkpool.size;
}
}
qunlock(&l);
unlock(&awkpool.l);
/* wakeup sleeping processes */
while(toAwake != nil){
p = toAwake->p;
if(p->lastWakeup < toAwake->time && p->state > Ready) {
/* debugged processes will miss wakeups,
* but the alternatives seem even worse
ainc(&p->nwakeups);
a->done = 0;
a->notified = p->notified ? 1 : 0;
a->time = 0;
a->next = nil;
return a;
}
static void
alarm_free(AwakeAlarm* a)
{
Proc *p = a->p;
#ifdef DEBUG
a->next = (void*)getcallerpc();
#endif
a->p = nil;
ainc(&awkpool.nfree);
adec(&p->nwakeups);
}
void
awake_fell_asleep(Proc *p)
{
Syscalls cs = p->cursyscall;
if(cs != 0 && cs != SysAwake){
/* awake_register might sleep() on alarm_new and we
* don't want this sleep to be interrupted.
*/
if(canqlock(&p->debug)){
if(!waserror()){
wakeupProc(p, toAwake->time);
poperror();
p->wakeups[p->notified].blockingsc = cs;
p->wakeups[p->notified].fell_asleep = sys->ticks;
}
qunlock(&p->debug);
}
}
tail = toAwake->next;
jehanne_free(toAwake);
toAwake = tail;
}
sleep(&awaker, return0, 0);
int
awake_should_wake_up(Proc *p)
{
AwakeAlarm *a = p->wakeups[p->notified].elapsed;
Syscalls blockingsc = p->wakeups[p->notified].blockingsc;
return a != nil
&& blockingsc
&& awake_can_interrupt(blockingsc)
;
}
void
awake_awakened(Proc *p)
{
AwakeAlarm *a;
Syscalls blockingsc = p->wakeups[p->notified].blockingsc;
if(blockingsc && awake_can_interrupt(blockingsc))
if(a = xchgm(&p->wakeups[p->notified].elapsed, nil)){
p->wakeups[p->notified].awakened = sys->ticks;
p->wakeups[p->notified].last = alarm2wkp(a);
p->wakeups[p->notified].blockingsc = 0;
}
}
/* called from noted() to remove all pending alarms */
void
awake_gc_note(Proc *p)
{
AwakeAlarm *a, **last;
assert(p->notified != 0);
lock(&rl);
awake_detect_loop(registry);
/* first pending alarm */
a = xchgm(&p->wakeups[1].elapsed, nil);
p->wakeups[1].blockingsc = 0;
/* then clear the registry */
last = &registry;
for(a = *last; a != nil && p->wakeups[1].count > 0; a = *last) {
if(!a->p)
panic("awake_reset: free alarm in registry");
if(a->p == p && a->notified){
adec(&p->wakeups[a->notified].count);
*last = a->next;
alarm_free(a);
} else {
last = &a->next;
}
}
awake_detect_loop(registry);
if(registry)
next_wakeup = registry->time;
unlock(&rl);
}
/*
* called on pexit
* called from pexit() and sysexec() to clear all pending
*/
void
clearwakeups(Proc *p)
awake_gc_proc(Proc *p)
{
PendingWakeup *w, **next, *freelist, **fend;
AwakeAlarm *a, **last;
freelist = nil;
/* clear all wakeups (process is exiting) */
lock(&rl);
awake_detect_loop(registry);
/* find all PendingWakeup* to free and remove them from alarms */
qlock(&l);
if(p->wakeups){
/* note: qlock(&l) && p->wakeups > 0 => alarms != nil */
next = &alarms;
fend = &freelist;
clearnext:
w = *next;
while (w != nil && w->p == p) {
/* found one to remove */
*fend = w; /* append to freelist */
*next = w->next; /* remove from alarms */
fend = &w->next; /* move fend to end of freelist */
*fend = nil; /* clean the end of freelist */
--p->wakeups;
w = *next; /* next to analyze */
}
/* while exited => w == nil || w->p != p */
if(p->wakeups){
/* note: p->wakeups > 0 => w != nil
* p->wakeups > 0 && w->p != p => w->next != nil
*/
next = &w->next;
goto clearnext;
}
}
qunlock(&l);
/* first pending alarm */
p->wakeups[0].elapsed = nil;
p->wakeups[0].blockingsc = 0;
p->wakeups[1].elapsed = nil;
p->wakeups[1].blockingsc = 0;
/* free the found PendingWakeup* (out of the lock) */
w = freelist;
while(w != nil) {
freelist = w->next;
jehanne_free(w);
w = freelist;
/* then clear the registry */
last = &registry;
for(a = *last; a != nil && (p->wakeups[0].count > 0 || p->wakeups[1].count > 0); a = *last) {
if(!a->p)
panic("awake_reset: free alarm in registry");
if(a->p == p){
adec(&p->wakeups[a->notified].count);
*last = a->next;
alarm_free(a);
} else {
last = &a->next;
}
}
awake_detect_loop(registry);
if(registry)
next_wakeup = registry->time;
unlock(&rl);
assert(p->wakeups[0].count == 0);
assert(p->wakeups[1].count == 0);
while(p->nwakeups > 0)
resrcwait(nil, nil);
}
static long
awake_register(long ms)
{
AwakeAlarm *a, *new, **last;
new = alarm_new(up);
ilock(&rl);
last = &registry;
awake_detect_loop(registry);
new->time = (sys->ticks + ms2tk(ms) + sys->nmach) & 0xffffffffffff;
for(a = *last; a != nil && a->time <= new->time; a = *last) {
if(!a->p)
panic("awake_register: free alarm in registry");
if(a->done){
adec(&a->p->wakeups[a->notified].count);
*last = a->next;
alarm_free(a);
continue;
}
if(a->time == new->time && a->p == up){
/* avoid two alarms at the same tick for a process */
++new->time;
}
last = &a->next;
}
*last = new;
new->next = a;
awake_detect_loop(registry);
ainc(&up->wakeups[new->notified].count);
assert(registry != nil);
next_wakeup = registry->time;
iunlock(&rl);
return alarm2wkp(new);
}
static long
awake_remove(long request)
{
AwakeAlarm *a;
if(request >= up->wakeups[up->notified].last)
return 0; /* already free */
a = wkp2alarm(request);
if(a >= awkpool.end)
return 0; /* should we send a note to up? */
if(a->p != up)
return 0; /* should we send a note to up? */
if(a->time != (~request)>>16)
return 0; /* should we send a note to up? */
lock(&rl); /* sync with awake_timer */
if(!CASV(&up->wakeups[up->notified].elapsed, a, nil)){
a->done = 1;
}
unlock(&rl);
return request;
}
long
sysawake(long request)
{
if(request == 0)
return up->wakeups[up->notified].last;
if(request < 0)
return awake_remove(request);
return awake_register(request);
}
/*
* called every clock tick
*/
void
checkwakeups(void)
awake_tick(unsigned long now)
{
PendingWakeup *p;
uint64_t now;
if(next_wakeup < now && (now&7) == 0)
wakeup(&producer);
}
p = alarms;
static void
try_wire_process(void)
{
int i;
/* wire to an online processor to reduce context switches
* but try to avoid boot processor as it runs awake_tick
*/
if(up->mach->machno > 0)
procwired(up, up->mach->machno);
else if(sys->nmach > 1){
i = sys->nmach;
while(up->wired != nil && i > 1)
if(sys->machptr[--i]->online)
procwired(up, i);
}
}
void
awake_timer(void* v)
{
long now;
AwakeAlarm **next, *tmp, *toAwake, **toAwakeEnd;
try_wire_process();
/* initialize wakeups wkppool */
pool_init();
CheckWakeups:
next_wakeup = ~0;
/* we fix time to preserve wakeup order */
now = sys->ticks;
if(p && p->time <= now)
wakeup(&awaker);
toAwake = nil;
toAwakeEnd = &toAwake;
/* search for processes to wakeup */
ilock(&rl);
next = &registry;
while((tmp = *next) != nil && tmp->time <= now){
if(tmp->p == nil)
panic("awake_timer: free alarm in registry");
if(tmp->done){
*next = tmp->next;
adec(&tmp->p->wakeups[tmp->notified].count);
alarm_free(tmp);
} else {
if(!CASV(&tmp->p->wakeups[tmp->notified].elapsed, nil, tmp)){
/* each wakeup must have a chance */
next = &tmp->next;
} else {
adec(&tmp->p->wakeups[tmp->notified].count);
*toAwakeEnd = tmp;
toAwakeEnd = &tmp->next;
*next = tmp->next;
}
}
}
if(toAwake != nil)
*toAwakeEnd = nil;
awake_detect_loop(registry);
iunlock(&rl);
if(toAwake != nil){
/* pass the elapsed wakeups to awake_ringer preserving order */
lock(&el);
*eEnd = toAwake;
eEnd = toAwakeEnd;
unlock(&el);
wakeup(&consumer);
}
static int64_t
wakeupafter(int64_t ms)
if(registry)
next_wakeup = registry->time;
sleep(&producer, return0, 0);
goto CheckWakeups;
}
/* Try to wake up a process
* Returns:
* - 0 if the alarm must be preserved in the elapsed list
* - 1 if the alarm must be removed from the elapsed list and freed
*/
static int
awake_dispatch(AwakeAlarm *a)
{
PendingWakeup *w, *new, **last;
int64_t when;
int canfree;
Rendez *r;
Proc *p;
Syscalls bs;
when = ms2tk(ms) + sys->ticks + 2; /* +2 against round errors and cpu's clocks misalignment */
new = jehanne_mallocz(sizeof(PendingWakeup), 1);
if(new == nil)
p = a->p;
if(p == nil)
panic("awake_dispatch: free alarm in elapsed list");
if(p < procalloc.arena || p > procalloc.arena + procalloc.nproc)
panic("awake_dispatch: dirty alarm");
if(!canlock(&p->rlock))
return 0;
new->p = up;
new->notified = up->notified;
new->time = when;
qlock(&l);
last = &alarms;
for(w = *last; w != nil && w->time <= when; w = w->next) {
last = &w->next;
}
new->next = w;
*last = new;
++up->wakeups;
qunlock(&l);
return -when;
/* sched() locks p->rlock before setting p->mach and p->state
*/
if(p->mach != nil && p->state <= Running){
canfree = p->state < Ready;
goto Done;
}
static int64_t
forgivewakeup(int64_t time)
if(p->wakeups[a->notified].elapsed != a){
/* cleared by awake_awakened */
canfree = 1;
goto Done;
}
canfree = 0;
if(a->done){
/* already signaled, we have to wait for awake_awakened */
goto Done;
}
if(a->notified && !p->notified){
/* this should never happen because p is not Running:
* either noted as been called previously (and thus awake_reset)
* or it has not
*
* so if this happens it's probably a but in awake_reset
*/
unlock(&p->rlock);
panic("awake_dispatch: notified alarm for not notified process");
}
if(p->notepending && !p->notedeferred){
/* notes take precedence */
goto Done;
}
bs = p->wakeups[a->notified].blockingsc;
if(bs == 0 || !awake_can_interrupt(bs)){
/* wait for a chance */
goto Done;
}
r = p->r;
if(r != nil){
if(canlock(&r->l)){
if(p->state != Wakeme || r->p != p)
panic("awake_dispatch: state %d %d %d", r->p != p, p->r != r, p->state);
a->done = 1;
p->r = nil;
r->p = nil;
ready(p);
unlock(&r->l);
}
} else {
if(p->state == Rendezvous || p->state == Queueing)
if(proc_interrupt_finalize(p))
a->done = 1;
}
Done:
unlock(&p->rlock);
return canfree;
}
void
awake_ringer(void* v)
{
PendingWakeup *w, **last;
AwakeAlarm *pending, **pLast, **next, *a;
if(up->lastWakeup >= time || up->pendingWakeup >= time)
return 0;
qlock(&l);
if(alarms == nil || up->wakeups == 0){
qunlock(&l);
return 0; // nothing to do
pending = nil;
pLast = &pending;
CheckElapsed:
lock(&el);
*pLast = elapsed;
elapsed = nil;
eEnd = &elapsed;
unlock(&el);
pLast = &pending;
next = pLast;
while(*next != nil){
a = *next;
if(awake_dispatch(a)){
*next = a->next;
alarm_free(a);
} else {
*pLast = a;
next = &a->next;
pLast = next;
}
}
last = &alarms;
for(w = *last; w != nil && w->time < time; w = w->next) {
last = &w->next;
if(pending == nil){
assert(pLast == &pending);
sleep(&consumer, return0, 0);
} else if(elapsed == nil){
tsleep(&up->sleep, return0, 0, 30);
}
while(w != nil && w->time == time && w->p != up){
last = &w->next;
w = w->next;
}
if(w == nil || w->time > time || w->p != up){
/* wakeup not found */
qunlock(&l);
return 0;
}
*last = w->next;
--up->wakeups;
qunlock(&l);
jehanne_free(w);
return -time;
}
int64_t
procawake(int64_t ms)
{
if(ms == 0)
return -up->lastWakeup; // nothing to do
if(ms < 0)
return forgivewakeup(-ms);
return wakeupafter(ms);
goto CheckElapsed;
}

View File

@ -1,360 +0,0 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
/*
* - locks
* - could instead coalesce free items on demand (cf. Wulf)
* - or lazy buddy (cf. Barkley)
*/
enum{
MinK= PGSHFT, /* default minimum size (one page) */
Nbits= sizeof(uintmem)*8,
MaxK= Nbits-1, /* last usable k (largest block is 2^k) */
Busy= 0x80, /* bit set in byte map if block busy (low order bits are block size, 0=unavailable) */
};
//#define usize uintmem
typedef struct Blk Blk;
struct Blk{
Blk* forw; /* free list */
Blk* back;
};
typedef struct Bfree Bfree;
struct Bfree{
Blk h; /* header */
Lock;
uint32_t avail;
};
struct Bpool{
Lock lk; /* TO DO: localise lock (need CAS update of pool->kofb) (also see Johnson & Davis 1992) */
Bfree blist[Nbits]; /* increasing powers of two */
uint8_t* kofb; /* k(block_index) with top bit set if busy */
uint32_t mink;
uint32_t maxk;
uint32_t maxb; /* limit to block index, in minbsize blocks (pool size) */
Blk* blocks; /* free list pointers */
uintmem base;
uintmem minbsize;
uintmem limit;
};
static uint8_t lg2table[256] = {
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
};
#define BI(a) ((a)>>pool->mink)
#define IB(x) ((uintmem)(x)<<pool->mink)
static int
lg2ceil(uintmem m)
{
uint32_t n, h;
int r;
r = (m & (m-1)) != 0; /* not a power of two => round up */
n = (uint32_t)m;
if(sizeof(uintmem)>sizeof(uint32_t)){
h = (uint64_t)m>>32;
if(h != 0){
n = h;
r += 32;
}
}
if((n>>8) == 0)
return lg2table[n] + r;
if((n>>16) == 0)
return 8 + lg2table[n>>8] + r;
if((n>>24) == 0)
return 16 + lg2table[n>>16] + r;
return 24 + lg2table[n>>24] + r;
}
Bpool*
bpoolcreate(uint32_t mink, uint32_t maxk, uintmem base, uintmem top, void* (*alloc)(usize, int))
{
int k;
Blk *b;
Bpool *pool;
if(mink == 0)
mink = MinK;
if(maxk > MaxK)
panic("bpoolcreate");
pool = alloc(sizeof(Bpool), 1);
if(pool == nil)
panic("bpoolcreate alloc");
pool->mink = mink;
pool->maxk = maxk;
pool->base = base;
pool->limit = top;
pool->maxb = BI(top-base);
pool->minbsize = (uintmem)1<<mink;
pool->blocks = alloc((pool->maxb+1)*sizeof(*pool->blocks), 0);
if(pool->blocks == nil)
panic("bpoolinit: can't allocate %ud blocks", pool->maxb+1);
for(k = 0; k < nelem(pool->blist); k++){
b = &pool->blist[k].h;
b->forw = b->back = b;
}
pool->kofb = alloc((pool->maxb+1)*sizeof(*pool->kofb), 1);
if(pool->kofb == nil)
panic("physinit: can't allocate %ud kofb", pool->maxb+1);
jehanne_print("pool %#p space base %#P top=%#P maxb=%#ux (%d)\n", pool, base, top, pool->maxb, pool->maxb);
return pool;
}
uintmem
bpoolalloc(Bpool *pool, usize size)
{
int j, k;
Blk *b, *b2;
uintmem a, a2;
uint32_t bi;
k = lg2ceil(size);
if(k < pool->mink)
k = pool->mink;
if(k > pool->maxk)
return 0;
DBG("%#p size=%#P k=%d\n", pool, (uintmem)size, k);
lock(&pool->lk);
for(j = k;;){
b = pool->blist[j].h.forw;
if(b != &pool->blist[j].h)
break;
if(++j > pool->maxk){
unlock(&pool->lk);
return 0; /* out of space */
}
}
if(b == nil)
panic("physalloc: nil");
/* set busy state */
bi = b - pool->blocks;
if(pool->kofb[bi] & Busy || b->forw == nil || b->back == nil)
panic("physalloc: inval k=%d j=%d %#p %d %#ux %#p %#p", k, j, b, bi, pool->kofb[bi], b->forw, b->back);
pool->kofb[bi] = k | Busy;
pool->blist[j].avail--;
b->forw->back = b->back;
b->back->forw = b->forw;
a = IB(bi);
while(j != k){
/* split */
j--;
a2 = a+((uintmem)1<<j);
bi = BI(a2);
DBG("split %#llux %#llux k=%d %#llux pool->kofb=%#ux\n", a, a2, j, (uintmem)1<<j, pool->kofb[bi]);
if(pool->kofb[bi] & Busy){
if(pool->kofb[bi] & ~Busy)
panic("bal: busy block %#llux k=%d\n", a, pool->kofb[bi] & ~Busy);
}
pool->kofb[bi] = j; /* new size, not busy */
b2 = &pool->blocks[bi];
b2->forw = &pool->blist[j].h;
b2->back = pool->blist[j].h.back;
pool->blist[j].h.back = b2;
b2->back->forw = b2;
pool->blist[j].avail++;
}
unlock(&pool->lk);
return a + pool->base;
}
void
bpoolfree(Bpool *pool, uintmem a, usize size)
{
int k;
Blk *b, *b2;
uintmem a2;
uint32_t bi, bi2;
k = lg2ceil(size); /* could look it up in pool->kofb */
if(k < pool->mink)
return;
if(k > pool->maxk)
k = pool->maxk;
DBG("%#p free %#llux %#P k%d\n", pool, a, (uintmem)size, k);
if(a < pool->base)
panic("bpoolfree");
a -= pool->base;
bi = BI(a);
lock(&pool->lk);
if(pool->kofb[bi] != 0 && pool->kofb[bi] != (Busy|k)){
unlock(&pool->lk);
panic("balfree: busy %#llux odd k k=%d kofb=%#ux\n", a, k, pool->kofb[bi]);
}
for(; k != pool->maxk; k++){
pool->kofb[bi] = Busy;
a2 = a ^ ((uintmem)1<<k); /* buddy */
bi2 = BI(a2);
b2 = &pool->blocks[bi2];
if(bi2 >= pool->maxb || pool->kofb[bi2] != k)
break;
/* valid, not busy or empty, size k */
DBG("combine %#llux %#llux %d %#llux\n", a, a2, k, (uintmem)1<<k);
b2->back->forw = b2->forw;
b2->forw->back = b2->back;
pool->kofb[bi2] = Busy;
pool->blist[k].avail--;
if(a2 < a){
a = a2;
bi = bi2;
}
}
pool->kofb[bi] = k; /* sets size and resets Busy */
b = &pool->blocks[bi];
b->forw = &pool->blist[k].h;
b->back = pool->blist[k].h.back;
pool->blist[k].h.back = b;
b->back->forw = b;
pool->blist[k].avail++;
unlock(&pool->lk);
}
void
bpoolallocrange(Bpool *pool, usize *low, usize *high)
{
*low = (usize)1<<pool->mink;
*high = (usize)1<<pool->maxk;
}
static void
ibpoolfree(Bpool *pool, uintmem base, usize size)
{
bpoolfree(pool, base+pool->base, size);
}
void
bpoolinitfree(Bpool *pool, uintmem base, uintmem lim)
{
uintmem m, size;
int i;
/* chop limit to min block alignment */
if(base >= pool->limit)
return;
if(pool->base > base)
base = pool->base;
if(lim > pool->limit)
lim = pool->limit;
base -= pool->base;
lim -= pool->base;
lim &= ~(pool->minbsize-1);
if(BI(lim) > pool->maxb){
jehanne_print("physinitfree: address space too large");
lim = IB(pool->maxb);
}
/* round base to min block alignment */
base = (base + pool->minbsize-1) & ~(pool->minbsize-1);
size = lim - base;
if(size < pool->minbsize)
return;
DBG("bpoolinitfree %#p %#P-%#P [%#P]\n", pool, pool->base+base, pool->base+lim, size);
/* move up from base in largest blocks that remain aligned */
for(i=pool->mink; i<pool->maxk; i++){
m = (uintmem)1 << i;
if(base & m){
if(size < m)
break;
if(base & (m-1)){
jehanne_print(" ** error: %#P %#P\n", base, m);
return;
}
ibpoolfree(pool, base, m);
base += m;
size -= m;
}
}
/* largest chunks, aligned */
m = (uintmem)1<<pool->maxk;
while(size >= m){
if(base & (m-1)){
jehanne_print(" ** error: %#P %#P\n", base, m);
return;
}
ibpoolfree(pool, base, m);
base += m;
size -= m;
}
/* free remaining chunks, decreasing alignment */
for(; size >= pool->minbsize; m >>= 1){
if(size & m){
DBG("\t%#P %#P\n", base, m);
if(base & (m-1)){
jehanne_print(" ** error: %#P %#P\n", base, m);
return;
}
ibpoolfree(pool, base, m);
base += m;
size &= ~m;
}
}
}
char*
seprintbpoolstats(Bpool *pool, char *s, char *e)
{
Bfree *b;
int i;
lock(&pool->lk);
for(i = 0; i < nelem(pool->blist); i++){
b = &pool->blist[i];
if(b->avail != 0)
s = jehanne_seprint(s, e, "%ud %ulldK blocks avail\n",
b->avail, (1ull<<i)/KiB);
}
unlock(&pool->lk);
return s;
}
void
bpooldump(Bpool *pool)
{
uintmem a;
uint32_t bi;
int i, k;
Blk *b;
for(i=0; i<nelem(pool->blist); i++){
b = pool->blist[i].h.forw;
if(b != &pool->blist[i].h){
jehanne_print("%d ", i);
for(; b != &pool->blist[i].h; b = b->forw){
bi = b-pool->blocks;
a = IB(bi);
k = pool->kofb[bi];
jehanne_print(" [%#llux %d %#ux b=%#llux]", a, k, 1<<k, a^((uintmem)1<<k));
}
jehanne_print("\n");
}
}
}

View File

@ -1,543 +0,0 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
enum
{
NHASH = 128,
MAXCACHE = 1024*1024,
NFILE = 4096,
NEXTENT = 200, /* extent allocation size */
};
typedef struct Extent Extent;
struct Extent
{
uint32_t start;
int len;
char *cache;
Extent *next;
};
typedef struct Mntcache Mntcache;
struct Mntcache
{
Qid qid;
uint32_t devno;
Dev* dev;
QLock;
Extent *list;
Mntcache *hash;
Mntcache *prev;
Mntcache *next;
};
typedef struct Cache Cache;
struct Cache
{
Lock l;
int pgno;
Mntcache *head;
Mntcache *tail;
Mntcache *hash[NHASH];
};
typedef struct Ecache Ecache;
struct Ecache
{
Lock l;
int total;
int free;
Extent* head;
};
//static Image fscache; // not used
static Cache cache;
static Ecache ecache;
static int maxcache = MAXCACHE;
static void
extentfree(Extent* e)
{
jehanne_free(e->cache);
e->cache = nil;
lock(&ecache.l);
e->next = ecache.head;
ecache.head = e;
ecache.free++;
unlock(&ecache.l);
}
static Extent*
extentalloc(void)
{
Extent *e;
int i;
lock(&ecache.l);
if(ecache.head == nil){
e = jehanne_malloc(NEXTENT*sizeof(Extent));
if(e == nil){
unlock(&ecache.l);
return nil;
}
for(i = 0; i < NEXTENT; i++){
e->next = ecache.head;
ecache.head = e;
e++;
}
ecache.free += NEXTENT;
ecache.total += NEXTENT;
}
e = ecache.head;
ecache.head = e->next;
jehanne_memset(e, 0, sizeof(Extent));
ecache.free--;
unlock(&ecache.l);
return e;
}
void
cinit(void)
{
int i;
Mntcache *mc;
if((cache.head = jehanne_malloc(sizeof(Mntcache)*NFILE)) == nil)
panic("cinit: no memory");
mc = cache.head;
/* a good algorithm to set maxcache would be nice */
for(i = 0; i < NFILE-1; i++) {
mc->next = mc+1;
mc->prev = mc-1;
mc++;
}
cache.tail = mc;
cache.tail->next = 0;
cache.head->prev = 0;
}
void
cnodata(Mntcache *mc)
{
Extent *e, *n;
/*
* Invalidate all extent data
* Image lru will waste the pages
*/
for(e = mc->list; e; e = n) {
n = e->next;
extentfree(e);
}
mc->list = 0;
}
void
ctail(Mntcache *mc)
{
/* Unlink and send to the tail */
if(mc->prev)
mc->prev->next = mc->next;
else
cache.head = mc->next;
if(mc->next)
mc->next->prev = mc->prev;
else
cache.tail = mc->prev;
if(cache.tail) {
mc->prev = cache.tail;
cache.tail->next = mc;
mc->next = 0;
cache.tail = mc;
}
else {
cache.head = mc;
cache.tail = mc;
mc->prev = 0;
mc->next = 0;
}
}
void
copen(Chan *c)
{
int h;
Extent *e, *next;
Mntcache *mc, *f, **l;
/* directories aren't cacheable and append-only files confuse us */
if(c->qid.type&(QTDIR|QTAPPEND))
return;
h = c->qid.path%NHASH;
lock(&cache.l);
for(mc = cache.hash[h]; mc != nil; mc = mc->hash) {
if(mc->qid.path == c->qid.path)
if(mc->qid.type == c->qid.type)
if(mc->devno == c->devno && mc->dev == c->dev) {
c->mc = mc;
ctail(mc);
unlock(&cache.l);
/* File was updated, invalidate cache */
if(mc->qid.vers != c->qid.vers) {
mc->qid.vers = c->qid.vers;
qlock(mc);
cnodata(mc);
qunlock(mc);
}
return;
}
}
/* LRU the cache headers */
mc = cache.head;
l = &cache.hash[mc->qid.path%NHASH];
for(f = *l; f; f = f->hash) {
if(f == mc) {
*l = mc->hash;
break;
}
l = &f->hash;
}
mc->qid = c->qid;
mc->devno = c->devno;
mc->dev = c->dev;
l = &cache.hash[h];
mc->hash = *l;
*l = mc;
ctail(mc);
qlock(mc);
c->mc = mc;
e = mc->list;
mc->list = 0;
unlock(&cache.l);
while(e) {
next = e->next;
extentfree(e);
e = next;
}
qunlock(mc);
}
static int
cdev(Mntcache *mc, Chan *c)
{
if(mc->qid.path != c->qid.path)
return 0;
if(mc->qid.type != c->qid.type)
return 0;
if(mc->devno != c->devno)
return 0;
if(mc->dev != c->dev)
return 0;
if(mc->qid.vers != c->qid.vers)
return 0;
return 1;
}
int
cread(Chan *c, uint8_t *buf, int len, int64_t off)
{
char *p;
Mntcache *mc;
Extent *e, **t;
int o, l, total;
uint32_t offset;
if(off+len > maxcache)
return 0;
mc = c->mc;
if(mc == nil)
return 0;
qlock(mc);
if(cdev(mc, c) == 0) {
qunlock(mc);
return 0;
}
offset = off;
t = &mc->list;
for(e = *t; e; e = e->next) {
if(offset >= e->start && offset < e->start+e->len)
break;
t = &e->next;
}
if(e == 0) {
qunlock(mc);
return 0;
}
total = 0;
while(len) {
p = e->cache;
if(p == nil){
*t = e->next;
extentfree(e);
qunlock(mc);
return total;
}
o = offset - e->start;
l = len;
if(l > e->len-o)
l = e->len-o;
if(waserror()) {
qunlock(mc);
nexterror();
}
jehanne_memmove(buf, p+o, l);
poperror();
buf += l;
len -= l;
offset += l;
total += l;
t = &e->next;
e = e->next;
if(e == 0 || e->start != offset)
break;
}
qunlock(mc);
return total;
}
Extent*
cchain(uint8_t *buf, uint32_t offset, int len, Extent **tail)
{
int l;
char *p;
Extent *e, *start, **t;
start = 0;
*tail = 0;
t = &start;
while(len) {
e = extentalloc();
if(e == 0)
break;
p = jehanne_mallocz(PGSZ, 0);
if(p == nil){
extentfree(e);
break;
}
l = len;
if(l > PGSZ)
l = PGSZ;
e->cache = p;
e->start = offset;
e->len = l;
jehanne_memmove(p, buf, l);
buf += l;
offset += l;
len -= l;
*t = e;
*tail = e;
t = &e->next;
}
return start;
}
int
cpgmove(Extent *e, uint8_t *buf, int boff, int len)
{
if(e->cache == nil){
/* shouldn't happen */
jehanne_print("CACHE: cpgmove %#p %d %d nil\n", e, boff, len);
return 0;
}
jehanne_memmove(e->cache+boff, buf, len);
return 1;
}
void
cupdate(Chan *c, uint8_t *buf, int len, int64_t off)
{
Mntcache *mc;
Extent *tail;
Extent *e, *f, *p;
int o, ee, eblock;
uint32_t offset;
if(off > maxcache || len == 0)
return;
mc = c->mc;
if(mc == nil)
return;
qlock(mc);
if(cdev(mc, c) == 0) {
qunlock(mc);
return;
}
/*
* Find the insertion point
*/
offset = off;
p = 0;
for(f = mc->list; f; f = f->next) {
if(f->start > offset)
break;
p = f;
}
/* trim if there is a successor */
eblock = offset+len;
if(f != 0 && eblock > f->start) {
len -= (eblock - f->start);
if(len <= 0) {
qunlock(mc);
return;
}
}
if(p == 0) { /* at the head */
e = cchain(buf, offset, len, &tail);
if(e != 0) {
mc->list = e;
tail->next = f;
}
qunlock(mc);
return;
}
/* trim to the predecessor */
ee = p->start+p->len;
if(offset < ee) {
o = ee - offset;
len -= o;
if(len <= 0) {
qunlock(mc);
return;
}
buf += o;
offset += o;
}
/* try and pack data into the predecessor */
if(offset == ee && p->len < PGSZ) {
o = len;
if(o > PGSZ - p->len)
o = PGSZ - p->len;
if(cpgmove(p, buf, p->len, o)) {
p->len += o;
buf += o;
len -= o;
offset += o;
if(len <= 0) {
if(f && p->start + p->len > f->start) jehanne_print("CACHE: p->start=%uld p->len=%d f->start=%uld\n", p->start, p->len, f->start);
qunlock(mc);
return;
}
}
}
e = cchain(buf, offset, len, &tail);
if(e != 0) {
p->next = e;
tail->next = f;
}
qunlock(mc);
}
void
cwrite(Chan* c, uint8_t *buf, int len, int64_t off)
{
int o, eo;
Mntcache *mc;
uint32_t eblock, ee;
Extent *p, *f, *e, *tail;
uint32_t offset;
if(off > maxcache || len == 0)
return;
mc = c->mc;
if(mc == nil)
return;
qlock(mc);
if(cdev(mc, c) == 0) {
qunlock(mc);
return;
}
offset = off;
mc->qid.vers++;
c->qid.vers++;
p = 0;
for(f = mc->list; f; f = f->next) {
if(f->start >= offset)
break;
p = f;
}
if(p != 0) {
ee = p->start+p->len;
eo = offset - p->start;
/* pack in predecessor if there is space */
if(offset <= ee && eo < PGSZ) {
o = len;
if(o > PGSZ - eo)
o = PGSZ - eo;
if(cpgmove(p, buf, eo, o)) {
if(eo+o > p->len)
p->len = eo+o;
buf += o;
len -= o;
offset += o;
}
}
}
/* free the overlap -- it's a rare case */
eblock = offset+len;
while(f && f->start < eblock) {
e = f->next;
extentfree(f);
f = e;
}
/* link the block (if any) into the middle */
e = cchain(buf, offset, len, &tail);
if(e != 0) {
tail->next = f;
f = e;
}
if(p == 0)
mc->list = f;
else
p->next = f;
qunlock(mc);
}

View File

@ -151,7 +151,6 @@ newchan(void)
c->dri = 0;
c->aux = 0;
c->mchan = 0;
c->mc = 0;
c->mux = 0;
jehanne_memset(&c->mqid, 0, sizeof(c->mqid));
c->path = 0;
@ -1442,9 +1441,6 @@ namec(char *aname, int amode, long omode, long perm)
/* save registers else error() in open has wrong value of c saved */
saveregisters();
if(omode == OEXEC)
c->flag &= ~CCACHE;
c = c->dev->open(c, omode&~OCEXEC);
if(omode & OCEXEC)

Some files were not shown because too many files have changed in this diff Show More