527 lines · 10.7 KB · C
RawDownload
#include <u.h>#include <libc.h>#include "dat.h"#include "fns.h"#include "x86.h" int persist = 1; typedef struct ExitInfo ExitInfo;struct ExitInfo {	char *raw;	char *name;	uvlong qual;	uvlong pa, va;	u32int ilen, iinfo;}; char *x86reg[16] = {	RAX, RCX, RDX, RBX,	RSP, RBP, RSI, RDI,	R8, R9, R10, R11,	R12, R13, R14, R15};char *x86segreg[8] = {	"cs", "ds", "es", "fs", "gs", "ss",}; static voidskipinstr(ExitInfo *ei){	rset(RPC, rget(RPC) + ei->ilen);} static voidiohandler(ExitInfo *ei){	int port, len, inc, isin;	int asz, seg;	uintptr addr;	u32int val;	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){ /* 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((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;	}	skipinstr(ei);err:	if((ei->qual & 1<<5) != 0)		rsetsz(RCX, cx, asz);	if(isin)		rsetsz(RDI, addr, asz);	else		rsetsz(RSI, addr, asz);} static uvlongdefaultmmio(int op, uvlong addr, uvlong val){	switch(op){	case MMIORD:		vmerror("read from unmapped address %#ullx (pc=%#ullx)", addr, rget(RPC));		break;	case MMIOWR:		vmerror("write to unmapped address %#ullx (val=%#ullx,pc=%#ullx)", addr, val, rget(RPC));		break;	}	return 0;} static voideptfault(ExitInfo *ei){	if(x86step() > 0)		skipinstr(ei);} typedef struct CPUID CPUID;struct CPUID {	u32int ax, bx, cx, dx;};static u32int cpuidmax;static u32int cpuidmaxext;static CPUID leaf1;static struct {	uvlong miscen;}msr; static uchar _cpuid[] = {	0x5E,			/* POP SI (PC) */	0x5D,			/* POP BP (CPUID&) */	0x58,			/* POP AX */	0x59,			/* POP CX */ 	0x51,			/* PUSH CX */	0x50,			/* PUSH AX */	0x55,			/* PUSH BP */	0x56,			/* PUSH SI */ 	0x31, 0xDB,		/* XOR BX, BX */	0x31, 0xD2,		/* XOR DX, DX */ 	0x0F, 0xA2,		/* CPUID */ 	0x89, 0x45, 0x00,	/* MOV AX, 0(BP) */	0x89, 0x5d, 0x04,	/* MOV BX, 4(BP) */	0x89, 0x4d, 0x08,	/* MOV CX, 8(BP) */	0x89, 0x55, 0x0C,	/* MOV DX, 12(BP) */	0xC3,			/* RET */}; static CPUID (*getcpuid)(ulong ax, ulong cx) = (CPUID(*)(ulong, ulong)) _cpuid; voidcpuidinit(void){	CPUID r;	int f; 	if(sizeof(uintptr) == 8) /* patch out POP BP -> POP AX */		_cpuid[1] = 0x58;	segflush(_cpuid, sizeof(_cpuid)); 	r = getcpuid(0, 0);	cpuidmax = r.ax;	r = getcpuid(0x80000000, 0);	cpuidmaxext = r.ax;	leaf1 = getcpuid(1, 0); 	memset(&msr, 0, sizeof(msr));	if((f = open("/dev/msr", OREAD)) >= 0){		pread(f, &msr.miscen, 8, 0x1a0);		msr.miscen &= 1<<0; /* fast strings */		close(f);	}} static int xsavesz[] = {	[1] = 512+64,	[3] = 512+64,	[7] = 512+64+256,}; static voidcpuid(ExitInfo *ei){	u32int ax, bx, cx, dx;	CPUID cp; 	ax = rget(RAX);	cx = rget(RCX);	bx = dx = 0;	cp = getcpuid(ax, cx);	switch(ax){	case 0x00: /* highest register & GenuineIntel */		ax = MIN(cpuidmax, 0x18);		bx = cp.bx;		dx = cp.dx;		cx = cp.cx;		break;	case 0x01: /* features */		ax = cp.ax;		bx = cp.bx & 0xffff;		/* some features removed, hypervisor added */		cx = cp.cx & 0x76de3217 | 0x80000000UL;		dx = cp.dx & 0x0f8aa579;		if(leaf1.cx & 1<<27){			if(rget("cr4real") & Cr4Osxsave)				cx |= 1<<27;		}else{			cx &= ~0x1c000000;		}		break;	case 0x02: goto literal; /* cache stuff */	case 0x03: goto zero; /* processor serial number */	case 0x04: goto literal; /* cache stuff */	case 0x05: goto zero; /* monitor/mwait */	case 0x06: goto zero; /* thermal management */	case 0x07: /* more features */		if(cx == 0){			ax = 0;			bx = cp.bx & 0x2369;			cx = 0;			if((leaf1.cx & 1<<27) == 0)				bx &= ~0xdc230020;		}else{			goto zero;		}		break;	case 0x08: goto zero;	case 0x09: goto literal; /* direct cache access */	case 0x0a: goto zero; /* performance counters */	case 0x0b: goto zero; /* extended topology */	case 0x0c: goto zero;	case 0x0d: /* extended state */		if((leaf1.cx & 1<<27) == 0)			goto zero;		if(cx == 0){ /* main leaf */			ax = cp.ax & 7; /* x87, sse, avx */			bx = xsavesz[rget("xcr0")]; /* current xsave size */			cx = xsavesz[ax]; /* max xsave size */		}else if(cx == 1){ /* sub leaf */			ax = cp.ax & 7; /* xsaveopt, xsavec, xgetbv1 */			bx = xsavesz[rget("xcr0")];			cx = 0;		}else if(cx == 2){			ax = xsavesz[7] - xsavesz[3];			bx = xsavesz[3];			cx = 0;		}else{			goto zero;		}		break;	case 0x0f: goto zero; /* RDT */	case 0x10: goto zero; /* RDT */	case 0x12: goto zero; /* SGX */	case 0x14: goto zero; /* PT */	case 0x15: goto zero; /* TSC */	case 0x16: goto zero; /* cpu clock */	case 0x17: goto zero; /* SoC */	case 0x18: goto literal; /* pages, tlb */ 	case 0x40000000: /* hypervisor */		ax = 0;		bx = 0x4b4d564b; /* act as KVM */		cx = 0x564b4d56;		dx = 0x4d;		break; 	case 0x80000000: /* highest register */		ax = MIN(cpuidmaxext, 0x80000008);		cx = 0;		break;	case 0x80000001: /* signature & ext features */		ax = cp.ax;		cx = cp.cx & 0x121;		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 */	case 0x80000004: goto literal; /* brand string */	case 0x80000005: goto zero; /* reserved */	case 0x80000006: goto literal; /* cache info */	case 0x80000007: goto zero; /* invariant tsc */	case 0x80000008: goto literal; /* address bits */	literal:		ax = cp.ax;		bx = cp.bx;		cx = cp.cx;		dx = cp.dx;		break;	default:		if((ax & 0xf0000000) != 0x40000000)			vmdebug("unknown cpuid field eax=%#ux", ax);	zero:		ax = cx = 0;		break;	}	rset(RAX, ax);	rset(RBX, bx);	rset(RCX, cx);	rset(RDX, dx);	skipinstr(ei);} static voidrdwrmsr(ExitInfo *ei){	u32int cx;	u64int val;	int rd;		rd = ei->name[1] == 'r';	cx = rget(RCX);	val = (uvlong)rget(RDX) << 32 | rget(RAX);	switch(cx){	case 0x277:		if(rd) val = rget("pat");		else rset("pat", val);		break;	case 0x8B: val = 0; break; /* microcode update */	case 0x1A0: /* IA32_MISC_ENABLE */		if(rd) val = msr.miscen;		break;	default:		if(rd){			vmdebug("read from unknown MSR %#ux ignored", cx);			val = 0;		}else			vmdebug("write to unknown MSR %#ux ignored (val=%#ullx)", cx, val);		break;	}	if(rd){		rset(RAX, (u32int)val);		rset(RDX, (u32int)(val >> 32));	}	skipinstr(ei);} static voidmovdr(ExitInfo *ei){	static char *dr[8] = { "dr0", "dr1", "dr2", "dr3", nil, nil, "dr6", "dr7" };	int q;		q = ei->qual;	if((q & 6) == 4){		postexc("#gp", 0);		return;	}	if((q & 16) != 0)		rset(x86reg[q >> 8 & 15], rget(dr[q & 7]));	else		rset(dr[q & 7], rget(x86reg[q >> 8 & 15]));	skipinstr(ei);} static voidmovcr(ExitInfo *ei){	u32int q;		q = ei->qual;	switch(q & 15){	case 0:		switch(q >> 4 & 3){		case 0:			vmdebug("illegal CR0 write, value %#ux", (u32int)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(q >> 4 & 3){		case 0:			vmdebug("illegal CR4 write, value %#ux", (u32int)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%ud", q & 15);		postexc("#ud", NOERRC);	}} static voiddbgexc(ExitInfo *ei){	rset("dr6", rget("dr6") | ei->qual);	postexc("#db", NOERRC);} static voidhlt(ExitInfo *ei){	if(irqactive < 0)		state = VMHALT;	skipinstr(ei);} static voidirqackhand(ExitInfo *ei){	irqack(ei->qual);} static voidxsetbv(ExitInfo *ei){	uvlong v; 	/* this should also #ud if LOCK prefix is used */ 	v = rget(RAX)&0xffffffff | rget(RDX)<<32;	if(rget(RCX) & 0xffffffff)		postexc("#gp", 0);	else if(v != 1 && v != 3 && v != 7)		postexc("#gp", 0);	else if((leaf1.cx & 1<<26) == 0 || (rget("cr4real") & Cr4Osxsave) == 0)		postexc("#ud", NOERRC);	else{		rset("xcr0", v);		skipinstr(ei);	}} typedef struct ExitType ExitType;struct ExitType {	char *name;	void (*f)(ExitInfo *);};static ExitType etypes[] = {	{"io", iohandler},	{".cpuid", cpuid},	{".hlt", hlt},	{"eptfault", eptfault},	{"*ack", irqackhand},	{".rdmsr", rdwrmsr},	{".wrmsr", rdwrmsr},	{".movdr", movdr},	{"#db", dbgexc},	{"movcr", movcr},	{".xsetbv", xsetbv},}; voidprocessexit(char *msg){	static char msgc[1024];	char *f[32];	int nf;	ExitType *et;	int i;	ExitInfo ei;	extern int getexit; 	strcpy(msgc, msg);	nf = tokenize(msgc, f, nelem(f));	if(nf < 2) sysfatal("invalid wait message: %s", msg);	memset(&ei, 0, sizeof(ei));	ei.raw = msg;	ei.name = f[0];	ei.qual = strtoull(f[1], nil, 0);	for(i = 2; i < nf; i += 2){		if(strcmp(f[i], "pc") == 0)			rpoke(RPC, strtoull(f[i+1], nil, 0), 1);		else if(strcmp(f[i], "sp") == 0)			rpoke(RSP, strtoull(f[i+1], nil, 0), 1);		else if(strcmp(f[i], "ax") == 0)			rpoke(RAX, strtoull(f[i+1], nil, 0), 1);		else if(strcmp(f[i], "ilen") == 0)			ei.ilen = strtoul(f[i+1], nil, 0);		else if(strcmp(f[i], "iinfo") == 0)

Lines 1–500 of 527.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t