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

This commit is contained in:
aiju 2017-06-18 22:17:35 +00:00
parent ed040d676a
commit 2806a34ec0
6 changed files with 370 additions and 40 deletions

View file

@ -14,6 +14,13 @@ struct ExitInfo {
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 @@ cpuid(ExitInfo *ei)
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 @@ rdwrmsr(ExitInfo *ei)
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 @@ rdwrmsr(ExitInfo *ei)
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,12 +340,64 @@ movdr(ExitInfo *ei)
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)
{
@ -380,6 +434,7 @@ static ExitType etypes[] = {
{".wrmsr", rdwrmsr},
{".movdr", movdr},
{"#db", dbgexc},
{"movcr", movcr},
};
void

View file

@ -363,6 +363,7 @@ picio(int isin, u16int port, u32int val, int sz, void *)
p->init = 4;
picupdate(p);
return 0;
case 0:
case 4:
p->imr = val;
picupdate(p);
@ -412,10 +413,12 @@ struct PITChannel {
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 @@ settimer(vlong targ)
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 @@ pitadvance(void)
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 @@ pitadvance(void)
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 @@ pitadvance(void)
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 @@ pitsetreload(int n, int hi, u8int v)
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 @@ pitio(int isin, u16int port, u32int val, int sz, void *)
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 @@ pitio(int isin, u16int port, u32int val, int sz, void *)
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 @@ struct I8042 {
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 @@ mousecmd(u8int val)
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 @@ i8042io(int isin, u16int port, u32int val, int sz, void *)
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 @@ IOHandler handlers[] = {
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 @@ IOHandler handlers[] = {
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 */

View file

@ -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 @@ tryelf(void)
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 @@ loadkernel(char *fn)
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);
}

View file

@ -258,6 +258,7 @@ vesamodeget(int addr, u16int mode, u16int *retv)
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;

View file

@ -152,6 +152,10 @@ vgaio(int isin, u16int port, u32int val, int sz, void *)
{
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){

View file

@ -53,6 +53,8 @@ struct VIONetDev {
VNETNOMULTI = 8,
VNETNOUNI = 16,
VNETNOBCAST = 32,
VNETHEADER = 1<<31,
} flags;
u64int macbloom, multibloom;
};
@ -447,6 +449,7 @@ vionetrproc(void *vp)
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 @@ vionetwproc(void *vp)
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 @@ vionetwproc(void *vp)
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 @@ vionetwproc(void *vp)
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 @@ vionetcmd(VIOQueue *q)
void
vionetreset(VIODev *d)
{
d->net.flags = 0;
d->net.flags &= VNETHEADER;
d->net.macbloom = 0;
d->net.multibloom = 0;
}
@ -618,10 +636,30 @@ mkvionet(char *net)
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 @@ mkvionet(char *net)
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;