diff options
author | aiju <devnull@localhost> | 2017-06-27 09:21:30 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2017-06-27 09:21:30 +0000 |
commit | dffbc1e45d61bb928ea6a9d0b1206d641daf24fe (patch) | |
tree | 8a80a3a2c7e964fc89b8776a6dd8326f545a53dc /sys/src/cmd/vmx | |
parent | b5a6dc7849cbd9f1fd23183ba46f0d5deb24e81d (diff) |
vmx(1): I/O string instructions, incomplete support for IDE disks, misc fixes
Diffstat (limited to 'sys/src/cmd/vmx')
-rw-r--r-- | sys/src/cmd/vmx/dat.h | 29 | ||||
-rw-r--r-- | sys/src/cmd/vmx/exith.c | 80 | ||||
-rw-r--r-- | sys/src/cmd/vmx/fns.h | 6 | ||||
-rw-r--r-- | sys/src/cmd/vmx/ide.c | 591 | ||||
-rw-r--r-- | sys/src/cmd/vmx/io.c | 41 | ||||
-rw-r--r-- | sys/src/cmd/vmx/ksetup.c | 6 | ||||
-rw-r--r-- | sys/src/cmd/vmx/mkfile | 1 | ||||
-rw-r--r-- | sys/src/cmd/vmx/pci.c | 2 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vga.c | 42 | ||||
-rw-r--r-- | sys/src/cmd/vmx/virtio.c | 4 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vmx.c | 93 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vmxgdb.c | 1 | ||||
-rw-r--r-- | sys/src/cmd/vmx/x86.c | 184 |
13 files changed, 1008 insertions, 72 deletions
diff --git a/sys/src/cmd/vmx/dat.h b/sys/src/cmd/vmx/dat.h index f34fd3164..34c2fd888 100644 --- a/sys/src/cmd/vmx/dat.h +++ b/sys/src/cmd/vmx/dat.h @@ -112,3 +112,32 @@ struct VgaMode { 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; +}; diff --git a/sys/src/cmd/vmx/exith.c b/sys/src/cmd/vmx/exith.c index 69aba6bac..127b8f062 100644 --- 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 @@ stepmmio(uvlong pa, uvlong *val, int size, ExitInfo *ei) 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((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; } - 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); 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 @@ getcpuid(ulong idx) return nil; } +int maxcpuid = 7; + static void cpuid(ExitInfo *ei) { @@ -237,7 +263,7 @@ cpuid(ExitInfo *ei) 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; diff --git a/sys/src/cmd/vmx/fns.h b/sys/src/cmd/vmx/fns.h index f37a6512d..464613030 100644 --- a/sys/src/cmd/vmx/fns.h +++ b/sys/src/cmd/vmx/fns.h @@ -3,6 +3,8 @@ void loadkernel(char *); 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 @@ u32int iowhine(int, u16int, u32int, int, void *); 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 roundpow2(u32int); 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); diff --git a/sys/src/cmd/vmx/ide.c b/sys/src/cmd/vmx/ide.c new file mode 100644 index 000000000..fdf48f9f4 --- /dev/null +++ b/sys/src/cmd/vmx/ide.c @@ -0,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; + +} diff --git a/sys/src/cmd/vmx/io.c b/sys/src/cmd/vmx/io.c index a61347248..b98d2f7ec 100644 --- 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 @@ kbdcmd(u8int val) case 0xee: keyputc(0xee); break; /* echo */ default: vmerror("unknown kbd command %#ux", val); + keyputc(0xfe); } } i8042kick(nil); @@ -1124,14 +1125,6 @@ uartinit(int n, char *cfg) } } -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 @@ struct IOHandler { 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 @@ IOHandler handlers[] = { 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; @@ -1283,3 +1277,28 @@ io(int dir, u16int port, u32int val, int size) 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; + } +} diff --git a/sys/src/cmd/vmx/ksetup.c b/sys/src/cmd/vmx/ksetup.c index d3a17e1bc..be0329528 100644 --- a/sys/src/cmd/vmx/ksetup.c +++ b/sys/src/cmd/vmx/ksetup.c @@ -244,7 +244,7 @@ elff(uchar **p, uchar *e, int sz) 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 @@ elfdata(void) } 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) diff --git a/sys/src/cmd/vmx/mkfile b/sys/src/cmd/vmx/mkfile index bc2478a85..c5c8996ac 100644 --- a/sys/src/cmd/vmx/mkfile +++ b/sys/src/cmd/vmx/mkfile @@ -8,6 +8,7 @@ OFILES=\ ksetup.$O \ exith.$O \ io.$O \ + ide.$O \ vga.$O \ pci.$O \ virtio.$O \ diff --git a/sys/src/cmd/vmx/pci.c b/sys/src/cmd/vmx/pci.c index 547c2f29e..80dc7aba9 100644 --- a/sys/src/cmd/vmx/pci.c +++ b/sys/src/cmd/vmx/pci.c @@ -280,7 +280,7 @@ pcibusmap(void) 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){ diff --git a/sys/src/cmd/vmx/vga.c b/sys/src/cmd/vmx/vga.c index 37c533539..e85807783 100644 --- a/sys/src/cmd/vmx/vga.c +++ b/sys/src/cmd/vmx/vga.c @@ -40,6 +40,8 @@ struct VGA { 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 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) 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 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) 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 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) 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 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) 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 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) 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){ diff --git a/sys/src/cmd/vmx/virtio.c b/sys/src/cmd/vmx/virtio.c index 420974a7d..86edccc97 100644 --- a/sys/src/cmd/vmx/virtio.c +++ b/sys/src/cmd/vmx/virtio.c @@ -145,7 +145,7 @@ waitloop: 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 @@ vioqaddrset(VIOQueue *q, u64int addr) 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){ diff --git a/sys/src/cmd/vmx/vmx.c b/sys/src/cmd/vmx/vmx.c index 481424935..faa684521 100644 --- a/sys/src/cmd/vmx/vmx.c +++ b/sys/src/cmd/vmx/vmx.c @@ -223,19 +223,52 @@ goti: 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 @@ siparse(char *s) } 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 @@ usage(void) threadexitsall("usage"); } +void (*kconfig)(void); + void threadmain(int argc, char **argv) { @@ -537,9 +605,16 @@ threadmain(int argc, char **argv) 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 @@ threadmain(int argc, char **argv) if(srvname != nil) usage(); srvname = EARGF(usage()); break; + case L'ι': + setiodebug(EARGF(usage())); + break; default: usage(); } ARGEND; @@ -586,6 +664,7 @@ threadmain(int argc, char **argv) pcibusmap(); if(srvname != nil) init9p(srvname); + if(kconfig != nil) kconfig(); runloop(); exits(nil); } diff --git a/sys/src/cmd/vmx/vmxgdb.c b/sys/src/cmd/vmx/vmxgdb.c index 827b7f508..07aba25c5 100644 --- a/sys/src/cmd/vmx/vmxgdb.c +++ b/sys/src/cmd/vmx/vmxgdb.c @@ -162,6 +162,7 @@ regpacket(void) 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); diff --git a/sys/src/cmd/vmx/x86.c b/sys/src/cmd/vmx/x86.c index 2846504b7..73397b28c 100644 --- a/sys/src/cmd/vmx/x86.c +++ b/sys/src/cmd/vmx/x86.c @@ -11,20 +11,22 @@ struct VMemReq { 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,35 +35,37 @@ translate32(uintptr va, uintptr *pa, uintptr cr4) 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; } static uintptr -translate64(uintptr, uintptr *, uintptr) +translate64(uintptr, uintptr *, int *) { vmerror("long mode translation not implemented"); return 0; } static uintptr (* -translator(uintptr *cr4p))(uintptr, uintptr *, uintptr) +translator(void))(uintptr, uintptr *, int *) { uintptr cr0, cr4, efer; @@ -72,7 +76,6 @@ translator(uintptr *cr4p))(uintptr, uintptr *, uintptr) if((efer & EferLme) != 0) return translate64; cr4 = rget("cr4real"); - *cr4p = cr4; if((cr4 & Cr4Pae) != 0) return translatepae; return translate32; @@ -82,25 +85,30 @@ static void 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); 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 @@ vmemread(void *buf, uintptr len, uintptr va) VMemReq req; memset(&req, 0, sizeof(VMemReq)); + req.wr = 0; req.buf = buf; req.len = len; req.va = va; @@ -123,3 +132,150 @@ vmemread(void *buf, uintptr len, uintptr va) 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; +} |