devvmx: more efficient data structure for memory map; simplified (more reliable) step function
This commit is contained in:
parent
174d7e52a3
commit
9616f61872
1 changed files with 95 additions and 144 deletions
|
@ -223,7 +223,7 @@ typedef struct VmMem VmMem;
|
|||
typedef struct VmIntr VmIntr;
|
||||
|
||||
struct VmMem {
|
||||
uvlong lo, hi;
|
||||
uvlong addr;
|
||||
Segment *seg;
|
||||
uintptr off;
|
||||
char *name;
|
||||
|
@ -271,10 +271,7 @@ struct Vmx {
|
|||
enum {
|
||||
GOTEXIT = 1,
|
||||
GOTIRQACK = 2,
|
||||
GOTSTEP = 4,
|
||||
GOTSTEPERR = 8,
|
||||
} got;
|
||||
VmMem *stepmap;
|
||||
VmIntr exc, irq, irqack;
|
||||
|
||||
u64int *msrhost, *msrguest;
|
||||
|
@ -619,11 +616,11 @@ eptfree(uvlong *tab, int level)
|
|||
}
|
||||
|
||||
static void
|
||||
epttranslate(VmMem *mp)
|
||||
epttranslate(VmMem *mp, uvlong end)
|
||||
{
|
||||
uvlong p, v;
|
||||
|
||||
if((mp->lo & 0xfff) != 0 || (mp->hi & 0xfff) != 0 || (uint)mp->attr >= 0x1000)
|
||||
if((mp->addr & 0xfff) != 0 || (end & 0xfff) != 0 || (uint)mp->attr >= 0x1000)
|
||||
error(Egreg);
|
||||
if(mp->seg != nil){
|
||||
switch(mp->seg->type & SG_TYPE){
|
||||
|
@ -633,13 +630,13 @@ epttranslate(VmMem *mp)
|
|||
case SG_STICKY:
|
||||
break;
|
||||
}
|
||||
if(mp->seg->base + mp->off + (mp->hi - mp->lo) > mp->seg->top)
|
||||
if(mp->seg->base + mp->off + (end - mp->addr) > mp->seg->top)
|
||||
error(Egreg);
|
||||
for(p = mp->lo, v = mp->off; p < mp->hi; p += BY2PG, v += BY2PG)
|
||||
for(p = mp->addr, v = mp->off; p != end; p += BY2PG, v += BY2PG)
|
||||
*eptwalk(p) = mp->seg->map[v/PTEMAPMEM]->pages[(v & PTEMAPMEM-1)/BY2PG]->pa | mp->attr;
|
||||
}else {
|
||||
for(p = mp->lo; p < mp->hi; p += BY2PG)
|
||||
*eptwalk(p) = mp->attr;
|
||||
for(p = mp->addr; p != end; p += BY2PG)
|
||||
*eptwalk(p) = 0;
|
||||
}
|
||||
vmx.onentry |= FLUSHEPT;
|
||||
}
|
||||
|
@ -657,7 +654,10 @@ cmdgetmeminfo(VmCmd *, va_list va)
|
|||
p0 = va_arg(va, char *);
|
||||
e = va_arg(va, char *);
|
||||
p = p0;
|
||||
if(p < e) *p = 0;
|
||||
for(mp = vmx.mem.next; mp != &vmx.mem; mp = mp->next){
|
||||
if(mp->seg == nil)
|
||||
continue;
|
||||
attr[0] = (mp->attr & 1) != 0 ? 'r' : '-';
|
||||
attr[1] = (mp->attr & 2) != 0 ? 'w' : '-';
|
||||
attr[2] = (mp->attr & 4) != 0 ? 'x' : '-';
|
||||
|
@ -665,32 +665,83 @@ cmdgetmeminfo(VmCmd *, va_list va)
|
|||
*(ushort*)mt = *(u16int*)mtype[mp->attr >> 3 & 7];
|
||||
mt[2] = (mp->attr & 0x40) != 0 ? '!' : 0;
|
||||
mt[3] = 0;
|
||||
if(mp->name == nil)
|
||||
p = seprint(p, e, "%s %s %#llux %#llux\n", attr, mt, mp->lo, mp->hi);
|
||||
else
|
||||
p = seprint(p, e, "%s %s %#llux %#llux %s %#llux\n", attr, mt, mp->lo, mp->hi, mp->name, (uvlong)mp->off);
|
||||
p = seprint(p, e, "%s %s %#llux %#llux %s %#llux\n", attr, mt, mp->addr, mp->next->addr, mp->name, (uvlong)mp->off);
|
||||
}
|
||||
return p - p0;
|
||||
}
|
||||
|
||||
static void
|
||||
vmmeminsert(VmMem *l, VmMem *p)
|
||||
{
|
||||
p->prev = l->prev;
|
||||
p->next = l;
|
||||
p->prev->next = p;
|
||||
p->next->prev = p;
|
||||
}
|
||||
|
||||
static VmMem *
|
||||
vmmemremove(VmMem *p)
|
||||
{
|
||||
VmMem *r;
|
||||
|
||||
r = p->next;
|
||||
p->next->prev = p->prev;
|
||||
p->prev->next = p->next;
|
||||
free(p->name);
|
||||
putseg(p->seg);
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdclearmeminfo(VmCmd *, va_list)
|
||||
{
|
||||
VmMem *mp, *mn;
|
||||
VmMem *mp;
|
||||
|
||||
eptfree(vmx.pml4, 0);
|
||||
for(mp = vmx.mem.next; mp != &vmx.mem; mp = mn){
|
||||
free(mp->name);
|
||||
putseg(mp->seg);
|
||||
mn = mp->next;
|
||||
free(mp);
|
||||
}
|
||||
for(mp = vmx.mem.next; mp != &vmx.mem; )
|
||||
mp = vmmemremove(mp);
|
||||
vmx.mem.prev = &vmx.mem;
|
||||
vmx.mem.next = &vmx.mem;
|
||||
vmx.onentry |= FLUSHEPT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
vmmemupdate(VmMem *mp, uvlong end)
|
||||
{
|
||||
VmMem *p, *q;
|
||||
|
||||
for(p = vmx.mem.prev; p != &vmx.mem; p = p->prev)
|
||||
if(p->addr <= end || end == 0)
|
||||
break;
|
||||
if(p == &vmx.mem || p->addr < mp->addr){
|
||||
q = smalloc(sizeof(VmMem));
|
||||
*q = *p;
|
||||
if(p->seg != nil){
|
||||
incref(q->seg);
|
||||
kstrdup(&q->name, p->name);
|
||||
}
|
||||
vmmeminsert(p->next, q);
|
||||
}else
|
||||
q = p;
|
||||
if(q->seg != nil)
|
||||
q->off += end - q->addr;
|
||||
q->addr = end;
|
||||
for(p = vmx.mem.next; p != &vmx.mem; p = p->next)
|
||||
if(p->addr >= mp->addr)
|
||||
break;
|
||||
vmmeminsert(p, mp);
|
||||
while(p != q)
|
||||
p = vmmemremove(p);
|
||||
for(p = vmx.mem.next; p != &vmx.mem; )
|
||||
if(p->seg == p->prev->seg && (p->seg == nil || p->addr - p->prev->addr == p->off - p->prev->off))
|
||||
p = vmmemremove(p);
|
||||
else
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
extern Segment* (*_globalsegattach)(char*);
|
||||
|
||||
static int
|
||||
|
@ -701,6 +752,7 @@ cmdsetmeminfo(VmCmd *, va_list va)
|
|||
char *f[10];
|
||||
VmMem *mp;
|
||||
int rc;
|
||||
uvlong end;
|
||||
|
||||
if(vmx.pml4 == nil)
|
||||
error(Egreg);
|
||||
|
@ -725,7 +777,10 @@ cmdsetmeminfo(VmCmd *, va_list va)
|
|||
}
|
||||
rc = tokenize(p, f, nelem(f));
|
||||
p = q + 1;
|
||||
if(rc == 0) goto next;
|
||||
if(rc == 0){
|
||||
poperror();
|
||||
continue;
|
||||
}
|
||||
if(rc != 4 && rc != 6) error("number of fields wrong");
|
||||
for(q = f[0]; *q != 0; q++)
|
||||
switch(*q){
|
||||
|
@ -743,27 +798,23 @@ cmdsetmeminfo(VmCmd *, va_list va)
|
|||
if(j == 8 || strlen(f[1]) > 3) error("invalid memory type");
|
||||
if(f[1][2] == '!') mp->attr |= 0x40;
|
||||
else if(f[1][2] != 0) error("invalid memory type");
|
||||
mp->lo = strtoull(f[2], &r, 0);
|
||||
if(*r != 0 || !vmokpage(mp->lo)) error("invalid low guest physical address");
|
||||
mp->hi = strtoull(f[3], &r, 0);
|
||||
if(*r != 0 || !vmokpage(mp->hi) || mp->hi <= mp->lo) error("invalid high guest physical address");
|
||||
mp->off = strtoull(f[5], &r, 0);
|
||||
if(*r != 0 || !vmokpage(mp->off)) error("invalid offset");
|
||||
mp->addr = strtoull(f[2], &r, 0);
|
||||
if(*r != 0 || !vmokpage(mp->addr)) error("invalid low guest physical address");
|
||||
end = strtoull(f[3], &r, 0);
|
||||
if(*r != 0 || !vmokpage(end) || end <= mp->addr) error("invalid high guest physical address");
|
||||
if((mp->attr & 7) != 0){
|
||||
if(rc != 6) error("number of fields wrong");
|
||||
mp->seg = _globalsegattach(f[4]);
|
||||
if(mp->seg == nil) error("no such segment");
|
||||
if(mp->seg->base + mp->off + (mp->hi - mp->lo) > mp->seg->top) error("out of bounds");
|
||||
if(mp->seg->base + mp->off + (end - mp->addr) > mp->seg->top) error("out of bounds");
|
||||
kstrdup(&mp->name, f[4]);
|
||||
mp->off = strtoull(f[5], &r, 0);
|
||||
if(*r != 0 || !vmokpage(mp->off)) error("invalid offset");
|
||||
}
|
||||
epttranslate(mp);
|
||||
mp->prev = vmx.mem.prev;
|
||||
mp->next = &vmx.mem;
|
||||
mp->prev->next = mp;
|
||||
mp->next->prev = mp;
|
||||
mp = nil;
|
||||
next:
|
||||
poperror();
|
||||
epttranslate(mp, end);
|
||||
vmmemupdate(mp, end);
|
||||
mp = nil;
|
||||
}
|
||||
free(mp);
|
||||
return p - p0;
|
||||
|
@ -1089,7 +1140,6 @@ cmdquit(VmCmd *p, va_list va)
|
|||
}
|
||||
vmx.got = 0;
|
||||
vmx.onentry = 0;
|
||||
vmx.stepmap = nil;
|
||||
|
||||
free(vmx.msrhost);
|
||||
free(vmx.msrguest);
|
||||
|
@ -1121,22 +1171,10 @@ processexit(void)
|
|||
case 7: /* IRQ window */
|
||||
case 8: /* NMI window */
|
||||
return;
|
||||
case 37:
|
||||
if((vmx.onentry & STEP) != 0){
|
||||
vmx.state = VMXREADY;
|
||||
vmx.got |= GOTSTEP;
|
||||
vmx.onentry &= ~STEP;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if((vmx.onentry & STEP) != 0){
|
||||
print("VMX: exit reason %#x when expected step...\n", reason & 0xffff);
|
||||
vmx.onentry &= ~STEP;
|
||||
vmx.got |= GOTSTEP|GOTSTEPERR;
|
||||
}
|
||||
vmx.state = VMXREADY;
|
||||
vmx.got |= GOTEXIT;
|
||||
vmx.onentry &= ~STEP;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1252,12 +1290,15 @@ cmdsetfpregs(VmCmd *, va_list va)
|
|||
static int
|
||||
cmdgo(VmCmd *, va_list va)
|
||||
{
|
||||
int step;
|
||||
char *r;
|
||||
|
||||
if(vmx.state != VMXREADY)
|
||||
error("VM not ready");
|
||||
step = va_arg(va, int);
|
||||
r = va_arg(va, char *);
|
||||
if(r != nil) setregs(r, ';', "=");
|
||||
if(step) vmx.onentry |= STEP;
|
||||
vmx.state = VMXRUNNING;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1342,43 +1383,6 @@ cmdwait(VmCmd *cp, va_list va)
|
|||
return p - p0;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdstep(VmCmd *cp, va_list va)
|
||||
{
|
||||
switch(cp->retval){
|
||||
case 0:
|
||||
if((vmx.got & GOTSTEP) != 0 || (vmx.onentry & STEP) != 0)
|
||||
error(Einuse);
|
||||
if(vmx.state != VMXREADY){
|
||||
print("pre-step in state %s\n", statenames[vmx.state]);
|
||||
error("not ready");
|
||||
}
|
||||
vmx.stepmap = va_arg(va, VmMem *);
|
||||
vmx.onentry |= STEP;
|
||||
vmx.state = VMXRUNNING;
|
||||
cp->flags |= CMDFPOSTP;
|
||||
return 1;
|
||||
case 1:
|
||||
if(vmx.state != VMXREADY){
|
||||
print("post-step in state %s\n", statenames[vmx.state]);
|
||||
vmx.onentry &= ~STEP;
|
||||
vmx.got &= ~(GOTSTEP|GOTSTEPERR);
|
||||
error("not ready");
|
||||
}
|
||||
if((vmx.got & GOTSTEP) == 0){
|
||||
cp->flags |= CMDFPOSTP;
|
||||
return 1;
|
||||
}
|
||||
if((vmx.got & GOTSTEPERR) != 0){
|
||||
vmx.got &= ~(GOTSTEP|GOTSTEPERR);
|
||||
error("step failed");
|
||||
}
|
||||
vmx.got &= ~(GOTSTEP|GOTSTEPERR);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
eventparse(char *p, VmIntr *vi)
|
||||
{
|
||||
|
@ -1547,28 +1551,6 @@ runcmd(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dostep(int setup)
|
||||
{
|
||||
static uvlong oldmap;
|
||||
static uvlong *mapptr;
|
||||
|
||||
if(setup){
|
||||
if(vmx.stepmap != nil){
|
||||
mapptr = eptwalk(vmx.stepmap->lo);
|
||||
oldmap = *mapptr;
|
||||
epttranslate(vmx.stepmap);
|
||||
}
|
||||
}else{
|
||||
vmcswrite(PROCB_CTLS, vmcsread(PROCB_CTLS) & ~(uvlong)PROCB_MONTRAP);
|
||||
if(vmx.stepmap != nil){
|
||||
*mapptr = oldmap;
|
||||
vmx.stepmap = nil;
|
||||
vmx.onentry |= FLUSHEPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vmxproc(void *)
|
||||
{
|
||||
|
@ -1594,14 +1576,8 @@ vmxproc(void *)
|
|||
runcmd();
|
||||
if(vmx.state == VMXRUNNING){
|
||||
procbctls = defprocbctls;
|
||||
if((vmx.onentry & STEP) != 0){
|
||||
procbctls |= PROCB_MONTRAP;
|
||||
dostep(1);
|
||||
if(waserror()){
|
||||
dostep(0);
|
||||
nexterror();
|
||||
}
|
||||
}
|
||||
if((vmx.onentry & STEP) != 0)
|
||||
defprocbctls |= PROCB_MONTRAP;
|
||||
if((vmx.onentry & POSTEX) != 0){
|
||||
vmcswrite(VMENTRY_INTRINFO, vmx.exc.info);
|
||||
vmcswrite(VMENTRY_INTRCODE, vmx.exc.code);
|
||||
|
@ -1648,10 +1624,6 @@ vmxproc(void *)
|
|||
if(rc < 0)
|
||||
error("vmlaunch failed");
|
||||
vmx.launched = 1;
|
||||
if((vmx.onentry & STEP) != 0){
|
||||
dostep(0);
|
||||
poperror();
|
||||
}
|
||||
processexit();
|
||||
}else{
|
||||
up->psstate = "Idle";
|
||||
|
@ -1853,8 +1825,6 @@ vmxwrite(Chan* c, void* a, long n, vlong off)
|
|||
Cmdtab *ct;
|
||||
char *s;
|
||||
int rc;
|
||||
int i;
|
||||
VmMem tmpmem;
|
||||
|
||||
switch((ulong)c->qid.path){
|
||||
case Qdir:
|
||||
|
@ -1886,6 +1856,7 @@ vmxwrite(Chan* c, void* a, long n, vlong off)
|
|||
vmxcmd(cmdquit);
|
||||
break;
|
||||
case CMgo:
|
||||
case CMstep:
|
||||
s = nil;
|
||||
if(cb->nf == 2) kstrdup(&s, cb->f[1]);
|
||||
else if(cb->nf != 1) error(Ebadarg);
|
||||
|
@ -1893,33 +1864,13 @@ vmxwrite(Chan* c, void* a, long n, vlong off)
|
|||
free(s);
|
||||
nexterror();
|
||||
}
|
||||
vmxcmd(cmdgo, s);
|
||||
vmxcmd(cmdgo, ct->index == CMstep, s);
|
||||
poperror();
|
||||
free(s);
|
||||
break;
|
||||
case CMstop:
|
||||
vmxcmd(cmdstop);
|
||||
break;
|
||||
case CMstep:
|
||||
rc = 0;
|
||||
for(i = 1; i < cb->nf; i++)
|
||||
if(strcmp(cb->f[i], "-map") == 0){
|
||||
rc = 1;
|
||||
if(i+4 > cb->nf) error("missing argument");
|
||||
memset(&tmpmem, 0, sizeof(tmpmem));
|
||||
tmpmem.lo = strtoull(cb->f[i+1], &s, 0);
|
||||
if(*s != 0 || !vmokpage(tmpmem.lo)) error("invalid address");
|
||||
tmpmem.hi = tmpmem.lo + BY2PG;
|
||||
tmpmem.attr = 0x407;
|
||||
tmpmem.seg = _globalsegattach(cb->f[i+2]);
|
||||
if(tmpmem.seg == nil) error("unknown segment");
|
||||
tmpmem.off = strtoull(cb->f[i+3], &s, 0);
|
||||
if(*s != 0 || !vmokpage(tmpmem.off)) error("invalid offset");
|
||||
i += 3;
|
||||
}else
|
||||
error(Ebadctl);
|
||||
vmxcmd(cmdstep, rc ? &tmpmem : nil);
|
||||
break;
|
||||
case CMexc:
|
||||
s = nil;
|
||||
kstrdup(&s, cb->f[1]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue