vmx(1): clean up region handling code; changes to support amd64

This commit is contained in:
aiju 2017-06-20 15:15:53 +00:00
parent becb89bae5
commit 164588e3e2
5 changed files with 115 additions and 91 deletions

View file

@ -35,7 +35,14 @@ enum {
struct Region {
uintptr start, end;
enum { REGNO, REGMEM, REGFB } type;
enum {
REGALLOC = 1, /* allocate memory for region */
REGRO = 2, /* read-only */
/* E820 types, 0 == omitted from memory map */
REGFREE = 1<<8, /* report to OS as free */
REGRES = 2<<8, /* report to OS as reserved */
} type;
char *segname;
uvlong segoff;
void *v, *ve;

View file

@ -33,7 +33,7 @@ stepmmio(uvlong pa, uvlong *val, int size, ExitInfo *ei)
extern uchar *tmp;
extern uvlong tmpoff;
void *targ;
uvlong pc, si;
uvlong pc;
char buf[ERRMAX];
extern int getexit;
@ -46,7 +46,6 @@ stepmmio(uvlong pa, uvlong *val, int size, ExitInfo *ei)
case 8: *(u64int*)targ = *val; break;
}
pc = rget(RPC);
si = rget("si");
rcflush(0);
if(ctl("step -map %#ullx vm %#ullx", pa & ~0xfff, tmpoff) < 0){
rerrstr(buf, sizeof(buf));
@ -262,7 +261,10 @@ cpuid(ExitInfo *ei)
ax = cp->ax;
bx = 0;
cx = cp->cx & 0x121;
dx = cp->dx & 0x04100000;
if(sizeof(uintptr) == 8)
dx = cp->dx & 0x24100800;
else
dx = cp->dx & 0x04100000;
break;
case 0x80000002: goto literal; /* brand string */
case 0x80000003: goto literal; /* brand string */
@ -308,10 +310,6 @@ rdwrmsr(ExitInfo *ei)
if(rd) val = rget("pat");
else rset("pat", val);
break;
case 0xC0000080:
if(rd) val = rget("efer");
else rset("efer", val);
break;
case 0x8B: val = 0; break; /* microcode update */
default:
if(rd){
@ -376,7 +374,7 @@ movcr(ExitInfo *ei)
}
break;
case 4:
switch(ei->qual >> 4 & 3){
switch(q >> 4 & 3){
case 0:
vmdebug("illegal CR4 write, value %#ux", rget(x86reg[q >> 8 & 15]));
rset("cr4real", rget(x86reg[q >> 8 & 15]));

View file

@ -14,27 +14,48 @@ extern char **bootmod;
extern int cmdlinen;
extern char **cmdlinev;
static int elf64;
static int
isusermem(Region *r)
biostype(Region *r)
{
return r->type == REGMEM;
return r->type >> 8 & 0xff;
}
static void *
pack(void *v, char *fmt, ...)
{
uchar *p;
va_list va;
p = v;
va_start(va, fmt);
for(; *fmt != 0; fmt++)
switch(*fmt){
case '.': p++; break;
case 's': PUT16(p, 0, va_arg(va, int)); p += 2; break;
case 'i': PUT32(p, 0, va_arg(va, u32int)); p += 4; break;
case 'v': PUT64(p, 0, va_arg(va, u64int)); p += 8; break;
case 'z': if(elf64) {PUT64(p, 0, va_arg(va, uintptr)); p += 8;} else {PUT32(p, 0, va_arg(va, uintptr)); p += 4;} break;
default: sysfatal("pack: unknown fmt character %c", *fmt);
}
va_end(va);
return p;
}
static int
putmmap(uchar *p0)
{
u32int *p;
uchar *p;
Region *r;
int t;
p = (u32int *) p0;
p = p0;
for(r = mmap; r != nil; r = r->next){
if(!isusermem(r)) continue;
if(gavail(p) < 20) sysfatal("out of guest memory");
p[0] = 20;
p[1] = r->start;
p[2] = r->end - r->start;
p[3] = 1;
t = biostype(r);
if(t == 0) continue;
if(gavail(p) < 24) sysfatal("out of guest memory");
p = pack(p, "ivvi", 20, (uvlong) r->start, (uvlong)(r->end - r->start), t);
}
return (uchar *) p - p0;
}
@ -131,6 +152,7 @@ trymultiboot(void)
bssend = -(-bssend & -BY2PG);
p = gptr(bssend, 128);
if(p == nil) sysfatal("no space for multiboot structure");
memset(p, 0, 128);
p[0] = 1<<0;
p[1] = gavail(gptr(0, 0)) >> 10;
if(p[1] > 640) p[1] = 640;
@ -165,7 +187,6 @@ trymultiboot(void)
return 1;
}
static int elf64;
typedef struct ELFHeader ELFHeader;
struct ELFHeader {
uintptr entry, phoff, shoff;
@ -283,11 +304,19 @@ static void
elfsymbol(ELFSymbol *s, uchar *p, uchar *e)
{
s->iname = elff(&p, e, 4);
s->addr = elff(&p, e, -1);
s->size = elff(&p, e, -1);
s->info = elff(&p, e, 1);
s->other = elff(&p, e, 1);
s->shndx = elff(&p, e, 2);
if(elf64){
s->info = elff(&p, e, 1);
s->other = elff(&p, e, 1);
s->shndx = elff(&p, e, 2);
s->addr = elff(&p, e, -1);
s->size = elff(&p, e, -1);
}else{
s->addr = elff(&p, e, -1);
s->size = elff(&p, e, -1);
s->info = elff(&p, e, 1);
s->other = elff(&p, e, 1);
s->shndx = elff(&p, e, 2);
}
}
static void
@ -442,7 +471,7 @@ symaddr(ELFSymbol *s)
}
static uchar *obsdarg, *obsdarg0, *obsdargnext;
static int obsdargc;
static int obsdarglen;
static int obsdconsdev = 12 << 8, obsddbcons = -1, obsdbootdev;
enum {
@ -459,35 +488,8 @@ enum {
BOOTARG_BOOTSR,
BOOTARG_EFIINFO,
BOOTARG_END = -1,
BIOS_MAP_END = 0,
BIOS_MAP_FREE,
BIOS_MAP_RES,
BIOS_MAP_ACPI,
BIOS_MAP_NVS,
};
static void *
pack(void *v, char *fmt, ...)
{
uchar *p;
va_list va;
p = v;
va_start(va, fmt);
for(; *fmt != 0; fmt++)
switch(*fmt){
case '.': p++; break;
case 's': PUT16(p, 0, va_arg(va, int)); p += 2; break;
case 'i': PUT32(p, 0, va_arg(va, u32int)); p += 4; break;
case 'v': PUT64(p, 0, va_arg(va, u64int)); p += 8; break;
case 'z': if(elf64) {PUT64(p, 0, va_arg(va, uintptr)); p += 8;} else {PUT32(p, 0, va_arg(va, uintptr)); p += 4;} break;
default: sysfatal("pack: unknown fmt character %c", *fmt);
}
va_end(va);
return p;
}
static void
obsdelfload(void)
{
@ -546,28 +548,25 @@ obsdend(void)
{
if(obsdarg == obsdarg0 + 12) obsdarg += 4;
PUT32(obsdarg0, 4, obsdarg - obsdarg0); /* size */
obsdarglen += obsdarg - obsdarg0;
PUT32(obsdargnext, 0, gpa(obsdarg0));
obsdargnext = obsdarg0 + 8;
obsdarg0 = nil;
obsdargc++;
}
static void
obsdargs(void)
{
Region *r;
uvlong s, e;
int t;
obsdstart(BOOTARG_MEMMAP);
obsdpack("vvi", (uvlong)0, (uvlong)0xa0000, BIOS_MAP_FREE);
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;
obsdpack("vvi", s, e - s, isusermem(r) ? BIOS_MAP_FREE : BIOS_MAP_RES);
t = biostype(r);
if(t == 0) continue;
obsdpack("vvi", (uvlong)r->start, (uvlong)(r->end - r->start), t);
}
obsdpack("vvi", 0ULL, 0ULL, BIOS_MAP_END);
obsdpack("vvi", 0ULL, 0ULL, 0);
obsdend();
obsdstart(BOOTARG_CONSDEV); obsdpack("iiii", obsdconsdev, -1, -1, 0); obsdend();
if(obsddbcons != -1){
@ -668,7 +667,7 @@ obsdload(void)
obsdargnext = &v[32]; /* bootargv */
obsdargs();
assert(obsdarg0 == nil);
PUT32(v, 28, obsdargc); /* bootargc */
PUT32(v, 28, obsdarglen); /* bootargc */
rset(RSP, sp);
rset(RPC, eh.entry);
return 1;
@ -708,7 +707,6 @@ linuxbootmod(char *fn, void *zp, u32int kend)
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);
@ -799,18 +797,15 @@ linuxe820(uchar *zp)
{
Region *r;
uchar *v;
uvlong s, e;
int t;
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);
t = biostype(r);
if(t == 0) continue;
v = pack(v, "vvi", r->start, r->end - r->start, t);
n++;
}
PUT8(zp, 0x1e8, n);

View file

@ -86,7 +86,7 @@ vioirq_(void *arg)
int val;
d = ((void**)arg)[0];
val = (int) ((void**)arg)[1];
val = (uintptr)((void**)arg)[1];
if(val != 0)
d->isrstat |= val;
else

View file

@ -64,7 +64,9 @@ vmxsetup(void)
static int fd;
static char buf[128];
Region *r;
int rc;
uvlong start, end, off;
char *name;
int rc, type;
fd = open("#X/status", OREAD);
if(fd < 0) sysfatal("open: %r");
@ -85,9 +87,26 @@ vmxsetup(void)
fd = open("#X/map", OWRITE|OTRUNC);
if(fd < 0) sysfatal("open: %r");
for(r = mmap; r != nil; r = r->next)
if(r->segname != nil && fprint(fd, "rwx wb %#ullx %#ullx %s %#ullx\n", (uvlong)r->start, (uvlong)r->end, r->segname, r->segoff) < 0)
for(r = mmap; r != nil; ){
if(r->segname == nil){
r = r->next;
continue;
}
start = r->start;
end = r->end;
name = r->segname;
off = r->segoff;
type = r->type;
while(r = r->next, r != nil){
if(r->segname == nil)
continue;
if(r->start != end || r->segoff != off + end - start || ((r->type ^ type) & REGRO) != 0)
break;
end = r->end;
}
if(fprint(fd, "r%cx wb %#ullx %#ullx %s %#ullx\n", (type & REGRO) != 0 ? '-' : 'w', start, end, name, off) < 0)
sysfatal("writing memory map: %r");
}
close(fd);
waitfd = open("#X/wait", OREAD);
@ -205,16 +224,15 @@ goti:
}
Region *
mkregion(u64int pa, u64int len, int type)
mkregion(u64int pa, u64int end, int type)
{
Region *r, **rp;
assert(pa + len >= pa);
r = emalloc(sizeof(Region));
if((pa & 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;
len = -(-len & -BY2PG);
r->end = pa + len;
r->end = end;
r->type = type;
for(rp = &mmap; *rp != nil; rp = &(*rp)->next)
;
@ -245,7 +263,7 @@ gpa(void *v)
for(r = mmap; r != nil; r = r->next)
if(v >= r->v && v < r->ve)
return (uchar *) v - (uchar *) r->v;
return (uchar *) v - (uchar *) r->v + r->start;
return -1;
}
@ -280,10 +298,8 @@ mksegment(char *sn)
sz = BY2PG;
for(r = mmap; r != nil; r = r->next){
switch(r->type){
case REGMEM: case REGFB: break;
default: continue;
}
if((r->type & REGALLOC) == 0)
continue;
r->segname = sn;
if(sz + (r->end - r->start) < sz)
sysfatal("out of address space");
@ -304,7 +320,7 @@ mksegment(char *sn)
gmem = segattach(0, sn, nil, sz);
if(gmem == (void*)-1) sysfatal("segattach: %r");
}
memset(gmem, 0, sz > 1>>24 ? 1>>24 : sz);
memset(gmem, 0, sz > 1<<24 ? 1<<24 : sz);
p = gmem;
for(r = mmap; r != nil; r = r->next){
if(r->segname == nil) continue;
@ -537,11 +553,19 @@ threadmain(int argc, char **argv)
cmdlinen = argc - 1;
cmdlinev = argv + 1;
mkregion(0, gmemsz, REGMEM);
if(gmemsz < 1<<20) sysfatal("640 KB of RAM is not enough for everyone");
mkregion(0, 0xa0000, REGALLOC|REGFREE);
mkregion(0xa0000, 0xc0000, REGALLOC);
mkregion(0xc0000, 0x100000, REGALLOC|REGRES);
if(fbsz != 0 && fbaddr < gmemsz){
mkregion(0x100000, fbaddr, REGALLOC|REGFREE);
mkregion(fbaddr + fbsz, gmemsz, REGALLOC|REGFREE);
}else
mkregion(0x100000, gmemsz, REGALLOC|REGFREE);
if(fbsz != 0){
if(fbaddr + fbsz < fbaddr) sysfatal("invalid fb address");
if(fbaddr + fbsz < gmemsz) sysfatal("framebuffer overlaps with physical memory");
mkregion(fbaddr, fbsz, REGFB);
if(fbaddr < 1<<20) sysfatal("framebuffer must not be within first 1 MB");
if(fbaddr != (u32int) fbaddr || (u32int)(fbaddr+fbsz) < fbaddr) sysfatal("framebuffer must be within first 4 GB");
mkregion(fbaddr, fbaddr+fbsz, REGALLOC);
}
mksegment("vm");
vmxsetup();