ref: dffbc1e45d61bb928ea6a9d0b1206d641daf24fe
parent: b5a6dc7849cbd9f1fd23183ba46f0d5deb24e81d
author: aiju <devnull@localhost>
date: Tue Jun 27 05:21:30 EDT 2017
vmx(1): I/O string instructions, incomplete support for IDE disks, misc fixes
--- a/sys/src/cmd/vmx/dat.h
+++ b/sys/src/cmd/vmx/dat.h
@@ -112,3 +112,32 @@
u32int chan;
VgaMode *next;
};
+
+extern uchar cmos[0x30];
+
+extern void (*kconfig)(void);
+
+/* arguments for x86access */
+enum {
+ SEGCS,
+ SEGDS,
+ SEGES,
+ SEGFS,
+ SEGGS,
+ SEGSS,
+ SEGMAX,
+};
+
+enum {
+ ACCR,
+ ACCW,
+ ACCX,
+};
+
+/* used to speed up consecutive x86access calls */
+typedef struct TLB TLB;
+struct TLB {
+ int asz, seg, acc;
+ uintptr start, end;
+ uchar *base;
+};
--- a/sys/src/cmd/vmx/exith.c
+++ b/sys/src/cmd/vmx/exith.c
@@ -5,7 +5,7 @@
#include "dat.h"
#include "fns.h"
-int persist = 0;
+int persist = 1;
typedef struct ExitInfo ExitInfo;
struct ExitInfo {
@@ -67,43 +67,67 @@
return 0;
}
-extern u32int io(int, u16int, u32int, int);
-
-u32int iodebug[32];
-
static void
iohandler(ExitInfo *ei)
{
- int port, len, isin;
+ int port, len, inc, isin;
+ int asz, seg;
+ uintptr addr;
u32int val;
- u64int ax;
+ uvlong vval;
+ uintptr cx;
+ static int seglook[8] = {SEGES, SEGCS, SEGSS, SEGDS, SEGFS, SEGGS};
+ TLB tlb;
port = ei->qual >> 16 & 0xffff;
len = (ei->qual & 7) + 1;
isin = (ei->qual & 8) != 0;
- if((ei->qual & 1<<4) != 0){
- vmerror("i/o string instruction not implemented");
- postexc("#ud", NOERRC);
+ if((ei->qual & 1<<4) == 0){ /* not a string instruction */
+ if(isin){
+ val = io(1, port, 0, len);
+ rsetsz(RAX, val, len);
+ }else
+ io(0, port, rget(RAX), len);
+ skipinstr(ei);
return;
}
- if(isin){
- val = io(1, port, 0, len);
- ax = rget(RAX);
- if(len == 1) ax = ax & ~0xff | val & 0xff;
- else if(len == 2) ax = ax & ~0xffff | val & 0xffff;
- else ax = val;
- rset(RAX, ax);
- }else{
- ax = rget(RAX);
- if(len == 1) ax = (u8int) ax;
- else if(len == 2) ax = (u16int) ax;
- io(0, port, ax, len);
- SET(val);
+ if((rget("flags") & 0x400) != 0) inc = -len;
+ else inc = len;
+ switch(ei->iinfo >> 7 & 7){
+ case 0: asz = 2; break;
+ default: asz = 4; break;
+ case 2: asz = 8; break;
}
- if(port < 0x400 && (iodebug[port >> 5] >> (port & 31) & 1) != 0)
- if(isin) vmdebug("in %#.4ux <- %#ux", port, val);
- else vmdebug("out %#.4ux <- %#ux", port, (int)ax);
+ if((ei->qual & 1<<5) != 0)
+ cx = rgetsz(RCX, asz);
+ else
+ cx = 1;
+ addr = isin ? rget(RDI) : rget(RSI);
+ if(isin)
+ seg = SEGES;
+ else
+ seg = seglook[ei->iinfo >> 15 & 7];
+ memset(&tlb, 0, sizeof(TLB));
+ for(; cx > 0; cx--){
+ if(isin){
+ vval = io(1, port, 0, len);
+ if(x86access(seg, addr, asz, &vval, len, ACCW, &tlb) < 0)
+ goto err;
+ }else{
+ if(x86access(seg, addr, asz, &vval, len, ACCR, &tlb) < 0)
+ goto err;
+ io(0, port, vval, len);
+ }
+ addr += inc;
+ }
skipinstr(ei);
+err:
+ if((ei->qual & 1<<5) != 0)
+ rsetsz(RCX, cx, asz);
+ if(isin)
+ rsetsz(RDI, addr, asz);
+ else
+ rsetsz(RSI, addr, asz);
}
typedef struct MemHandler MemHandler;
@@ -225,6 +249,8 @@
return nil;
}
+int maxcpuid = 7;
+
static void
cpuid(ExitInfo *ei)
{
@@ -237,7 +263,7 @@
if(cp == nil) cp = &def;
switch(ax){
case 0: /* highest register & GenuineIntel */
- ax = 7;
+ ax = maxcpuid;
bx = cp->bx;
dx = cp->dx;
cx = cp->cx;
--- a/sys/src/cmd/vmx/fns.h
+++ b/sys/src/cmd/vmx/fns.h
@@ -3,6 +3,8 @@
uvlong rget(char *);
void rpoke(char *, uvlong, int);
#define rset(a,b) rpoke(a,b,0)
+void rsetsz(char *, uvlong, int);
+uvlong rgetsz(char *, int);
void processexit(char *);
void pitadvance(void);
void rtcadvance(void);
@@ -30,6 +32,7 @@
void elcr(u16int);
int mkvionet(char *);
int mkvioblk(char *);
+int mkideblk(char *);
char* rcflush(int);
void i8042kick(void *);
#define GET8(p,n) (*((u8int*)(p)+(n)))
@@ -44,3 +47,6 @@
u32int vgagetpal(u8int);
void vgasetpal(u8int, u32int);
uintptr vmemread(void *, uintptr, uintptr);
+uintptr vmemwrite(void *, uintptr, uintptr);
+int x86access(int, uintptr, int, uvlong*, int, int, TLB *);
+u32int io(int, u16int, u32int, int);
--- /dev/null
+++ b/sys/src/cmd/vmx/ide.c
@@ -1,0 +1,591 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+typedef struct IDE IDE;
+typedef struct IDEIO IDEIO;
+
+struct IDEIO {
+ QLock;
+ Rendez;
+ u8int rbuf[8192];
+ u8int wbuf[1024];
+ u16int rbufrp, rbufwp;
+ u16int wbufrp, wbufwp;
+ vlong addr;
+ int cnt;
+ u8int err, scratched, wr;
+ enum {
+ IIOIDLE,
+ IIOBUSY,
+ } state;
+};
+
+struct IDE {
+ IDEIO io;
+ enum {
+ IDEPRESENT = 1,
+ IDEKEEPFEAT = 2,
+ IDEPIOREAD = 4,
+ IDEPIOWRITE = 8,
+ } flags;
+ u8int stat, err, irq;
+ u8int ctrl , feat;
+ u8int sec, cnt;
+ u16int cyl;
+ u8int head;
+ int fd;
+ vlong size;
+ int pcyl, phead, psec;
+ int lcyl, lhead, lsec;
+} ide[4];
+
+uchar ideint13[4*12];
+int idediskno;
+enum {
+ /* ctrl */
+ IDESRST = 4,
+ IDENIEN = 2,
+ /* stat */
+ IDEBUSY = 0x80,
+ IDEDRDY = 0x40,
+ IDEDSC = 0x10,
+ IDEDRQ = 0x08,
+ IDEERR = 0x01,
+ /* error */
+ IDEUNC = 0x40,
+ IDEIDNF = 0x10,
+ IDEABRT = 0x04,
+};
+
+static void
+idereset(IDE *d)
+{
+ d->ctrl &= IDESRST;
+ if((d->flags & IDEPRESENT) != 0){
+ qlock(&d->io);
+ while(d->io.state != IIOIDLE){
+ d->io.scratched = 1;
+ rwakeup(&d->io);
+ rsleep(&d->io);
+ }
+ d->io.rbufrp = d->io.rbufwp = 0;
+ d->io.scratched = 0;
+ qunlock(&d->io);
+ d->stat = IDEDRDY | IDEDSC;
+ d->err = 1;
+ d->flags &= IDEPRESENT | IDEKEEPFEAT;
+ d->sec = d->cnt = 1;
+ d->cyl = 0;
+ d->head = 0xa0;
+ d->feat = 0;
+ }
+}
+
+static void
+ideirq(IDE *d, int n)
+{
+ IDE *s;
+
+ if(n >= 0)
+ d->irq = n;
+ s = (d - ide & ~1) + (d->head >> 4 & 1) + ide;
+ irqline(14 + (d - ide)/2, s->irq && (s->ctrl & IDENIEN) == 0);
+}
+
+static vlong
+getlba(IDE *d)
+{
+ int he;
+
+ if((d->head & 0x40) != 0)
+ return d->sec | d->cyl << 8 | (d->head & 0xf) << 16;
+ if(d->sec == 0 || d->sec > d->lsec)
+ return -1;
+ he = d->head & 0xf;
+ if(d->cyl >= d->lcyl || he >= d->lhead)
+ return -1;
+ return d->sec - 1 + (he + d->cyl * d->lhead) * d->lsec;
+
+}
+
+static void
+idegoio(IDE *d, int wr)
+{
+ vlong addr;
+
+ addr = getlba(d);
+ if(addr < 0){
+ vmerror("ide%d: access to invalid sector address (access to CHS=(%#.4ux,%#ux,%#.2ux); geometry is (%#.4ux,%#ux,%#.2ux)", d-ide, d->cyl, d->head&0xf, d->sec, d->lcyl, d->lhead, d->lsec);
+ postexc("#bp", NOERRC);
+ d->stat = IDEDRDY | IDEDSC | IDEDRQ | IDEERR;
+ d->err = IDEIDNF;
+ ideirq(d, 1);
+ return;
+ }
+ if(wr){
+ d->stat = IDEDRDY | IDEDRQ | IDEDSC;
+ d->flags |= IDEPIOWRITE;
+ }else{
+ d->stat = IDEDRDY | IDEBUSY | IDEDSC;
+ d->flags |= IDEPIOREAD;
+ }
+ qlock(&d->io);
+ while(d->io.state != IIOIDLE)
+ rsleep(&d->io);
+ d->io.addr = addr;
+ d->io.cnt = (d->cnt - 1 & 0xff) + 1;
+ d->io.err = 0;
+ d->io.wr = wr;
+ d->io.state = IIOBUSY;
+ rwakeup(&d->io);
+ qunlock(&d->io);
+}
+
+static void
+ideincaddr(IDE *d)
+{
+ if((d->head & 0x40) != 0){
+ if(d->sec++ == 255 && d->cyl++ == 65535)
+ d->head = d->head + 1 & 0xf | d->head & 0xf0;
+ }else{
+ if(d->sec++ == d->lsec){
+ d->sec = 1;
+ if((d->head & 0xf) == d->lhead-1){
+ d->head &= 0xf0;
+ if(d->cyl++ == d->lcyl)
+ d->cyl = 0;
+ }else
+ d->head++;
+ }
+ }
+}
+
+static void
+idesecrend(IDE *d)
+{
+ if((d->flags & IDEPIOREAD) == 0){
+ d->stat &= ~IDEDRQ;
+ return;
+ }
+ ideincaddr(d);
+ if(d->io.rbufwp == (u16int)(d->io.rbufrp + sizeof(d->io.rbuf) - 512))
+ rwakeup(&d->io);
+ if(--d->cnt == 0){
+ d->stat &= ~(IDEBUSY|IDEDRQ);
+ d->flags &= ~IDEPIOREAD;
+ }else if(d->io.rbufrp == d->io.rbufwp){
+ if(d->io.err != 0){
+ d->stat = d->stat | IDEERR;
+ d->err = d->io.err;
+ ideirq(d, 1);
+ }else
+ d->stat = d->stat & ~IDEDRQ | IDEBUSY;
+ }else
+ ideirq(d, 1);
+}
+
+static void
+idesecrdone(void *dp)
+{
+ IDE *d;
+
+ d = dp;
+ qlock(&d->io);
+ d->stat = d->stat & ~IDEBUSY | IDEDRQ;
+ if(d->io.err != 0){
+ d->stat |= IDEERR;
+ d->err = d->io.err;
+ }
+ ideirq(d, 1);
+ qunlock(&d->io);
+}
+
+static void
+idesecwend(IDE *d)
+{
+ ideincaddr(d);
+ d->cnt--;
+ if((u16int)(d->io.wbufwp - 512) == d->io.wbufrp)
+ rwakeup(&d->io);
+ if(d->io.wbufwp == (u16int)(d->io.wbufrp + sizeof(d->io.wbuf))){
+ d->stat = d->stat & ~IDEDRQ | IDEBUSY;
+ }else{
+ if(d->cnt == 0)
+ d->stat = d->stat & ~(IDEDRQ|IDEBUSY);
+ ideirq(d, 1);
+ }
+}
+
+static void
+idesecwdone(void *dp)
+{
+ IDE *d;
+
+ d = dp;
+ qlock(&d->io);
+ if(d->cnt == 0)
+ d->stat = d->stat & ~(IDEDRQ|IDEBUSY);
+ else
+ d->stat = d->stat & ~IDEBUSY | IDEDRQ;
+ ideirq(d, 1);
+ qunlock(&d->io);
+}
+
+typedef struct Sector Sector;
+struct Sector {
+ uchar data[512];
+ vlong addr;
+ Sector *next;
+};
+Sector *sectors;
+
+static int
+getsector(vlong a, uchar *p)
+{
+ Sector *s;
+
+ for(s = sectors; s != nil; s = s->next)
+ if(s->addr == a){
+ vmdebug("reading updated sector %lld", a);
+ memmove(p, s->data, 512);
+ return 0;
+ }
+ return -1;
+}
+
+static void
+putsector(vlong a, uchar *dp)
+{
+ Sector *s, **p;
+
+ for(p = §ors; s = *p, s != nil; p = &s->next)
+ if(s->addr == a){
+ memmove(s->data, dp, 512);
+ return;
+ }
+ s = emalloc(sizeof(Sector));
+ s->addr = a;
+ memmove(s->data, dp, 512);
+ *p = s;
+}
+
+static void
+ideioproc(void *dp)
+{
+ IDE *d;
+ IDEIO *io;
+ vlong a;
+ uchar *p;
+ int i, n;
+
+ d = dp;
+ io = &d->io;
+ threadsetname("ide");
+ for(;;){
+ qlock(io);
+ io->state = IIOIDLE;
+ rwakeup(io);
+ while(io->state == IIOIDLE)
+ rsleep(io);
+ a = io->addr;
+ n = io->cnt;
+ qunlock(io);
+
+ if(io->wr){
+ for(i = 0; i < n; i++){
+ qlock(io);
+ while(!io->scratched && io->wbufrp == (io->wbufwp & ~511))
+ rsleep(io);
+ if(io->scratched){
+ qunlock(io);
+ break;
+ }
+ p = io->wbuf + (io->wbufrp & sizeof(io->wbuf) - 1);
+ qunlock(io);
+ putsector(a+i, p);
+ qlock(io);
+ if(io->wbufwp == (u16int)(io->wbufrp + sizeof(io->wbuf)))
+ sendnotif(idesecwdone, d);
+ io->wbufrp += 512;
+ qunlock(io);
+ }
+ }else{
+ for(i = 0; i < n; i++){
+ qlock(io);
+ while(!io->scratched && io->rbufwp == (u16int)((io->rbufrp & ~511) + sizeof(io->rbuf)))
+ rsleep(io);
+ if(io->scratched){
+ qunlock(io);
+ break;
+ }
+ p = io->rbuf + (io->rbufwp & sizeof(io->rbuf) - 1);
+ qunlock(io);
+ werrstr("eof");
+ if(getsector(a+i, p) < 0 && pread(d->fd, p, 512, (a+i)*512) < 512){
+ vmerror("ide%d: read: %r", d - ide);
+ qlock(io);
+ io->err = IDEUNC;
+ qunlock(io);
+ sendnotif(idesecrdone, d);
+ break;
+ }
+ qlock(io);
+ if(io->rbufrp == io->rbufwp)
+ sendnotif(idesecrdone, d);
+ io->rbufwp += 512;
+ qunlock(io);
+ }
+ }
+ }
+}
+
+static void
+idecmd(IDE *d, u8int cmd)
+{
+ u8int *p;
+ vlong vl;
+
+ if(cmd == 0)
+ return;
+ switch(cmd){
+ case 0x90:
+ break;
+ default:
+ if((d->flags & IDEPRESENT) == 0){
+ vmerror("ide%d: command %#ux issued to absent drive", d-ide, cmd);
+ return;
+ }
+ }
+ if(cmd >> 4 == 1 || cmd >> 4 == 7){
+ /* relibrate / seek */
+ d->stat = IDEDRDY|IDEDSC;
+ ideirq(d, 1);
+ return;
+ }
+ switch(cmd){
+ case 0x20: case 0x21: /* read (pio) */
+ idegoio(d, 0);
+ break;
+ case 0x30: case 0x31: /* write (pio) */
+ idegoio(d, 1);
+ break;
+ case 0x90: /* diagnostics */
+ d = (d - ide & ~1) + ide;
+ d[0].err = 0;
+ d[1].err = 0;
+ if((d->flags & IDEPRESENT) != 0){
+ d->stat = IDEDRDY|IDEDSC;
+ ideirq(d, 1);
+ }
+ break;
+ case 0x91: /* set translation mode */
+ d->lhead = (d->head & 0xf) + 1;
+ d->lsec = d->cnt;
+ if(d->cnt != 0){
+ vl = d->size / (d->lhead * d->lsec);
+ d->lcyl = vl >= 65535 ? 65535 : vl;
+ }
+ d->stat = IDEDRDY|IDEDSC;
+ ideirq(d, 1);
+ break;
+ case 0xec: /* identify */
+ qlock(&d->io);
+ while(d->io.state != IIOIDLE)
+ rsleep(&d->io);
+ p = d->io.rbuf + (d->io.rbufwp & sizeof(d->io.rbuf) - 1);
+ d->io.rbufwp += 512;
+ memset(p, 0, 512);
+ strcpy((char*)p+20, "13149562358579393248");
+ strcpy((char*)p+46, ".2.781 ");
+ sprint((char*)p+54, "%-40s", "jhidks s");
+ PUT16(p, 0, 0x40);
+ PUT16(p, 2, d->pcyl);
+ PUT16(p, 6, d->phead);
+ PUT16(p, 8, d->psec << 9);
+ PUT16(p, 10, 512);
+ PUT16(p, 12, d->psec);
+ PUT16(p, 98, 0x200);
+ if(d->lsec != 0){
+ PUT16(d, 106, 1);
+ PUT16(d, 108, d->lcyl);
+ PUT16(d, 110, d->lhead);
+ PUT16(d, 112, d->lsec);
+ PUT32(d, 114, d->lcyl * d->lhead * d->lsec);
+ }
+ PUT32(p, 120, d->size >= (u32int)-1 ? -1 : d->size);
+ PUT16(p, 160, 7);
+ qunlock(&d->io);
+ d->stat = IDEDRDY|IDEDSC|IDEDRQ;
+ ideirq(d, 1);
+ break;
+ case 0xef: /* set feature */
+ switch(d->feat){
+ case 1: case 0x81: /* enable/disable 8-bit transfers */
+ case 2: case 0x82: /* enable/disable cache */
+ break;
+ case 0x66: d->flags |= IDEKEEPFEAT; break; /* retain settings */
+ case 0xcc: d->flags &= ~IDEKEEPFEAT; break; /* revert to default on reset */
+ default:
+ vmerror("ide%d: unknown feature %#ux", d-ide, d->feat);
+ d->stat = IDEDRDY|IDEDSC|IDEERR;
+ d->err = IDEABRT;
+ return;
+ }
+ d->stat = IDEDRDY|IDEDSC;
+ break;
+ default:
+ vmerror("ide%d: unknown command %#ux", d-ide, cmd);
+ d->stat = IDEDRDY|IDEDSC|IDEERR;
+ d->err = IDEABRT;
+ }
+}
+
+u32int
+ideio(int isin, u16int port, u32int val, int sz, void *)
+{
+ IDE *d, *e;
+ u32int rc;
+
+ d = &ide[2 * ((port & 0x80) == 0)];
+ d += d->head >> 4 & 1;
+ e = (d - ide ^ 1) + ide;
+ if((port|0x80) != 0x1f0){
+ if(sz != 1)
+ vmerror("ide: access to port %#x with incorrect size %d", port, sz);
+ val = (u8int) val;
+ }
+ if(isin && (d->flags & IDEPRESENT) == 0)
+ return 0;
+ switch(isin << 16 | port | 0x80){
+ case 0x001f0:
+ if((d->flags & IDEPIOWRITE) == 0)
+ return 0;
+ qlock(&d->io);
+ PUT16(d->io.wbuf, d->io.wbufwp & sizeof(d->io.wbuf) - 1, (u16int)val);
+ d->io.wbufwp += 2;
+ if((d->io.wbufwp & 511) == 0)
+ idesecwend(d);
+ qunlock(&d->io);
+ if(sz == 4)
+ ideio(0, port, val >> 16, 2, nil);
+ return 0;
+ case 0x001f1: d->feat = e->feat = val; return 0;
+ case 0x001f2: d->cnt = e->cnt = val; return 0;
+ case 0x001f3: d->sec = e->sec = val; return 0;
+ case 0x001f4: d->cyl = d->cyl & 0xff00 | val; e->cyl = e->cyl & 0xff00 | val; return 0;
+ case 0x001f5: d->cyl = d->cyl & 0xff | val << 8; e->cyl = e->cyl & 0xff | val << 8; return 0;
+ case 0x001f6: d->head = e->head = val | 0xa0; return 0;
+ case 0x001f7: idecmd(d, val); return 0;
+ case 0x003f6:
+ d->ctrl = e->ctrl = val;
+ if((val & IDESRST) != 0){
+ idereset(d);
+ idereset(e);
+ }
+ ideirq(d, -1);
+ return 0;
+
+ case 0x101f0:
+ qlock(&d->io);
+ if(d->io.rbufrp != d->io.rbufwp){
+ rc = GET16(d->io.rbuf, d->io.rbufrp & sizeof(d->io.rbuf)-1);
+ d->io.rbufrp += 2;
+ if((d->io.rbufrp & 511) == 0)
+ idesecrend(d);
+ }else
+ rc = 0;
+ qunlock(&d->io);
+ if(sz == 4)
+ rc |= ideio(1, port, 0, 2, nil) << 16;
+ return rc;
+ case 0x101f1: return d->err;
+ case 0x101f2: return d->cnt;
+ case 0x101f3: return d->sec;
+ case 0x101f4: return (u8int)d->cyl;
+ case 0x101f5: return d->cyl >> 8;
+ case 0x101f6: return d->head;
+ case 0x101f7:
+ ideirq(d, 0);
+ case 0x103f6:
+ if((d->ctrl & IDESRST) != 0)
+ return IDEBUSY;
+ rc = d->stat;
+ /* stupid hack to work around different expectations of how DRQ behaves on error */
+ if((d->stat & IDEERR) != 0)
+ d->stat &= ~IDEDRQ;
+ return rc;
+ default: return iowhine(isin, port, val, sz, "ide");
+ }
+}
+
+static int
+idegeom(vlong vsz, int *cp, int *hp, int *sp)
+{
+ int sz, c, h, s, t;
+ int max;
+
+ if(vsz >= 63*255*1023){
+ *cp = 1023;
+ *hp = 255;
+ *sp = 63;
+ return 0;
+ }
+ sz = vsz;
+ max = 0;
+ for(s = 63; s >= 1; s--)
+ for(h = 1; h <= 16; h++){
+ c = sz / (h * s);
+ if(c >= 1023) c = 1023;
+ t = c * h * s;
+ if(t > max){
+ max = t;
+ *cp = c;
+ *hp = h;
+ *sp = s;
+ }
+ }
+ return max == 0 ? -1 : 0;
+}
+
+int
+mkideblk(char *fn)
+{
+ int fd;
+ IDE *d;
+ uchar *p;
+
+ if(idediskno >= 4){
+ werrstr("too many ide disks");
+ return -1;
+ }
+ d = &ide[idediskno];
+ d->io.Rendez.l = &d->io;
+ fd = open(fn, ORDWR);
+ if(fd < 0)
+ return -1;
+ d->size = seek(fd, 0, 2) >> 9;
+ if(idegeom(d->size, &d->pcyl, &d->phead, &d->psec) < 0){
+ werrstr("disk file too small");
+ return -1;
+ }
+ if(idediskno < 2){
+ cmos[0x12] |= 0xf << (1-idediskno) * 4;
+ cmos[0x19 + idediskno] = 47;
+ }
+ p = ideint13 + idediskno * 12;
+ PUT16(p, 0, 0x80 | idediskno);
+ PUT32(p, 2, d->pcyl);
+ PUT16(p, 6, d->psec);
+ PUT16(p, 8, d->phead);
+ PUT16(p, 10, 1);
+ d->flags |= IDEPRESENT;
+ d->fd = fd;
+ idereset(&ide[idediskno]);
+ idediskno++;
+ proccreate(ideioproc, d, 8192);
+ return 0;
+
+}
--- a/sys/src/cmd/vmx/io.c
+++ b/sys/src/cmd/vmx/io.c
@@ -7,7 +7,7 @@
#include "dat.h"
#include "fns.h"
-static uchar cmos[0x30] = {
+uchar cmos[0x30] = {
[1] 0xff, [3] 0xff, [5] 0xff,
[0xa] 0x26,
[0xb] 1<<1,
@@ -727,6 +727,7 @@
case 0xee: keyputc(0xee); break; /* echo */
default:
vmerror("unknown kbd command %#ux", val);
+ keyputc(0xfe);
}
}
i8042kick(nil);
@@ -1124,14 +1125,6 @@
}
}
-static u32int
-ideio(int, u16int port, u32int, int, void *)
-{
- switch(port & 7){
- case 7: return 0x71;
- default: return -1;
- }
-}
/* floppy dummy controller */
typedef struct Floppy Floppy;
@@ -1226,6 +1219,7 @@
u32int vgaio(int, u16int, u32int, int, void *);
u32int pciio(int, u16int, u32int, int, void *);
u32int vesaio(int, u16int, u32int, int, void *);
+u32int ideio(int, u16int, u32int, int, void *);
IOHandler handlers[] = {
0x20, 0x21, picio, nil,
0x40, 0x43, pitio, nil,
@@ -1268,8 +1262,8 @@
0xa79, 0xa79, nopio, nil, /* isa pnp */
};
-u32int
-io(int dir, u16int port, u32int val, int size)
+static u32int
+io0(int dir, u16int port, u32int val, int size)
{
IOHandler *h;
extern PCIBar iobars;
@@ -1282,4 +1276,29 @@
if(port >= p->addr && port < p->addr + p->length)
return p->io(dir, port - p->addr, val, size, p->aux);
return iowhine(dir, port, val, size, nil);
+}
+
+u32int iodebug[32];
+
+u32int
+io(int isin, u16int port, u32int val, int sz)
+{
+ int dbg;
+
+ dbg = port < 0x400 && (iodebug[port >> 5] >> (port & 31) & 1) != 0;
+ if(isin){
+ val = io0(isin, port, val, sz);
+ if(sz == 1) val = (u8int)val;
+ else if(sz == 2) val = (u16int)val;
+ if(dbg)
+ vmdebug("in %#.4ux <- %#.*ux", port, sz*2, val);
+ return val;
+ }else{
+ if(sz == 1) val = (u8int)val;
+ else if(sz == 2) val = (u16int)val;
+ io0(isin, port, val, sz);
+ if(dbg)
+ vmdebug("out %#.4ux <- %#.*ux", port, sz*2, val);
+ return 0;
+ }
}
--- a/sys/src/cmd/vmx/ksetup.c
+++ b/sys/src/cmd/vmx/ksetup.c
@@ -244,7 +244,7 @@
if(sz == -1)
sz = elf64 ? 8 : 4;
if(*p + sz > e){
- fprint(2, "out of bounds: %p > %p", *p + sz, e);
+ fprint(2, "out of bounds: %#p > %#p", *p + sz, e);
return 0;
}
switch(sz){
@@ -396,12 +396,12 @@
}
v = gptr(ph[i].paddr, ph[i].memsz);
if(v == nil)
- sysfatal("invalid address %p (length=%p) in elf", (void*)ph[i].paddr, (void*)ph[i].memsz);
+ sysfatal("invalid address %#p (length=%#p) in elf", (void*)ph[i].paddr, (void*)ph[i].memsz);
if(ph[i].type == PT_OPENBSD_RANDOMIZE)
genrandom(v, ph[i].memsz);
else{
if(ph[i].filesz > ph[i].memsz)
- sysfatal("elf: header entry shorter in memory than in the file (%p < %p)", (void*)ph[i].memsz, (void*)ph[i].filesz);
+ sysfatal("elf: header entry shorter in memory than in the file (%#p < %#p)", (void*)ph[i].memsz, (void*)ph[i].filesz);
if(ph[i].filesz != 0)
epreadn(v, ph[i].filesz, ph[i].offset, "elfdata");
if(ph[i].filesz < ph[i].memsz)
--- a/sys/src/cmd/vmx/mkfile
+++ b/sys/src/cmd/vmx/mkfile
@@ -8,6 +8,7 @@
ksetup.$O \
exith.$O \
io.$O \
+ ide.$O \
vga.$O \
pci.$O \
virtio.$O \
--- a/sys/src/cmd/vmx/pci.c
+++ b/sys/src/cmd/vmx/pci.c
@@ -280,7 +280,7 @@
int i;
iop = 0x1000;
- irqs = 1<<5|1<<7|1<<9|1<<10|1<<11|1<<14|1<<15;
+ irqs = 1<<5|1<<7|1<<9|1<<10|1<<11;
uirqs = 0;
irq = 0;
for(d = pcidevs; d != nil; d = d->next){
--- a/sys/src/cmd/vmx/vga.c
+++ b/sys/src/cmd/vmx/vga.c
@@ -40,6 +40,8 @@
u8int sidx; /* sequencer */
u16int rdidx, wdidx; /* bit 0-1: color */
u8int attr[32];
+ u8int seq[5];
+ u8int graph[9];
u32int pal[256];
Image *col[256];
Image *acol[16];
@@ -155,9 +157,9 @@
if(novga)
return 0;
- if(port == 0x3d4 && sz == 2 && !isin){
- vgaio(0, 0x3d4, (u8int)val, 1, nil);
- return vgaio(0, 0x3d5, (u8int)(val >> 8), 1, nil);
+ if(port != 0x3df && sz == 2 && !isin){
+ vgaio(0, port, (u8int)val, 1, nil);
+ return vgaio(0, port+1, (u8int)(val >> 8), 1, nil);
}
if(sz != 1) vmdebug("vga: non-byte access to port %#ux, sz=%d", port, sz);
val = (u8int) val;
@@ -173,7 +175,12 @@
return 0;
case 0x3c2: vga.miscout = val; return 0;
case 0x3c4: vga.sidx = val; return 0;
- case 0x3c5: vmerror("vga: write to unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
+ case 0x3c5:
+ switch(vga.sidx){
+ case 0: vga.seq[vga.sidx] = val & 3; return 0;
+ case 4: vga.seq[vga.sidx] = val & 0xe; return 0;
+ default: vmerror("vga: write to unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
+ }
case 0x3c6: return 0;
case 0x3c7: vga.rdidx = val << 2; return 0;
case 0x3c8: vga.wdidx = val << 2; return 0;
@@ -183,7 +190,14 @@
vga.wdidx = vga.wdidx + 1 + (vga.wdidx >> 1 & 1) & 0x3ff;
return 0;
case 0x3ce: vga.gidx = val; return 0;
- case 0x3cf: vmerror("vga: write to unknown graphics register %#ux (val=%#ux)", vga.gidx, val); return 0;
+ case 0x3cf:
+ switch(vga.gidx){
+ case 4: vga.graph[vga.gidx] = val & 3; break;
+ case 8: vga.graph[vga.gidx] = val; break;
+ default:
+ vmerror("vga: write to unknown graphics register %#ux (val=%#ux)", vga.gidx, val);
+ }
+ return 0;
case 0x3d4: vga.cidx = val; return 0;
case 0x3d5:
switch(vga.cidx){
@@ -197,7 +211,13 @@
case 0x103c0: return vga.aidx & 0x3f;
case 0x103c1: return vga.attr[vga.aidx & 0x1f];
case 0x103c4: return vga.sidx;
- case 0x103c5: vmerror("vga: read from unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
+ case 0x103c5:
+ switch(vga.sidx){
+ case 0:
+ case 4:
+ return vga.seq[vga.sidx];
+ default: vmerror("vga: read from unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
+ }
case 0x103c6: return 0xff;
case 0x103c7: return vga.rdidx >> 2;
case 0x103c8: return vga.wdidx >> 2;
@@ -207,7 +227,15 @@
return m;
case 0x103cc: return vga.miscout;
case 0x103ce: return vga.gidx;
- case 0x103cf: vmerror("vga: read from unknown graphics register %#ux", vga.gidx); return 0;
+ case 0x103cf:
+ switch(vga.gidx){
+ case 4:
+ case 8:
+ return vga.graph[vga.gidx];
+ default:
+ vmerror("vga: read from unknown graphics register %#ux", vga.gidx);
+ return 0;
+ }
case 0x103d4: return vga.cidx;
case 0x103d5:
switch(vga.cidx){
--- a/sys/src/cmd/vmx/virtio.c
+++ b/sys/src/cmd/vmx/virtio.c
@@ -145,7 +145,7 @@
b->flags = GET16(dp, 12);
b->p = gptr(b->addr, b->len);
if(b->p == nil){
- vmerror("virtio device %#x: invalid buffer pointer %p in queue, ignoring descriptor", q->d->pci->bdf, (void*)b->addr);
+ vmerror("virtio device %#x: invalid buffer pointer %#p in queue, ignoring descriptor", q->d->pci->bdf, (void*)b->addr);
free(b);
break;
}
@@ -291,7 +291,7 @@
sz = sz1 + (-(-(8 * q->size + 6) & -4096));
p = gptr(addr, sz);
if(p == nil)
- vmerror("virtio device %#x: attempt to set queue to invalid address %p", q->d->pci->bdf, (void *) addr);
+ vmerror("virtio device %#x: attempt to set queue to invalid address %#p", q->d->pci->bdf, (void *) addr);
qlock(q);
q->addr = addr;
if(p == nil){
--- a/sys/src/cmd/vmx/vmx.c
+++ b/sys/src/cmd/vmx/vmx.c
@@ -223,19 +223,52 @@
rcdirty[i>>6] |= 1ULL<<(i&63);
}
+uvlong
+rgetsz(char *reg, int sz)
+{
+ switch(sz){
+ case 1: return (u8int)rget(reg);
+ case 2: return (u16int)rget(reg);
+ case 4: return (u32int)rget(reg);
+ case 8: return rget(reg);
+ default:
+ vmerror("invalid size operand for rgetsz");
+ assert(0);
+ return 0;
+ }
+}
+
+void
+rsetsz(char *reg, uvlong val, int sz)
+{
+ switch(sz){
+ case 1: rset(reg, (u8int)val | rget(reg) & ~0xffULL); break;
+ case 2: rset(reg, (u16int)val | rget(reg) & ~0xffffULL); break;
+ case 4: rset(reg, (u32int)val); break;
+ case 8: rset(reg, val); break;
+ default:
+ vmerror("invalid size operand for rsetsz");
+ assert(0);
+ }
+}
+
Region *
mkregion(u64int pa, u64int end, int type)
{
- Region *r, **rp;
+ Region *r, *s, **rp;
r = emalloc(sizeof(Region));
- if(end < pa) sysfatal("end of region %p before start of region %p", (void*)end, (void*)pa);
- if((pa & BY2PG-1) != 0 || (end & BY2PG-1) != 0) sysfatal("address %p not page aligned", (void*)pa);
+ if(end < pa) sysfatal("end of region %p before start of region %#p", (void*)end, (void*)pa);
+ if((pa & BY2PG-1) != 0 || (end & BY2PG-1) != 0) sysfatal("address %#p not page aligned", (void*)pa);
r->start = pa;
r->end = end;
r->type = type;
- for(rp = &mmap; *rp != nil; rp = &(*rp)->next)
+ for(s = mmap; s != nil; s = s->next)
+ if(!(pa < s->start && end < s->end || pa >= s->start && pa >= s->end))
+ sysfatal("region %#p-%#p overlaps region %#p-%#p", pa, end, s->start, s->end);
+ for(rp = &mmap; (*rp) != nil && (*rp)->start < end; rp = &(*rp)->next)
;
+ r->next = *rp;
*rp = r;
return r;
}
@@ -487,6 +520,39 @@
}
static void
+setiodebug(char *s)
+{
+ char *p;
+ int n, m, neg;
+ extern u32int iodebug[32];
+
+ do{
+ if(neg = *s == '!')
+ s++;
+ n = strtoul(s, &p, 0);
+ if(s == p)
+no: sysfatal("invalid iodebug argument (error at %#q)", s);
+ if(n >= sizeof(iodebug)*8)
+range: sysfatal("out of iodebug range (0-%#ux)", sizeof(iodebug)*8-1);
+ s = p + 1;
+ if(*p == '-'){
+ m = strtoul(s, &p, 0);
+ if(m >= sizeof(iodebug)*8)
+ goto range;
+ if(s == p || m < n) goto no;
+ s = p + 1;
+ }else
+ m = n;
+ for(; n <= m; n++)
+ if(neg)
+ iodebug[n>>5] &= ~(1<<(n&31));
+ else
+ iodebug[n>>5] |= 1<<(n&31);
+ }while(*p == ',');
+ if(*p != 0) goto no;
+}
+
+static void
usage(void)
{
char *blanks, *p;
@@ -499,6 +565,8 @@
threadexitsall("usage");
}
+void (*kconfig)(void);
+
void
threadmain(int argc, char **argv)
{
@@ -537,9 +605,16 @@
break;
case 'd':
assert(edevn < nelem(edev));
- edev[edevn] = mkvioblk;
- edevt[edevn] = "virtio block";
- edevaux[edevn++] = strdup(EARGF(usage()));
+ edevaux[edevn] = strdup(EARGF(usage()));
+ if(strncmp(edevaux[edevn], "ide:", 4) == 0){
+ edevaux[edevn] += 4;
+ edev[edevn] = mkideblk;
+ edevt[edevn] = "ide block";
+ }else{
+ edev[edevn] = mkvioblk;
+ edevt[edevn] = "virtio block";
+ }
+ edevn++;
break;
case 'M':
gmemsz = siparse(EARGF(usage()));
@@ -552,6 +627,9 @@
if(srvname != nil) usage();
srvname = EARGF(usage());
break;
+ case L'ι':
+ setiodebug(EARGF(usage()));
+ break;
default:
usage();
} ARGEND;
@@ -586,6 +664,7 @@
pcibusmap();
if(srvname != nil) init9p(srvname);
+ if(kconfig != nil) kconfig();
runloop();
exits(nil);
}
--- a/sys/src/cmd/vmx/vmxgdb.c
+++ b/sys/src/cmd/vmx/vmxgdb.c
@@ -162,6 +162,7 @@
pos += 4;
}
if(i == nelem(regname)) continue;
+ if(f[0][1] == 's' && f[0][2] == 0) v = 0;
l = 4;
while(l--){
sprint(tbuf, "%.2ux", (u8int)v);
--- a/sys/src/cmd/vmx/x86.c
+++ b/sys/src/cmd/vmx/x86.c
@@ -11,20 +11,22 @@
uintptr va, len;
void *buf;
uintptr rc;
+ int wr;
};
static uintptr
-translateflat(uintptr va, uintptr *pa, uintptr)
+translateflat(uintptr va, uintptr *pa, int *perm)
{
if(sizeof(uintptr) != 4 && va >> 32 != 0) return 0;
*pa = va;
if(va == 0)
return 0xFFFFFFFFUL;
+ if(perm != 0) *perm = -1;
return -va;
}
static uintptr
-translate32(uintptr va, uintptr *pa, uintptr cr4)
+translate32(uintptr va, uintptr *pa, int *perm)
{
void *pd, *pt;
u32int pde, pte;
@@ -33,21 +35,23 @@
pd = gptr(rget("cr3") & ~0xfff, 4096);
if(pd == nil) return 0;
pde = GET32(pd, (va >> 22) * 4);
+ if(perm != nil) *perm = pde;
if((pde & 1) == 0) return 0;
- if((pde & 0x80) != 0 && (cr4 & Cr4Pse) != 0){
- *pa = pde & (1<<22) - 1 | (uintptr)(pde & 0xfe000) << 19;
- return (1<<22) - (va & (1<<22)-1);
+ if((pde & 0x80) != 0 && (rget("cr4real") & Cr4Pse) != 0){
+ *pa = pde & 0xffc00000 | (uintptr)(pde & 0x3fe000) << 19 | va & 0x3fffff;
+ return 0x400000 - (va & 0x3fffff);
}
pt = gptr(pde & ~0xfff, 4096);
if(pt == nil) return 0;
pte = GET32(pt, va >> 10 & 0xffc);
if((pte & 1) == 0) return 0;
+ if(perm != nil) *perm &= pte;
*pa = pte & ~0xfff | va & 0xfff;
return 0x1000 - (va & 0xfff);
}
static uintptr
-translatepae(uintptr, uintptr *, uintptr)
+translatepae(uintptr, uintptr *, int *)
{
vmerror("PAE translation not implemented");
return 0;
@@ -54,7 +58,7 @@
}
static uintptr
-translate64(uintptr, uintptr *, uintptr)
+translate64(uintptr, uintptr *, int *)
{
vmerror("long mode translation not implemented");
return 0;
@@ -61,7 +65,7 @@
}
static uintptr (*
-translator(uintptr *cr4p))(uintptr, uintptr *, uintptr)
+translator(void))(uintptr, uintptr *, int *)
{
uintptr cr0, cr4, efer;
@@ -72,7 +76,6 @@
if((efer & EferLme) != 0)
return translate64;
cr4 = rget("cr4real");
- *cr4p = cr4;
if((cr4 & Cr4Pae) != 0)
return translatepae;
return translate32;
@@ -82,18 +85,20 @@
vmemread0(void *aux)
{
VMemReq *req;
- uintptr va, pa, n, ok, pok, cr4;
+ uintptr va, pa, n, ok, pok;
void *v;
- uintptr (*trans)(uintptr, uintptr *, uintptr);
+ uintptr (*trans)(uintptr, uintptr *, int *);
uchar *p;
+ int wr;
req = aux;
va = req->va;
n = req->len;
p = req->buf;
- trans = translator(&cr4);
+ wr = req->wr;
+ trans = translator();
while(n > 0){
- ok = trans(va, &pa, cr4);
+ ok = trans(va, &pa, nil);
if(ok == 0) break;
if(ok > n) ok = n;
v = gptr(pa, 1);
@@ -100,7 +105,10 @@
if(v == nil) break;
pok = gavail(v);
if(ok > pok) ok = pok;
- memmove(p, v, ok);
+ if(wr)
+ memmove(v, p, ok);
+ else
+ memmove(p, v, ok);
n -= ok;
p += ok;
va += ok;
@@ -115,6 +123,7 @@
VMemReq req;
memset(&req, 0, sizeof(VMemReq));
+ req.wr = 0;
req.buf = buf;
req.len = len;
req.va = va;
@@ -122,4 +131,151 @@
sendnotif(vmemread0, &req);
qlock(&req);
return req.rc;
+}
+
+uintptr
+vmemwrite(void *buf, uintptr len, uintptr va)
+{
+ VMemReq req;
+
+ memset(&req, 0, sizeof(VMemReq));
+ req.wr = 1;
+ req.buf = buf;
+ req.len = len;
+ req.va = va;
+ qlock(&req);
+ sendnotif(vmemread0, &req);
+ qlock(&req);
+ return req.rc;
+}
+
+int
+x86access(int seg, uintptr addr0, int asz, uvlong *val, int sz, int acc, TLB *tlb)
+{
+ int cpl;
+ static char *baser[] = {"csbase", "dsbase", "esbase", "fsbase", "gsbase", "ssbase"};
+ static char *limitr[] = {"cslimit", "dslimit", "eslimit", "fslimit", "gslimit", "sslimit"};
+ static char *permr[] = {"csperm", "dsperm", "esperm", "fsperm", "gsperm", "ssperm"};
+ u32int limit, perm;
+ uintptr addr, base, szmax;
+ int pperm, wp, i;
+ uintptr pa[8], pav;
+ uintptr l;
+ uchar *ptr;
+
+ switch(asz){
+ case 2: addr0 = (u16int)addr0; break;
+ case 4: addr0 = (u32int)addr0; break;
+ case 8: break;
+ default:
+ vmerror("invalid asz=%d in x86access", asz);
+ assert(0);
+ }
+ assert(seg < SEGMAX && (uint)acc <= ACCX);
+ addr = addr0;
+ if(tlb != nil && tlb->asz == asz && tlb->seg == seg && tlb->acc == acc && addr >= tlb->start && addr + sz >= addr && addr + sz < tlb->end){
+ ptr = tlb->base + addr;
+ goto fast;
+ }
+ if(sizeof(uintptr) == 8 && asz == 8){
+ if(seg == SEGFS || seg == SEGGS)
+ addr += rget(baser[seg]);
+ if((u16int)((addr >> 48) + 1) > 1){
+ gpf:
+ vmdebug("gpf");
+ postexc("#gp", 0);
+ return -1;
+ }
+ if((vlong)addr >= 0)
+ szmax = (1ULL<<48) - addr;
+ else
+ szmax = -addr;
+ }else{
+ limit = rget(limitr[seg]);
+ perm = rget(permr[seg]);
+ if((perm & 0xc) == 0x4){
+ if((u32int)(addr + sz - 1) < addr || addr <= limit)
+ goto limfault;
+ szmax = (u32int)-addr;
+ }else{
+ if((u64int)addr + sz - 1 >= limit){
+ limfault:
+ vmdebug("limit fault");
+ postexc(seg == SEGSS ? "#ss" : "#gp", 0);
+ return -1;
+ }
+ szmax = limit - addr + 1;
+ }
+ if((perm & 0x10080) != 0x80)
+ goto gpf;
+ switch(acc){
+ case ACCR: if((perm & 0xa) == 8) goto gpf; break;
+ case ACCW: if((perm & 0xa) != 2) goto gpf; break;
+ case ACCX: if((perm & 8) == 0) goto gpf; break;
+ }
+ base = rget(baser[seg]);
+ addr = (u32int)(addr + base);
+ }
+ cpl = rget("cs") & 3;
+ wp = (rget("cr0real") & 1<<16) != 0;
+ for(i = 0; i < sz; ){
+ l = translator()(addr+i, &pav, &pperm);
+ if(l == 0){
+ pf:
+ vmdebug("page fault @ %#p", addr+i);
+ postexc("#pf", pperm & 1 | (acc == ACCW) << 1 | (cpl == 3) << 2 | (acc == ACCX) << 4);
+ rset("cr2", addr+i);
+ return -1;
+ }
+ if((cpl == 3 || wp) && acc == ACCW && (pperm & 2) == 0)
+ goto pf;
+ if(cpl == 3 && (pperm & 4) == 0)
+ goto pf;
+ if(i == 0 && l < szmax) szmax = l;
+ while(i < sz && l-- > 0)
+ pa[i++] = pav++;
+ }
+ if(szmax >= sz){
+ ptr = gptr(pa[0], sz);
+ if(ptr == nil) goto slow;
+ if(tlb != nil){
+ l = gavail(ptr);
+ if(l < szmax) szmax = l;
+ tlb->asz = asz;
+ tlb->seg = seg;
+ tlb->acc = acc;
+ tlb->start = addr0;
+ tlb->end = addr0 + szmax;
+ tlb->base = ptr - addr0;
+ }
+ fast:
+ if(acc == ACCW)
+ switch(sz){
+ case 1: PUT8(ptr, 0, *val); break;
+ case 2: PUT16(ptr, 0, *val); break;
+ case 4: PUT32(ptr, 0, *val); break;
+ case 8: PUT64(ptr, 0, *val); break;
+ }
+ else
+ switch(sz){
+ case 1: *val = GET8(ptr, 0); break;
+ case 2: *val = GET16(ptr, 0); break;
+ case 4: *val = GET32(ptr, 0); break;
+ case 8: *val = GET64(ptr, 0); break;
+ }
+ }else{
+ slow:
+ if(acc != ACCW)
+ *val = 0;
+ for(i = 0; i < sz; i++){
+ ptr = gptr(pa[i], 1);
+ if(ptr == nil)
+ vmerror("x86access: access to unmapped address %#p", pa[i]);
+ else if(acc == ACCW)
+ *ptr = GET8(val, i);
+ else
+ PUT8(val, i, *ptr);
+ }
+ }
+ return 0;
}