ref: 2806a34ec0152cbf8ac0e2198bda111a9b1273bd
parent: ed040d676ad95858211ec8b5f43e8713692fdeb2
author: aiju <devnull@localhost>
date: Sun Jun 18 18:17:35 EDT 2017
vmx(1): linux kernel loading; PIT fixes to support linux; support VGA 0x3D4 word writes; support sending virtio ethernet packets to a file and prepending snoopy headers
--- a/sys/src/cmd/vmx/exith.c
+++ b/sys/src/cmd/vmx/exith.c
@@ -14,6 +14,13 @@
u32int ilen, iinfo;
};
+static char *x86reg[16] = {
+ RAX, RCX, RDX, RBX,
+ RSP, RBP, RSI, RDI,
+ R8, R9, R10, R11,
+ R12, R13, R14, R15
+};
+
static void
skipinstr(ExitInfo *ei)
{
@@ -242,7 +249,7 @@
break;
case 2: goto literal; /* cache stuff */
case 3: goto zero; /* processor serial number */
- case 4: goto literal; /* cache stuff */
+ case 4: goto zero; /* cache stuff */
case 5: goto zero; /* monitor/mwait */
case 6: goto zero; /* thermal management */
case 7: goto zero; /* more features */
@@ -305,6 +312,7 @@
if(rd) val = rget("efer");
else rset("efer", val);
break;
+ case 0x8B: val = 0; break; /* microcode update */
default:
if(rd){
vmerror("read from unknown MSR %#ux ignored", cx);
@@ -323,12 +331,6 @@
static void
movdr(ExitInfo *ei)
{
- static char *reg[16] = {
- RAX, RCX, RDX, RBX,
- RSP, RBP, RSI, RDI,
- R8, R9, R10, R11,
- R12, R13, R14, R15
- };
static char *dr[8] = { "dr0", "dr1", "dr2", "dr3", nil, nil, "dr6", "dr7" };
int q;
@@ -338,13 +340,65 @@
return;
}
if((q & 16) != 0)
- rset(reg[q >> 8 & 15], rget(dr[q & 7]));
+ rset(x86reg[q >> 8 & 15], rget(dr[q & 7]));
else
- rset(dr[q & 7], rget(reg[q >> 8 & 15]));
+ rset(dr[q & 7], rget(x86reg[q >> 8 & 15]));
skipinstr(ei);
}
static void
+movcr(ExitInfo *ei)
+{
+ u32int q;
+
+ q = ei->qual;
+ switch(q & 15){
+ case 0:
+ switch(q >> 4 & 3){
+ case 0:
+ vmdebug("illegal CR0 write, value %#ux", rget(x86reg[q >> 8 & 15]));
+ rset("cr0real", rget(x86reg[q >> 8 & 15]));
+ skipinstr(ei);
+ break;
+ case 1:
+ vmerror("shouldn't happen: trap on MOV from CR0");
+ rset(x86reg[q >> 8 & 15], rget("cr0fake"));
+ skipinstr(ei);
+ break;
+ case 2:
+ vmerror("shouldn't happen: trap on CLTS");
+ rset("cr0real", rget("cr0real") & ~8);
+ skipinstr(ei);
+ break;
+ case 3:
+ vmerror("LMSW handler unimplemented");
+ postexc("#ud", NOERRC);
+ }
+ break;
+ case 4:
+ switch(ei->qual >> 4 & 3){
+ case 0:
+ vmdebug("illegal CR4 write, value %#ux", rget(x86reg[q >> 8 & 15]));
+ rset("cr4real", rget(x86reg[q >> 8 & 15]));
+ skipinstr(ei);
+ break;
+ case 1:
+ vmerror("shouldn't happen: trap on MOV from CR4");
+ rset(x86reg[q >> 8 & 15], rget("cr3fake"));
+ skipinstr(ei);
+ break;
+ default:
+ vmerror("unknown CR4 operation %d", q);
+ postexc("#ud", NOERRC);
+ }
+ break;
+ default:
+ vmerror("access to unknown control register CR%d", ei->qual & 15);
+ postexc("#ud", NOERRC);
+ }
+}
+
+static void
dbgexc(ExitInfo *ei)
{
rset("dr6", rget("dr6") | ei->qual);
@@ -380,6 +434,7 @@
{".wrmsr", rdwrmsr},
{".movdr", movdr},
{"#db", dbgexc},
+ {"movcr", movcr},
};
void
--- a/sys/src/cmd/vmx/io.c
+++ b/sys/src/cmd/vmx/io.c
@@ -363,6 +363,7 @@
p->init = 4;
picupdate(p);
return 0;
+ case 0:
case 4:
p->imr = val;
picupdate(p);
@@ -412,10 +413,12 @@
enum { READLO, READHI, READLATLO, READLATHI } readstate;
u8int writestate;
vlong lastnsec;
+ u8int output;
};
PITChannel pit[3] = {
[0] { .state 1 },
};
+u8int port61;
enum { PERIOD = 838 };
void
@@ -437,6 +440,18 @@
threadint(timerid);
}
+static void
+pitout(int n, int v)
+{
+ if(n == 0)
+ irqline(0, v);
+ switch(v){
+ case IRQLLOHI: case 1: pit[n].output = 1; break;
+ case 0: pit[n].output = 0; break;
+ case IRQLTOGGLE: pit[n].output ^= 1; break;
+ }
+}
+
void
pitadvance(void)
{
@@ -455,11 +470,11 @@
case 0:
if(p->state != 0){
nc = t / PERIOD;
- if(p->count <= nc && i == 0)
- irqline(0, 1);
+ if(p->count <= nc)
+ pitout(i, 1);
p->count -= nc;
p->lastnsec -= t % PERIOD;
- if(i == 0 && (pic[0].lines & 1<<0) == 0)
+ if(!p->output)
settimer(p->lastnsec + p->count * PERIOD);
}
break;
@@ -474,8 +489,7 @@
nc -= p->count - 1;
nc %= rel;
p->count = rel - nc + 1;
- if(i == 0)
- irqline(0, IRQLLOHI);
+ pitout(i, IRQLLOHI);
}
p->lastnsec -= t % PERIOD;
settimer(p->lastnsec + p->count * PERIOD);
@@ -492,8 +506,7 @@
nc -= p->count;
nc %= rel;
p->count = rel - nc;
- if(i == 0)
- irqline(0, IRQLTOGGLE);
+ pitout(i, IRQLTOGGLE);
}
p->lastnsec -= t % PERIOD;
settimer(p->lastnsec + p->count / 2 * PERIOD);
@@ -515,8 +528,7 @@
p->reload = p->reload & 0xff00 | v;
switch(p->mode){
case 0:
- if(n == 0)
- irqline(0, 0);
+ pitout(n, 0);
if(p->access != 3 || hi){
p->count = p->reload;
p->state = 1;
@@ -569,6 +581,7 @@
return pit[n].latch >> 8;
}
return 0;
+ case 0x10061: return port61 | pit[2].output << 5;
case 0x40:
case 0x41:
case 0x42:
@@ -608,16 +621,18 @@
pit[n].readstate = pit[n].access == 1 ? READHI : READLO;
pit[n].writestate = pit[n].access == 1 ? READHI : READLO;
pit[n].lastnsec = nsec();
- if(n == 0)
- switch(pit[n].mode){
- case 0:
- irqline(0, 0);
- break;
- default:
- irqline(0, 1);
- }
+ switch(pit[n].mode){
+ case 0:
+ pitout(n, 0);
+ break;
+ default:
+ pitout(n, 1);
+ }
}
return 0;
+ case 0x61:
+ port61 = port61 & 0xf0 | val & 0x0f;
+ return 0;
}
return iowhine(isin, port, val, sz, "pit");
}
@@ -628,7 +643,7 @@
int cmd;
u16int buf; /* |0x100 == kbd, |0x200 == mouse, |0x400 == cmd */
} i8042 = {
- .cfg 0x34,
+ .cfg 0x74,
.stat 0x10,
.oport 0x01,
.cmd -1,
@@ -809,7 +824,13 @@
break;
case 0xe7: mouseputc(0xfa); mouse.scaling21 = 1; break; /* set 2:1 scaling */
case 0xe6: mouseputc(0xfa); mouse.scaling21 = 0; break; /* set 1:1 scaling */
- default: vmerror("unknown mouse command %#ux", val); mouseputc(0xfc);
+
+ case 0x88: case 0x00: case 0x0a: /* sentelic & cypress */
+ case 0xe1: /* trackpoint */
+ mouseputc(0xfe);
+ break;
+
+ default: vmerror("unknown mouse command %#ux", val); mouseputc(0xfe);
}
}
i8042kick(nil);
@@ -894,6 +915,12 @@
case 0x60: case 0xd1: case 0xd2: case 0xd3: case 0xd4:
i8042.cmd = val;
return 0;
+ case 0xf0: case 0xf2: case 0xf4: case 0xf6: /* pulse reset line */
+ case 0xf8: case 0xfa: case 0xfc: case 0xfe:
+ sysfatal("i8042: system reset");
+ case 0xf1: case 0xf3: case 0xf5: case 0xf7: /* no-op */
+ case 0xf9: case 0xfb: case 0xfd: case 0xff:
+ return 0;
}
vmerror("unknown i8042 command %#ux", val);
return 0;
@@ -1204,6 +1231,7 @@
0x70, 0x71, rtcio, nil,
0xa0, 0xa1, picio, nil,
0x60, 0x60, i8042io, nil,
+ 0x61, 0x61, pitio, nil, /* pc speaker */
0x64, 0x64, i8042io, nil,
0x2f8, 0x2ff, uartio, nil,
0x3b0, 0x3bb, vgaio, nil,
@@ -1219,7 +1247,7 @@
0x3f6, 0x3f6, ideio, nil, /* ide primary (aux) */
0x3f0, 0x3f7, fdcio, nil, /* floppy */
- 0x061, 0x061, nopio, nil, /* pc speaker */
+ 0x080, 0x080, nopio, nil, /* dma -- used by linux for delay by dummy write */
0x084, 0x084, nopio, nil, /* dma -- used by openbsd for delay by dummy read */
0x100, 0x110, nopio, nil, /* elnk3 */
0x240, 0x25f, nopio, nil, /* ne2000 */
--- a/sys/src/cmd/vmx/ksetup.c
+++ b/sys/src/cmd/vmx/ksetup.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
+#include <draw.h>
#include <libsec.h>
#include "dat.h"
#include "fns.h"
@@ -687,6 +688,208 @@
return 0;
}
+static void
+linuxbootmod(char *fn, void *zp, u32int kend)
+{
+ u32int addr;
+ uintptr memend;
+ int fd;
+ vlong sz;
+ void *v;
+ int rc;
+
+ fd = open(fn, OREAD);
+ if(fd < 0) sysfatal("linux: initrd: open: %r");
+ sz = seek(fd, 0, 2);
+ if(sz < 0) sysfatal("linux: initrd: seek: %r");
+ if(sz == 0) sysfatal("linux: empty initrd");
+ addr = GET32(zp, 0x22c);
+ memend = (1<<20) + gavail(gptr(1<<20, 0));
+ if(addr >= memend) addr = memend - 1;
+ if((addr - (sz - 1) & -4) < kend) sysfatal("linux: no room for initrd");
+ addr = addr - (sz - 1) & -4;
+ print("%#ux %#ux\n", addr, (u32int)sz);
+ v = gptr(addr, sz);
+ if(v == nil) sysfatal("linux: initrd: gptr failed");
+ seek(fd, 0, 0);
+ rc = readn(fd, v, sz);
+ if(rc < 0) sysfatal("linux: initrd: read: %r");
+ if(rc < sz) sysfatal("linux: initrd: short read");
+ close(fd);
+ PUT32(zp, 0x218, addr);
+ PUT32(zp, 0x21C, sz);
+}
+
+static void
+linuxscreeninfo(void *zp)
+{
+ extern VgaMode *curmode, textmode;
+ extern uintptr fbaddr, fbsz;
+ uintptr extmem;
+ int i, p, s;
+
+ extmem = gavail(gptr(1<<20, 0)) >> 10;
+ if(extmem >= 65535) extmem = 65535;
+ PUT16(zp, 0x02, extmem);
+
+ if(curmode == nil) return;
+ if(curmode == &textmode){
+ PUT8(zp, 0x06, 3); /* mode 3 */
+ PUT8(zp, 0x07, 80); /* 80 cols */
+ PUT8(zp, 0x0e, 25); /* 25 rows */
+ PUT8(zp, 0x0f, 0x22); /* VGA */
+ PUT16(zp, 0x10, 16); /* characters are 16 pixels high */
+ }else{
+ PUT8(zp, 0x0f, 0x23); /* VESA linear framebuffer */
+ PUT16(zp, 0x12, curmode->w);
+ PUT16(zp, 0x14, curmode->h);
+ PUT16(zp, 0x16, chantodepth(curmode->chan));
+ PUT32(zp, 0x18, fbaddr);
+ PUT32(zp, 0x1C, fbsz);
+ PUT16(zp, 0x24, curmode->hbytes);
+ for(i = 0, p = 0; i < 4; i++){
+ s = curmode->chan >> 8 * i & 15;
+ if(s == 0) continue;
+ switch(curmode->chan >> 8 * i + 4 & 15){
+ case CRed: PUT16(zp, 0x26, s | p << 8); break;
+ case CGreen: PUT16(zp, 0x28, s | p << 8); break;
+ case CBlue: PUT16(zp, 0x2a, s | p << 8); break;
+ case CAlpha: case CIgnore: PUT16(zp, 0x2c, s | p << 8); ; break;
+ }
+ p += s;
+ }
+ PUT16(zp, 0x34, 1<<0|1<<1|1<<3|1<<4|1<<5|1<<6|1<<7); /* attributes */
+ }
+}
+
+enum {
+ GDTRW = 2<<8,
+ GDTRX = 10<<8,
+ GDTS = 1<<12,
+ GDTP = 1<<15,
+ GDT64 = 1<<21,
+ GDT32 = 1<<22,
+ GDTG = 1<<23,
+};
+#define GDTLIM0(l) ((l) & 0x0ffff)
+#define GDTLIM1(l) ((l) & 0xf0000)
+#define GDTBASE0(b) ((b) << 16)
+#define GDTBASE1(b) ((b) >> 16 & 0xff | (b) & 0xff000000)
+
+static void
+linuxgdt(void *v)
+{
+ u32int base;
+
+ base = gpa(v);
+ rset("gdtrbase", base);
+ v = pack(v, "ii", 0, 0);
+ v = pack(v, "ii", 0, 0);
+ v = pack(v, "ii", GDTLIM0(-1) | GDTBASE0(0), GDTLIM1(-1) | GDTBASE1(0) | GDTRX | GDTG | GDTS | GDTP | GDT32);
+ v = pack(v, "ii", GDTLIM0(-1) | GDTBASE0(0), GDTLIM1(-1) | GDTBASE1(0) | GDTRW | GDTG | GDTS | GDTP | GDT32);
+ rset("gdtrlimit", gpa(v) - base - 1);
+ rset("cs", 0x10);
+ rset("ds", 0x18);
+ rset("es", 0x18);
+ rset("ss", 0x18);
+}
+
+static void
+linuxe820(uchar *zp)
+{
+ Region *r;
+ uchar *v;
+ uvlong s, e;
+ int n;
+
+ v = zp + 0x2d0;
+ v = pack(v, "vvi", (uvlong)0, (uvlong)0xa0000, BIOS_MAP_FREE);
+ n = 1;
+ for(r = mmap; r != nil; r = r->next){
+ s = r->start;
+ e = r->end;
+ if(s < (1<<20)) s = 1<<20;
+ if(e <= s || r->type == REGFB) continue;
+ v = pack(v, "vvi", s, e - s, isusermem(r) ? BIOS_MAP_FREE : BIOS_MAP_RES);
+ n++;
+ }
+ PUT8(zp, 0x1e8, n);
+}
+
+static int
+trylinux(void)
+{
+ char buf[1024];
+ u8int loadflags;
+ u16int version;
+ uchar *zp;
+ void *v;
+ u32int ncmdline, cmdlinemax, syssize, setupsects;
+
+ seek(fd, 0, 0);
+ if(readn(fd, buf, sizeof(buf)) < 1024) return 0;
+ if(GET16(buf, 0x1FE) != 0xAA55 || GET32(buf, 0x202) != 0x53726448) return 0;
+ version = GET16(buf, 0x206);
+ if(version < 0x206){
+ vmerror("linux: kernel too old (boot protocol version %d.%.2d, needs to be 2.06 or newer)", version >> 8, version & 0xff);
+ return 0;
+ }
+ loadflags = GET8(buf, 0x211);
+ if((loadflags & 1) == 0){
+ vmerror("linux: zImage is not supported");
+ return 0;
+ }
+ zp = gptr(0x1000, 0x1000);
+ if(zp == nil) sysfatal("linux: gptr for zeropage failed");
+ rset(RSI, 0x1000);
+ memset(zp, 0, 0x1000);
+ memmove(zp + 0x1f1, buf + 0x1f1, 0x202 + GET8(buf, 0x201) - 0x1f1);
+ setupsects = GET8(zp, 0x1F1);
+ if(setupsects == 0) setupsects = 4;
+ syssize = GET32(zp, 0x1F4);
+ cmdlinemax = GET32(zp, 0x238);
+
+ v = gptr(1<<20, syssize << 4);
+ if(v == nil) sysfatal("linux: not enough room for kernel");
+ epreadn(v, syssize << 4, (setupsects + 1) * 512, "trylinux");
+
+ v = gptr(0x20000, 1);
+ if(v == nil) sysfatal("linux: gptr for cmdline failed");
+ ncmdline = putcmdline(v);
+ if(ncmdline == 0)
+ *(uchar*)v = 0;
+ else
+ if(ncmdline - 1 > cmdlinemax) sysfatal("linux: cmdline too long (%d > %d)", ncmdline, cmdlinemax);
+ PUT32(zp, 0x228, 0x20000);
+
+ switch(bootmodn){
+ case 0: break;
+ default:
+ vmerror("linux: ignoring extra boot modules (only one supported)");
+ /* wet floor */
+ case 1:
+ linuxbootmod(*bootmod, zp, (1<<20) + (syssize << 4));
+ }
+
+ linuxscreeninfo(zp);
+ v = gptr(0x3000, 256);
+ if(v == nil) sysfatal("linux: gptr for gdt failed");
+ linuxgdt(v);
+
+ linuxe820(zp);
+
+ PUT16(zp, 0x1FA, 0xffff);
+ PUT8(zp, 0x210, 0xFF); /* bootloader ID */
+ PUT8(zp, 0x211, loadflags | 0x80); /* kernel can use heap */
+
+ PUT32(zp, 0x224, 0xfe00); /* kernel can use full segment */
+ rset(RPC, GET32(zp, 0x214));
+ rset(RBP, 0);
+ rset(RDI, 0);
+ rset(RBX, 0);
+ return 1;
+}
+
void
loadkernel(char *fn)
{
@@ -694,7 +897,7 @@
if(fd < 0) sysfatal("open: %r");
if(readn(fd, hdr, sizeof(hdr)) <= 0)
sysfatal("readn: %r");
- if(!trymultiboot() && !tryelf())
+ if(!trymultiboot() && !tryelf() && !trylinux())
sysfatal("%s: unknown format", fn);
close(fd);
}
--- a/sys/src/cmd/vmx/vesa.c
+++ b/sys/src/cmd/vmx/vesa.c
@@ -258,6 +258,7 @@
pos = 0;
for(i = 0; i < 4; i++){
s = p->chan >> 8 * i & 15;
+ if(s == 0) continue;
switch(p->chan >> 8 * i + 4 & 15){
case CRed: nred = s; pred = pos; break;
case CGreen: ngreen = s; pgreen = pos; break;
--- a/sys/src/cmd/vmx/vga.c
+++ b/sys/src/cmd/vmx/vga.c
@@ -152,6 +152,10 @@
{
u32int m;
+ if(port == 0x3d4 && sz == 2 && !isin){
+ vgaio(0, 0x3d4, (u8int)val, 1, nil);
+ return vgaio(0, 0x3d5, (u8int)(val >> 8), 1, nil);
+ }
if(sz != 1) vmdebug("vga: non-byte access to port %#ux, sz=%d", port, sz);
val = (u8int) val;
switch(isin << 16 | port){
--- a/sys/src/cmd/vmx/virtio.c
+++ b/sys/src/cmd/vmx/virtio.c
@@ -53,6 +53,8 @@
VNETNOMULTI = 8,
VNETNOUNI = 16,
VNETNOBCAST = 32,
+
+ VNETHEADER = 1<<31,
} flags;
u64int macbloom, multibloom;
};
@@ -447,6 +449,7 @@
threadsetname("vionetrproc");
v = vp;
q = &v->qu[0];
+ memset(rxhead, 0, sizeof(rxhead));
for(;;){
rc = read(v->net.readfd, rxbuf, sizeof(rxbuf));
if(rc == 0){
@@ -481,8 +484,9 @@
VIOQueue *q;
VIOBuf *vb;
uchar txhead[10];
- uchar txbuf[1600];
+ uchar txbuf[1610];
int rc, len;
+ uvlong ns;
threadsetname("vionetwproc");
v = vp;
@@ -494,8 +498,8 @@
threadexits("viogetbuf: %r");
}
vioqread(vb, txhead, sizeof(txhead));
- len = vioqread(vb, txbuf, sizeof(txbuf));
- if(len == sizeof(txbuf)){
+ len = vioqread(vb, txbuf+10, sizeof(txbuf)-10);
+ if(len == sizeof(txbuf)-10){
vmerror("virtio net: ignoring excessively long packet");
vioputbuf(vb);
continue;
@@ -507,10 +511,24 @@
vioputbuf(vb);
continue;
}else if(len < 60){ /* openbsd doesn't seem to know about ethernet minimum packet lengths either */
- memset(txbuf + len, 0, 60 - len);
+ memset(txbuf + 10 + len, 0, 60 - len);
len = 60;
- }
- rc = write(v->net.writefd, txbuf, len);
+ }
+ if((v->net.flags & VNETHEADER) != 0){
+ txbuf[0] = len >> 8;
+ txbuf[1] = len;
+ ns = nsec();
+ txbuf[2] = ns >> 56;
+ txbuf[3] = ns >> 48;
+ txbuf[4] = ns >> 40;
+ txbuf[5] = ns >> 32;
+ txbuf[6] = ns >> 24;
+ txbuf[7] = ns >> 16;
+ txbuf[8] = ns >> 8;
+ txbuf[9] = ns;
+ rc = write(v->net.writefd, txbuf, len + 10);
+ }else
+ rc = write(v->net.writefd, txbuf + 10, len);
vioputbuf(vb);
if(rc < 0){
vmerror("write(vionetwproc): %r");
@@ -607,7 +625,7 @@
void
vionetreset(VIODev *d)
{
- d->net.flags = 0;
+ d->net.flags &= VNETHEADER;
d->net.macbloom = 0;
d->net.multibloom = 0;
}
@@ -618,10 +636,30 @@
int fd, cfd;
VIODev *d;
int i;
+ int flags;
+ enum { VNETFILE = 1 };
- fd = dial(netmkaddr("-1", net, nil), nil, nil, &cfd);
- if(fd < 0) return -1;
- if(cfd >= 0) fprint(cfd, "promiscuous");
+ flags = 0;
+ for(;;){
+ if(strncmp(net, "hdr!", 4) == 0){
+ net += 4;
+ flags |= VNETHEADER;
+ }else if(strncmp(net, "file!", 5) == 0){
+ net += 5;
+ flags |= VNETFILE;
+ }else
+ break;
+ }
+ if((flags & VNETFILE) != 0){
+ flags &= ~VNETFILE;
+ fd = open(net, ORDWR);
+ if(fd < 0) return -1;
+ }else{
+ fd = dial(netmkaddr("-1", net, nil), nil, nil, &cfd);
+ if(fd < 0) return -1;
+ if(cfd >= 0) fprint(cfd, "promiscuous");
+ }
+
d = mkviodev(0x1000, 0x020000, 1);
mkvioqueue(d, 1024, viowakeup);
mkvioqueue(d, 1024, viowakeup);
@@ -629,6 +667,7 @@
for(i = 0; i < 6; i++)
d->net.mac[i] = rand();
d->net.mac[0] = d->net.mac[0] & ~1 | 2;
+ d->net.flags = flags;
d->devfeat = 1<<5|1<<16|1<<17|1<<18|1<<20;
d->io = vionetio;
d->reset = vionetreset;