627 lines
11 KiB
C
627 lines
11 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
|
|
#include "ureg.h"
|
|
|
|
typedef struct IOMap IOMap;
|
|
struct IOMap
|
|
{
|
|
IOMap *next;
|
|
int reserved;
|
|
char tag[13];
|
|
uint32_t start;
|
|
uint32_t end;
|
|
};
|
|
|
|
static struct
|
|
{
|
|
Lock;
|
|
IOMap *map;
|
|
IOMap *free;
|
|
IOMap maps[32]; // some initial free maps
|
|
|
|
QLock ql; // lock for reading map
|
|
} iomap;
|
|
|
|
enum {
|
|
Qdir = 0,
|
|
Qioalloc = 1,
|
|
Qiob,
|
|
Qiow,
|
|
Qiol,
|
|
Qmsr,
|
|
Qbase,
|
|
|
|
Qmax = 32,
|
|
};
|
|
|
|
typedef long Rdwrfn(Chan*, void*, long, int64_t);
|
|
|
|
static Rdwrfn *readfn[Qmax];
|
|
static Rdwrfn *writefn[Qmax];
|
|
|
|
static Dirtab archdir[Qmax] = {
|
|
".", { Qdir, 0, QTDIR }, 0, 0555,
|
|
"ioalloc", { Qioalloc, 0 }, 0, 0444,
|
|
"iob", { Qiob, 0 }, 0, 0660,
|
|
"iow", { Qiow, 0 }, 0, 0660,
|
|
"iol", { Qiol, 0 }, 0, 0660,
|
|
"msr", { Qmsr, 0 }, 0, 0660,
|
|
};
|
|
Lock archwlock; /* the lock is only for changing archdir */
|
|
int narchdir = Qbase;
|
|
|
|
/*
|
|
* Add a file to the #P listing. Once added, you can't delete it.
|
|
* You can't add a file with the same name as one already there,
|
|
* and you get a pointer to the Dirtab entry so you can do things
|
|
* like change the Qid version. Changing the Qid path is disallowed.
|
|
*/
|
|
Dirtab*
|
|
addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
|
|
{
|
|
int i;
|
|
Dirtab d;
|
|
Dirtab *dp;
|
|
|
|
memset(&d, 0, sizeof d);
|
|
strcpy(d.name, name);
|
|
d.perm = perm;
|
|
|
|
lock(&archwlock);
|
|
if(narchdir >= Qmax){
|
|
unlock(&archwlock);
|
|
print("addarchfile: out of entries for %s\n", name);
|
|
return nil;
|
|
}
|
|
|
|
for(i=0; i<narchdir; i++)
|
|
if(strcmp(archdir[i].name, name) == 0){
|
|
unlock(&archwlock);
|
|
return nil;
|
|
}
|
|
|
|
d.qid.path = narchdir;
|
|
archdir[narchdir] = d;
|
|
readfn[narchdir] = rdfn;
|
|
writefn[narchdir] = wrfn;
|
|
dp = &archdir[narchdir++];
|
|
unlock(&archwlock);
|
|
|
|
return dp;
|
|
}
|
|
|
|
void
|
|
ioinit(void)
|
|
{
|
|
char *excluded;
|
|
int i;
|
|
|
|
for(i = 0; i < nelem(iomap.maps)-1; i++)
|
|
iomap.maps[i].next = &iomap.maps[i+1];
|
|
iomap.maps[i].next = nil;
|
|
iomap.free = iomap.maps;
|
|
|
|
/*
|
|
* Someone needs to explain why this was here...
|
|
*/
|
|
ioalloc(0x0fff, 1, 0, "dummy"); // i82557 is at 0x1000, the dummy
|
|
// entry is needed for swappable devs.
|
|
|
|
if ((excluded = getconf("ioexclude")) != nil) {
|
|
char *s;
|
|
|
|
s = excluded;
|
|
while (s && *s != '\0' && *s != '\n') {
|
|
char *ends;
|
|
int io_s, io_e;
|
|
|
|
io_s = (int)strtol(s, &ends, 0);
|
|
if (ends == nil || ends == s || *ends != '-') {
|
|
print("ioinit: cannot parse option string\n");
|
|
break;
|
|
}
|
|
s = ++ends;
|
|
|
|
io_e = (int)strtol(s, &ends, 0);
|
|
if (ends && *ends == ',')
|
|
*ends++ = '\0';
|
|
s = ends;
|
|
|
|
ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Reserve a range to be ioalloced later.
|
|
* This is in particular useful for exchangable cards, such
|
|
* as pcmcia and cardbus cards.
|
|
*/
|
|
int
|
|
ioreserve(int _1, int size, int align, char *tag)
|
|
{
|
|
IOMap *map, **l;
|
|
int i, port;
|
|
|
|
lock(&iomap);
|
|
// find a free port above 0x400 and below 0x1000
|
|
port = 0x400;
|
|
for(l = &iomap.map; *l; l = &(*l)->next){
|
|
map = *l;
|
|
if (map->start < 0x400)
|
|
continue;
|
|
i = map->start - port;
|
|
if(i > size)
|
|
break;
|
|
if(align > 0)
|
|
port = ((port+align-1)/align)*align;
|
|
else
|
|
port = map->end;
|
|
}
|
|
if(*l == nil){
|
|
unlock(&iomap);
|
|
return -1;
|
|
}
|
|
map = iomap.free;
|
|
if(map == nil){
|
|
print("ioalloc: out of maps");
|
|
unlock(&iomap);
|
|
return port;
|
|
}
|
|
iomap.free = map->next;
|
|
map->next = *l;
|
|
map->start = port;
|
|
map->end = port + size;
|
|
map->reserved = 1;
|
|
strncpy(map->tag, tag, sizeof(map->tag));
|
|
map->tag[sizeof(map->tag)-1] = 0;
|
|
*l = map;
|
|
|
|
archdir[0].qid.vers++;
|
|
|
|
unlock(&iomap);
|
|
return map->start;
|
|
}
|
|
|
|
/*
|
|
* alloc some io port space and remember who it was
|
|
* alloced to. if port < 0, find a free region.
|
|
*/
|
|
int
|
|
ioalloc(int port, int size, int align, char *tag)
|
|
{
|
|
IOMap *map, **l;
|
|
int i;
|
|
|
|
lock(&iomap);
|
|
if(port < 0){
|
|
// find a free port above 0x400 and below 0x1000
|
|
port = 0x400;
|
|
for(l = &iomap.map; *l; l = &(*l)->next){
|
|
map = *l;
|
|
if (map->start < 0x400)
|
|
continue;
|
|
i = map->start - port;
|
|
if(i > size)
|
|
break;
|
|
if(align > 0)
|
|
port = ((port+align-1)/align)*align;
|
|
else
|
|
port = map->end;
|
|
}
|
|
if(*l == nil){
|
|
unlock(&iomap);
|
|
return -1;
|
|
}
|
|
} else {
|
|
// Only 64KB I/O space on the x86.
|
|
if((port+size) > 0x10000){
|
|
unlock(&iomap);
|
|
return -1;
|
|
}
|
|
// see if the space clashes with previously allocated ports
|
|
for(l = &iomap.map; *l; l = &(*l)->next){
|
|
map = *l;
|
|
if(map->end <= port)
|
|
continue;
|
|
if(map->reserved && map->start == port && map->end == port + size) {
|
|
map->reserved = 0;
|
|
unlock(&iomap);
|
|
return map->start;
|
|
}
|
|
if(map->start >= port+size)
|
|
break;
|
|
unlock(&iomap);
|
|
return -1;
|
|
}
|
|
}
|
|
map = iomap.free;
|
|
if(map == nil){
|
|
print("ioalloc: out of maps");
|
|
unlock(&iomap);
|
|
return port;
|
|
}
|
|
iomap.free = map->next;
|
|
map->next = *l;
|
|
map->start = port;
|
|
map->end = port + size;
|
|
strncpy(map->tag, tag, sizeof(map->tag));
|
|
map->tag[sizeof(map->tag)-1] = 0;
|
|
*l = map;
|
|
|
|
archdir[0].qid.vers++;
|
|
|
|
unlock(&iomap);
|
|
return map->start;
|
|
}
|
|
|
|
void
|
|
iofree(int port)
|
|
{
|
|
IOMap *map, **l;
|
|
|
|
lock(&iomap);
|
|
for(l = &iomap.map; *l; l = &(*l)->next){
|
|
if((*l)->start == port){
|
|
map = *l;
|
|
*l = map->next;
|
|
map->next = iomap.free;
|
|
iomap.free = map;
|
|
break;
|
|
}
|
|
if((*l)->start > port)
|
|
break;
|
|
}
|
|
archdir[0].qid.vers++;
|
|
unlock(&iomap);
|
|
}
|
|
|
|
int
|
|
iounused(int start, int end)
|
|
{
|
|
IOMap *map;
|
|
|
|
for(map = iomap.map; map; map = map->next){
|
|
if(start >= map->start && start < map->end
|
|
|| start <= map->start && end > map->start)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
checkport(int start, int end)
|
|
{
|
|
/* standard vga regs are OK */
|
|
if(start >= 0x2b0 && end <= 0x2df+1)
|
|
return;
|
|
if(start >= 0x3c0 && end <= 0x3da+1)
|
|
return;
|
|
|
|
if(iounused(start, end))
|
|
return;
|
|
error(Eperm);
|
|
}
|
|
|
|
static Chan*
|
|
archattach(Chan *c, Chan *ac, char *spec, int flags)
|
|
{
|
|
return devattach('P', spec);
|
|
}
|
|
|
|
Walkqid*
|
|
archwalk(Chan* c, Chan *nc, char** name, int nname)
|
|
{
|
|
return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
|
|
}
|
|
|
|
static long
|
|
archstat(Chan* c, uint8_t* dp, long n)
|
|
{
|
|
return devstat(c, dp, n, archdir, narchdir, devgen);
|
|
}
|
|
|
|
static Chan*
|
|
archopen(Chan* c, unsigned long omode)
|
|
{
|
|
return devopen(c, omode, archdir, narchdir, devgen);
|
|
}
|
|
|
|
static void
|
|
archclose(Chan* _1)
|
|
{
|
|
}
|
|
|
|
enum
|
|
{
|
|
Linelen= 31,
|
|
};
|
|
|
|
static long
|
|
archread(Chan *c, void *a, long n, int64_t offset)
|
|
{
|
|
char *buf, *p;
|
|
int port;
|
|
uint16_t *sp;
|
|
uint32_t *lp;
|
|
int64_t *vp;
|
|
IOMap *map;
|
|
Rdwrfn *fn;
|
|
|
|
switch((uint32_t)c->qid.path){
|
|
|
|
case Qdir:
|
|
return devdirread(c, a, n, archdir, narchdir, devgen);
|
|
|
|
case Qiob:
|
|
port = offset;
|
|
checkport(offset, offset+n);
|
|
for(p = a; port < offset+n; port++)
|
|
*p++ = inb(port);
|
|
return n;
|
|
|
|
case Qiow:
|
|
if(n & 1)
|
|
error(Ebadarg);
|
|
checkport(offset, offset+n);
|
|
sp = a;
|
|
for(port = offset; port < offset+n; port += 2)
|
|
*sp++ = ins(port);
|
|
return n;
|
|
|
|
case Qiol:
|
|
if(n & 3)
|
|
error(Ebadarg);
|
|
checkport(offset, offset+n);
|
|
lp = a;
|
|
for(port = offset; port < offset+n; port += 4)
|
|
*lp++ = inl(port);
|
|
return n;
|
|
|
|
case Qmsr:
|
|
if(n & 7)
|
|
error(Ebadarg);
|
|
vp = a;
|
|
for(port = offset; port < offset+n; port += 8)
|
|
*vp++ = rdmsr(port);
|
|
return n;
|
|
|
|
case Qioalloc:
|
|
break;
|
|
|
|
default:
|
|
if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
|
|
return fn(c, a, n, offset);
|
|
error(Eperm);
|
|
break;
|
|
}
|
|
|
|
if((buf = malloc(n)) == nil)
|
|
error(Enomem);
|
|
p = buf;
|
|
n = n/Linelen;
|
|
offset = offset/Linelen;
|
|
|
|
lock(&iomap);
|
|
for(map = iomap.map; n > 0 && map != nil; map = map->next){
|
|
if(offset-- > 0)
|
|
continue;
|
|
sprint(p, "%#8lux %#8lux %-12.12s\n", map->start, map->end-1, map->tag);
|
|
p += Linelen;
|
|
n--;
|
|
}
|
|
unlock(&iomap);
|
|
|
|
n = p - buf;
|
|
memmove(a, buf, n);
|
|
free(buf);
|
|
|
|
return n;
|
|
}
|
|
|
|
static long
|
|
archwrite(Chan *c, void *a, long n, int64_t offset)
|
|
{
|
|
char *p;
|
|
int port;
|
|
uint16_t *sp;
|
|
uint32_t *lp;
|
|
int64_t *vp;
|
|
Rdwrfn *fn;
|
|
|
|
switch((uint32_t)c->qid.path){
|
|
|
|
case Qiob:
|
|
p = a;
|
|
checkport(offset, offset+n);
|
|
for(port = offset; port < offset+n; port++)
|
|
outb(port, *p++);
|
|
return n;
|
|
|
|
case Qiow:
|
|
if(n & 1)
|
|
error(Ebadarg);
|
|
checkport(offset, offset+n);
|
|
sp = a;
|
|
for(port = offset; port < offset+n; port += 2)
|
|
outs(port, *sp++);
|
|
return n;
|
|
|
|
case Qiol:
|
|
if(n & 3)
|
|
error(Ebadarg);
|
|
checkport(offset, offset+n);
|
|
lp = a;
|
|
for(port = offset; port < offset+n; port += 4)
|
|
outl(port, *lp++);
|
|
return n;
|
|
|
|
case Qmsr:
|
|
if(n & 7)
|
|
error(Ebadarg);
|
|
vp = a;
|
|
for(port = offset; port < offset+n; port += 8)
|
|
wrmsr(port, *vp++);
|
|
return n;
|
|
|
|
default:
|
|
if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
|
|
return fn(c, a, n, offset);
|
|
error(Eperm);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Dev archdevtab = {
|
|
'P',
|
|
"arch",
|
|
|
|
devreset,
|
|
devinit,
|
|
devshutdown,
|
|
archattach,
|
|
archwalk,
|
|
archstat,
|
|
archopen,
|
|
devcreate,
|
|
archclose,
|
|
archread,
|
|
devbread,
|
|
archwrite,
|
|
devbwrite,
|
|
devremove,
|
|
devwstat,
|
|
};
|
|
|
|
/*
|
|
*/
|
|
void
|
|
nop(void)
|
|
{
|
|
}
|
|
|
|
void (*coherence)(void) = mfence;
|
|
|
|
static long
|
|
cputyperead(Chan* _1, void *a, long n, int64_t off)
|
|
{
|
|
char str[32];
|
|
|
|
snprint(str, sizeof(str), "%s %ud\n", "AMD64", m->cpumhz);
|
|
return readstr(off, a, n, str);
|
|
}
|
|
|
|
static long
|
|
numcoresread(Chan* c, void *a, long n, int64_t off)
|
|
{
|
|
char buf[8];
|
|
snprint(buf, 8, "%d\n", sys->nmach);
|
|
return readstr(off, a, n, buf);
|
|
}
|
|
|
|
void
|
|
archinit(void)
|
|
{
|
|
addarchfile("cputype", 0444, cputyperead, nil);
|
|
addarchfile("numcores", 0444, numcoresread, nil);
|
|
}
|
|
|
|
void
|
|
archreset(void)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* BUG: virtualbox does not reset with the code after this call,
|
|
* but this makes the kernel reset correctly everywhere.
|
|
*/
|
|
i8042systemreset();
|
|
|
|
/*
|
|
* And sometimes there is no keyboard...
|
|
*
|
|
* The reset register (0xcf9) is usually in one of the bridge
|
|
* chips. The actual location and sequence could be extracted from
|
|
* ACPI but why bother, this is the end of the line anyway.
|
|
print("Takes a licking and keeps on ticking...\n");
|
|
*/
|
|
*(uint16_t*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */
|
|
i = inb(0xcf9); /* ICHx reset control */
|
|
i &= 0x06;
|
|
outb(0xcf9, i|0x02); /* SYS_RST */
|
|
millidelay(1);
|
|
outb(0xcf9, i|0x06); /* RST_CPU transition */
|
|
|
|
for(;;)
|
|
pause();
|
|
}
|
|
|
|
/*
|
|
* return value and speed of timer
|
|
*/
|
|
uint64_t
|
|
fastticks(uint64_t* hz)
|
|
{
|
|
if(hz != nil)
|
|
*hz = m->cpuhz;
|
|
return rdtsc();
|
|
}
|
|
|
|
uint32_t
|
|
microseconds(void)
|
|
{
|
|
return fastticks2us(rdtsc());
|
|
}
|
|
|
|
/*
|
|
* set next timer interrupt
|
|
*/
|
|
void
|
|
timerset(uint64_t x)
|
|
{
|
|
extern void lapictimerset(uint64_t);
|
|
|
|
lapictimerset(x);
|
|
}
|
|
|
|
void
|
|
cycles(uint64_t* t)
|
|
{
|
|
*t = rdtsc();
|
|
}
|
|
|
|
void
|
|
delay(int millisecs)
|
|
{
|
|
uint64_t r, t;
|
|
|
|
if(millisecs <= 0)
|
|
millisecs = 1;
|
|
r = rdtsc();
|
|
for(t = r + m->cpumhz*1000ull*millisecs; r < t; r = rdtsc())
|
|
;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|