summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/man/1/vmx50
-rw-r--r--sys/man/3/vmx29
-rw-r--r--sys/src/9/pc/devvmx.c29
-rw-r--r--sys/src/cmd/ktrace.c18
-rw-r--r--sys/src/cmd/vmx/exith.c73
-rw-r--r--sys/src/cmd/vmx/io.c70
-rw-r--r--sys/src/cmd/vmx/ksetup.c205
-rw-r--r--sys/src/cmd/vmx/vesa.c1
-rw-r--r--sys/src/cmd/vmx/vga.c4
-rw-r--r--sys/src/cmd/vmx/virtio.c59
10 files changed, 463 insertions, 75 deletions
diff --git a/sys/man/1/vmx b/sys/man/1/vmx
index 4f4a005b8..9f8b8ac57 100644
--- a/sys/man/1/vmx
+++ b/sys/man/1/vmx
@@ -39,7 +39,7 @@ kernel
.I Vmx
uses Intel VT-x through
.IR vmx (3)
-to simulate a virtual PC, running the specified kernel (which must be Multiboot compliant).
+to simulate a virtual PC, running the specified kernel (see below for supported formats).
.PP
By default the virtual PC has 64 MB of memory.
The amount of memory can be changed with the
@@ -60,15 +60,24 @@ argument.
.PP
If
.B -v
-is specified, a graphics device is simulated.
-The argument to
-.B -v
-is either
-.IR text,
-which simulates a CGA text-mode console, or
-"\fIwidth\fLx\fIheight\fLx\fIchan\fL@\fIaddr\fR",
-which simulates a framebuffer at address \fIaddr\fR of the specified size and channel format \fIchan\fR (see
+is specified, a graphics device, PS/2 keyboard and mouse are simulated.
+Clicking on the screen "grabs" the mouse; pressing Ctrl and Alt simultaneously releases the grab.
+Valid values for the argument are
+.TP
+.B text
+Simulate a VGA text-mode console.
+.TP
+\fIwidth\fLx\fIheight\fR [ \fLx\fIchan\fR ] [ \fL@\fIaddr\fR ]
+Simulate a framebuffer at address \fIaddr\fR of the specified size and channel format \fIchan\fR (see
.IR image (6)).
+\fLx\fIchan\fR and \fL@\fIaddr\fR are optional, in which case they default to \fLx8r8g8b8\fR and \fL0xf0000000\fR, respectively
+(in this mode there is no way to change the resolution and accesses to VGA registers have no effect).
+.TP
+\fLvesa:\fImodes\fR [ \fL@\fIaddr\fR ]
+(\fImodes\fR is a comma separated list of modes of the format \fIwidth\fLx\fIheight\fLx\fIchan\fR with the \fLchan\fR optional as before.)
+Simulate a VESA-compatible PCI graphics adapter, initially in text mode.
+The guest can use VESA functions to switch the mode to one of those listed, which are assigned consecutive mode numbers starting with \fL0x120\fR.
+The first mode is indicated as the preferred mode of the monitor.
.PP
The
.B -c
@@ -87,6 +96,10 @@ The argument to
specifies a physical network device (such as
.BR ether0 )
to use.
+Alternatively, a dial string such as \fLudp!\fIhost\fL!\fIport\fR can be used.
+It can also be prefixed by \fLfile!\fR to interpret the argument as a file instead and it can be prefixed by \fLhdr!\fR to enable headers matching the binary
+.IR snoopy (8)
+format.
.PP
A
.B -d
@@ -94,6 +107,23 @@ option adds a
.I virtio
block device (a hard disk)
with the argument as a disk image.
+.SS "Multiboot kernels"
+.PP
+If the specified kernel complies with the Multiboot specification, then \fIargs\fR is concatenated with spaces and passed as the \fIcmdline\fR; the \fL-m\fR modules are passed as boot modules.
+.PP
+Note that 9front is Multiboot compliant and interprets the first boot module as
+.IR plan9.ini (8).
+.SS "OpenBSD kernels"
+.PP
+If the specified kernel is an OpenBSD kernel, the boot modules are ignored and the \fIcmdline\fR is interpreted as
+.IP
+[ \fL-asdc\fR ] [ \fIvar\fL=\fIvalue\fR ... ]
+.PP
+where the options correspond to the \fLboot\fR options and the assignments to the boot variables, which are defined in the OpenBSD manpage \fIboot\fR(8).
+.SS "Linux kernels"
+.PP
+If the specified kernel is a Linux kernel (which must be 2.6.22 or newer and in \fLbzImage\fR format), \fIargs\fR are concatenated with spaces and passed as the \fIcmdline\fR (see \fLDocumentation/admin-guide/kernel-parameters.txt\fR).
+The first boot module is passed as the \fIinitrd\fR; any further boot modules are ignored.
.SH SOURCE
.B /sys/src/cmd/vmx
.SH SEE ALSO
@@ -104,6 +134,8 @@ with the argument as a disk image.
can and will crash your kernel.
.PP
Currently only one core and only one VM at a time is supported.
+.PP
+The Linux \fLvga=\fR option is not supported, as well as many of the OpenBSD boot variables.
.SH HISTORY
.I Vmx
first appeared in 9front (June, 2017).
diff --git a/sys/man/3/vmx b/sys/man/3/vmx
index b4b9e98f4..114d4bf36 100644
--- a/sys/man/3/vmx
+++ b/sys/man/3/vmx
@@ -37,10 +37,10 @@ No virtual CPU.
The virtual CPU is being initialized.
.TP
\fLready\fR
-The virtual CPU is idle and waiting for commands from user space.
+The virtual CPU is idle.
.TP
\fLrunning\fR
-The virtual CPU is busy.
+The virtual CPU is executing code.
.TP
\fLdead\fR
The virtual CPU suffered a fatal error.
@@ -84,6 +84,12 @@ Writes to the file (in the same format) write to the referenced registers (if po
Multiple lines can be written at once but all lines written must be newline terminated.
.PP
+Some registers (\fLCR0\fR and \fLCR4\fR) are split into three registers, suffixed \fLreal\fR, \fLfake\fR and \fLmask\fR.
+In this case, \fLreal\fR corresponds to the bits that affect actual CPU execution, \fLfake\fR corresponds to the bits read back by the guest and the bits set in \fLmask\fR are those "owned" by the host.
+The guest is free to modify the bits that it owns (in which case it always has the same value in both \fLreal\fR and \fLfake\fR), but attempting to change a host-owned bit from the status in \fLfake\fR causes a VM exit.
+Certain bits are owned by the kernel, which means they are fixed in both \fLmask\fR and \fLreal\fR.
+
+.PP
Reading the
.B wait
file will stall the reading process until the virtual CPU reaches a point where it cannot continue (a "VM exit").
@@ -99,12 +105,16 @@ Some notable exit causes are (see kernel source code for a complete list)
.TP
\fL#\fR\fIexception\fR
Exception of the specified type (e.g. \fL#gp\fR for general protection fault).
+Currently only debug exceptions are configured to cause VM exits.
.TP
\fLtriplef\fR
Triple fault.
.TP
\fLeptfault\fR
-The virtual CPU attempted a memory access that does not match any entry in the \Lmap\fR file.
+The virtual CPU attempted a memory access that does not match any entry in the \fLmap\fR file.
+.TP
+\fLmovcr\fR
+Illegal access to a control register (see above).
.IP "\fL.\fR\fIinstr\fR"
The virtual CPU attempted to execute the instruction \fIinstr\fR.
.TP
@@ -118,7 +128,7 @@ file contains the virtual CPU's floating point registers, in the same binary for
.IR proc (3).
.SS Control messages
-.TF "\fLexc\fR \fI[excep]\fR"
+.TF "\fLirq\fR [ \fIexcep\fR ]"
.TP
.B init
Create a new virtual CPU.
@@ -126,22 +136,25 @@ Create a new virtual CPU.
.B quit
Destroy the current virtual CPU.
.TP
-.BI go [regs]
+\fLgo\fR [ \fIregs\fR ]
Launch the virtual CPU.
\fIRegs\fR is an optional list of register changes in the format \fIname\fL=\fIvalue\fL;\fR that will be applied before launching.
.TP
-\fLstep\fR [ \fL-map\fR \fIaddr segment offset\fR \fB]\fR
+.B stop
+Stop the virtual CPU.
+.TP
+\fLstep\fR [ \fL-map\fR \fIaddr segment offset\fR ]\fR
Executes a single instruction with the virtual CPU.
If the \fL-map\fR option is specified, a 4 KB page at address \fIaddr\fR will be temporarily (for the duration of the step) mapped to the spceified \fIsegment\fR and \fIoffset\fR.
A step can fail, in which case a VM exit message is sent to
.BR wait .
.TP
-.BI exc excep
+\fLexc\fR \fIexcep\fR
The exception \fIexcep\fR is triggered in the virtual CPU.
\fIExcep\fR can either be a named exception (such as \fL#gp\fR, in lower case) or an exception number.
A number may be preeded by \fL#\fR to mark it as an exception, otherwise it is delivered as an interrupt (but always disregarding whether interrupts are enabled).
.TP
-.BI irq [excep]
+\fLirq\fR [ \fIexcep\fR ]
An Interrupt is posted, i.e. the exception \fIexcep\fR will be triggered the next time interrupts are enabled in the virtual CPU, at which point a
.B *ack
message is sent to
diff --git a/sys/src/9/pc/devvmx.c b/sys/src/9/pc/devvmx.c
index 91c92e0ff..77074a5a8 100644
--- a/sys/src/9/pc/devvmx.c
+++ b/sys/src/9/pc/devvmx.c
@@ -349,7 +349,7 @@ cr0fakeread(char *p, char *e)
guest = vmcsread(GUEST_CR0);
mask = vmcsread(GUEST_CR0MASK);
shadow = vmcsread(GUEST_CR0SHADOW);
- return seprint(p, e, "%#.*ullx", sizeof(uintptr) * 2, guest & mask | shadow & ~mask);
+ return seprint(p, e, "%#.*ullx", sizeof(uintptr) * 2, guest & ~mask | shadow & mask);
}
static char *
@@ -360,7 +360,7 @@ cr4fakeread(char *p, char *e)
guest = vmcsread(GUEST_CR4);
mask = vmcsread(GUEST_CR4MASK);
shadow = vmcsread(GUEST_CR4SHADOW);
- return seprint(p, e, "%#.*ullx", sizeof(uintptr) * 2, guest & mask | shadow & ~mask);
+ return seprint(p, e, "%#.*ullx", sizeof(uintptr) * 2, guest & ~mask | shadow & mask);
}
static int
@@ -757,7 +757,7 @@ vmcsinit(void)
if(rdmsr(VMX_PROCB_CTLS_MSR, &msr) < 0) error("rdmsr(VMX_PROCB_CTLS_MSR failed");
x = (u32int)procb_ctls | 1<<1 | 7<<4 | 1<<8 | 1<<13 | 1<<14 | 1<<26; /* currently reserved default1 bits */
x |= PROCB_EXITHLT | PROCB_EXITMWAIT;
- x |= PROCB_EXITMOVDR | PROCB_EXITIO | PROCB_EXITMONITOR | PROCB_EXITPAUSE;
+ x |= PROCB_EXITMOVDR | PROCB_EXITIO | PROCB_EXITMONITOR;
x |= PROCB_USECTLS2;
x &= msr >> 32;
vmcswrite(PROCB_CTLS, x);
@@ -993,15 +993,20 @@ cmdgetregs(VmCmd *, va_list va)
p0 = va_arg(va, char *);
e = va_arg(va, char *);
p = p0;
- for(r = guestregs; r < guestregs + nelem(guestregs); r++){
- if(r->offset >= 0)
- val = vmcsread(r->offset);
- else
- val = *(uintptr*)((uchar*)&vmx + ~r->offset);
- s = r->size;
- if(s == 0) s = sizeof(uintptr);
- p = seprint(p, e, "%s %#.*llux\n", r->name, s * 2, val);
- }
+ for(r = guestregs; r < guestregs + nelem(guestregs); r++)
+ if(r->read != nil){
+ p = seprint(p, e, "%s ", r->name);
+ p = r->read(p, e);
+ p = strecpy(p, e, "\n");
+ }else{
+ if(r->offset >= 0)
+ val = vmcsread(r->offset);
+ else
+ val = *(uintptr*)((uchar*)&vmx + ~r->offset);
+ s = r->size;
+ if(s == 0) s = sizeof(uintptr);
+ p = seprint(p, e, "%s %#.*llux\n", r->name, s * 2, val);
+ }
return p - p0;
}
diff --git a/sys/src/cmd/ktrace.c b/sys/src/cmd/ktrace.c
index d816d57e5..86ff89569 100644
--- a/sys/src/cmd/ktrace.c
+++ b/sys/src/cmd/ktrace.c
@@ -370,7 +370,7 @@ getval(uvlong a)
{
char buf[256];
int i, n;
- uvlong r;
+ uvlong r, m;
if(interactive){
print("// data at %#8.8llux? ", a);
@@ -379,17 +379,25 @@ getval(uvlong a)
return 0;
buf[n] = '\0';
r = strtoull(buf, 0, 16);
+ }else{
+ r = 0;
switch(fhdr.magic){
case S_MAGIC:
- r = (long)r; // sign extend
+ m = 0xffffffff00000000ULL;
break;
+ default:
+ m = 0;
}
- }else{
- r = 0;
for(i=0; i<naddr; i++)
- if(addr[i] == a)
+ if((addr[i]|m) == a)
r = val[i];
}
+ switch(fhdr.magic){
+ case S_MAGIC:
+ r = (long)r; // sign extend
+ break;
+ }
+
return r;
}
diff --git a/sys/src/cmd/vmx/exith.c b/sys/src/cmd/vmx/exith.c
index f68566607..0c27bc9bd 100644
--- a/sys/src/cmd/vmx/exith.c
+++ b/sys/src/cmd/vmx/exith.c
@@ -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,13 +340,65 @@ 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)
{
rset("dr6", rget("dr6") | ei->qual);
@@ -380,6 +434,7 @@ static ExitType etypes[] = {
{".wrmsr", rdwrmsr},
{".movdr", movdr},
{"#db", dbgexc},
+ {"movcr", movcr},
};
void
diff --git a/sys/src/cmd/vmx/io.c b/sys/src/cmd/vmx/io.c
index 507dda972..6579a1e93 100644
--- a/sys/src/cmd/vmx/io.c
+++ b/sys/src/cmd/vmx/io.c
@@ -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,
@@ -707,7 +722,7 @@ kbdcmd(u8int val)
case 0xff: keyputc(0xfa); keyputc(0xaa); break; /* reset */
case 0xf5: kbd.quiet = 1; keyputc(0xfa); break; /* disable scanning */
case 0xf4: kbd.quiet = 0; keyputc(0xfa); break; /* enable scanning */
- case 0xf2: keyputc(0xfa); keyputc(0xab); keyputc(0x83); break; /* keyboard id */
+ case 0xf2: keyputc(0xfa); keyputc(0xab); keyputc(0x41); break; /* keyboard id */
case 0xee: keyputc(0xee); break; /* echo */
default:
vmerror("unknown kbd command %#ux", val);
@@ -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 */
diff --git a/sys/src/cmd/vmx/ksetup.c b/sys/src/cmd/vmx/ksetup.c
index 9b8b54baf..3dac6de8b 100644
--- 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 @@ 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);
}
diff --git a/sys/src/cmd/vmx/vesa.c b/sys/src/cmd/vmx/vesa.c
index 96d8ef83f..35c7a0d38 100644
--- a/sys/src/cmd/vmx/vesa.c
+++ b/sys/src/cmd/vmx/vesa.c
@@ -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;
diff --git a/sys/src/cmd/vmx/vga.c b/sys/src/cmd/vmx/vga.c
index 4880a1566..edd141c46 100644
--- a/sys/src/cmd/vmx/vga.c
+++ b/sys/src/cmd/vmx/vga.c
@@ -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){
diff --git a/sys/src/cmd/vmx/virtio.c b/sys/src/cmd/vmx/virtio.c
index 345c94b3c..d4d573fc7 100644
--- a/sys/src/cmd/vmx/virtio.c
+++ b/sys/src/cmd/vmx/virtio.c
@@ -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;