shithub: riscv

Download patch

ref: 3749e92cdb88a157f99c0709a264bd508603be9b
parent: 404c901f299c4d93cb159a3c44c2977a25408319
parent: 32291b52bcbd6976051acff1692b571e321ac859
author: Alex Musolino <[email protected]>
date: Tue Dec 15 15:55:41 EST 2020

merge

--- a/rc/bin/service/!tcp17007
+++ b/rc/bin/service/!tcp17007
@@ -1,3 +1,3 @@
 #!/bin/rc
 netdir=`{echo $3 | sed 's;/[0-9]+$;!*!0;'}
-exec /bin/exportfs -a -A $netdir
+exec /bin/oexportfs -a -A $netdir
--- a/rc/bin/src
+++ b/rc/bin/src
@@ -13,6 +13,8 @@
 fn go{
 	type=`{file <$1 | sed 's/stdin: //'}
 	switch($type){
+	case 'Ascii'
+		plumbit $1 '(ascii file)'
 	case 'rc executable file'
 		plumbit $1 '(rc executable)'
 	case *executable* *'plan 9 boot image'*
--- a/sys/lib/kbmap/ascii
+++ b/sys/lib/kbmap/ascii
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/by
+++ b/sys/lib/kbmap/by
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/de
+++ b/sys/lib/kbmap/de
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/fi
+++ b/sys/lib/kbmap/fi
@@ -13,7 +13,7 @@
 1	2	'!
 1	3	'"
 1	4	'#
-1	5	'$
+1	5	'¤
 1	6	'%
 1	7	'&
 1	8	'/
@@ -40,7 +40,13 @@
 3	10	']
 3	11	'}
 3	12	'\
+3	13	'¸
 3	18	'€
 3	27	'~
 3	46	'¢
 3	86	'|
+3	39	'ø
+3	40	'æ
+7	13	'˛
+7	39	'Ø
+7	40	'Æ
--- a/sys/lib/kbmap/hr
+++ b/sys/lib/kbmap/hr
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/hu
+++ b/sys/lib/kbmap/hu
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/it
+++ b/sys/lib/kbmap/it
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/pl
+++ b/sys/lib/kbmap/pl
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/ru
+++ b/sys/lib/kbmap/ru
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/sg
+++ b/sys/lib/kbmap/sg
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/sr
+++ b/sys/lib/kbmap/sr
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/sr-latin
+++ b/sys/lib/kbmap/sr-latin
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/ua
+++ b/sys/lib/kbmap/ua
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/lib/kbmap/us
+++ b/sys/lib/kbmap/us
@@ -345,7 +345,7 @@
 2	88	0
 2	89	0
 2	90	0
-2	91	0
+2	91	0xf868
 2	92	0
 2	93	0
 2	94	0
--- a/sys/man/1/cpu
+++ b/sys/man/1/cpu
@@ -99,7 +99,7 @@
 argument is present, the
 .I patternfile
 is passed to
-.IR exportfs (4)
+.IR oexportfs (4)
 to control how much of the local name space will be exported to
 the remote system.
 .PP
@@ -210,7 +210,7 @@
 The name space of the terminal side of the
 .I cpu
 command is mounted, via
-.IR exportfs (4),
+.IR oexportfs (4),
 on the CPU side on directory
 .BR /mnt/term .
 The files such as
@@ -222,7 +222,7 @@
 .IR rcpu (1) ,
 .IR rc (1) ,
 .IR rio (1) ,
-.IR exportfs (4) ,
+.IR oexportfs (4) ,
 .IR aan (8)
 .SH BUGS
 Binds and mounts done after the terminal
--- a/sys/man/1/sega
+++ b/sys/man/1/sega
@@ -14,7 +14,7 @@
 .I Md
 is an emulator for the Sega Megadrive/Genesis.
 It executes the romfile given as an argument.
-The \fBz\fR, \fBx\fR, \fBa\fR, return and shift keys correspond to A, B, C, Start and Select, respectively.
+The \fBz\fR, \fBx\fR, \fBc\fR, return and shift keys correspond to A, B, C, Start and Select, respectively.
 Other keys:
 .TP
 F1
--- a/sys/man/1/vmx
+++ b/sys/man/1/vmx
@@ -4,6 +4,9 @@
 .SH SYNOPSIS
 .B vmx
 [
+.B -D
+]
+[
 .B -M
 .I mem
 ]
@@ -24,7 +27,7 @@
 .I blockfile
 ]
 [
-.B -v
+.BR -v | -w
 .I vga
 ]
 [
@@ -58,9 +61,17 @@
 .B -m
 argument.
 .PP
+.B -D
+enables debug messages.
+.PP
 If
 .B -v
 is specified, a graphics device, PS/2 keyboard and mouse are simulated.
+The
+.B -w
+flag behaves the same as
+.B -v
+but also creates a new window for the screen.
 Clicking on the screen "grabs" the mouse; pressing Ctrl and Alt simultaneously releases the grab.
 Valid values for the argument are
 .TP
--- a/sys/man/2/dup
+++ b/sys/man/2/dup
@@ -32,6 +32,19 @@
 .I newfd
 be no greater than 20 more than the highest file descriptor ever used by
 the program.
+.PP
+.I Dup
+does not copy the per file descriptor
+.B OCEXEC
+flag,
+meaning that
+.I newfd
+will not be closed on
+.IR exec(2)
+syscall,
+when
+.I oldfd
+had been previously opend with it.
 .SH SOURCE
 .B /sys/src/libc/9syscall
 .SH SEE ALSO
--- a/sys/man/3/mouse
+++ b/sys/man/3/mouse
@@ -147,17 +147,20 @@
 unlike
 .BR swap .
 .TP
+.B scrollswap
+inverts the scroll wheel.
+.TP
 .B reset
 clears the mouse
 to its default state.
 .TP
 .B blank
-Blank the screen.
+blanks the screen.
 The screen also blanks after 30 minutes of inactivity.
 The screen can be unblanked by moving the mouse.
 .TP
 .BI blanktime " minutes"
-Set the timeout before the
+sets the timeout before the
 screen blanks; the default is 30 minutes.
 If
 .I minutes
--- a/sys/man/4/exportfs
+++ b/sys/man/4/exportfs
@@ -1,6 +1,6 @@
 .TH EXPORTFS 4
 .SH NAME
-exportfs, srvfs \- network file server plumbing
+exportfs, srvfs \- file server plumbing
 .SH SYNOPSIS
 .B exportfs
 [
@@ -26,17 +26,11 @@
 .I path
 .SH DESCRIPTION
 .I Exportfs
-is a user level file server that allows Plan 9 compute servers, rather
-than file servers, to export portions of a name space across networks.
-The service is started either by the
-.IR cpu (1)
-command or by a network listener process.  An initial protocol
-establishes a root directory for the exported name space.
-The
-connection to
-.I exportfs
-is then mounted, typically on
-.BR /mnt/term .
+is a user level file server that allows Plan 9 cpu servers, rather
+than file servers, to export portions of a name space.
+It is usually started by other programs such as
+.IR rcpu (1)
+after a secure channel has been established.
 .I Exportfs
 then acts as a relay file server: operations in the imported file
 tree are executed on the remote server and the results returned.  This
@@ -44,50 +38,7 @@
 into a local file tree.
 .PP
 The options are:
-.TF "-A \fIaddress"
-.PD
 .TP
-.B -A \fIaddress
-Use the network
-.I address
-to announce
-.IR aan (8)
-connections,
-if requested by the initial protocol.
-.TP
-.B -a
-Authenticate the user with the
-.I p9any
-protocol before running the regular
-.I exportfs
-session; used when 
-.I exportfs
-is invoked to handle an incoming network connection.
-.I Exportfs
-creates a new name space for each connection, using
-.B /lib/namespace
-by default (see
-.IR namespace (6)).
-.TP
-.B -B \fIaddress
-Dial
-.IR address ,
-authenticate as a
-.I p9any
-client, and then
-serve that network connection.
-Requires setting the root of the name space with 
-.B -r
-or
-.BR -s .
-The remote system should run
-.B import
-.B -B
-to handle the call.
-See
-.IR import (4)
-for an example.
-.TP
 .B -d -f \fIdbgfile
 Log all 9P traffic to
 .I dbgfile
@@ -94,31 +45,6 @@
 (default
 .BR /tmp/exportdb ).
 .TP
-.B -e '\fIenc auth\fL'
-Set the encryption and authentication algorithms to use for
-encrypting the wire traffic (see
-.IR ssl (3)).
-The defaults are
-.B rc4_256
-and
-.BR sha1 .
-.TP
-.B -m \fImsize
-Set the maximum message size that 
-.I exportfs
-should offer to send (see
-.IR version (5));
-this helps tunneled
-9P connections to avoid unnecessary fragmentation.
-.TP
-.B -N \fInsfile
-Serve the name space described by
-.IR nsfile .
-.TP
-.B -n
-Disallow mounts by user
-.BR none .
-.TP
 .B -P \fIpatternfile
 Restrict the set of exported files.
 .I Patternfile
@@ -137,11 +63,11 @@
 Make the served name space read only.
 .TP
 .B -r \fIroot
-Bypass the initial protocol, serving the name space rooted at
+Serve the name space rooted at
 .IR root .
 .TP
 .B -S \fIservice
-bypass the initial protocol, serving the result of mounting
+Serve the result of mounting
 .IR service .
 A separate mount is used for each
 .IR attach (5)
@@ -156,29 +82,16 @@
 .B -r
 .BR / ;
 kept for compatibility.
+.TP
+.B -m \fImsize
+Set the maximum message size that 
+.I exportfs
+should offer to send (see
+.IR version (5));
+this helps tunneled
+9P connections to avoid unnecessary fragmentation.
 .PD
 .PP
-The
-.B cpu
-command uses
-.I exportfs
-to serve device files in the terminal.  The
-.IR import (4)
-command calls
-.I exportfs
-on a remote machine, permitting users to access arbitrary pieces of
-name space on other systems.
-.PP
-Because the kernel disallows reads and writes on mounted pipes
-(as might be found in
-.BR /srv ),
-.I exportfs
-calls itself (with appropriate
-.B -m
-and
-.B -S 
-options) to simulate reads and writes on such files.
-.PP
 .I Srvfs
 invokes
 .I exportprog
@@ -251,7 +164,4 @@
 .br
 .B /sys/src/cmd/srvfs.c
 .SH SEE ALSO
-.IR dial (2),
-.IR import (4),
-.IR aan (8),
-.IR listen (8)
+.IR rcpu (1)
--- a/sys/man/4/import
+++ b/sys/man/4/import
@@ -45,7 +45,7 @@
 remote machine, with authority of the user of
 .IR import ,
 to perform work for the local machine using the
-.IR exportfs (4)
+.IR oexportfs (4)
 service.
 The default port used is TCP 17007.
 If
@@ -76,7 +76,7 @@
 .B -z
 Bypass the initial protocol request for which remote tree to serve.
 This is necessary when the remote 
-.IR exportfs (4) 
+.IR oexportfs (4) 
 is running with the 
 .B -r 
 or 
@@ -144,7 +144,7 @@
 .I p9any
 authentication (as server) over its file descriptor 0
 (expected to be an incoming network connection from
-.B exportfs
+.B oexportfs
 .BR -B ),
 mounts the connection onto
 .IR mntpt ,
@@ -189,7 +189,7 @@
 boots, it runs
 .IP
 .EX
-exportfs -R -r /usr/web -B tcp!webvax!999
+oexportfs -R -r /usr/web -B tcp!webvax!999
 .EE
 .PP
 to serve a read-only copy of
@@ -212,7 +212,7 @@
 .IR rcpu (1),
 .IR bind (1),
 .IR ssl (3),
-.IR exportfs (4),
+.IR oexportfs (4),
 .IR srv (4),
 .IR aan (8),
 .IR listen (8),
--- /dev/null
+++ b/sys/man/4/oexportfs
@@ -1,0 +1,146 @@
+.TH OEXPORTFS 4
+.SH NAME
+oexportfs \- legacy exportfs for cpu and import
+.SH SYNOPSIS
+.PP
+.B oexportfs
+[
+.I options
+]
+.SH DESCRIPTION
+.I Oexportfs
+is older version of the
+.IR exportfs (4)
+program that handles an initial protocol to establish a root directory
+for the exported name space.
+It also provides authentication and encryption using
+the
+.IR ssl (3)
+device.
+.PP
+It is used exclusively by the deprecated
+.IR cpu (1)
+and
+.IR import (4)
+services.
+.PP
+The options are:
+.TP
+.B -d -f \fIdbgfile
+Log all 9P traffic to
+.I dbgfile
+(default
+.BR /tmp/exportdb ).
+.TP
+.B -P \fIpatternfile
+Restrict the set of exported files.
+.I Patternfile
+contains one regular expression per line,
+to be matched against path names
+relative to the current working directory
+and starting with
+.BR / .
+For a file to be exported, all lines with a prefix
+.B +
+must match and all those with prefix
+.B -
+must not match.
+.TP
+.B -R
+Make the served name space read only.
+.TP
+.B -r \fIroot
+Serve the name space rooted at
+.IR root .
+.TP
+.B -S \fIservice
+Serve the result of mounting
+.IR service .
+A separate mount is used for each
+.IR attach (5)
+message,
+to correctly handle servers in which each mount
+corresponds to a different client
+.IR e.g. , (
+.IR rio (4)).
+.TP
+.B -s
+equivalent to
+.B -r
+.BR / ;
+kept for compatibility.
+.TP
+.B -m \fImsize
+Set the maximum message size that 
+.I oexportfs
+should offer to send (see
+.IR version (5));
+this helps tunneled
+9P connections to avoid unnecessary fragmentation.
+.TP
+.B -A \fIaddress
+Use the network
+.I address
+to announce
+.IR aan (8)
+connections,
+if requested by the initial protocol.
+.TP
+.B -a
+Authenticate the user with the
+.I p9any
+protocol before running the regular
+.I oexportfs
+session; used when 
+.I oexportfs
+is invoked to handle an incoming network connection.
+.I Exportfs
+creates a new name space for each connection, using
+.B /lib/namespace
+by default (see
+.IR namespace (6)).
+.TP
+.B -B \fIaddress
+Dial
+.IR address ,
+authenticate as a
+.I p9any
+client, and then
+serve that network connection.
+Requires setting the root of the name space with 
+.B -r
+or
+.BR -s .
+The remote system should run
+.B import
+.B -B
+to handle the call.
+See
+.IR import (4)
+for an example.
+.TP
+.B -e '\fIenc auth\fL'
+Set the encryption and authentication algorithms to use for
+encrypting the wire traffic (see
+.IR ssl (3)).
+The defaults are
+.B rc4_256
+and
+.BR sha1 .
+.TP
+.B -N \fInsfile
+Serve the name space described by
+.IR nsfile .
+.TP
+.B -n
+Disallow mounts by user
+.BR none .
+.EE
+.SH SOURCE
+.B /sys/src/cmd/exportfs/oexportfs.c
+.SH SEE ALSO
+.IR dial (2),
+.IR exportfs (4),
+.IR import (4),
+.IR aan (8),
+.IR listen (8)
--- a/sys/man/4/ptrap
+++ b/sys/man/4/ptrap
@@ -7,10 +7,7 @@
 .B ptrap
 .I port
 [\fB!\fR]\fIregexp\fR
-[
-.I port
-[\fB!\fR]\fIregexp\fR ...
-]
+[ +\fIattr\fR [\fB!\fR]\fIregexp\fR ... ] ...
 .SH DESCRIPTION
 .I Ptrap
 is a program that mounts itself over a
@@ -20,20 +17,20 @@
 and filters incoming messages according to the rules provided on the command line.
 .PP
 .I Ptrap
-accepts an arbitrary number of argument pairs; each pair consists of a port name
-.I port
-and a regular expression
-.I regexp
-(see
-.IR regexp (6)).
-Each incoming message that does not match
-.I regexp
-is discarded.
-The
-.I regexp
-can be optionally prefixed by
-.B !
-to indicate logical inversion (i.e. messages matching the regexp are discarded).
+accepts an arbitrary number of filters;
+each filter applies to a port, and may match over both the data and attributes of plumb messages.
+.PP
+A filter is formatted as a port name, a data filter, and a list of attribute filters.
+.PP
+The data filter is a
+.IR regex (6)
+that matches the plumbed data.
+The attribute filter consists of the attribute name prefixed with a '+', followed by a
+.IR regex (6)
+that matches the contents of the attribute.
+Any regex may be prefixed with a '!' in order to negate a match,
+causing all matches for that regex to be discarded.
+All parts of a filter must match in order for a plumb message to be forwarded.
 .SH EXAMPLES
 Start a 
 .IR sam (1)
@@ -51,6 +48,15 @@
 .EX
 ptrap edit '!^/sys/src/9/'
 sam
+.EE
+.PP
+Start an
+.IR acme (1)
+instance instance dedicated to reading plumbed manual pages:
+.IP
+.EX
+ptrap edit '.*' +action '^showdata' +filename '^/man/'
+acme -c1
 .EE
 .SH SOURCE
 .B /sys/src/cmd/ptrap.c
--- a/sys/man/8/plan9.ini
+++ b/sys/man/8/plan9.ini
@@ -815,11 +815,13 @@
 This limits the maximum amount of memory (in megabytes) the graphics
 image memory pool can grow. The default is unlimited for terminals
 and cpu servers.
-.SS \fL*nomce=\fIvalue\fP
+.SS \fL*noavx=\fP
+Disables AVX and AVX2 on AMD64 CPUs.
+.SS \fL*nomce=\fP
 If machine check exceptions are supported by the processor,
 then they are enabled by default.
-Setting this variable to
-.B 1
+Setting
+.B *nomce
 causes them to be disabled even when available.
 .SS \fL*nomp=\fP
 A multiprocessor machine will enable all processors by default.
@@ -836,7 +838,12 @@
 .SS \fL*apicdebug=\fP
 Prints a summary of the multiprocessor APIC interrupt configuration.
 .SS \fL*nomsi=\fP
-Disables message signaled interrupts.
+Disables message signaled interrupts for PCI devices.
+This option has no effect when
+.B *nomp
+is set.
+.SS \fL*nomtrr=\fP
+Disables memory type range register (MTRR) support when set. (debug)
 .SS \fL*notsc=\fP
 Disables the use of the per processor timestamp counter registers
 as high resolution clock.
--- a/sys/man/8/statusbar
+++ b/sys/man/8/statusbar
@@ -9,8 +9,9 @@
 [
 .B -w
 .I minx,miny,maxx,maxy
-]
+] [
 .I title
+]
 .br
 .B aux/statusmsg
 [
--- a/sys/src/9/pc/cputemp.c
+++ b/sys/src/9/pc/cputemp.c
@@ -13,7 +13,7 @@
 
 	if(m->cpuiddx & Acpif)
 	if(strcmp(m->cpuidid, "GenuineIntel") == 0){
-		cpuid(6, regs);
+		cpuid(6, 0, regs);
 		return regs[0] & 1;
 	}
 	return 0;
@@ -28,7 +28,7 @@
 	ulong regs[4];
 	static ulong tj;
 
-	cpuid(6, regs);
+	cpuid(6, 0, regs);
 	if((regs[0] & 1) == 0)
 		goto unsup;
 	if(tj == 0){
--- a/sys/src/9/pc/dat.h
+++ b/sys/src/9/pc/dat.h
@@ -250,7 +250,7 @@
 	int	pdbfree;
 	
 	u32int	dr7;			/* shadow copy of dr7 */
-	
+	u32int	xcr0;
 	void*	vmx;
 
 	int	stack[1];
--- a/sys/src/9/pc/devarch.c
+++ b/sys/src/9/pc/devarch.c
@@ -18,11 +18,6 @@
 	Qmax = 32,
 };
 
-enum {
-	CR4Osfxsr = 1 << 9,
-	CR4Oxmmex = 1 << 10,
-};
-
 enum {				/* cpuid standard function codes */
 	Highstdfunc = 0,	/* also returns vendor string */
 	Procsig,
@@ -496,24 +491,27 @@
  *		(if so turn it on)
  *	- whether or not it supports the page global flag
  *		(if so turn it on)
+ *	- detect PAT feature and add write-combining entry
+ *	- detect MTRR support and synchronize state with cpu0
+ *	- detect NX support and enable it for AMD64
+ *	- detect watchpoint support
+ *	- detect FPU features and enable the FPU
  */
 int
 cpuidentify(void)
 {
-	char *p;
-	int family, model, nomce;
+	int family, model;
 	X86type *t, *tab;
-	uintptr cr4;
 	ulong regs[4];
-	vlong mca, mct, pat;
+	uintptr cr4;
 
-	cpuid(Highstdfunc, regs);
+	cpuid(Highstdfunc, 0, regs);
 	memmove(m->cpuidid,   &regs[1], BY2WD);	/* bx */
 	memmove(m->cpuidid+4, &regs[3], BY2WD);	/* dx */
 	memmove(m->cpuidid+8, &regs[2], BY2WD);	/* cx */
 	m->cpuidid[12] = '\0';
 
-	cpuid(Procsig, regs);
+	cpuid(Procsig, 0, regs);
 	m->cpuidax = regs[0];
 	m->cpuidcx = regs[2];
 	m->cpuiddx = regs[3];
@@ -572,14 +570,13 @@
 	 * If machine check was enabled clear out any lingering status.
 	 */
 	if(m->cpuiddx & (Pge|Mce|Pse)){
+		vlong mca, mct;
+
 		cr4 = getcr4();
 		if(m->cpuiddx & Pse)
 			cr4 |= 0x10;		/* page size extensions */
-		if(p = getconf("*nomce"))
-			nomce = strtoul(p, 0, 0);
-		else
-			nomce = 0;
-		if((m->cpuiddx & Mce) != 0 && !nomce){
+
+		if((m->cpuiddx & Mce) != 0 && getconf("*nomce") == nil){
 			if((m->cpuiddx & Mca) != 0){
 				vlong cap;
 				int bank;
@@ -631,7 +628,6 @@
 			cr4 |= 0x80;		/* page global enable bit */
 			m->havepge = 1;
 		}
-
 		putcr4(cr4);
 
 		if((m->cpuiddx & (Mca|Mce)) == Mce)
@@ -640,25 +636,20 @@
 
 #ifdef PATWC
 	/* IA32_PAT write combining */
-	if((m->cpuiddx & Pat) != 0 && rdmsr(0x277, &pat) != -1){
-		pat &= ~(255LL<<(PATWC*8));
-		pat |= 1LL<<(PATWC*8);	/* WC */
-		wrmsr(0x277, pat);
+	if((m->cpuiddx & Pat) != 0){
+		vlong pat;
+
+		if(rdmsr(0x277, &pat) != -1){
+			pat &= ~(255LL<<(PATWC*8));
+			pat |= 1LL<<(PATWC*8);	/* WC */
+			wrmsr(0x277, pat);
+		}
 	}
 #endif
 
-	if(m->cpuiddx & Mtrr)
+	if((m->cpuiddx & Mtrr) != 0 && getconf("*nomtrr") == nil)
 		mtrrsync();
 
-	if((m->cpuiddx & (Sse|Fxsr)) == (Sse|Fxsr)){			/* have sse fp? */
-		fpsave = fpssesave;
-		fprestore = fpsserestore;
-		putcr4(getcr4() | CR4Osfxsr|CR4Oxmmex);
-	} else {
-		fpsave = fpx87save;
-		fprestore = fpx87restore;
-	}
-
 	if(strcmp(m->cpuidid, "GenuineIntel") == 0 && (m->cpuidcx & Rdrnd) != 0)
 		hwrandbuf = rdrandbuf;
 	else
@@ -669,9 +660,9 @@
 		m->havewatchpt8 = 1;
 
 		/* check and enable NX bit */
-		cpuid(Highextfunc, regs);
+		cpuid(Highextfunc, 0, regs);
 		if(regs[0] >= Procextfeat){
-			cpuid(Procextfeat, regs);
+			cpuid(Procextfeat, 0, regs);
 			if((regs[3] & (1<<20)) != 0){
 				vlong efer;
 
@@ -689,13 +680,15 @@
 		|| family == 6 && (model == 15 || model == 23 || model == 28))
 			m->havewatchpt8 = 1;
 		/* Intel SDM claims amd64 support implies 8-byte watchpoint support */
-		cpuid(Highextfunc, regs);
+		cpuid(Highextfunc, 0, regs);
 		if(regs[0] >= Procextfeat){
-			cpuid(Procextfeat, regs);
+			cpuid(Procextfeat, 0, regs);
 			if((regs[3] & 1<<29) != 0)
 				m->havewatchpt8 = 1;
 		}
 	}
+
+	fpuinit();
 
 	cputype = t;
 	return t->family;
--- a/sys/src/9/pc/devvmx.c
+++ b/sys/src/9/pc/devvmx.c
@@ -44,6 +44,7 @@
 	
 	PROCB_CTLS = 0x4002,
 	PROCB_IRQWIN = 1<<2,
+	PROCB_TSCOFFSET = 1<<3,
 	PROCB_EXITHLT = 1<<7,
 	PROCB_EXITINVLPG = 1<<9,
 	PROCB_EXITMWAIT = 1<<10,
@@ -100,6 +101,7 @@
 	VMENTRY_INTRCODE = 0x4018,
 	VMENTRY_INTRILEN = 0x401a,
 	
+	VMCS_TSC_OFFSET = 0x2010,
 	VMCS_LINK = 0x2800,
 	
 	GUEST_ES = 0x800,
@@ -264,7 +266,9 @@
 	int index, machno;
 	char errstr[ERRMAX];
 	Ureg ureg;
+	uvlong tscoffset;
 	uintptr cr2;
+	uintptr xcr0;
 	uintptr dr[8]; /* DR7 is also kept in VMCS */
 	u8int launched;
 	u8int vpid;
@@ -484,6 +488,13 @@
 }
 
 static int
+xcr0write(Vmx *vmx, char *s)
+{
+	vmx->xcr0 = parseval(s) & 7;
+	return 0;
+}
+
+static int
 readonly(Vmx *, char *)
 {
 	return -1;
@@ -581,6 +592,7 @@
 	{VMXVAR(dr[2]), 0, "dr2"},
 	{VMXVAR(dr[3]), 0, "dr3"},
 	{VMXVAR(dr[6]), 0, "dr6", nil, dr6write},
+	{VMXVAR(xcr0), 0, "xcr0", nil, xcr0write},
 	{GUEST_DR7, 0, "dr7", nil, dr7write},
 	{VM_INSTRERR, 4, "instructionerror", nil, readonly},
 	{VM_EXREASON, 4, "exitreason", nil, readonly},
@@ -857,7 +869,7 @@
 	vlong msr;
 	int i;
 
-	cpuid(1, regs);
+	cpuid(1, 0, regs);
 	if((regs[2] & 1<<5) == 0) return;
 	/* check if disabled by BIOS */
 	if(rdmsr(0x3a, &msr) < 0) return;
@@ -945,8 +957,8 @@
 	
 	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_MSRBITMAP;
+	x |= PROCB_TSCOFFSET | PROCB_EXITMWAIT | PROCB_EXITMONITOR | PROCB_EXITHLT;
+	x |= PROCB_EXITMOVDR | PROCB_EXITIO | PROCB_MSRBITMAP;
 	x |= PROCB_USECTLS2;
 	x &= msr >> 32;
 	vmcswrite(PROCB_CTLS, x);
@@ -1042,8 +1054,8 @@
 	
 	vmx->onentry = FLUSHVPID | FLUSHEPT;
 	fpinit();
-	fpsave(&vmx->fp);
-	
+	vmx->xcr0 = m->xcr0 & 1; /* x87 alone */
+
 	memset(vmx->msrbits, -1, 4096);
 	vmxtrapmsr(vmx, Efer, 0);
 	vmcswrite(VMENTRY_MSRLDADDR, PADDR(vmx->msrguest));
@@ -1051,6 +1063,9 @@
 	vmcswrite(VMEXIT_MSRLDADDR, PADDR(vmx->msrhost));
 	vmcswrite(MSR_BITMAP, PADDR(vmx->msrbits));
 	
+	cycles(&vmx->tscoffset);
+	vmcswrite(VMCS_TSC_OFFSET, vmx->tscoffset);
+
 	if(sizeof(uintptr) == 8){
 		vmxaddmsr(vmx, Star, 0);
 		vmxaddmsr(vmx, Lstar, 0);
@@ -1074,7 +1089,7 @@
 	uintptr cr;
 	vlong x;
 
-	putcr4(getcr4() | 0x2000); /* set VMXE */
+	putcr4(getcr4() | CR4VMXE);
 	putcr0(getcr0() | 0x20); /* set NE */
 	cr = getcr0();
 	if(rdmsr(VMX_CR0_FIXED0, &msr) < 0) error("rdmsr(VMX_CR0_FIXED0) failed");
@@ -1590,8 +1605,9 @@
 static void
 vmxproc(void *vmxp)
 {
-	int init, rc, x;
+	int init, rc, x, useend;
 	u32int procbctls, defprocbctls;
+	u64int start, end, adj;
 	vlong v;
 	Vmx *vmx;
 
@@ -1599,6 +1615,8 @@
 	procwired(up, vmx->machno);
 	sched();
 	init = 0;
+	useend = 0;
+	adj = 0;
 	defprocbctls = 0;
 	while(waserror()){
 		kstrcpy(vmx->errstr, up->errstr, ERRMAX);
@@ -1653,11 +1671,29 @@
 			}
 			if((vmx->dr[7] & ~0xd400) != 0)
 				putdr01236(vmx->dr);
-			fpsserestore(&vmx->fp);
-			putcr2(vmx->cr2);
+
+			fprestore(&vmx->fp);
+			if(m->xcr0 != 0 && vmx->xcr0 != m->xcr0)
+				putxcr0(vmx->xcr0);
+			if(vmx->cr2 != getcr2())
+				putcr2(vmx->cr2);
+			cycles(&start);
+			if(useend){
+				vmx->tscoffset -= end - start + adj;
+				vmcswrite(VMCS_TSC_OFFSET, vmx->tscoffset);
+			}
+			if(adj == 0){
+				cycles(&adj);
+				adj -= start;
+			}
 			rc = vmlaunch(&vmx->ureg, vmx->launched);
+			cycles(&end);
+			useend = 1;
 			vmx->cr2 = getcr2();
-			fpssesave(&vmx->fp);
+			if(m->xcr0 != 0 && vmx->xcr0 != m->xcr0)
+				putxcr0(m->xcr0);
+			fpsave(&vmx->fp);
+
 			splx(x);
 			if(rc < 0)
 				error("vmlaunch failed");
@@ -1799,6 +1835,7 @@
 		free(vmx);
 		nexterror();
 	}
+	memset(vmx, 0, sizeof(Vmx));
 	vmx->state = VMXINIT;
 	vmx->lastcmd = &vmx->firstcmd;
 	vmx->mem.next = &vmx->mem;
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -15,7 +15,8 @@
 int	(*cmpswap)(long*, long, long);
 int	cmpswap486(long*, long, long);
 void	(*coherence)(void);
-void	cpuid(int, ulong regs[]);
+void	cpuid(int, int, ulong regs[]);
+void	fpuinit(void);
 int	cpuidentify(void);
 void	cpuidprint(void);
 void	(*cycles)(uvlong*);
@@ -40,12 +41,6 @@
 void	fpoff(void);
 void	(*fprestore)(FPsave*);
 void	(*fpsave)(FPsave*);
-void	fpsserestore(FPsave*);
-void	fpssesave(FPsave*);
-void	fpx87restore(FPsave*);
-void	fpx87restore0(FPsave*);
-void	fpx87save(FPsave*);
-void	fpx87save0(FPsave*);
 ulong	getcr0(void);
 ulong	getcr2(void);
 ulong	getcr3(void);
@@ -83,7 +78,6 @@
 int	isaconfig(char*, int, ISAConf*);
 void*	kaddr(ulong);
 #define	kmapinval()
-void	ldmxcsr(ulong);
 void	lgdt(ushort[3]);
 void	lldt(ulong);
 void	lidt(ushort[3]);
@@ -138,6 +132,7 @@
 void	putcr2(ulong);
 void	putcr3(ulong);
 void	putcr4(ulong);
+void	putxcr0(ulong);
 void	putdr(u32int*);
 void	putdr01236(uintptr*);
 void	putdr6(u32int);
--- /dev/null
+++ b/sys/src/9/pc/fpu.c
@@ -1,0 +1,309 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+enum {
+	CR4Osfxsr  = 1 << 9,
+	CR4Oxmmex  = 1 << 10,
+};
+
+/* from l.s */
+extern void fpsserestore(FPsave*);
+extern void fpssesave(FPsave*);
+extern void fpx87restore0(FPsave*);
+extern void fpx87save0(FPsave*);
+extern void ldmxcsr(ulong);
+
+void
+putxcr0(ulong)
+{
+}
+
+/*
+ * we keep FPsave structure in SSE format emulating FXSAVE / FXRSTOR
+ * instructions for legacy x87 fpu.
+ */
+static void
+fpx87save(FPsave *fps)
+{
+	ushort tag;
+
+	fpx87save0(fps);
+
+	/*
+	 * convert x87 tag word to fxsave tag byte:
+	 * 00, 01, 10 -> 1, 11 -> 0
+	 */
+	tag = ~fps->tag;
+	tag = (tag | (tag >> 1)) & 0x5555;
+	tag = (tag | (tag >> 1)) & 0x3333;
+	tag = (tag | (tag >> 2)) & 0x0F0F;
+	tag = (tag | (tag >> 4)) & 0x00FF;
+
+	/* NOP fps->fcw = fps->control; */
+	fps->fsw = fps->status;
+	fps->ftw = tag;
+	fps->fop = fps->opcode;
+	fps->fpuip = fps->pc;
+	fps->cs = fps->selector;
+	fps->fpudp = fps->operand;
+	fps->ds = fps->oselector;
+
+#define MOVA(d,s) \
+	*((ushort*)(d+8)) = *((ushort*)(s+8)), \
+	*((ulong*)(d+4)) = *((ulong*)(s+4)), \
+	*((ulong*)(d)) = *((ulong*)(s))
+
+	MOVA(fps->xregs+0x70, fps->regs+70);
+	MOVA(fps->xregs+0x60, fps->regs+60);
+	MOVA(fps->xregs+0x50, fps->regs+50);
+	MOVA(fps->xregs+0x40, fps->regs+40);
+	MOVA(fps->xregs+0x30, fps->regs+30);
+	MOVA(fps->xregs+0x20, fps->regs+20);
+	MOVA(fps->xregs+0x10, fps->regs+10);
+	MOVA(fps->xregs+0x00, fps->regs+00);
+
+#undef MOVA
+
+#define CLR6(d)	\
+	*((ulong*)(d)) = 0, \
+	*((ushort*)(d+4)) = 0
+
+	CLR6(fps->xregs+0x70+10);
+	CLR6(fps->xregs+0x60+10);
+	CLR6(fps->xregs+0x50+10);
+	CLR6(fps->xregs+0x40+10);
+	CLR6(fps->xregs+0x30+10);
+	CLR6(fps->xregs+0x20+10);
+	CLR6(fps->xregs+0x10+10);
+	CLR6(fps->xregs+0x00+10);
+
+#undef CLR6
+
+	fps->rsrvd1 = fps->rsrvd2 = fps->mxcsr = fps->mxcsr_mask = 0;
+}
+
+static void
+fpx87restore(FPsave *fps)
+{
+	ushort msk, tos, tag, *reg;
+
+	/* convert fxsave tag byte to x87 tag word */
+	tag = 0;
+	tos = 7 - ((fps->fsw >> 11) & 7);
+	for(msk = 0x80; msk != 0; tos--, msk >>= 1){
+		tag <<= 2;
+		if((fps->ftw & msk) != 0){
+			reg = (ushort*)&fps->xregs[(tos & 7) << 4];
+			switch(reg[4] & 0x7fff){
+			case 0x0000:
+				if((reg[0] | reg[1] | reg[2] | reg[3]) == 0){
+					tag |= 1;	/* 01 zero */
+					break;
+				}
+				/* no break */
+			case 0x7fff:
+				tag |= 2;		/* 10 special */
+				break;
+			default:
+				if((reg[3] & 0x8000) == 0)
+					break;		/* 00 valid */
+				tag |= 2;		/* 10 special */
+				break;
+			}
+		}else{
+			tag |= 3;			/* 11 empty */
+		}
+	}
+
+#define MOVA(d,s) \
+	*((ulong*)(d)) = *((ulong*)(s)), \
+	*((ulong*)(d+4)) = *((ulong*)(s+4)), \
+	*((ushort*)(d+8)) = *((ushort*)(s+8))
+
+	MOVA(fps->regs+00, fps->xregs+0x00);
+	MOVA(fps->regs+10, fps->xregs+0x10);
+	MOVA(fps->regs+20, fps->xregs+0x20);
+	MOVA(fps->regs+30, fps->xregs+0x30);
+	MOVA(fps->regs+40, fps->xregs+0x40);
+	MOVA(fps->regs+50, fps->xregs+0x50);
+	MOVA(fps->regs+60, fps->xregs+0x60);
+	MOVA(fps->regs+70, fps->xregs+0x70);
+
+#undef MOVA
+
+	fps->oselector = fps->ds;
+	fps->operand = fps->fpudp;
+	fps->opcode = fps->fop & 0x7ff;
+	fps->selector = fps->cs;
+	fps->pc = fps->fpuip;
+	fps->tag = tag;
+	fps->status = fps->fsw;
+	/* NOP fps->control = fps->fcw;  */
+
+	fps->r1 = fps->r2 = fps->r3 = fps->r4 = 0;
+
+	fpx87restore0(fps);
+}
+
+static char* mathmsg[] =
+{
+	nil,	/* handled below */
+	"denormalized operand",
+	"division by zero",
+	"numeric overflow",
+	"numeric underflow",
+	"precision loss",
+};
+
+static void
+mathnote(ulong status, ulong pc)
+{
+	char *msg, note[ERRMAX];
+	int i;
+
+	/*
+	 * Some attention should probably be paid here to the
+	 * exception masks and error summary.
+	 */
+	msg = "unknown exception";
+	for(i = 1; i <= 5; i++){
+		if(!((1<<i) & status))
+			continue;
+		msg = mathmsg[i];
+		break;
+	}
+	if(status & 0x01){
+		if(status & 0x40){
+			if(status & 0x200)
+				msg = "stack overflow";
+			else
+				msg = "stack underflow";
+		}else
+			msg = "invalid operation";
+	}
+	snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux",
+		msg, pc, status);
+	postnote(up, 1, note, NDebug);
+}
+
+/*
+ *  math coprocessor error
+ */
+static void
+matherror(Ureg*, void*)
+{
+	/*
+	 *  a write cycle to port 0xF0 clears the interrupt latch attached
+	 *  to the error# line from the 387
+	 */
+	if(!(m->cpuiddx & Fpuonchip))
+		outb(0xF0, 0xFF);
+
+	/*
+	 *  get floating point state to check out error
+	 */
+	fpsave(up->fpsave);
+	up->fpstate = FPinactive;
+	mathnote(up->fpsave->fsw, up->fpsave->fpuip);
+}
+
+/*
+ *  SIMD error
+ */
+static void
+simderror(Ureg *ureg, void*)
+{
+	fpsave(up->fpsave);
+	up->fpstate = FPinactive;
+	mathnote(up->fpsave->mxcsr & 0x3f, ureg->pc);
+}
+
+/*
+ *  math coprocessor emulation fault
+ */
+static void
+mathemu(Ureg *ureg, void*)
+{
+	ulong status, control;
+
+	if(up->fpstate & FPillegal){
+		/* someone did floating point in a note handler */
+		postnote(up, 1, "sys: floating point in note handler", NDebug);
+		return;
+	}
+	switch(up->fpstate){
+	case FPinit:
+		fpinit();
+		if(fpsave == fpssesave)
+			ldmxcsr(0x1f80);	/* no simd exceptions on 386 */
+		while(up->fpsave == nil)
+			up->fpsave = mallocalign(sizeof(FPsave), FPalign, 0, 0);
+		up->fpstate = FPactive;
+		break;
+	case FPinactive:
+		/*
+		 * Before restoring the state, check for any pending
+		 * exceptions, there's no way to restore the state without
+		 * generating an unmasked exception.
+		 * More attention should probably be paid here to the
+		 * exception masks and error summary.
+		 */
+		status = up->fpsave->fsw;
+		control = up->fpsave->fcw;
+		if((status & ~control) & 0x07F){
+			mathnote(status, up->fpsave->fpuip);
+			break;
+		}
+		fprestore(up->fpsave);
+		up->fpstate = FPactive;
+		break;
+	case FPactive:
+		panic("math emu pid %ld %s pc 0x%lux", 
+			up->pid, up->text, ureg->pc);
+		break;
+	}
+}
+
+/*
+ *  math coprocessor segment overrun
+ */
+static void
+mathover(Ureg*, void*)
+{
+	pexit("math overrun", 0);
+}
+
+void
+mathinit(void)
+{
+	trapenable(VectorCERR, matherror, 0, "matherror");
+	if(m->cpuidfamily == 3)
+		intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
+	trapenable(VectorCNA, mathemu, 0, "mathemu");
+	trapenable(VectorCSO, mathover, 0, "mathover");
+	trapenable(VectorSIMD, simderror, 0, "simderror");
+}
+
+/*
+ * fpuinit(), called from cpuidentify() for each cpu.
+ */
+void
+fpuinit(void)
+{
+	uintptr cr4;
+
+	if((m->cpuiddx & (Sse|Fxsr)) == (Sse|Fxsr)){ /* have sse fp? */
+		fpsave = fpssesave;
+		fprestore = fpsserestore;
+		cr4 = getcr4() | CR4Osfxsr|CR4Oxmmex;
+		putcr4(cr4);
+	} else {
+		fpsave = fpx87save;
+		fprestore = fpx87restore;
+	}
+}
--- a/sys/src/9/pc/l.s
+++ b/sys/src/9/pc/l.s
@@ -520,7 +520,7 @@
  * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
  * toggled then it's an older 486 of some kind.
  *
- *	cpuid(fun, regs[4]);
+ *	cpuid(fn, sublvl, regs[4]);
  */
 TEXT cpuid(SB), $0
 	MOVL	$0x240000, AX
@@ -539,6 +539,7 @@
 	TESTL	$0x200000, AX			/* Id */
 	JZ	_cpu486				/* can't toggle this bit on some 486 */
 	MOVL	fn+0(FP), AX
+	MOVL	sublvl+4(FP), CX
 	CPUID
 	JMP	_cpuid
 _cpu486:
@@ -555,7 +556,7 @@
 	XORL	CX, CX
 	XORL	DX, DX
 _cpuid:
-	MOVL	regs+4(FP), BP
+	MOVL	regs+8(FP), BP
 	MOVL	AX, 0(BP)
 	MOVL	BX, 4(BP)
 	MOVL	CX, 8(BP)
--- a/sys/src/9/pc/main.c
+++ b/sys/src/9/pc/main.c
@@ -234,272 +234,6 @@
 }
 
 /*
- * we keep FPsave structure in SSE format emulating FXSAVE / FXRSTOR
- * instructions for legacy x87 fpu.
- */
-void
-fpx87save(FPsave *fps)
-{
-	ushort tag;
-
-	fpx87save0(fps);
-
-	/*
-	 * convert x87 tag word to fxsave tag byte:
-	 * 00, 01, 10 -> 1, 11 -> 0
-	 */
-	tag = ~fps->tag;
-	tag = (tag | (tag >> 1)) & 0x5555;
-	tag = (tag | (tag >> 1)) & 0x3333;
-	tag = (tag | (tag >> 2)) & 0x0F0F;
-	tag = (tag | (tag >> 4)) & 0x00FF;
-
-	/* NOP fps->fcw = fps->control; */
-	fps->fsw = fps->status;
-	fps->ftw = tag;
-	fps->fop = fps->opcode;
-	fps->fpuip = fps->pc;
-	fps->cs = fps->selector;
-	fps->fpudp = fps->operand;
-	fps->ds = fps->oselector;
-
-#define MOVA(d,s) \
-	*((ushort*)(d+8)) = *((ushort*)(s+8)), \
-	*((ulong*)(d+4)) = *((ulong*)(s+4)), \
-	*((ulong*)(d)) = *((ulong*)(s))
-
-	MOVA(fps->xregs+0x70, fps->regs+70);
-	MOVA(fps->xregs+0x60, fps->regs+60);
-	MOVA(fps->xregs+0x50, fps->regs+50);
-	MOVA(fps->xregs+0x40, fps->regs+40);
-	MOVA(fps->xregs+0x30, fps->regs+30);
-	MOVA(fps->xregs+0x20, fps->regs+20);
-	MOVA(fps->xregs+0x10, fps->regs+10);
-	MOVA(fps->xregs+0x00, fps->regs+00);
-
-#undef MOVA
-
-#define CLR6(d)	\
-	*((ulong*)(d)) = 0, \
-	*((ushort*)(d+4)) = 0
-
-	CLR6(fps->xregs+0x70+10);
-	CLR6(fps->xregs+0x60+10);
-	CLR6(fps->xregs+0x50+10);
-	CLR6(fps->xregs+0x40+10);
-	CLR6(fps->xregs+0x30+10);
-	CLR6(fps->xregs+0x20+10);
-	CLR6(fps->xregs+0x10+10);
-	CLR6(fps->xregs+0x00+10);
-
-#undef CLR6
-
-	fps->rsrvd1 = fps->rsrvd2 = fps->mxcsr = fps->mxcsr_mask = 0;
-}
-
-void
-fpx87restore(FPsave *fps)
-{
-	ushort msk, tos, tag, *reg;
-
-	/* convert fxsave tag byte to x87 tag word */
-	tag = 0;
-	tos = 7 - ((fps->fsw >> 11) & 7);
-	for(msk = 0x80; msk != 0; tos--, msk >>= 1){
-		tag <<= 2;
-		if((fps->ftw & msk) != 0){
-			reg = (ushort*)&fps->xregs[(tos & 7) << 4];
-			switch(reg[4] & 0x7fff){
-			case 0x0000:
-				if((reg[0] | reg[1] | reg[2] | reg[3]) == 0){
-					tag |= 1;	/* 01 zero */
-					break;
-				}
-				/* no break */
-			case 0x7fff:
-				tag |= 2;		/* 10 special */
-				break;
-			default:
-				if((reg[3] & 0x8000) == 0)
-					break;		/* 00 valid */
-				tag |= 2;		/* 10 special */
-				break;
-			}
-		}else{
-			tag |= 3;			/* 11 empty */
-		}
-	}
-
-#define MOVA(d,s) \
-	*((ulong*)(d)) = *((ulong*)(s)), \
-	*((ulong*)(d+4)) = *((ulong*)(s+4)), \
-	*((ushort*)(d+8)) = *((ushort*)(s+8))
-
-	MOVA(fps->regs+00, fps->xregs+0x00);
-	MOVA(fps->regs+10, fps->xregs+0x10);
-	MOVA(fps->regs+20, fps->xregs+0x20);
-	MOVA(fps->regs+30, fps->xregs+0x30);
-	MOVA(fps->regs+40, fps->xregs+0x40);
-	MOVA(fps->regs+50, fps->xregs+0x50);
-	MOVA(fps->regs+60, fps->xregs+0x60);
-	MOVA(fps->regs+70, fps->xregs+0x70);
-
-#undef MOVA
-
-	fps->oselector = fps->ds;
-	fps->operand = fps->fpudp;
-	fps->opcode = fps->fop & 0x7ff;
-	fps->selector = fps->cs;
-	fps->pc = fps->fpuip;
-	fps->tag = tag;
-	fps->status = fps->fsw;
-	/* NOP fps->control = fps->fcw;  */
-
-	fps->r1 = fps->r2 = fps->r3 = fps->r4 = 0;
-
-	fpx87restore0(fps);
-}
-
-static char* mathmsg[] =
-{
-	nil,	/* handled below */
-	"denormalized operand",
-	"division by zero",
-	"numeric overflow",
-	"numeric underflow",
-	"precision loss",
-};
-
-static void
-mathnote(ulong status, ulong pc)
-{
-	char *msg, note[ERRMAX];
-	int i;
-
-	/*
-	 * Some attention should probably be paid here to the
-	 * exception masks and error summary.
-	 */
-	msg = "unknown exception";
-	for(i = 1; i <= 5; i++){
-		if(!((1<<i) & status))
-			continue;
-		msg = mathmsg[i];
-		break;
-	}
-	if(status & 0x01){
-		if(status & 0x40){
-			if(status & 0x200)
-				msg = "stack overflow";
-			else
-				msg = "stack underflow";
-		}else
-			msg = "invalid operation";
-	}
-	snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux",
-		msg, pc, status);
-	postnote(up, 1, note, NDebug);
-}
-
-/*
- *  math coprocessor error
- */
-static void
-matherror(Ureg*, void*)
-{
-	/*
-	 *  a write cycle to port 0xF0 clears the interrupt latch attached
-	 *  to the error# line from the 387
-	 */
-	if(!(m->cpuiddx & Fpuonchip))
-		outb(0xF0, 0xFF);
-
-	/*
-	 *  get floating point state to check out error
-	 */
-	fpsave(up->fpsave);
-	up->fpstate = FPinactive;
-	mathnote(up->fpsave->fsw, up->fpsave->fpuip);
-}
-
-/*
- *  SIMD error
- */
-static void
-simderror(Ureg *ureg, void*)
-{
-	fpsave(up->fpsave);
-	up->fpstate = FPinactive;
-	mathnote(up->fpsave->mxcsr & 0x3f, ureg->pc);
-}
-
-/*
- *  math coprocessor emulation fault
- */
-static void
-mathemu(Ureg *ureg, void*)
-{
-	ulong status, control;
-
-	if(up->fpstate & FPillegal){
-		/* someone did floating point in a note handler */
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
-	}
-	switch(up->fpstate){
-	case FPinit:
-		fpinit();
-		if(fpsave == fpssesave)
-			ldmxcsr(0x1f80);	/* no simd exceptions on 386 */
-		while(up->fpsave == nil)
-			up->fpsave = mallocalign(sizeof(FPsave), FPalign, 0, 0);
-		up->fpstate = FPactive;
-		break;
-	case FPinactive:
-		/*
-		 * Before restoring the state, check for any pending
-		 * exceptions, there's no way to restore the state without
-		 * generating an unmasked exception.
-		 * More attention should probably be paid here to the
-		 * exception masks and error summary.
-		 */
-		status = up->fpsave->fsw;
-		control = up->fpsave->fcw;
-		if((status & ~control) & 0x07F){
-			mathnote(status, up->fpsave->fpuip);
-			break;
-		}
-		fprestore(up->fpsave);
-		up->fpstate = FPactive;
-		break;
-	case FPactive:
-		panic("math emu pid %ld %s pc 0x%lux", 
-			up->pid, up->text, ureg->pc);
-		break;
-	}
-}
-
-/*
- *  math coprocessor segment overrun
- */
-static void
-mathover(Ureg*, void*)
-{
-	pexit("math overrun", 0);
-}
-
-void
-mathinit(void)
-{
-	trapenable(VectorCERR, matherror, 0, "matherror");
-	if(m->cpuidfamily == 3)
-		intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
-	trapenable(VectorCNA, mathemu, 0, "mathemu");
-	trapenable(VectorCSO, mathover, 0, "mathover");
-	trapenable(VectorSIMD, simderror, 0, "simderror");
-}
-
-/*
  *  set up floating point for a new process
  */
 void
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -49,6 +49,7 @@
 OBJ=\
 	l.$O\
 	cga.$O\
+	fpu.$O\
 	i8253.$O\
 	i8259.$O\
 	main.$O\
--- a/sys/src/9/pc/mtrr.c
+++ b/sys/src/9/pc/mtrr.c
@@ -289,9 +289,9 @@
 	ulong regs[4];
 	uvlong mask;
 
-	cpuid(Exthighfunc, regs);
+	cpuid(Exthighfunc, 0, regs);
 	if(regs[0] >= Extaddrsz) {			/* ax */
-		cpuid(Extaddrsz, regs);
+		cpuid(Extaddrsz, 0, regs);
 		mask = (1ULL << (regs[0] & 0xFF)) - 1;	/* ax */
 	} else {
 		mask = (1ULL << 36) - 1;
@@ -305,12 +305,14 @@
 	vlong v;
 	int i;
 
-	s->mask = physmask();
+	if(rdmsr(MTRRCap, &s->cap) < 0)
+		return -1;
 
+	if((s->cap & (Capfix|Capvcnt)) == 0)
+		return -1;
+
 	if(rdmsr(MTRRDefaultType, &s->def) < 0)
 		return -1;
-	if(rdmsr(MTRRCap, &s->cap) < 0)
-		return -1;
 
 	if(s->cap & Capfix){
 		for(i = 0; i < nelem(fixreg); i++){
@@ -332,6 +334,8 @@
 			return -1;
 	}
 
+	s->mask = physmask();
+
 	if(strcmp(m->cpuidid, "AuthenticAMD") != 0
 	|| m->cpuidfamily < 15
 	|| rdmsr(AMDK8SysCfg, &v) < 0
@@ -354,7 +358,7 @@
 static void
 putstate(State *s)
 {
-	ulong cr0, cr4;
+	uintptr cr0, cr4;
 	int i, x;
 
 	x = splhi();
@@ -673,6 +677,10 @@
 	new.size = size;
 	if((new.type = str2type(tstr)) < 0)
 		return "bad cache type";
+
+	if(new.type == Writecomb
+	&& (cpu0state.cap & Capwc) == 0)
+		return "write combining not supported";
 
 	qlock(&mtrrlk);
 	newstate = cpu0state;
--- a/sys/src/9/pc/sdiahci.c
+++ b/sys/src/9/pc/sdiahci.c
@@ -21,7 +21,6 @@
 #define	idprint(...)	if(prid)	print(__VA_ARGS__); else USED(prid)
 #define	aprint(...)	if(datapi)	print(__VA_ARGS__); else USED(datapi)
 #define	ledprint(...)	if(dled)	print(__VA_ARGS__); else USED(dled)
-#define	Pciwaddrh(a)	0
 #define Tname(c)	tname[(c)->type]
 #define	Ticks		MACHP(0)->ticks
 #define	MS2TK(t)	(((ulong)(t)*HZ)/1000)
@@ -284,20 +283,23 @@
 	Actab *t;
 	Alist *l;
 	Aprdt *p;
+	uvlong pa;
 
 	t = m->ctab;
 	if(data && len > 0){
+		pa = PCIWADDR(data);
 		p = &t->prdt;
-		p->dba = PCIWADDR(data);
-		p->dbahi = Pciwaddrh(data);
+		p->dba = pa;
+		p->dbahi = pa>>32;
 		p->count = 1<<31 | len - 2 | 1;
 		flags |= 1<<16;
 	}
+	pa = PCIWADDR(t);
 	l = m->list;
 	l->flags = flags | 0x5;
 	l->len = 0;
-	l->ctab = PCIWADDR(t);
-	l->ctabhi = Pciwaddrh(t);
+	l->ctab = pa;
+	l->ctabhi = pa>>32;
 	return l;
 }
 
@@ -600,6 +602,7 @@
 static int
 ahciconfigdrive(Ahba *h, Aportc *c, int mode)
 {
+	uvlong pa;
 	Aportm *m;
 	Aport *p;
 	int i;
@@ -618,10 +621,12 @@
 		return -1;
 	}
 
-	p->list = PCIWADDR(m->list);
-	p->listhi = Pciwaddrh(m->list);
-	p->fis = PCIWADDR(m->fis.base);
-	p->fishi = Pciwaddrh(m->fis.base);
+	pa = PCIWADDR(m->list);
+	p->list = pa;
+	p->listhi = pa>>32;
+	pa = PCIWADDR(m->fis.base);
+	p->fis = pa;
+	p->fishi = pa>>32;
 
 	p->cmd |= Afre;
 
@@ -1553,7 +1558,6 @@
 	}
 	if(c->ndrive == 0)
 		panic("iaenable: zero s->ctlr->ndrive");
-	pcisetbme(c->pci);
 	snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
 	intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
 	/* supposed to squelch leftover interrupts here. */
@@ -2228,6 +2232,7 @@
 			c->drive[d->driveno] = d;
 			iadrive[niadrive + d->driveno] = d;
 		}
+		pcisetbme(c->pci);
 		for(i = 0; i < n; i++){
 			c->drive[i]->mode = DMautoneg;
 			configdrive(c->drive[i]);
--- a/sys/src/9/pc64/dat.h
+++ b/sys/src/9/pc64/dat.h
@@ -2,6 +2,8 @@
 typedef struct BIOS32ci	BIOS32ci;
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPssestate	FPssestate;
+typedef struct FPavxstate	FPavxstate;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -49,7 +51,7 @@
 	uintptr	pc;
 };
 
-struct FPsave
+struct FPssestate
 {
 	u16int	fcw;			/* x87 control word */
 	u16int	fsw;			/* x87 status word */
@@ -65,6 +67,18 @@
 	uchar	ign[96];		/* reserved, ignored */
 };
 
+struct FPavxstate
+{
+	FPssestate;
+	uchar	header[64];		/* XSAVE header */
+	uchar	ymm[256];		/* upper 128-bit regs (AVX) */
+};
+
+struct FPsave
+{
+	FPavxstate;
+};
+
 enum
 {
 	/* this is a state */
@@ -224,9 +238,10 @@
 	int	havewatchpt8;
 	int	havenx;
 	uvlong	tscticks;
-	
+
 	u64int	dr7;			/* shadow copy of dr7 */
-	
+	u64int	xcr0;
+
 	void*	vmx;
 
 	uintptr	stack[1];
@@ -270,8 +285,14 @@
 
 /* cpuid instruction result register bits */
 enum {
+	/* ax */
+	Xsaveopt = 1<<0,
+	Xsaves = 1<<3,
+
 	/* cx */
 	Monitor	= 1<<3,
+	Xsave = 1<<26,
+	Avx	= 1<<28,
 
 	/* dx */
 	Fpuonchip = 1<<0,
--- a/sys/src/9/pc64/fns.h
+++ b/sys/src/9/pc64/fns.h
@@ -15,7 +15,8 @@
 int	(*cmpswap)(long*, long, long);
 int	cmpswap486(long*, long, long);
 void	(*coherence)(void);
-void	cpuid(int, ulong regs[]);
+void	cpuid(int, int, ulong regs[]);
+void	fpuinit(void);
 int	cpuidentify(void);
 void	cpuidprint(void);
 void	(*cycles)(uvlong*);
@@ -38,10 +39,10 @@
 void	fpinit(void);
 void	(*fprestore)(FPsave*);
 void	(*fpsave)(FPsave*);
-void	fpsserestore(FPsave*);
-void	fpssesave(FPsave*);
-void	fpx87restore(FPsave*);
-void	fpx87save(FPsave*);
+void	fpuprocsetup(Proc*);
+void	fpuprocfork(Proc*);
+void	fpuprocsave(Proc*);
+void	fpuprocrestore(Proc*);
 int	fpusave(void);
 void	fpurestore(int);
 u64int	getcr0(void);
@@ -48,6 +49,7 @@
 u64int	getcr2(void);
 u64int	getcr3(void);
 u64int	getcr4(void);
+u64int	getxcr0(void);
 u64int	getdr6(void);
 char*	getconf(char*);
 void	guesscpuhz(int);
@@ -138,6 +140,7 @@
 void	putcr2(u64int);
 void	putcr3(u64int);
 void	putcr4(u64int);
+void	putxcr0(u64int);
 void	putdr(u64int*);
 void	putdr01236(u64int*);
 void	putdr6(u64int);
--- /dev/null
+++ b/sys/src/9/pc64/fpu.c
@@ -1,0 +1,374 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "io.h"
+
+enum {
+	CR4Osfxsr  = 1 << 9,
+	CR4Oxmmex  = 1 << 10,
+	CR4Oxsave  = 1 << 18,
+};
+
+/*
+ * SIMD Floating Point.
+ * Assembler support to get at the individual instructions
+ * is in l.s.
+ */
+extern void _clts(void);
+extern void _fldcw(u16int);
+extern void _fnclex(void);
+extern void _fninit(void);
+extern void _fxrstor(void*);
+extern void _fxsave(void*);
+extern void _xrstor(void*);
+extern void _xsave(void*);
+extern void _xsaveopt(void*);
+extern void _fwait(void);
+extern void _ldmxcsr(u32int);
+extern void _stts(void);
+
+static void
+fpssesave(FPsave *s)
+{
+	_fxsave(s);
+	_stts();
+}
+static void
+fpsserestore(FPsave *s)
+{
+	_clts();
+	_fxrstor(s);
+}
+
+static void
+fpxsave(FPsave *s)
+{
+	_xsave(s);
+	_stts();
+}
+static void
+fpxrestore(FPsave *s)
+{
+	_clts();
+	_xrstor(s);
+}
+
+static void
+fpxsaves(FPsave *s)
+{
+	_xsaveopt(s);
+	_stts();
+}
+static void
+fpxrestores(FPsave *s)
+{
+	_clts();
+	_xrstor(s);
+}
+
+static void
+fpxsaveopt(FPsave *s)
+{
+	_xsaveopt(s);
+	_stts();
+}
+
+static char* mathmsg[] =
+{
+	nil,	/* handled below */
+	"denormalized operand",
+	"division by zero",
+	"numeric overflow",
+	"numeric underflow",
+	"precision loss",
+};
+
+static void
+mathnote(ulong status, uintptr pc)
+{
+	char *msg, note[ERRMAX];
+	int i;
+
+	/*
+	 * Some attention should probably be paid here to the
+	 * exception masks and error summary.
+	 */
+	msg = "unknown exception";
+	for(i = 1; i <= 5; i++){
+		if(!((1<<i) & status))
+			continue;
+		msg = mathmsg[i];
+		break;
+	}
+	if(status & 0x01){
+		if(status & 0x40){
+			if(status & 0x200)
+				msg = "stack overflow";
+			else
+				msg = "stack underflow";
+		}else
+			msg = "invalid operation";
+	}
+	snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=0x%lux",
+		msg, pc, status);
+	postnote(up, 1, note, NDebug);
+}
+
+/*
+ *  math coprocessor error
+ */
+static void
+matherror(Ureg *, void*)
+{
+	/*
+	 * Save FPU state to check out the error.
+	 */
+	fpsave(up->fpsave);
+	up->fpstate = FPinactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
+	mathnote(up->fpsave->fsw, up->fpsave->rip);
+}
+
+/*
+ *  SIMD error
+ */
+static void
+simderror(Ureg *ureg, void*)
+{
+	fpsave(up->fpsave);
+	up->fpstate = FPinactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
+	mathnote(up->fpsave->mxcsr & 0x3f, ureg->pc);
+}
+
+void
+fpinit(void)
+{
+	/*
+	 * A process tries to use the FPU for the
+	 * first time and generates a 'device not available'
+	 * exception.
+	 * Turn the FPU on and initialise it for use.
+	 * Set the precision and mask the exceptions
+	 * we don't care about from the generic Mach value.
+	 */
+	_clts();
+	_fninit();
+	_fwait();
+	_fldcw(0x0232);
+	_ldmxcsr(0x1900);
+}
+
+/*
+ *  math coprocessor emulation fault
+ */
+static void
+mathemu(Ureg *ureg, void*)
+{
+	ulong status, control;
+	int index;
+
+	if(up->fpstate & FPillegal){
+		/* someone did floating point in a note handler */
+		postnote(up, 1, "sys: floating point in note handler", NDebug);
+		return;
+	}
+	switch(up->fpstate & ~(FPnouser|FPkernel|FPindexm)){
+	case FPactive	| FPpush:
+		_clts();
+		fpsave(up->fpsave);
+	case FPinactive	| FPpush:
+		up->fpstate += FPindex1;
+	case FPinit	| FPpush:
+	case FPinit:
+		fpinit();
+		index = up->fpstate >> FPindexs;
+		if(index < 0 || index > (FPindexm>>FPindexs))
+			panic("fpslot index overflow: %d", index);
+		if(userureg(ureg)){
+			if(index != 0)
+				panic("fpslot index %d != 0 for user", index);
+		} else {
+			if(index == 0)
+				up->fpstate |= FPnouser;
+			up->fpstate |= FPkernel;
+		}
+		while(up->fpslot[index] == nil)
+			up->fpslot[index] = mallocalign(sizeof(FPsave), FPalign, 0, 0);
+		up->fpsave = up->fpslot[index];
+		up->fpstate = FPactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
+		break;
+	case FPinactive:
+		/*
+		 * Before restoring the state, check for any pending
+		 * exceptions, there's no way to restore the state without
+		 * generating an unmasked exception.
+		 * More attention should probably be paid here to the
+		 * exception masks and error summary.
+		 */
+		status = up->fpsave->fsw;
+		control = up->fpsave->fcw;
+		if((status & ~control) & 0x07F){
+			mathnote(status, up->fpsave->rip);
+			break;
+		}
+		fprestore(up->fpsave);
+		up->fpstate = FPactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
+		break;
+	case FPactive:
+		panic("math emu pid %ld %s pc %#p", 
+			up->pid, up->text, ureg->pc);
+		break;
+	}
+}
+
+/*
+ *  math coprocessor segment overrun
+ */
+static void
+mathover(Ureg*, void*)
+{
+	pexit("math overrun", 0);
+}
+
+void
+mathinit(void)
+{
+	trapenable(VectorCERR, matherror, 0, "matherror");
+	if(m->cpuidfamily == 3)
+		intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
+	trapenable(VectorCNA, mathemu, 0, "mathemu");
+	trapenable(VectorCSO, mathover, 0, "mathover");
+	trapenable(VectorSIMD, simderror, 0, "simderror");
+}
+
+/*
+ * fpuinit(), called from cpuidentify() for each cpu.
+ */
+void
+fpuinit(void)
+{
+	u64int cr4;
+	ulong regs[4];
+
+	cr4 = getcr4() | CR4Osfxsr|CR4Oxmmex;
+	if((m->cpuidcx & (Xsave|Avx)) == (Xsave|Avx) && getconf("*noavx") == nil){
+		cr4 |= CR4Oxsave;
+		putcr4(cr4);
+		m->xcr0 = 7; /* x87, sse, avx */
+		putxcr0(m->xcr0);
+		cpuid(0xd, 1, regs);
+		if(regs[0] & Xsaves){
+			fpsave = fpxsaves;
+			fprestore = fpxrestores;
+		} else {
+			if(regs[0] & Xsaveopt)
+				fpsave = fpxsaveopt;
+			else
+				fpsave = fpxsave;
+			fprestore = fpxrestore;
+		}
+	} else {
+		putcr4(cr4);
+		fpsave = fpssesave;
+		fprestore = fpsserestore;
+	}
+}
+
+void
+fpuprocsetup(Proc *p)
+{
+	p->fpstate = FPinit;
+	_stts();
+}
+
+void
+fpuprocfork(Proc *p)
+{
+	int s;
+
+	/* save floating point state */
+	s = splhi();
+	switch(up->fpstate & ~FPillegal){
+	case FPactive	| FPpush:
+		_clts();
+	case FPactive:
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive | (up->fpstate & FPpush);
+	case FPactive	| FPkernel:
+	case FPinactive	| FPkernel:
+	case FPinactive	| FPpush:
+	case FPinactive:
+		while(p->fpslot[0] == nil)
+			p->fpslot[0] = mallocalign(sizeof(FPsave), FPalign, 0, 0);
+		memmove(p->fpsave = p->fpslot[0], up->fpslot[0], sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	splx(s);
+}
+
+void
+fpuprocsave(Proc *p)
+{
+	switch(p->fpstate & ~(FPnouser|FPkernel|FPindexm)){
+	case FPactive	| FPpush:
+		_clts();
+	case FPactive:
+		if(p->state == Moribund){
+			_fnclex();
+			_stts();
+			break;
+		}
+		/*
+		 * Fpsave() stores without handling pending
+		 * unmasked exeptions. Postnote() can't be called
+		 * here as sleep() already has up->rlock, so
+		 * the handling of pending exceptions is delayed
+		 * until the process runs again and generates an
+		 * emulation fault to activate the FPU.
+		 */
+		fpsave(p->fpsave);
+		p->fpstate = FPinactive | (p->fpstate & ~FPactive);
+		break;
+	}
+}
+
+void
+fpuprocrestore(Proc*)
+{
+}
+
+
+/*
+ * Fpusave and fpurestore lazily save and restore FPU state across
+ * system calls and the pagefault handler so that we can take
+ * advantage of SSE instructions such as AES-NI in the kernel.
+ */
+int
+fpusave(void)
+{
+	int ostate = up->fpstate;
+	if((ostate & ~(FPnouser|FPkernel|FPindexm)) == FPactive)
+		_stts();
+	up->fpstate = FPpush | (ostate & ~FPillegal);
+	return ostate;
+}
+void
+fpurestore(int ostate)
+{
+	int astate = up->fpstate;
+	if(astate == (FPpush | (ostate & ~FPillegal))){
+		if((ostate & ~(FPnouser|FPkernel|FPindexm)) == FPactive)
+			_clts();
+	} else {
+		if(astate == FPinit)	/* don't restore on procexec()/procsetup() */
+			return;
+		if((astate & ~(FPnouser|FPkernel|FPindexm)) == FPactive)
+			_stts();
+		up->fpsave = up->fpslot[ostate>>FPindexs];
+		if(ostate & FPactive)
+			ostate = FPinactive | (ostate & ~FPactive);
+	}
+	up->fpstate = ostate;
+}
--- a/sys/src/9/pc64/l.s
+++ b/sys/src/9/pc64/l.s
@@ -249,9 +249,10 @@
  */
 TEXT cpuid(SB), $-4
 	MOVL	RARG, AX			/* function in AX */
+	MOVL	cx+8(FP), CX		/* sub-level in CX */
 	CPUID
 
-	MOVQ	info+8(FP), BP
+	MOVQ	info+16(FP), BP
 	MOVL	AX, 0(BP)
 	MOVL	BX, 4(BP)
 	MOVL	CX, 8(BP)
@@ -399,6 +400,21 @@
 	MOVQ	RARG, CR4
 	RET
 
+TEXT getxcr0(SB), 1, $-4			/* XCR0 - extended control */
+	XORQ CX, CX
+	WORD $0x010f; BYTE $0xd0	// XGETBV
+	SHLQ $32, DX
+	ORQ DX, AX
+	RET
+
+TEXT putxcr0(SB), 1, $-4
+	XORQ CX, CX
+	MOVL RARG, DX
+	SHRQ $32, DX
+	MOVL RARG, AX
+	WORD $0x010f; BYTE $0xd1	// XSETBV
+	RET
+
 TEXT mb386(SB), 1, $-4				/* hack */
 TEXT mb586(SB), 1, $-4
 	XORL	AX, AX
@@ -624,6 +640,36 @@
 
 TEXT _fxsave(SB), 1, $-4
 	FXSAVE64 (RARG)
+	RET
+
+TEXT _xrstor(SB), 1, $-4
+	MOVL $7, AX
+	XORL DX, DX
+	BYTE $0x48; BYTE $0x0f; BYTE $0xae; BYTE $0x6d; BYTE $0x00 // XRSTOR (RARG)
+	RET
+
+TEXT _xrstors(SB), 1, $-4
+	MOVL $7, AX
+	XORL DX, DX
+	BYTE $0x48; BYTE $0x0f; BYTE $0xc7; BYTE $0x5d; BYTE $0x00 // XRSTORS (RARG)
+	RET
+
+TEXT _xsave(SB), 1, $-4
+	MOVL $7, AX
+	XORL DX, DX
+	BYTE $0x48; BYTE $0x0f; BYTE $0xae; BYTE $0x65; BYTE $0x00 // XSAVE (RARG)
+	RET
+
+TEXT _xsaveopt(SB), 1, $-4
+	MOVL $7, AX
+	XORL DX, DX
+	BYTE $0x48; BYTE $0x0f; BYTE $0xae; BYTE $0x75; BYTE $0x00 // XSAVEOPT (RARG)
+	RET
+
+TEXT _xsaves(SB), 1, $-4
+	MOVL $7, AX
+	XORL DX, DX
+	BYTE $0x48; BYTE $0x0f; BYTE $0xc7; BYTE $0x6d; BYTE $0x00 // XSAVES (RARG)
 	RET
 
 TEXT _fwait(SB), 1, $-4
--- a/sys/src/9/pc64/main.c
+++ b/sys/src/9/pc64/main.c
@@ -293,218 +293,10 @@
 	rebootjump((uintptr)entry & (ulong)~0xF0000000UL, PADDR(code), size);
 }
 
-/*
- * SIMD Floating Point.
- * Assembler support to get at the individual instructions
- * is in l.s.
- */
-extern void _clts(void);
-extern void _fldcw(u16int);
-extern void _fnclex(void);
-extern void _fninit(void);
-extern void _fxrstor(void*);
-extern void _fxsave(void*);
-extern void _fwait(void);
-extern void _ldmxcsr(u32int);
-extern void _stts(void);
-
-/*
- * not used, AMD64 mandated SSE
- */
 void
-fpx87save(FPsave*)
-{
-}
-void
-fpx87restore(FPsave*)
-{
-}
-
-void
-fpssesave(FPsave *s)
-{
-	_fxsave(s);
-	_stts();
-}
-void
-fpsserestore(FPsave *s)
-{
-	_clts();
-	_fxrstor(s);
-}
-
-static char* mathmsg[] =
-{
-	nil,	/* handled below */
-	"denormalized operand",
-	"division by zero",
-	"numeric overflow",
-	"numeric underflow",
-	"precision loss",
-};
-
-static void
-mathnote(ulong status, uintptr pc)
-{
-	char *msg, note[ERRMAX];
-	int i;
-
-	/*
-	 * Some attention should probably be paid here to the
-	 * exception masks and error summary.
-	 */
-	msg = "unknown exception";
-	for(i = 1; i <= 5; i++){
-		if(!((1<<i) & status))
-			continue;
-		msg = mathmsg[i];
-		break;
-	}
-	if(status & 0x01){
-		if(status & 0x40){
-			if(status & 0x200)
-				msg = "stack overflow";
-			else
-				msg = "stack underflow";
-		}else
-			msg = "invalid operation";
-	}
-	snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=0x%lux",
-		msg, pc, status);
-	postnote(up, 1, note, NDebug);
-}
-
-/*
- *  math coprocessor error
- */
-static void
-matherror(Ureg *, void*)
-{
-	/*
-	 * Save FPU state to check out the error.
-	 */
-	fpsave(up->fpsave);
-	up->fpstate = FPinactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
-	mathnote(up->fpsave->fsw, up->fpsave->rip);
-}
-
-/*
- *  SIMD error
- */
-static void
-simderror(Ureg *ureg, void*)
-{
-	fpsave(up->fpsave);
-	up->fpstate = FPinactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
-	mathnote(up->fpsave->mxcsr & 0x3f, ureg->pc);
-}
-
-void
-fpinit(void)
-{
-	/*
-	 * A process tries to use the FPU for the
-	 * first time and generates a 'device not available'
-	 * exception.
-	 * Turn the FPU on and initialise it for use.
-	 * Set the precision and mask the exceptions
-	 * we don't care about from the generic Mach value.
-	 */
-	_clts();
-	_fninit();
-	_fwait();
-	_fldcw(0x0232);
-	_ldmxcsr(0x1900);
-}
-
-/*
- *  math coprocessor emulation fault
- */
-static void
-mathemu(Ureg *ureg, void*)
-{
-	ulong status, control;
-	int index;
-
-	if(up->fpstate & FPillegal){
-		/* someone did floating point in a note handler */
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
-	}
-	switch(up->fpstate & ~(FPnouser|FPkernel|FPindexm)){
-	case FPactive	| FPpush:
-		_clts();
-		fpsave(up->fpsave);
-	case FPinactive	| FPpush:
-		up->fpstate += FPindex1;
-	case FPinit	| FPpush:
-	case FPinit:
-		fpinit();
-		index = up->fpstate >> FPindexs;
-		if(index < 0 || index > (FPindexm>>FPindexs))
-			panic("fpslot index overflow: %d", index);
-		if(userureg(ureg)){
-			if(index != 0)
-				panic("fpslot index %d != 0 for user", index);
-		} else {
-			if(index == 0)
-				up->fpstate |= FPnouser;
-			up->fpstate |= FPkernel;
-		}
-		while(up->fpslot[index] == nil)
-			up->fpslot[index] = mallocalign(sizeof(FPsave), FPalign, 0, 0);
-		up->fpsave = up->fpslot[index];
-		up->fpstate = FPactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
-		break;
-	case FPinactive:
-		/*
-		 * Before restoring the state, check for any pending
-		 * exceptions, there's no way to restore the state without
-		 * generating an unmasked exception.
-		 * More attention should probably be paid here to the
-		 * exception masks and error summary.
-		 */
-		status = up->fpsave->fsw;
-		control = up->fpsave->fcw;
-		if((status & ~control) & 0x07F){
-			mathnote(status, up->fpsave->rip);
-			break;
-		}
-		fprestore(up->fpsave);
-		up->fpstate = FPactive | (up->fpstate & (FPnouser|FPkernel|FPindexm));
-		break;
-	case FPactive:
-		panic("math emu pid %ld %s pc %#p", 
-			up->pid, up->text, ureg->pc);
-		break;
-	}
-}
-
-/*
- *  math coprocessor segment overrun
- */
-static void
-mathover(Ureg*, void*)
-{
-	pexit("math overrun", 0);
-}
-
-void
-mathinit(void)
-{
-	trapenable(VectorCERR, matherror, 0, "matherror");
-	if(m->cpuidfamily == 3)
-		intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
-	trapenable(VectorCNA, mathemu, 0, "mathemu");
-	trapenable(VectorCSO, mathover, 0, "mathover");
-	trapenable(VectorSIMD, simderror, 0, "simderror");
-}
-
-void
 procsetup(Proc *p)
 {
-	p->fpstate = FPinit;
-	_stts();
+	fpuprocsetup(p);
 
 	/* clear debug registers */
 	memset(p->dr, 0, sizeof(p->dr));
@@ -520,29 +312,10 @@
 void
 procfork(Proc *p)
 {
-	int s;
-
 	p->kentry = up->kentry;
 	p->pcycles = -p->kentry;
 
-	/* save floating point state */
-	s = splhi();
-	switch(up->fpstate & ~FPillegal){
-	case FPactive	| FPpush:
-		_clts();
-	case FPactive:
-		fpsave(up->fpsave);
-		up->fpstate = FPinactive | (up->fpstate & FPpush);
-	case FPactive	| FPkernel:
-	case FPinactive	| FPkernel:
-	case FPinactive	| FPpush:
-	case FPinactive:
-		while(p->fpslot[0] == nil)
-			p->fpslot[0] = mallocalign(sizeof(FPsave), FPalign, 0, 0);
-		memmove(p->fpsave = p->fpslot[0], up->fpslot[0], sizeof(FPsave));
-		p->fpstate = FPinactive;
-	}
-	splx(s);
+	fpuprocfork(p);
 }
 
 void
@@ -558,6 +331,8 @@
 	if(p->vmx != nil)
 		vmxprocrestore(p);
 
+	fpuprocrestore(p);
+
 	if(p->kp)
 		return;
 
@@ -582,27 +357,7 @@
 	if(p->state == Moribund)
 		p->dr[7] = 0;
 
-	switch(p->fpstate & ~(FPnouser|FPkernel|FPindexm)){
-	case FPactive	| FPpush:
-		_clts();
-	case FPactive:
-		if(p->state == Moribund){
-			_fnclex();
-			_stts();
-			break;
-		}
-		/*
-		 * Fpsave() stores without handling pending
-		 * unmasked exeptions. Postnote() can't be called
-		 * here as sleep() already has up->rlock, so
-		 * the handling of pending exceptions is delayed
-		 * until the process runs again and generates an
-		 * emulation fault to activate the FPU.
-		 */
-		fpsave(p->fpsave);
-		p->fpstate = FPinactive | (p->fpstate & ~FPactive);
-		break;
-	}
+	fpuprocsave(p);
 
 	/*
 	 * While this processor is in the scheduler, the process could run
@@ -616,37 +371,4 @@
 	 * especially on VMware, but it turns out not to matter.
 	 */
 	mmuflushtlb();
-}
-
-/*
- * Fpusave and fpurestore lazily save and restore FPU state across
- * system calls and the pagefault handler so that we can take
- * advantage of SSE instructions such as AES-NI in the kernel.
- */
-int
-fpusave(void)
-{
-	int ostate = up->fpstate;
-	if((ostate & ~(FPnouser|FPkernel|FPindexm)) == FPactive)
-		_stts();
-	up->fpstate = FPpush | (ostate & ~FPillegal);
-	return ostate;
-}
-void
-fpurestore(int ostate)
-{
-	int astate = up->fpstate;
-	if(astate == (FPpush | (ostate & ~FPillegal))){
-		if((ostate & ~(FPnouser|FPkernel|FPindexm)) == FPactive)
-			_clts();
-	} else {
-		if(astate == FPinit)	/* don't restore on procexec()/procsetup() */
-			return;
-		if((astate & ~(FPnouser|FPkernel|FPindexm)) == FPactive)
-			_stts();
-		up->fpsave = up->fpslot[ostate>>FPindexs];
-		if(ostate & FPactive)
-			ostate = FPinactive | (ostate & ~FPactive);
-	}
-	up->fpstate = ostate;
 }
--- a/sys/src/9/pc64/mem.h
+++ b/sys/src/9/pc64/mem.h
@@ -26,7 +26,7 @@
 #define	ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
 #define	PGROUND(s)	ROUND(s, BY2PG)
 #define	BLOCKALIGN	8
-#define	FPalign		16
+#define	FPalign		64
 
 #define	MAXMACH		128			/* max # cpus system can run */
 
--- a/sys/src/9/pc64/mkfile
+++ b/sys/src/9/pc64/mkfile
@@ -47,6 +47,7 @@
 OBJ=\
 	l.$O\
 	cga.$O\
+	fpu.$O\
 	i8253.$O\
 	i8259.$O\
 	main.$O\
--- a/sys/src/9/port/auth.c
+++ b/sys/src/9/port/auth.c
@@ -97,13 +97,12 @@
 		nexterror();
 	}
 
-	fd = newfd(ac);
+	/* always mark it close on exec */
+	fd = newfd(ac, OCEXEC);
 	if(fd < 0)
 		error(Enofd);
 	poperror();	/* ac */
 
-	/* always mark it close on exec */
-	ac->flag |= CCEXEC;
 	return (uintptr)fd;
 }
 
--- a/sys/src/9/port/chan.c
+++ b/sys/src/9/port/chan.c
@@ -1468,9 +1468,6 @@
 			saveregisters();
 
 			c = devtab[c->type]->open(c, omode&~OCEXEC);
-
-			if(omode & OCEXEC)
-				c->flag |= CCEXEC;
 			if(omode & ORCLOSE)
 				c->flag |= CRCLOSE;
 			break;
@@ -1571,11 +1568,9 @@
 			incref(cnew->path);
 
 			cnew = devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
-			poperror();
-			if(omode & OCEXEC)
-				cnew->flag |= CCEXEC;
 			if(omode & ORCLOSE)
 				cnew->flag |= CRCLOSE;
+			poperror();
 			putmhead(m);
 			cclose(c);
 			c = cnew;
--- a/sys/src/9/port/devdup.c
+++ b/sys/src/9/port/devdup.c
@@ -63,6 +63,8 @@
 	Chan *f;
 	int fd, twicefd;
 
+	if(omode & ORCLOSE)
+		error(Eperm);
 	if(c->qid.type & QTDIR){
 		if(omode != 0)
 			error(Eisdir);
--- a/sys/src/9/port/devmouse.c
+++ b/sys/src/9/port/devmouse.c
@@ -304,8 +304,6 @@
 
 	one = two = three = 0;
 	for(i = 0; i < 3; i++){
-		if(map[i] == 0)
-			error(Ebadarg);
 		if(map[i] == '1'){
 			if(one)
 				error(Ebadarg);
--- a/sys/src/9/port/devshr.c
+++ b/sys/src/9/port/devshr.c
@@ -396,6 +396,8 @@
 	case Qcmpt:
 		if(omode&OTRUNC)
 			error(Eexist);
+		if(omode&ORCLOSE)
+			error(Eperm);
 		shr = sch->shr;
 		mpt = sch->mpt;
 		devpermcheck(mpt->owner, mpt->perm, mode);
--- a/sys/src/9/port/devsrv.c
+++ b/sys/src/9/port/devsrv.c
@@ -135,6 +135,8 @@
 
 	if(omode&OTRUNC)
 		error(Eexist);
+	if(omode&ORCLOSE)
+		error(Eperm);
 	if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR)
 		error(Eperm);
 	devpermcheck(sp->owner, sp->perm, omode);
@@ -338,8 +340,6 @@
 		cclose(c1);
 		nexterror();
 	}
-	if(c1->flag & (CCEXEC|CRCLOSE))
-		error("posted fd has remove-on-close or close-on-exec");
 	if(c1->qid.type & QTAUTH)
 		error("cannot post auth file in srv");
 	sp = srvlookup(nil, c->qid.path);
--- a/sys/src/9/port/lib.h
+++ b/sys/src/9/port/lib.h
@@ -176,7 +176,7 @@
 #define	ORDWR	2	/* read and write */
 #define	OEXEC	3	/* execute, == read but check execute permission */
 #define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
-#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	OCEXEC	32	/* or'ed in (per file descriptor), close on exec */
 #define	ORCLOSE	64	/* or'ed in, remove on close */
 #define OEXCL   0x1000	/* or'ed in, exclusive create */
 
--- a/sys/src/9/port/pgrp.c
+++ b/sys/src/9/port/pgrp.c
@@ -140,7 +140,8 @@
 
 	new = smalloc(sizeof(Fgrp));
 	if(f == nil){
-		new->fd = smalloc(DELTAFD*sizeof(Chan*));
+		new->flag = smalloc(DELTAFD*sizeof(new->flag[0]));
+		new->fd = smalloc(DELTAFD*sizeof(new->fd[0]));
 		new->nfd = DELTAFD;
 		new->ref = 1;
 		return new;
@@ -152,12 +153,19 @@
 	i = new->nfd%DELTAFD;
 	if(i != 0)
 		new->nfd += DELTAFD - i;
-	new->fd = malloc(new->nfd*sizeof(Chan*));
+	new->fd = malloc(new->nfd*sizeof(new->fd[0]));
 	if(new->fd == nil){
 		unlock(f);
 		free(new);
 		error("no memory for fgrp");
 	}
+	new->flag = malloc(new->nfd*sizeof(new->flag[0]));
+	if(new->flag == nil){
+		unlock(f);
+		free(new->fd);
+		free(new);
+		error("no memory for fgrp");
+	}
 	new->ref = 1;
 
 	new->maxfd = f->maxfd;
@@ -165,6 +173,7 @@
 		if((c = f->fd[i]) != nil){
 			incref(c);
 			new->fd[i] = c;
+			new->flag[i] = f->flag[i];
 		}
 	}
 	unlock(f);
@@ -194,6 +203,7 @@
 	up->closingfgrp = nil;
 
 	free(f->fd);
+	free(f->flag);
 	free(f);
 }
 
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -125,7 +125,7 @@
 	COPEN	= 0x0001,		/* for i/o */
 	CMSG	= 0x0002,		/* the message channel for a mount */
 /*rsc	CCREATE	= 0x0004,		/* permits creation if c->mnt */
-	CCEXEC	= 0x0008,		/* close on exec */
+	CCEXEC	= 0x0008,		/* close on exec (per file descriptor) */
 	CFREE	= 0x0010,		/* not in use */
 	CRCLOSE	= 0x0020,		/* remove on close */
 	CCACHE	= 0x0080,		/* client cache */
@@ -509,6 +509,7 @@
 	Ref;
 	Lock;
 	Chan	**fd;
+	uchar	*flag;			/* per file-descriptor flags (CCEXEC) */
 	int	nfd;			/* number allocated */
 	int	maxfd;			/* highest fd in use */
 	int	exceed;			/* debugging */
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -201,7 +201,7 @@
 void		nameerror(char*, char*);
 int		needpages(void*);
 Chan*		newchan(void);
-int		newfd(Chan*);
+int		newfd(Chan*, int);
 Mhead*		newmhead(Chan*);
 Mount*		newmount(Chan*, int, char*);
 Page*		newpage(int, Segment **, uintptr);
--- a/sys/src/9/port/sysfile.c
+++ b/sys/src/9/port/sysfile.c
@@ -25,33 +25,45 @@
 growfd(Fgrp *f, int fd)	/* fd is always >= 0 */
 {
 	Chan **newfd, **oldfd;
+	uchar *newflag, *oldflag;
+	int nfd;
 
-	if(fd < f->nfd)
+	nfd = f->nfd;
+	if(fd < nfd)
 		return 0;
-	if(fd >= f->nfd+DELTAFD)
+	if(fd >= nfd+DELTAFD)
 		return -1;	/* out of range */
 	/*
 	 * Unbounded allocation is unwise; besides, there are only 16 bits
 	 * of fid in 9P
 	 */
-	if(f->nfd >= 5000){
+	if(nfd >= 5000){
     Exhausted:
 		print("no free file descriptors\n");
 		return -1;
 	}
-	newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*));
+	oldfd = f->fd;
+	oldflag = f->flag;
+	newfd = malloc((nfd+DELTAFD)*sizeof(newfd[0]));
 	if(newfd == nil)
 		goto Exhausted;
-	oldfd = f->fd;
-	memmove(newfd, oldfd, f->nfd*sizeof(Chan*));
+	memmove(newfd, oldfd, nfd*sizeof(newfd[0]));
+	newflag = malloc((nfd+DELTAFD)*sizeof(newflag[0]));
+	if(newflag == nil){
+		free(newfd);
+		goto Exhausted;
+	}
+	memmove(newflag, oldflag, nfd*sizeof(newflag[0]));
 	f->fd = newfd;
-	free(oldfd);
-	f->nfd += DELTAFD;
+	f->flag = newflag;
+	f->nfd = nfd+DELTAFD;
 	if(fd > f->maxfd){
 		if(fd/100 > f->maxfd/100)
 			f->exceed = (fd/100)*100;
 		f->maxfd = fd;
 	}
+	free(oldfd);
+	free(oldflag);
 	return 1;
 }
 
@@ -72,9 +84,9 @@
 }
 
 int
-newfd(Chan *c)
+newfd(Chan *c, int mode)
 {
-	int fd;
+	int fd, flag;
 	Fgrp *f;
 
 	f = up->fgrp;
@@ -87,6 +99,13 @@
 	if(fd > f->maxfd)
 		f->maxfd = fd;
 	f->fd[fd] = c;
+
+	/* per file-descriptor flags */
+	flag = 0;
+	if(mode & OCEXEC)
+		flag |= CCEXEC;
+	f->flag[fd] = flag;
+
 	unlockfgrp(f);
 	return fd;
 }
@@ -112,6 +131,8 @@
 		f->maxfd = fd[1];
 	f->fd[fd[0]] = c[0];
 	f->fd[fd[1]] = c[1];
+	f->flag[fd[0]] = 0;
+	f->flag[fd[1]] = 0;
 	unlockfgrp(f);
 	return 0;
 }
@@ -247,6 +268,7 @@
 
 		oc = f->fd[fd];
 		f->fd[fd] = c;
+		f->flag[fd] = 0;
 		unlockfgrp(f);
 		if(oc != nil)
 			cclose(oc);
@@ -255,7 +277,7 @@
 			cclose(c);
 			nexterror();
 		}
-		fd = newfd(c);
+		fd = newfd(c, 0);
 		if(fd < 0)
 			error(Enofd);
 		poperror();
@@ -280,7 +302,7 @@
 		cclose(c);
 		nexterror();
 	}
-	fd = newfd(c);
+	fd = newfd(c, mode);
 	if(fd < 0)
 		error(Enofd);
 	poperror();
@@ -295,7 +317,7 @@
 
 	lock(f);
 	c = fd <= f->maxfd ? f->fd[fd] : nil;
-	if(c == nil || (flag != 0 && (c->flag&flag) == 0)){
+	if(c == nil || (flag != 0 && ((f->flag[fd]|c->flag)&flag) == 0)){
 		unlock(f);
 		return;
 	}
@@ -1166,7 +1188,7 @@
 		cclose(c);
 		nexterror();
 	}
-	fd = newfd(c);
+	fd = newfd(c, mode);
 	if(fd < 0)
 		error(Enofd);
 	poperror();
--- a/sys/src/9/xen/archxen.c
+++ b/sys/src/9/xen/archxen.c
@@ -83,18 +83,3 @@
 int	mtrrprint(char*, long) { return 0; }
 char*	mtrr(uvlong, uvlong, char *) { return nil; }
 void	mtrrsync(void) {}
-
-/*
- * XXX until fpsave is debugged
- */
-void
-fpssesave(FPsave* f)
-{
-	fpx87save(f);
-}
-
-void
-fpsserestore(FPsave* f)
-{
-	fpx87restore(f);
-}
--- a/sys/src/9/xen/fns.h
+++ b/sys/src/9/xen/fns.h
@@ -9,7 +9,8 @@
 int		(*cmpswap)(long*, long, long);
 int		cmpswap486(long*, long, long);
 void	(*coherence)(void);
-void	cpuid(int, ulong regs[]);
+void	cpuid(int, int, ulong regs[]);
+void	fpuinit(void);
 int		cpuidentify(void);
 void	cpuidprint(void);
 void	(*cycles)(uvlong*);
@@ -16,18 +17,10 @@
 void	delay(int);
 #define	evenaddr(x)				/* x86 doesn't care */
 void	fpclear(void);
-void	fpenv(FPsave*);
 void	fpinit(void);
 void	fpoff(void);
 void	(*fprestore)(FPsave*);
 void	(*fpsave)(FPsave*);
-void	fpsserestore(FPsave*);
-void	fpsserestore0(FPsave*);
-void	fpssesave(FPsave*);
-void	fpssesave0(FPsave*);
-ulong	fpstatus(void);
-void	fpx87restore(FPsave*);
-void	fpx87save(FPsave*);
 ulong	getcr4(void);
 char*	getconf(char*);
 void	guesscpuhz(int);
--- a/sys/src/9/xen/l.s
+++ b/sys/src/9/xen/l.s
@@ -163,13 +163,13 @@
 	WAIT
 	RET
 
-TEXT fpx87save(SB), $0				/* save state and disable */
+TEXT fpx87save0(SB), $0				/* save state and disable */
 	MOVL	p+0(FP), AX
 	FSAVE	0(AX)				/* no WAIT */
 	FPOFF(l2)
 	RET
 
-TEXT fpx87restore(SB), $0				/* enable and restore state */
+TEXT fpx87restore0(SB), $0				/* enable and restore state */
 	FPON
 	MOVL	p+0(FP), AX
 	FRSTOR	0(AX)
@@ -176,19 +176,27 @@
 	WAIT
 	RET
 
-TEXT fpstatus(SB), $0				/* get floating point status */
-	FSTSW	AX
+TEXT fpclear(SB), $0				/* clear pending exceptions */
+	FPON
+	FCLEX					/* no WAIT */
+	FPOFF(l3)
 	RET
 
-TEXT fpenv(SB), $0				/* save state without waiting */
+TEXT fpssesave(SB), $0				/* save state and disable */
 	MOVL	p+0(FP), AX
-	FSTENV	0(AX)
+	FXSAVE	0(AX)				/* no WAIT */
+	FPOFF(l4)
 	RET
 
-TEXT fpclear(SB), $0				/* clear pending exceptions */
+TEXT fpsserestore(SB), $0			/* enable and restore state */
 	FPON
-	FCLEX					/* no WAIT */
-	FPOFF(l3)
+	MOVL	p+0(FP), AX
+	FXRSTOR	0(AX)
+	WAIT
+	RET
+
+TEXT ldmxcsr(SB), $0				/* Load MXCSR */
+	LDMXCSR	mxcsr+0(FP)
 	RET
 
 /*
--- a/sys/src/9/xen/main.c
+++ b/sys/src/9/xen/main.c
@@ -322,133 +322,6 @@
 	}
 }
 
-static char* mathmsg[] =
-{
-	nil,	/* handled below */
-	"denormalized operand",
-	"division by zero",
-	"numeric overflow",
-	"numeric underflow",
-	"precision loss",
-};
-
-static void
-mathnote(void)
-{
-	int i;
-	ulong status;
-	char *msg, note[ERRMAX];
-
-	status = up->fpsave->status;
-
-	/*
-	 * Some attention should probably be paid here to the
-	 * exception masks and error summary.
-	 */
-	msg = "unknown exception";
-	for(i = 1; i <= 5; i++){
-		if(!((1<<i) & status))
-			continue;
-		msg = mathmsg[i];
-		break;
-	}
-	if(status & 0x01){
-		if(status & 0x40){
-			if(status & 0x200)
-				msg = "stack overflow";
-			else
-				msg = "stack underflow";
-		}else
-			msg = "invalid operation";
-	}
- 	snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux",
- 		msg, up->fpsave->pc, status);
-	postnote(up, 1, note, NDebug);
-}
-
-/*
- *  math coprocessor error
- */
-static void
-matherror(Ureg *ur, void*)
-{
-	/*
-	 *  a write cycle to port 0xF0 clears the interrupt latch attached
-	 *  to the error# line from the 387
-	 */
-	if(!(m->cpuiddx & 0x01))
-		outb(0xF0, 0xFF);
-
-	/*
-	 *  save floating point state to check out error
-	 */
-	fpenv(up->fpsave);
-	mathnote();
-
-	if(ur->pc & KZERO)
-		panic("fp: status %ux fppc=0x%lux pc=0x%lux",
-			up->fpsave->status, up->fpsave->pc, ur->pc);
-}
-
-/*
- *  math coprocessor emulation fault
- */
-static void
-mathemu(Ureg *ureg, void*)
-{
-	if(up->fpstate & FPillegal){
-		/* someone did floating point in a note handler */
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
-	}
-	switch(up->fpstate){
-	case FPinit:
-		fpinit();
-		while(up->fpsave == nil)
-			up->fpsave = mallocalign(sizeof(FPsave), FPalign, 0, 0);
-		up->fpstate = FPactive;
-		break;
-	case FPinactive:
-		/*
-		 * Before restoring the state, check for any pending
-		 * exceptions, there's no way to restore the state without
-		 * generating an unmasked exception.
-		 * More attention should probably be paid here to the
-		 * exception masks and error summary.
-		 */
-		if((up->fpsave->status & ~up->fpsave->control) & 0x07F){
-			mathnote();
-			break;
-		}
-		fprestore(up->fpsave);
-		up->fpstate = FPactive;
-		break;
-	case FPactive:
-		panic("math emu pid %ld %s pc 0x%lux", 
-			up->pid, up->text, ureg->pc);
-		break;
-	}
-}
-
-/*
- *  math coprocessor segment overrun
- */
-static void
-mathover(Ureg*, void*)
-{
-	pexit("math overrun", 0);
-}
-
-void
-mathinit(void)
-{
-	trapenable(VectorCERR, matherror, 0, "matherror");
-	//if(X86FAMILY(m->cpuidax) == 3)
-	//	intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
-	trapenable(VectorCNA, mathemu, 0, "mathemu");
-	trapenable(VectorCSO, mathover, 0, "mathover");
-}
-
 /*
  *  set up floating point for a new process
  */
--- a/sys/src/9/xen/mkfile
+++ b/sys/src/9/xen/mkfile
@@ -68,6 +68,7 @@
 	plan9l.$O\
 	xen.$O\
 	main.$O\
+	fpu.$O\
 	mmu.$O\
 	random.$O\
 	rdb.$O\
--- a/sys/src/cmd/aux/kbdfs/kbdfs.c
+++ b/sys/src/cmd/aux/kbdfs/kbdfs.c
@@ -179,7 +179,7 @@
 [0x40]	0,	0,	0,	0,	0,	0,	Kbreak,	Khome,
 [0x48]	Kup,	Kpgup,	0,	Kleft,	0,	Kright,	0,	Kend,
 [0x50]	Kdown,	Kpgdown,Kins,	Kdel,	0,	0,	0,	0,
-[0x58]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x58]	0,	0,	0,	Kmod4,	0,	0,	0,	0,
 [0x60]	0,	0,	0,	0,	0,	0,	0,	0,
 [0x68]	0,	0,	0,	0,	0,	0,	0,	0,
 [0x70]	0,	0,	0,	0,	0,	0,	0,	0,
--- a/sys/src/cmd/aux/statusbar.c
+++ b/sys/src/cmd/aux/statusbar.c
@@ -21,10 +21,10 @@
 	text = display->black;
 	light = allocimagemix(display, DPalegreen, DWhite);
 	dark = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen);
+	if(light == nil || dark == nil) sysfatal("initcolor: %r");
 }
 
 Rectangle rbar;
-Point ptext;
 vlong n, d;
 int last;
 int lastp = -1;
@@ -75,7 +75,7 @@
 	if(lastp != p){
 		sprint(buf, "%3d%%", p);
 		
-		stringbg(screen, addpt(screen->r.min, Pt(Dx(rbar)-30, 4)), text, ZP, display->defaultfont, buf, light, ZP);
+		stringbg(screen, Pt(screen->r.max.x-4-stringwidth(display->defaultfont, buf), screen->r.min.y+4), text, ZP, display->defaultfont, buf, light, ZP);
 		lastp = p;
 	}
 
@@ -94,24 +94,13 @@
 void
 eresized(int new)
 {
-	Point p, q;
-	Rectangle r;
-
 	if(new && getwindow(display, Refnone) < 0)
 		fprint(2,"can't reattach to window");
 
-	r = screen->r;
-	draw(screen, r, light, nil, ZP);
-	p = string(screen, addpt(r.min, Pt(4,4)), text, ZP,
-		display->defaultfont, title);
-
-	p.x = r.min.x+4;
-	p.y += display->defaultfont->height+4;
-
-	q = subpt(r.max, Pt(4,4));
-	rbar = Rpt(p, q);
-
-	ptext = Pt(r.max.x-4-stringwidth(display->defaultfont, "100%"), r.min.x+4);
+	draw(screen, screen->r, light, nil, ZP);
+	if(title) string(screen, addpt(screen->r.min, Pt(4,4)), text, ZP, font, title);
+	rbar = insetrect(screen->r, 4);
+	rbar.min.y += font->height + 4;
 	border(screen, rbar, -2, dark, ZP);
 	last = 0;
 	lastp = -1;
@@ -163,7 +152,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: aux/statusbar [-kt] [-w minx,miny,maxx,maxy] 'title'\n");
+	fprint(2, "usage: %s [-kt] [-w minx,miny,maxx,maxy] [title]\n", argv0);
 	exits("usage");
 }
 
@@ -190,11 +179,14 @@
 		usage();
 	}ARGEND;
 
-	if(argc != 1)
+	switch(argc){
+	default:
 		usage();
-
-	title = argv[0];
-
+	case 1:
+		title = argv[0];
+	case 0:
+		break;
+	}
 	lfd = dup(0, -1);
 
 	while(q = strchr(p, ','))
@@ -204,7 +196,7 @@
 		textmode = 1;
 		rbar = Rect(0, 0, 60, 1);
 	}else{
-		if(initdraw(0, 0, title) < 0)
+		if(initdraw(0, 0, title ? title : argv0) < 0)
 			exits("initdraw");
 		initcolor();
 		einit(Emouse|Ekeyboard);
--- a/sys/src/cmd/aux/statusmsg.c
+++ b/sys/src/cmd/aux/statusmsg.c
@@ -22,6 +22,7 @@
 {
 	text = display->black;
 	light = allocimagemix(display, DPalegreen, DWhite);
+	if(light == nil) sysfatal("initcolor: %r");
 }
 
 void
@@ -136,7 +137,6 @@
 		usage();
 	case 1:
 		title = argv[0];
-		break;
 	case 0:
 		break;
 	}
@@ -153,7 +153,7 @@
 		if((bout = Bfdopen(1, OWRITE)) == nil)
 			sysfatal("Bfdopen: %r");
 	}else{
-		if(initdraw(0, 0, title) < 0)
+		if(initdraw(0, 0, title ? title : argv0) < 0)
 			sysfatal("initdraw: %r");
 		initcolor();
 		einit(Emouse|Ekeyboard);
--- a/sys/src/cmd/cpu.c
+++ b/sys/src/cmd/cpu.c
@@ -37,7 +37,7 @@
 char	*origargs;
 
 char	*srvname = "ncpu";
-char	*exportfs = "/bin/exportfs";
+char	*exportfs = "/bin/oexportfs";
 char	*ealgs = "rc4_256 sha1";
 
 /* message size for exportfs; may be larger so we can do big graphics in CPU window */
--- a/sys/src/cmd/exportfs/exportfs.c
+++ b/sys/src/cmd/exportfs/exportfs.c
@@ -1,135 +1,44 @@
-/*
- * exportfs - Export a plan 9 name space across a network
- */
 #include <u.h>
 #include <libc.h>
-#include <auth.h>
 #include <fcall.h>
-#include <libsec.h>
 #define Extern
 #include "exportfs.h"
 
-#define QIDPATH	((1LL<<48)-1)
-vlong newqid = 0;
-
-enum {
-	Encnone,
-	Encssl,
-	Enctls,
-};
-
-void (*fcalls[])(Fsrpc*) =
-{
-	[Tversion]	Xversion,
-	[Tauth]	Xauth,
-	[Tflush]	Xflush,
-	[Tattach]	Xattach,
-	[Twalk]		Xwalk,
-	[Topen]		slave,
-	[Tcreate]	Xcreate,
-	[Tclunk]	Xclunk,
-	[Tread]		slave,
-	[Twrite]	slave,
-	[Tremove]	Xremove,
-	[Tstat]		Xstat,
-	[Twstat]	Xwstat,
-};
-
-/* accounting and debugging counters */
-int	filecnt;
-int	freecnt;
-int	qidcnt;
-int	qfreecnt;
-int	ncollision;
-
 int	srvfd = -1;
-int	nonone = 1;
-char	*filterp;
-char	*ealgs = "rc4_256 sha1";
-char	*aanfilter = "/bin/aan";
-int	encproto = Encnone;
 int	readonly;
 
-static void mksecret(char *, uchar *);
-static char *anstring  = "tcp!*!0";
-
-char *netdir = "", *local = "", *remote = "";
-
-void	filter(int, char *, char *);
-
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-adnsR] [-f dbgfile] [-m msize] [-r root] "
-		"[-S srvfile] [-e 'crypt hash'] [-P exclusion-file] "
-		"[-A announce-string] [-B address]\n", argv0);
+	fprint(2, "usage: %s [-dsR] [-f dbgfile] [-m msize] [-r root] "
+		"[-S srvfile] [-P exclusion-file]\n", argv0);
 	fatal("usage");
 }
 
-static void
-noteconn(int fd)
-{
-	NetConnInfo *nci;
-
-	nci = getnetconninfo(nil, fd);
-	if(nci == nil)
-		return;
-	netdir = estrdup(nci->dir);
-	local = estrdup(nci->lsys);
-	remote = estrdup(nci->rsys);
-	freenetconninfo(nci);
-}
-
 void
 main(int argc, char **argv)
 {
-	char buf[ERRMAX], ebuf[ERRMAX], initial[4], *ini, *srvfdfile;
-	char *dbfile, *srv, *na, *nsfile, *keyspec;
-	int doauth, n, fd;
-	AuthInfo *ai;
-	Fsrpc *r;
+	char *dbfile, *srv, *srvfdfile;
+	int n;
 
 	dbfile = "/tmp/exportdb";
 	srv = nil;
 	srvfd = -1;
 	srvfdfile = nil;
-	na = nil;
-	nsfile = nil;
-	keyspec = "";
-	doauth = 0;
 
-	ai = nil;
 	ARGBEGIN{
-	case 'a':
-		doauth = 1;
-		break;
-
 	case 'd':
 		dbg++;
 		break;
 
-	case 'e':
-		ealgs = EARGF(usage());
-		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
-			ealgs = nil;
-		break;
-
 	case 'f':
 		dbfile = EARGF(usage());
 		break;
 
-	case 'k':
-		keyspec = EARGF(usage());
-		break;
-
 	case 'm':
 		messagesize = strtoul(EARGF(usage()), nil, 0);
 		break;
 
-	case 'n':
-		nonone = 0;
-		break;
-
 	case 'r':
 		srv = EARGF(usage());
 		break;
@@ -138,22 +47,10 @@
 		srv = "/";
 		break;
 
-	case 'A':
-		anstring = EARGF(usage());
-		break;
-
-	case 'B':
-		na = EARGF(usage());
-		break;
-
 	case 'F':
 		/* accepted but ignored, for backwards compatibility */
 		break;
 
-	case 'N':
-		nsfile = EARGF(usage());
-		break;
-
 	case 'P':
 		patternfile = EARGF(usage());
 		break;
@@ -173,53 +70,16 @@
 	}ARGEND
 	USED(argc, argv);
 
-	if(na == nil && doauth){
-		/*
-		 * We use p9any so we don't have to visit this code again, with the
-		 * cost that this code is incompatible with the old world, which
-		 * requires p9sk2. (The two differ in who talks first, so compatibility
-		 * is awkward.)
-		 */
-		ai = auth_proxy(0, auth_getkey, "proto=p9any role=server %s", keyspec);
-		if(ai == nil)
-			fatal("auth_proxy: %r");
-		if(nonone && strcmp(ai->cuid, "none") == 0)
-			fatal("exportfs by none disallowed");
-		if(auth_chuid(ai, nsfile) < 0)
-			fatal("auth_chuid: %r");
-		else {	/* chown network connection */
-			Dir nd;
-			nulldir(&nd);
-			nd.mode = 0660;
-			nd.uid = ai->cuid;
-			dirfwstat(0, &nd);
-		}
-		putenv("service", "exportfs");
-	}
-
 	if(srvfdfile != nil){
+		if(srv != nil){
+			fprint(2, "exportfs: -S cannot be used with -r or -s\n");
+			usage();
+		}
 		if((srvfd = open(srvfdfile, ORDWR)) < 0)
 			fatal("open %s: %r", srvfdfile);
-	}
+	} else if(srv == nil)
+		usage();
 
-	if(na != nil){
-		if(srv == nil)
-			fatal("-B requires -s");
-
-		local = "me";
-		remote = na;
-		if((fd = dial(netmkaddr(na, 0, "importfs"), 0, 0, 0)) < 0)
-			fatal("can't dial %s: %r", na);
-	
-		ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client %s", keyspec);
-		if(ai == nil)
-			fatal("%r: %s", na);
-
-		dup(fd, 0);
-		dup(fd, 1);
-		close(fd);
-	}
-
 	exclusions();
 
 	if(dbg) {
@@ -228,11 +88,6 @@
 		close(n);
 	}
 
-	if(srvfd >= 0 && srv != nil){
-		fprint(2, "exportfs: -S cannot be used with -r or -s\n");
-		usage();
-	}
-
 	DEBUG(DFD, "exportfs: started\n");
 
 	rfork(RFNOTEG|RFREND);
@@ -246,695 +101,18 @@
 
 	fmtinstall('F', fcallfmt);
 
-	/*
-	 * Get tree to serve from network connection,
-	 * check we can get there and ack the connection
- 	 */
-	if(srvfd != -1) {
-		/* do nothing */
-	}
-	else if(srv != nil) {
+	if(srvfd == -1) {
 		if(chdir(srv) < 0) {
+			char ebuf[ERRMAX];
 			ebuf[0] = '\0';
 			errstr(ebuf, sizeof ebuf);
-			r = getsbuf();
-			r->work.tag = NOTAG;
-			r->work.fid = NOFID;
-			r->work.type = Rerror;
-			r->work.ename = ebuf;
-			n = convS2M(&r->work, r->buf, messagesize);
-			write(0, r->buf, n);
 			DEBUG(DFD, "chdir(\"%s\"): %s\n", srv, ebuf);
-			exits(ebuf);
+			mounterror(ebuf);
 		}
 		DEBUG(DFD, "invoked as server for %s", srv);
-		strncpy(buf, srv, sizeof buf);
 	}
-	else {
-		noteconn(0);
-		buf[0] = 0;
-		n = read(0, buf, sizeof(buf)-1);
-		if(n < 0) {
-			errstr(buf, sizeof buf);
-			fprint(0, "read(0): %s\n", buf);
-			DEBUG(DFD, "read(0): %s\n", buf);
-			exits(buf);
-		}
-		buf[n] = 0;
-		if(chdir(buf) < 0) {
-			errstr(ebuf, sizeof ebuf);
-			fprint(0, "chdir(%d:\"%s\"): %s\n", n, buf, ebuf);
-			DEBUG(DFD, "chdir(%d:\"%s\"): %s\n", n, buf, ebuf);
-			exits(ebuf);
-		}
-	}
 
 	DEBUG(DFD, "\niniting root\n");
 	initroot();
-
-	DEBUG(DFD, "exportfs: %s\n", buf);
-
-	if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2)
-		fatal("open ack write");
-
-	ini = initial;
-	n = readn(0, initial, sizeof(initial));
-	if(n == 0)
-		fatal(nil);	/* port scan or spurious open/close on exported /srv file (unmount) */
-	if(n < sizeof(initial))
-		fatal("can't read initial string: %r");
-
-	if(memcmp(ini, "impo", 4) == 0) {
-		char buf[128], *p, *args[3];
-
-		ini = nil;
-		p = buf;
-		for(;;){
-			if((n = read(0, p, 1)) < 0)
-				fatal("can't read impo arguments: %r");
-			if(n == 0)
-				fatal("connection closed while reading arguments");
-			if(*p == '\n') 
-				*p = '\0';
-			if(*p++ == '\0')
-				break;
-			if(p >= buf + sizeof(buf))
-				fatal("import parameters too long");
-		}
-		
-		if(tokenize(buf, args, nelem(args)) != 2)
-			fatal("impo arguments invalid: impo%s...", buf);
-
-		if(strcmp(args[0], "aan") == 0)
-			filterp = aanfilter;
-		else if(strcmp(args[0], "nofilter") != 0)
-			fatal("import filter argument unsupported: %s", args[0]);
-
-		if(strcmp(args[1], "ssl") == 0)
-			encproto = Encssl;
-		else if(strcmp(args[1], "tls") == 0)
-			encproto = Enctls;
-		else if(strcmp(args[1], "clear") != 0)
-			fatal("import encryption proto unsupported: %s", args[1]);
-
-		if(encproto == Enctls)
-			fatal("%s: tls has not yet been implemented", argv[0]);
-	}
-
-	if(encproto != Encnone && ealgs != nil && ai != nil) {
-		uchar key[16], digest[SHA1dlen];
-		char fromclientsecret[21];
-		char fromserversecret[21];
-		int i;
-
-		if(ai->nsecret < 8)
-			fatal("secret too small for ssl");
-		memmove(key+4, ai->secret, 8);
-
-		/* exchange random numbers */
-		srand(truerand());
-		for(i = 0; i < 4; i++)
-			key[i+12] = rand();
-
-		if(ini != nil) 
-			fatal("Protocol botch: old import");
-		if(readn(0, key, 4) != 4)
-			fatal("can't read key part; %r");
-
-		if(write(0, key+12, 4) != 4)
-			fatal("can't write key part; %r");
-
-		/* scramble into two secrets */
-		sha1(key, sizeof(key), digest, nil);
-		mksecret(fromclientsecret, digest);
-		mksecret(fromserversecret, digest+10);
-
-		if(filterp != nil)
-			filter(0, filterp, na);
-
-		switch(encproto) {
-		case Encssl:
-			fd = pushssl(0, ealgs, fromserversecret, fromclientsecret, nil);
-			if(fd < 0)
-				fatal("can't establish ssl connection: %r");
-			if(fd != 0){
-				dup(fd, 0);
-				close(fd);
-			}
-			break;
-		case Enctls:
-		default:
-			fatal("Unsupported encryption protocol");
-		}
-	}
-	else if(filterp != nil) {
-		if(ini != nil)
-			fatal("Protocol botch: don't know how to deal with this");
-		filter(0, filterp, na);
-	}
-	dup(0, 1);
-
-	if(ai != nil)
-		auth_freeAI(ai);
-
-	if(ini != nil){
-		r = getsbuf();
-		memmove(r->buf, ini, BIT32SZ);
-		n = GBIT32(r->buf);
-		if(n <= BIT32SZ || n > messagesize)
-			fatal("bad length in 9P2000 message header");
-		n -= BIT32SZ;
-		if(readn(0, r->buf+BIT32SZ, n) != n)
-			fatal(nil);
-		n += BIT32SZ;
-		goto Message;
-	}
-
-	/*
-	 * Start serving file requests from the network
-	 */
-	for(;;) {
-		r = getsbuf();
-		n = read9pmsg(0, r->buf, messagesize);
-		if(n <= 0)
-			fatal(nil);
-	Message:
-		if(convM2S(r->buf, n, &r->work) != n)
-			fatal("convM2S format error");
-
-		DEBUG(DFD, "%F\n", &r->work);
-		(fcalls[r->work.type])(r);
-	}
-}
-
-void
-reply(Fcall *r, Fcall *t, char *err)
-{
-	uchar *data;
-	int n;
-
-	t->tag = r->tag;
-	t->fid = r->fid;
-	if(err != nil) {
-		t->type = Rerror;
-		t->ename = err;
-	}
-	else 
-		t->type = r->type + 1;
-
-	DEBUG(DFD, "\t%F\n", t);
-
-	data = malloc(messagesize);	/* not mallocz; no need to clear */
-	if(data == nil)
-		fatal(Enomem);
-	n = convS2M(t, data, messagesize);
-	if(write(0, data, n) != n){
-		/* not fatal, might have got a note due to flush */
-		fprint(2, "exportfs: short write in reply: %r\n");
-	}
-	free(data);
-}
-
-Fid *
-getfid(int nr)
-{
-	Fid *f;
-
-	for(f = fidhash(nr); f != nil; f = f->next)
-		if(f->nr == nr)
-			return f;
-
-	return nil;
-}
-
-int
-freefid(int nr)
-{
-	Fid *f, **l;
-	char buf[128];
-
-	l = &fidhash(nr);
-	for(f = *l; f != nil; f = f->next) {
-		if(f->nr == nr) {
-			if(f->mid) {
-				snprint(buf, sizeof(buf), "/mnt/exportfs/%d", f->mid);
-				unmount(0, buf);
-				psmap[f->mid] = 0;
-			}
-			if(f->f != nil) {
-				freefile(f->f);
-				f->f = nil;
-			}
-			if(f->dir != nil){
-				free(f->dir);
-				f->dir = nil;
-			}
-			*l = f->next;
-			f->next = fidfree;
-			fidfree = f;
-			return 1;
-		}
-		l = &f->next;
-	}
-
-	return 0;	
-}
-
-Fid *
-newfid(int nr)
-{
-	Fid *new, **l;
-	int i;
-
-	l = &fidhash(nr);
-	for(new = *l; new != nil; new = new->next)
-		if(new->nr == nr)
-			return nil;
-
-	if(fidfree == nil) {
-		fidfree = emallocz(sizeof(Fid) * Fidchunk);
-
-		for(i = 0; i < Fidchunk-1; i++)
-			fidfree[i].next = &fidfree[i+1];
-
-		fidfree[Fidchunk-1].next = nil;
-	}
-
-	new = fidfree;
-	fidfree = new->next;
-
-	memset(new, 0, sizeof(Fid));
-	new->next = *l;
-	*l = new;
-	new->nr = nr;
-	new->fid = -1;
-	new->mid = 0;
-
-	return new;	
-}
-
-static struct {
-	Lock;
-	Fsrpc	*free;
-
-	/* statistics */
-	int	nalloc;
-	int	nfree;
-}	sbufalloc;
-
-Fsrpc *
-getsbuf(void)
-{
-	Fsrpc *w;
-
-	lock(&sbufalloc);
-	w = sbufalloc.free;
-	if(w != nil){
-		sbufalloc.free = w->next;
-		w->next = nil;
-		sbufalloc.nfree--;
-		unlock(&sbufalloc);
-	} else {
-		sbufalloc.nalloc++;
-		unlock(&sbufalloc);
-		w = emallocz(sizeof(*w) + messagesize);
-	}
-	w->flushtag = NOTAG;
-	return w;
-}
-
-void
-putsbuf(Fsrpc *w)
-{
-	w->flushtag = NOTAG;
-	lock(&sbufalloc);
-	w->next = sbufalloc.free;
-	sbufalloc.free = w;
-	sbufalloc.nfree++;
-	unlock(&sbufalloc);
-}
-
-void
-freefile(File *f)
-{
-	File *parent, *child;
-
-	while(--f->ref == 0){
-		freecnt++;
-		DEBUG(DFD, "free %s\n", f->name);
-		/* delete from parent */
-		parent = f->parent;
-		if(parent->child == f)
-			parent->child = f->childlist;
-		else{
-			for(child = parent->child; child->childlist != f; child = child->childlist) {
-				if(child->childlist == nil)
-					fatal("bad child list");
-			}
-			child->childlist = f->childlist;
-		}
-		freeqid(f->qidt);
-		free(f->name);
-		free(f);
-		f = parent;
-	}
-}
-
-File *
-file(File *parent, char *name)
-{
-	Dir *dir;
-	char *path;
-	File *f;
-
-	DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
-
-	path = makepath(parent, name);
-	if(patternfile != nil && excludefile(path)){
-		free(path);
-		return nil;
-	}
-	dir = dirstat(path);
-	free(path);
-	if(dir == nil)
-		return nil;
-
-	for(f = parent->child; f != nil; f = f->childlist)
-		if(strcmp(name, f->name) == 0)
-			break;
-
-	if(f == nil){
-		f = emallocz(sizeof(File));
-		f->name = estrdup(name);
-
-		f->parent = parent;
-		f->childlist = parent->child;
-		parent->child = f;
-		parent->ref++;
-		f->ref = 0;
-		filecnt++;
-	}
-	f->ref++;
-	f->qid.type = dir->qid.type;
-	f->qid.vers = dir->qid.vers;
-	f->qidt = uniqueqid(dir);
-	f->qid.path = f->qidt->uniqpath;
-
-	f->inval = 0;
-
-	free(dir);
-
-	return f;
-}
-
-void
-initroot(void)
-{
-	Dir *dir;
-
-	root = emallocz(sizeof(File));
-	root->name = estrdup(".");
-
-	dir = dirstat(root->name);
-	if(dir == nil)
-		fatal("root stat");
-
-	root->ref = 1;
-	root->qid.vers = dir->qid.vers;
-	root->qidt = uniqueqid(dir);
-	root->qid.path = root->qidt->uniqpath;
-	root->qid.type = QTDIR;
-	free(dir);
-
-	psmpt = emallocz(sizeof(File));
-	psmpt->name = estrdup("/");
-
-	dir = dirstat(psmpt->name);
-	if(dir == nil)
-		return;
-
-	psmpt->ref = 1;
-	psmpt->qid.vers = dir->qid.vers;
-	psmpt->qidt = uniqueqid(dir);
-	psmpt->qid.path = psmpt->qidt->uniqpath;
-	free(dir);
-
-	psmpt = file(psmpt, "mnt");
-	if(psmpt == nil)
-		return;
-	psmpt = file(psmpt, "exportfs");
-}
-
-char*
-makepath(File *p, char *name)
-{
-	int i, n;
-	char *c, *s, *path, *seg[256];
-
-	seg[0] = name;
-	n = strlen(name)+2;
-	for(i = 1; i < 256 && p; i++, p = p->parent){
-		seg[i] = p->name;
-		n += strlen(p->name)+1;
-	}
-	path = emallocz(n);
-	s = path;
-
-	while(i--) {
-		for(c = seg[i]; *c; c++)
-			*s++ = *c;
-		*s++ = '/';
-	}
-	while(s[-1] == '/')
-		s--;
-	*s = '\0';
-
-	return path;
-}
-
-int
-qidhash(vlong path)
-{
-	int h, n;
-
-	h = 0;
-	for(n=0; n<64; n+=Nqidbits){
-		h ^= path;
-		path >>= Nqidbits;
-	}
-	return h & (Nqidtab-1);
-}
-
-void
-freeqid(Qidtab *q)
-{
-	ulong h;
-	Qidtab *l;
-
-	if(--q->ref)
-		return;
-	qfreecnt++;
-	h = qidhash(q->path);
-	if(qidtab[h] == q)
-		qidtab[h] = q->next;
-	else{
-		for(l=qidtab[h]; l->next!=q; l=l->next)
-			if(l->next == nil)
-				fatal("bad qid list");
-		l->next = q->next;
-	}
-	free(q);
-}
-
-Qidtab*
-qidlookup(Dir *d)
-{
-	ulong h;
-	Qidtab *q;
-
-	h = qidhash(d->qid.path);
-	for(q=qidtab[h]; q!=nil; q=q->next)
-		if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
-			return q;
-	return nil;
-}
-
-int
-qidexists(vlong path)
-{
-	int h;
-	Qidtab *q;
-
-	for(h=0; h<Nqidtab; h++)
-		for(q=qidtab[h]; q!=nil; q=q->next)
-			if(q->uniqpath == path)
-				return 1;
-	return 0;
-}
-
-Qidtab*
-uniqueqid(Dir *d)
-{
-	ulong h;
-	vlong path;
-	Qidtab *q;
-
-	q = qidlookup(d);
-	if(q != nil){
-		q->ref++;
-		return q;
-	}
-	path = d->qid.path;
-	while(qidexists(path)){
-		DEBUG(DFD, "collision on %s\n", d->name);
-		/* collision: find a new one */
-		ncollision++;
-		path &= QIDPATH;
-		++newqid;
-		if(newqid >= (1<<16)){
-			DEBUG(DFD, "collision wraparound\n");
-			newqid = 1;
-		}
-		path |= newqid<<48;
-		DEBUG(DFD, "assign qid %.16llux\n", path);
-	}
-	qidcnt++;
-	q = emallocz(sizeof(Qidtab));
-	q->ref = 1;
-	q->type = d->type;
-	q->dev = d->dev;
-	q->path = d->qid.path;
-	q->uniqpath = path;
-	h = qidhash(d->qid.path);
-	q->next = qidtab[h];
-	qidtab[h] = q;
-	return q;
-}
-
-void
-fatal(char *s, ...)
-{
-	char buf[ERRMAX];
-	va_list arg;
-	Proc *m;
-
-	if(s != nil) {
-		va_start(arg, s);
-		vsnprint(buf, ERRMAX, s, arg);
-		va_end(arg);
-	}
-
-	/* Clear away the slave children */
-	for(m = Proclist; m != nil; m = m->next)
-		postnote(PNPROC, m->pid, "kill");
-
-	if(s != nil) {
-		DEBUG(DFD, "%s\n", buf);
-		sysfatal("%s", buf);	/* caution: buf could contain '%' */
-	} else
-		exits(nil);
-}
-
-void*
-emallocz(uint n)
-{
-	void *p;
-
-	p = mallocz(n, 1);
-	if(p == nil)
-		fatal(Enomem);
-	setmalloctag(p, getcallerpc(&n));
-	return p;
-}
-
-char*
-estrdup(char *s)
-{
-	char *t;
-
-	t = strdup(s);
-	if(t == nil)
-		fatal(Enomem);
-	setmalloctag(t, getcallerpc(&s));
-	return t;
-}
-
-void
-filter(int fd, char *cmd, char *host)
-{
-	char addr[128], buf[256], *s, *file, *argv[16];
-	int lfd, p[2], len, argc;
-
-	if(host == nil){
-		/* Get a free port and post it to the client. */
-		if (announce(anstring, addr) < 0)
-			fatal("filter: Cannot announce %s: %r", anstring);
-
-		snprint(buf, sizeof(buf), "%s/local", addr);
-		if ((lfd = open(buf, OREAD)) < 0)
-			fatal("filter: Cannot open %s: %r", buf);
-		if ((len = read(lfd, buf, sizeof buf - 1)) < 0)
-			fatal("filter: Cannot read %s: %r", buf);
-		close(lfd);
-		buf[len] = '\0';
-		if ((s = strchr(buf, '\n')) != nil)
-			len = s - buf;
-		if (write(fd, buf, len) != len) 
-			fatal("filter: cannot write port; %r");
-	} else {
-		/* Read address string from connection */
-		if ((len = read(fd, buf, sizeof buf - 1)) < 0)
-			sysfatal("filter: cannot write port; %r");
-		buf[len] = '\0';
-
-		if ((s = strrchr(buf, '!')) == nil)
-			sysfatal("filter: illegally formatted port %s", buf);
-		strecpy(addr, addr+sizeof(addr), netmkaddr(host, "tcp", s+1));
-		strecpy(strrchr(addr, '!'), addr+sizeof(addr), s);
-	}
-
-	DEBUG(DFD, "filter: %s\n", addr);
-
-	snprint(buf, sizeof(buf), "%s", cmd);
-	argc = tokenize(buf, argv, nelem(argv)-3);
-	if (argc == 0)
-		sysfatal("filter: empty command");
-
-	if(host != nil)
-		argv[argc++] = "-c";
-	argv[argc++] = addr;
-	argv[argc] = nil;
-
-	file = argv[0];
-	if((s = strrchr(argv[0], '/')) != nil)
-		argv[0] = s+1;
-
-	if(pipe(p) < 0)
-		sysfatal("pipe: %r");
-
-	switch(rfork(RFNOWAIT|RFPROC|RFMEM|RFFDG|RFREND)) {
-	case -1:
-		fatal("filter: rfork; %r\n");
-	case 0:
-		close(fd);
-		if (dup(p[0], 1) < 0)
-			fatal("filter: Cannot dup to 1; %r");
-		if (dup(p[0], 0) < 0)
-			fatal("filter: Cannot dup to 0; %r");
-		close(p[0]);
-		close(p[1]);
-		exec(file, argv);
-		fatal("filter: exec; %r");
-	default:
-		dup(p[1], fd);
-		close(p[0]);
-		close(p[1]);
-	}
-}
-
-static void
-mksecret(char *t, uchar *f)
-{
-	sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
-		f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
+	io();
 }
--- a/sys/src/cmd/exportfs/exportfs.h
+++ b/sys/src/cmd/exportfs/exportfs.h
@@ -112,7 +112,10 @@
 void Xwstat(Fsrpc*);
 void slave(Fsrpc*);
 
+void	io(void);
 void	reply(Fcall*, Fcall*, char*);
+void	mounterror(char*);
+
 Fid 	*getfid(int);
 int	freefid(int);
 Fid	*newfid(int);
--- a/sys/src/cmd/exportfs/exportsrv.c
+++ b/sys/src/cmd/exportfs/exportsrv.c
@@ -5,8 +5,6 @@
 #define Extern	extern
 #include "exportfs.h"
 
-extern char *netdir, *local, *remote;
-
 char Ebadfid[] = "Bad fid";
 char Enotdir[] = "Not a directory";
 char Edupfid[] = "Fid already in use";
@@ -493,12 +491,6 @@
 			return;
 
 		case 0:
-			if (local[0] != '\0')
-				if (netdir[0] != '\0')
-					procsetname("%s: %s -> %s", netdir, 
-						local, remote);
-				else
-					procsetname("%s -> %s", local, remote);
 			blockingslave(m);
 			_exits(0);
 
--- /dev/null
+++ b/sys/src/cmd/exportfs/io.c
@@ -1,0 +1,503 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#define Extern
+#include "exportfs.h"
+
+#define QIDPATH	((1LL<<48)-1)
+vlong newqid = 0;
+
+void (*fcalls[])(Fsrpc*) =
+{
+	[Tversion]	Xversion,
+	[Tauth]	Xauth,
+	[Tflush]	Xflush,
+	[Tattach]	Xattach,
+	[Twalk]		Xwalk,
+	[Topen]		slave,
+	[Tcreate]	Xcreate,
+	[Tclunk]	Xclunk,
+	[Tread]		slave,
+	[Twrite]	slave,
+	[Tremove]	Xremove,
+	[Tstat]		Xstat,
+	[Twstat]	Xwstat,
+};
+
+/* accounting and debugging counters */
+int	filecnt;
+int	freecnt;
+int	qidcnt;
+int	qfreecnt;
+int	ncollision;
+
+
+/*
+ * Start serving file requests from the network
+ */
+void
+io(void)
+{
+	Fsrpc *r;
+	int n;
+
+	for(;;) {
+		r = getsbuf();
+		n = read9pmsg(0, r->buf, messagesize);
+		if(n <= 0)
+			fatal(nil);
+		if(convM2S(r->buf, n, &r->work) != n)
+			fatal("convM2S format error");
+
+		DEBUG(DFD, "%F\n", &r->work);
+		(fcalls[r->work.type])(r);
+	}
+}
+
+void
+reply(Fcall *r, Fcall *t, char *err)
+{
+	uchar *data;
+	int n;
+
+	t->tag = r->tag;
+	t->fid = r->fid;
+	if(err != nil) {
+		t->type = Rerror;
+		t->ename = err;
+	}
+	else 
+		t->type = r->type + 1;
+
+	DEBUG(DFD, "\t%F\n", t);
+
+	data = malloc(messagesize);	/* not mallocz; no need to clear */
+	if(data == nil)
+		fatal(Enomem);
+	n = convS2M(t, data, messagesize);
+	if(write(1, data, n) != n){
+		/* not fatal, might have got a note due to flush */
+		fprint(2, "exportfs: short write in reply: %r\n");
+	}
+	free(data);
+}
+
+void
+mounterror(char *err)
+{
+	Fsrpc *r;
+	int n;
+
+	r = getsbuf();
+	r->work.tag = NOTAG;
+	r->work.fid = NOFID;
+	r->work.type = Rerror;
+	r->work.ename = err;
+	n = convS2M(&r->work, r->buf, messagesize);
+	write(1, r->buf, n);
+	exits(err);
+}
+
+Fid *
+getfid(int nr)
+{
+	Fid *f;
+
+	for(f = fidhash(nr); f != nil; f = f->next)
+		if(f->nr == nr)
+			return f;
+
+	return nil;
+}
+
+int
+freefid(int nr)
+{
+	Fid *f, **l;
+	char buf[128];
+
+	l = &fidhash(nr);
+	for(f = *l; f != nil; f = f->next) {
+		if(f->nr == nr) {
+			if(f->mid) {
+				snprint(buf, sizeof(buf), "/mnt/exportfs/%d", f->mid);
+				unmount(0, buf);
+				psmap[f->mid] = 0;
+			}
+			if(f->f != nil) {
+				freefile(f->f);
+				f->f = nil;
+			}
+			if(f->dir != nil){
+				free(f->dir);
+				f->dir = nil;
+			}
+			*l = f->next;
+			f->next = fidfree;
+			fidfree = f;
+			return 1;
+		}
+		l = &f->next;
+	}
+
+	return 0;	
+}
+
+Fid *
+newfid(int nr)
+{
+	Fid *new, **l;
+	int i;
+
+	l = &fidhash(nr);
+	for(new = *l; new != nil; new = new->next)
+		if(new->nr == nr)
+			return nil;
+
+	if(fidfree == nil) {
+		fidfree = emallocz(sizeof(Fid) * Fidchunk);
+
+		for(i = 0; i < Fidchunk-1; i++)
+			fidfree[i].next = &fidfree[i+1];
+
+		fidfree[Fidchunk-1].next = nil;
+	}
+
+	new = fidfree;
+	fidfree = new->next;
+
+	memset(new, 0, sizeof(Fid));
+	new->next = *l;
+	*l = new;
+	new->nr = nr;
+	new->fid = -1;
+	new->mid = 0;
+
+	return new;	
+}
+
+static struct {
+	Lock;
+	Fsrpc	*free;
+
+	/* statistics */
+	int	nalloc;
+	int	nfree;
+}	sbufalloc;
+
+Fsrpc *
+getsbuf(void)
+{
+	Fsrpc *w;
+
+	lock(&sbufalloc);
+	w = sbufalloc.free;
+	if(w != nil){
+		sbufalloc.free = w->next;
+		w->next = nil;
+		sbufalloc.nfree--;
+		unlock(&sbufalloc);
+	} else {
+		sbufalloc.nalloc++;
+		unlock(&sbufalloc);
+		w = emallocz(sizeof(*w) + messagesize);
+	}
+	w->flushtag = NOTAG;
+	return w;
+}
+
+void
+putsbuf(Fsrpc *w)
+{
+	w->flushtag = NOTAG;
+	lock(&sbufalloc);
+	w->next = sbufalloc.free;
+	sbufalloc.free = w;
+	sbufalloc.nfree++;
+	unlock(&sbufalloc);
+}
+
+void
+freefile(File *f)
+{
+	File *parent, *child;
+
+	while(--f->ref == 0){
+		freecnt++;
+		DEBUG(DFD, "free %s\n", f->name);
+		/* delete from parent */
+		parent = f->parent;
+		if(parent->child == f)
+			parent->child = f->childlist;
+		else{
+			for(child = parent->child; child->childlist != f; child = child->childlist) {
+				if(child->childlist == nil)
+					fatal("bad child list");
+			}
+			child->childlist = f->childlist;
+		}
+		freeqid(f->qidt);
+		free(f->name);
+		free(f);
+		f = parent;
+	}
+}
+
+File *
+file(File *parent, char *name)
+{
+	Dir *dir;
+	char *path;
+	File *f;
+
+	DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
+
+	path = makepath(parent, name);
+	if(patternfile != nil && excludefile(path)){
+		free(path);
+		return nil;
+	}
+	dir = dirstat(path);
+	free(path);
+	if(dir == nil)
+		return nil;
+
+	for(f = parent->child; f != nil; f = f->childlist)
+		if(strcmp(name, f->name) == 0)
+			break;
+
+	if(f == nil){
+		f = emallocz(sizeof(File));
+		f->name = estrdup(name);
+
+		f->parent = parent;
+		f->childlist = parent->child;
+		parent->child = f;
+		parent->ref++;
+		f->ref = 0;
+		filecnt++;
+	}
+	f->ref++;
+	f->qid.type = dir->qid.type;
+	f->qid.vers = dir->qid.vers;
+	f->qidt = uniqueqid(dir);
+	f->qid.path = f->qidt->uniqpath;
+
+	f->inval = 0;
+
+	free(dir);
+
+	return f;
+}
+
+void
+initroot(void)
+{
+	Dir *dir;
+
+	root = emallocz(sizeof(File));
+	root->name = estrdup(".");
+
+	dir = dirstat(root->name);
+	if(dir == nil)
+		fatal("root stat");
+
+	root->ref = 1;
+	root->qid.vers = dir->qid.vers;
+	root->qidt = uniqueqid(dir);
+	root->qid.path = root->qidt->uniqpath;
+	root->qid.type = QTDIR;
+	free(dir);
+
+	psmpt = emallocz(sizeof(File));
+	psmpt->name = estrdup("/");
+
+	dir = dirstat(psmpt->name);
+	if(dir == nil)
+		return;
+
+	psmpt->ref = 1;
+	psmpt->qid.vers = dir->qid.vers;
+	psmpt->qidt = uniqueqid(dir);
+	psmpt->qid.path = psmpt->qidt->uniqpath;
+	free(dir);
+
+	psmpt = file(psmpt, "mnt");
+	if(psmpt == nil)
+		return;
+	psmpt = file(psmpt, "exportfs");
+}
+
+char*
+makepath(File *p, char *name)
+{
+	int i, n;
+	char *c, *s, *path, *seg[256];
+
+	seg[0] = name;
+	n = strlen(name)+2;
+	for(i = 1; i < 256 && p; i++, p = p->parent){
+		seg[i] = p->name;
+		n += strlen(p->name)+1;
+	}
+	path = emallocz(n);
+	s = path;
+
+	while(i--) {
+		for(c = seg[i]; *c; c++)
+			*s++ = *c;
+		*s++ = '/';
+	}
+	while(s[-1] == '/')
+		s--;
+	*s = '\0';
+
+	return path;
+}
+
+int
+qidhash(vlong path)
+{
+	int h, n;
+
+	h = 0;
+	for(n=0; n<64; n+=Nqidbits){
+		h ^= path;
+		path >>= Nqidbits;
+	}
+	return h & (Nqidtab-1);
+}
+
+void
+freeqid(Qidtab *q)
+{
+	ulong h;
+	Qidtab *l;
+
+	if(--q->ref)
+		return;
+	qfreecnt++;
+	h = qidhash(q->path);
+	if(qidtab[h] == q)
+		qidtab[h] = q->next;
+	else{
+		for(l=qidtab[h]; l->next!=q; l=l->next)
+			if(l->next == nil)
+				fatal("bad qid list");
+		l->next = q->next;
+	}
+	free(q);
+}
+
+Qidtab*
+qidlookup(Dir *d)
+{
+	ulong h;
+	Qidtab *q;
+
+	h = qidhash(d->qid.path);
+	for(q=qidtab[h]; q!=nil; q=q->next)
+		if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
+			return q;
+	return nil;
+}
+
+int
+qidexists(vlong path)
+{
+	int h;
+	Qidtab *q;
+
+	for(h=0; h<Nqidtab; h++)
+		for(q=qidtab[h]; q!=nil; q=q->next)
+			if(q->uniqpath == path)
+				return 1;
+	return 0;
+}
+
+Qidtab*
+uniqueqid(Dir *d)
+{
+	ulong h;
+	vlong path;
+	Qidtab *q;
+
+	q = qidlookup(d);
+	if(q != nil){
+		q->ref++;
+		return q;
+	}
+	path = d->qid.path;
+	while(qidexists(path)){
+		DEBUG(DFD, "collision on %s\n", d->name);
+		/* collision: find a new one */
+		ncollision++;
+		path &= QIDPATH;
+		++newqid;
+		if(newqid >= (1<<16)){
+			DEBUG(DFD, "collision wraparound\n");
+			newqid = 1;
+		}
+		path |= newqid<<48;
+		DEBUG(DFD, "assign qid %.16llux\n", path);
+	}
+	qidcnt++;
+	q = emallocz(sizeof(Qidtab));
+	q->ref = 1;
+	q->type = d->type;
+	q->dev = d->dev;
+	q->path = d->qid.path;
+	q->uniqpath = path;
+	h = qidhash(d->qid.path);
+	q->next = qidtab[h];
+	qidtab[h] = q;
+	return q;
+}
+
+void
+fatal(char *s, ...)
+{
+	char buf[ERRMAX];
+	va_list arg;
+	Proc *m;
+
+	if(s != nil) {
+		va_start(arg, s);
+		vsnprint(buf, ERRMAX, s, arg);
+		va_end(arg);
+	}
+
+	/* Clear away the slave children */
+	for(m = Proclist; m != nil; m = m->next)
+		postnote(PNPROC, m->pid, "kill");
+
+	if(s != nil) {
+		DEBUG(DFD, "%s\n", buf);
+		sysfatal("%s", buf);	/* caution: buf could contain '%' */
+	} else
+		exits(nil);
+}
+
+void*
+emallocz(uint n)
+{
+	void *p;
+
+	p = mallocz(n, 1);
+	if(p == nil)
+		fatal(Enomem);
+	setmalloctag(p, getcallerpc(&n));
+	return p;
+}
+
+char*
+estrdup(char *s)
+{
+	char *t;
+
+	t = strdup(s);
+	if(t == nil)
+		fatal(Enomem);
+	setmalloctag(t, getcallerpc(&s));
+	return t;
+}
--- a/sys/src/cmd/exportfs/mkfile
+++ b/sys/src/cmd/exportfs/mkfile
@@ -1,10 +1,10 @@
 </$objtype/mkfile
 
-TARG=exportfs
+TARG=exportfs oexportfs
 OFILES=\
-	exportfs.$O\
 	exportsrv.$O\
 	pattern.$O\
+	io.$O\
 
 HFILES=exportfs.h\
 
@@ -15,4 +15,4 @@
 	$HFILES\
 	${OFILES:%.$O=%.c}\
 
-</sys/src/cmd/mkone
+</sys/src/cmd/mkmany
--- /dev/null
+++ b/sys/src/cmd/exportfs/oexportfs.c
@@ -1,0 +1,443 @@
+/*
+ * oexportfs - legacy exportfs for cpu and import
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <libsec.h>
+#define Extern
+#include "exportfs.h"
+
+enum {
+	Encnone,
+	Encssl,
+	Enctls,
+};
+
+int	srvfd = -1;
+int	nonone = 1;
+char	*filterp;
+char	*ealgs = "rc4_256 sha1";
+char	*aanfilter = "/bin/aan";
+int	encproto = Encnone;
+int	readonly;
+
+static char *anstring  = "tcp!*!0";
+
+static void
+filter(int fd, char *cmd, char *host)
+{
+	char addr[128], buf[256], *s, *file, *argv[16];
+	int lfd, p[2], len, argc;
+
+	if(host == nil){
+		/* Get a free port and post it to the client. */
+		if (announce(anstring, addr) < 0)
+			fatal("filter: Cannot announce %s: %r", anstring);
+
+		snprint(buf, sizeof(buf), "%s/local", addr);
+		if ((lfd = open(buf, OREAD)) < 0)
+			fatal("filter: Cannot open %s: %r", buf);
+		if ((len = read(lfd, buf, sizeof buf - 1)) < 0)
+			fatal("filter: Cannot read %s: %r", buf);
+		close(lfd);
+		buf[len] = '\0';
+		if ((s = strchr(buf, '\n')) != nil)
+			len = s - buf;
+		if (write(fd, buf, len) != len) 
+			fatal("filter: cannot write port; %r");
+	} else {
+		/* Read address string from connection */
+		if ((len = read(fd, buf, sizeof buf - 1)) < 0)
+			sysfatal("filter: cannot write port; %r");
+		buf[len] = '\0';
+
+		if ((s = strrchr(buf, '!')) == nil)
+			sysfatal("filter: illegally formatted port %s", buf);
+		strecpy(addr, addr+sizeof(addr), netmkaddr(host, "tcp", s+1));
+		strecpy(strrchr(addr, '!'), addr+sizeof(addr), s);
+	}
+
+	DEBUG(DFD, "filter: %s\n", addr);
+
+	snprint(buf, sizeof(buf), "%s", cmd);
+	argc = tokenize(buf, argv, nelem(argv)-3);
+	if (argc == 0)
+		sysfatal("filter: empty command");
+
+	if(host != nil)
+		argv[argc++] = "-c";
+	argv[argc++] = addr;
+	argv[argc] = nil;
+
+	file = argv[0];
+	if((s = strrchr(argv[0], '/')) != nil)
+		argv[0] = s+1;
+
+	if(pipe(p) < 0)
+		sysfatal("pipe: %r");
+
+	switch(rfork(RFNOWAIT|RFPROC|RFMEM|RFFDG|RFREND)) {
+	case -1:
+		fatal("filter: rfork; %r\n");
+	case 0:
+		close(fd);
+		if (dup(p[0], 1) < 0)
+			fatal("filter: Cannot dup to 1; %r");
+		if (dup(p[0], 0) < 0)
+			fatal("filter: Cannot dup to 0; %r");
+		close(p[0]);
+		close(p[1]);
+		exec(file, argv);
+		fatal("filter: exec; %r");
+	default:
+		dup(p[1], fd);
+		close(p[0]);
+		close(p[1]);
+	}
+}
+
+static void
+mksecret(char *t, uchar *f)
+{
+	sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
+		f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-adnsR] [-f dbgfile] [-m msize] [-r root] "
+		"[-S srvfile] [-e 'crypt hash'] [-P exclusion-file] "
+		"[-A announce-string] [-B address]\n", argv0);
+	fatal("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	char buf[ERRMAX], ebuf[ERRMAX], initial[4], *ini, *srvfdfile;
+	char *dbfile, *srv, *na, *nsfile, *keyspec;
+	int doauth, n, fd;
+	AuthInfo *ai;
+	Fsrpc *r;
+
+	dbfile = "/tmp/exportdb";
+	srv = nil;
+	srvfd = -1;
+	srvfdfile = nil;
+	na = nil;
+	nsfile = nil;
+	keyspec = "";
+	doauth = 0;
+
+	ai = nil;
+	ARGBEGIN{
+	case 'a':
+		doauth = 1;
+		break;
+
+	case 'd':
+		dbg++;
+		break;
+
+	case 'e':
+		ealgs = EARGF(usage());
+		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
+			ealgs = nil;
+		break;
+
+	case 'f':
+		dbfile = EARGF(usage());
+		break;
+
+	case 'k':
+		keyspec = EARGF(usage());
+		break;
+
+	case 'm':
+		messagesize = strtoul(EARGF(usage()), nil, 0);
+		break;
+
+	case 'n':
+		nonone = 0;
+		break;
+
+	case 'r':
+		srv = EARGF(usage());
+		break;
+
+	case 's':
+		srv = "/";
+		break;
+
+	case 'A':
+		anstring = EARGF(usage());
+		break;
+
+	case 'B':
+		na = EARGF(usage());
+		break;
+
+	case 'F':
+		/* accepted but ignored, for backwards compatibility */
+		break;
+
+	case 'N':
+		nsfile = EARGF(usage());
+		break;
+
+	case 'P':
+		patternfile = EARGF(usage());
+		break;
+
+	case 'R':
+		readonly = 1;
+		break;
+
+	case 'S':
+		if(srvfdfile != nil)
+			usage();
+		srvfdfile = EARGF(usage());
+		break;
+
+	default:
+		usage();
+	}ARGEND
+	USED(argc, argv);
+
+	if(na == nil && doauth){
+		/*
+		 * We use p9any so we don't have to visit this code again, with the
+		 * cost that this code is incompatible with the old world, which
+		 * requires p9sk2. (The two differ in who talks first, so compatibility
+		 * is awkward.)
+		 */
+		ai = auth_proxy(0, auth_getkey, "proto=p9any role=server %s", keyspec);
+		if(ai == nil)
+			fatal("auth_proxy: %r");
+		if(nonone && strcmp(ai->cuid, "none") == 0)
+			fatal("exportfs by none disallowed");
+		if(auth_chuid(ai, nsfile) < 0)
+			fatal("auth_chuid: %r");
+		else {	/* chown network connection */
+			Dir nd;
+			nulldir(&nd);
+			nd.mode = 0660;
+			nd.uid = ai->cuid;
+			dirfwstat(0, &nd);
+		}
+		putenv("service", "exportfs");
+	}
+
+	if(srvfdfile != nil){
+		if((srvfd = open(srvfdfile, ORDWR)) < 0)
+			fatal("open %s: %r", srvfdfile);
+	}
+
+	if(na != nil){
+		if(srv == nil)
+			fatal("-B requires -s");
+
+		if((fd = dial(netmkaddr(na, 0, "importfs"), 0, 0, 0)) < 0)
+			fatal("can't dial %s: %r", na);
+	
+		ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client %s", keyspec);
+		if(ai == nil)
+			fatal("%r: %s", na);
+
+		dup(fd, 0);
+		dup(fd, 1);
+		close(fd);
+	}
+
+	exclusions();
+
+	if(dbg) {
+		n = create(dbfile, OWRITE|OTRUNC, 0666);
+		dup(n, DFD);
+		close(n);
+	}
+
+	if(srvfd >= 0 && srv != nil){
+		fprint(2, "%s: -S cannot be used with -r or -s\n", argv0);
+		usage();
+	}
+
+	DEBUG(DFD, "%s: started\n", argv0);
+
+	rfork(RFNOTEG|RFREND);
+
+	if(messagesize == 0){
+		messagesize = iounit(0);
+		if(messagesize == 0)
+			messagesize = 8192+IOHDRSZ;
+	}
+	fhash = emallocz(sizeof(Fid*)*FHASHSIZE);
+
+	fmtinstall('F', fcallfmt);
+
+	/*
+	 * Get tree to serve from network connection,
+	 * check we can get there and ack the connection
+ 	 */
+	if(srvfd != -1) {
+		/* do nothing */
+	}
+	else if(srv != nil) {
+		if(chdir(srv) < 0) {
+			ebuf[0] = '\0';
+			errstr(ebuf, sizeof ebuf);
+			DEBUG(DFD, "chdir(\"%s\"): %s\n", srv, ebuf);
+			mounterror(ebuf);
+		}
+		DEBUG(DFD, "invoked as server for %s", srv);
+		strncpy(buf, srv, sizeof buf);
+	}
+	else {
+		buf[0] = 0;
+		n = read(0, buf, sizeof(buf)-1);
+		if(n < 0) {
+			errstr(buf, sizeof buf);
+			fprint(0, "read(0): %s\n", buf);
+			DEBUG(DFD, "read(0): %s\n", buf);
+			exits(buf);
+		}
+		buf[n] = 0;
+		if(chdir(buf) < 0) {
+			errstr(ebuf, sizeof ebuf);
+			fprint(0, "chdir(%d:\"%s\"): %s\n", n, buf, ebuf);
+			DEBUG(DFD, "chdir(%d:\"%s\"): %s\n", n, buf, ebuf);
+			exits(ebuf);
+		}
+	}
+
+	DEBUG(DFD, "\niniting root\n");
+	initroot();
+
+	DEBUG(DFD, "%s: %s\n", argv0, buf);
+
+	if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2)
+		fatal("open ack write");
+
+	ini = initial;
+	n = readn(0, initial, sizeof(initial));
+	if(n == 0)
+		fatal(nil);	/* port scan or spurious open/close on exported /srv file (unmount) */
+	if(n < sizeof(initial))
+		fatal("can't read initial string: %r");
+
+	if(memcmp(ini, "impo", 4) == 0) {
+		char buf[128], *p, *args[3];
+
+		ini = nil;
+		p = buf;
+		for(;;){
+			if((n = read(0, p, 1)) < 0)
+				fatal("can't read impo arguments: %r");
+			if(n == 0)
+				fatal("connection closed while reading arguments");
+			if(*p == '\n') 
+				*p = '\0';
+			if(*p++ == '\0')
+				break;
+			if(p >= buf + sizeof(buf))
+				fatal("import parameters too long");
+		}
+		
+		if(tokenize(buf, args, nelem(args)) != 2)
+			fatal("impo arguments invalid: impo%s...", buf);
+
+		if(strcmp(args[0], "aan") == 0)
+			filterp = aanfilter;
+		else if(strcmp(args[0], "nofilter") != 0)
+			fatal("import filter argument unsupported: %s", args[0]);
+
+		if(strcmp(args[1], "ssl") == 0)
+			encproto = Encssl;
+		else if(strcmp(args[1], "tls") == 0)
+			encproto = Enctls;
+		else if(strcmp(args[1], "clear") != 0)
+			fatal("import encryption proto unsupported: %s", args[1]);
+
+		if(encproto == Enctls)
+			fatal("%s: tls has not yet been implemented", argv[0]);
+	}
+
+	if(encproto != Encnone && ealgs != nil && ai != nil) {
+		uchar key[16], digest[SHA1dlen];
+		char fromclientsecret[21];
+		char fromserversecret[21];
+		int i;
+
+		if(ai->nsecret < 8)
+			fatal("secret too small for ssl");
+		memmove(key+4, ai->secret, 8);
+
+		/* exchange random numbers */
+		srand(truerand());
+		for(i = 0; i < 4; i++)
+			key[i+12] = rand();
+
+		if(ini != nil) 
+			fatal("Protocol botch: old import");
+		if(readn(0, key, 4) != 4)
+			fatal("can't read key part; %r");
+
+		if(write(0, key+12, 4) != 4)
+			fatal("can't write key part; %r");
+
+		/* scramble into two secrets */
+		sha1(key, sizeof(key), digest, nil);
+		mksecret(fromclientsecret, digest);
+		mksecret(fromserversecret, digest+10);
+
+		if(filterp != nil)
+			filter(0, filterp, na);
+
+		switch(encproto) {
+		case Encssl:
+			fd = pushssl(0, ealgs, fromserversecret, fromclientsecret, nil);
+			if(fd < 0)
+				fatal("can't establish ssl connection: %r");
+			if(fd != 0){
+				dup(fd, 0);
+				close(fd);
+			}
+			break;
+		case Enctls:
+		default:
+			fatal("Unsupported encryption protocol");
+		}
+	}
+	else if(filterp != nil) {
+		if(ini != nil)
+			fatal("Protocol botch: don't know how to deal with this");
+		filter(0, filterp, na);
+	}
+	dup(0, 1);
+
+	if(ai != nil)
+		auth_freeAI(ai);
+
+	if(ini != nil){
+		extern void (*fcalls[])(Fsrpc*);
+
+		r = getsbuf();
+		memmove(r->buf, ini, BIT32SZ);
+		n = GBIT32(r->buf);
+		if(n <= BIT32SZ || n > messagesize)
+			fatal("bad length in 9P2000 message header");
+		n -= BIT32SZ;
+		if(readn(0, r->buf+BIT32SZ, n) != n)
+			fatal(nil);
+		n += BIT32SZ;
+
+		if(convM2S(r->buf, n, &r->work) != n)
+			fatal("convM2S format error");
+		DEBUG(DFD, "%F\n", &r->work);
+		(fcalls[r->work.type])(r);
+	}
+	io();
+}
--- a/sys/src/cmd/iostats.c
+++ b/sys/src/cmd/iostats.c
@@ -354,6 +354,7 @@
 		sysfatal("fork: %r");
 	case 0:
 		dup(efd[0], 0);
+		dup(efd[0], 1);
 		close(efd[0]);
 		close(efd[1]);
 		close(pfd[1]);
--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -637,7 +637,7 @@
 			s->h = v;
 			break;
 
-		case 0x0D0051:	/* Conteact identifier */
+		case 0x0D0051:	/* Contact identifier */
 			s->id = v;
 			break;
 
@@ -684,7 +684,7 @@
 {
 	char	err[ERRMAX], mbuf[80];
 	uchar	lastk[64], uk, dk;
-	int	i, c, nerrs, lastb, nlastk;
+	int	i, c, nerrs, bpress, lastb, nlastk;
 	int	abs, x, y, z, b;
 	Hidreport p;
 	Hidslot lasts[nelem(p.s)], *s, *l;
@@ -774,7 +774,7 @@
 			continue;
 
 		/* combine all the slots */
-		abs = x = y = z = b = 0;
+		bpress = abs = x = y = z = b = 0;
 		for(i=0; i<p.ns; *l = *s, i++){
 			s = &p.s[i];
 
@@ -785,7 +785,7 @@
 			if(l == &lasts[nelem(lasts)-1] || !l->valid)
 				*l = *s;
 
-			/* convet absolute z to relative */
+			/* convert absolute z to relative */
 			z += s->z;
 			if(s->abs & 4)
 				z -= l->z;
@@ -808,6 +808,7 @@
 				b |= 2;
 			if(s->b & 2)
 				b |= 4;
+			bpress |= s->m;
 
 			/* X/Y are absolute? */
 			if((s->abs & 3) == 3){
@@ -825,7 +826,9 @@
 				y += s->y;
 			}
 		}
-	
+
+		if(bpress == 0)
+			b = lastb & 7;
 		if(z != 0)
 			b |= z > 0 ? 8 : 16;
 
--- a/sys/src/cmd/ptrap.c
+++ b/sys/src/cmd/ptrap.c
@@ -8,6 +8,7 @@
 
 typedef struct IOProc IOProc;
 typedef struct PFilter PFilter;
+typedef struct FAttr FAttr;
 typedef struct PFid PFid;
 
 struct IOProc {
@@ -27,11 +28,19 @@
 };
 Qid rootqid = {.type QTDIR};
 
+struct FAttr {
+	char *name;
+	Reprog *filt;
+	int invert;
+	FAttr *next;
+};
+
 struct PFilter {
 	char *name;
 	Reprog *filt;
-	PFilter *next;
 	int invert;
+	FAttr *attr;
+	PFilter *next;
 };
 PFilter *filters;
 
@@ -162,12 +171,32 @@
 }
 
 static int
+filter(PFilter *f, Plumbmsg *pm)
+{
+	FAttr *a;
+	char *value;
+
+	if(!(regexec(f->filt, pm->data, nil, 0) ^ f->invert))
+		return 0;
+	for(a = f->attr; a; a = a->next){
+		value = plumblookup(pm->attr, a->name);
+		if(value == nil)
+			return 0;
+		if(!(regexec(a->filt, value, nil, 0) ^ f->attr->invert))
+			return 0;
+	}
+	return 1;
+}
+
+static int
 filterread(Req *r, PFid *pf)
 {
 	int rc, len, more;
 	char *buf;
 	Plumbmsg *pm;
+	PFilter *f;
 	
+	f = pf->filter;
 	for(;;){
 		if(pf->msg != nil){
 			rc = r->ifcall.count;
@@ -194,7 +223,7 @@
 			len += rc;
 		}
 		free(buf);
-		if(regexec(pf->filter->filt, pm->data, nil, 0) ^ pf->filter->invert){
+		if(filter(f, pm)){
 			pf->msg = plumbpack(pm, &pf->msgn);
 			pf->msgp = 0;
 		}
@@ -341,7 +370,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s port regex [ port regex ... ]\n", argv0);
+	fprint(2, "usage: %s port regex [ +attr regex ... ] ...\n", argv0);
 	exits("usage");
 }
 
@@ -349,6 +378,7 @@
 threadmain(int argc, char **argv)
 {
 	PFilter *f;
+	FAttr *fa;
 	char *p;
 	int i;
 
@@ -357,19 +387,33 @@
 	}ARGEND;
 
 	if(argc == 0 || argc % 2) usage();
-	for(i = 0; i < argc; i += 2){
+	for(i = 0; i+1 < argc;){
+		p = argv[i];
 		f = emalloc9p(sizeof(PFilter));
-		f->name = strdup(argv[i]);
+		f->name = estrdup9p(p);
 		p = argv[i+1];
-		if(*p == '!'){
+		if(p[0] == '!'){
 			p++;
 			f->invert = 1;
 		}
-		f->filt = regcomp(p);
-		if(f->filt == nil)
-			sysfatal("%r");
+		if((f->filt = regcomp(p)) == nil)
+			sysfatal("regcomp: %r");
 		f->next = filters;
 		filters = f;
+		for(i += 2; p = argv[i], i+1 < argc && p[0] == '+'; i += 2){
+			p++;
+			fa = emalloc9p(sizeof(FAttr));
+			fa->name = estrdup9p(p);
+			p = argv[i+1];
+			if(p[0] == '!'){
+				p++;
+				fa->invert = 1;
+			}
+			if((fa->filt = regcomp(p)) == nil)
+				sysfatal("regcomp: %r");
+			fa->next = f->attr;
+			f->attr = fa;
+		}
 	}
 	threadpostmountsrv(&ptrapsrv, nil, "/mnt/plumb", MREPL | MCREATE);
 
--- a/sys/src/cmd/rio/fsys.c
+++ b/sys/src/cmd/rio/fsys.c
@@ -118,10 +118,9 @@
 Filsys*
 filsysinit(Channel *cxfidalloc)
 {
-	int n, fd, pid, p0;
+	int p0;
 	Filsys *fs;
 	Channel *c;
-	char buf[128];
 
 	fs = emalloc(sizeof(Filsys));
 	if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
@@ -128,20 +127,11 @@
 		goto Rescue;
 	fmtinstall('F', fcallfmt);
 	clockfd = open("/dev/time", OREAD|OCEXEC);
-	fd = open("/dev/user", OREAD);
-	strcpy(buf, "Jean-Paul_Belmondo");
-	if(fd >= 0){
-		n = read(fd, buf, sizeof buf-1);
-		if(n > 0)
-			buf[n] = 0;
-		close(fd);
-	}
-	fs->user = estrdup(buf);
+	fs->user = getuser();
 	fs->csyncflush = chancreate(sizeof(int), 0);
 	if(fs->csyncflush == nil)
 		error("chancreate syncflush");
 	fs->cxfidalloc = cxfidalloc;
-	pid = getpid();
 
 	/*
 	 * Create and post wctl pipe
@@ -148,7 +138,7 @@
 	 */
 	if(cexecpipe(&p0, &wctlfd) < 0)
 		goto Rescue;
-	snprint(srvwctl, sizeof(srvwctl), "/srv/riowctl.%s.%d", fs->user, pid);
+	snprint(srvwctl, sizeof(srvwctl), "/srv/riowctl.%s.%lud", fs->user, (ulong)getpid());
 	post(srvwctl, "wctl", p0);
 	close(p0);
 
@@ -165,7 +155,7 @@
 	/*
 	 * Post srv pipe
 	 */
-	snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%d", fs->user, pid);
+	snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%lud", fs->user, (ulong)getpid());
 	post(srvpipe, "wsys", fs->cfd);
 
 	return fs;
@@ -234,7 +224,7 @@
 	char buf[32];
 
 	close(fs->sfd);	/* close server end so mount won't hang if exiting */
-	sprint(buf, "%d", id);
+	snprint(buf, sizeof buf, "%d", id);
 	if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) == -1){
 		fprint(2, "mount failed: %r\n");
 		return -1;
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -244,7 +244,7 @@
 
 	if(snarffd<0 || nsnarf==0)
 		return;
-	fd = open("/dev/snarf", OWRITE);
+	fd = open("/dev/snarf", OWRITE|OCEXEC);
 	if(fd < 0)
 		return;
 	/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
@@ -394,12 +394,13 @@
 {
 	x -= lo;
 	hi -= lo;
-	if(hi < 20)
-		return x > 0 ? 2 : 0;
-	if(x < 20)
-		return 0;
-	if(x > hi-20)
-		return 2;
+	if(x < hi/2){
+		if(x < 20)
+			return 0;
+	} else {
+		if(x > hi-20)
+			return 2;
+	}
 	return 1;
 }
 
@@ -857,7 +858,7 @@
 }
 
 void
-drawedge(Image **bp, Rectangle r)
+drawedge(Image **bp, Image *col, Rectangle r)
 {
 	Image *b = *bp;
 	if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
@@ -865,28 +866,30 @@
 	else{
 		freeimage(b);
 		b = allocwindow(wscreen, r, Refbackup, DNofill);
-		if(b != nil) draw(b, r, sizecol, nil, ZP);
+		if(b != nil) draw(b, r, col, nil, ZP);
 		*bp = b;
 	}
 }
 
 void
-drawborder(Rectangle r, int show)
+drawborder(Rectangle r, Image *col)
 {
-	static Image *b[4];
-	int i;
-	if(show == 0){
-		for(i = 0; i < 4; i++){
-			freeimage(b[i]);
-			b[i] = nil;
-		}
-	}else{
+	static Image *b[4], *lastcol;
+
+	if(col != lastcol){
+		freeimage(b[0]), b[0] = nil;
+		freeimage(b[1]), b[1] = nil;
+		freeimage(b[2]), b[2] = nil;
+		freeimage(b[3]), b[3] = nil;
+	}
+	if(col != nil){
 		r = canonrect(r);
-		drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
-		drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
-		drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
-		drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
+		drawedge(&b[0], col, Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
+		drawedge(&b[1], col, Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
+		drawedge(&b[2], col, Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
+		drawedge(&b[3], col, Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
 	}
+	lastcol = col;
 }
 
 Image*
@@ -901,17 +904,17 @@
 	dm = subpt(om, w->screenr.min);
 	d = subpt(w->screenr.max, w->screenr.min);
 	op = subpt(om, dm);
-	drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
+	drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), sizecol);
 	while(mouse->buttons==4){
 		p = subpt(mouse->xy, dm);
 		if(!eqpt(p, op)){
-			drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
+			drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), sizecol);
 			op = p;
 		}
 		readmouse(mousectl);
 	}
 	r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
-	drawborder(r, 0);
+	drawborder(r, nil);
 	p = mouse->xy;
 	riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil);
 	menuing = FALSE;
@@ -935,7 +938,7 @@
 	or = w->screenr;
 	but = mouse->buttons;
 	startp = onscreen(mouse->xy);
-	drawborder(or, 1);
+	drawborder(or, sizecol);
 	while(mouse->buttons == but) {
 		p = onscreen(mouse->xy);
 		which = whichcorner(or, p);
@@ -945,12 +948,14 @@
 		}
 		r = whichrect(or, p, owhich);
 		if(!eqrect(r, or) && goodrect(r)){
+			drawborder(r, sizecol);
 			or = r;
-			drawborder(r, 1);
 		}
 		readmouse(mousectl);
 	}
-	drawborder(or, 0);
+	drawborder(or, nil);
+	if(!goodrect(or))
+		riosetcursor(nil);
 	if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr)
 	|| abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){
 		flushimage(display, 1);
--- a/sys/src/cmd/rio/wctl.c
+++ b/sys/src/cmd/rio/wctl.c
@@ -88,7 +88,7 @@
 int
 goodrect(Rectangle r)
 {
-	if(!eqrect(canonrect(r), r))
+	if(badrect(r) || !eqrect(canonrect(r), r))
 		return 0;
 	/* reasonable sizes only please */
 	if(Dx(r) > BIG*Dx(screen->r))
@@ -95,7 +95,12 @@
 		return 0;
 	if(Dy(r) > BIG*Dy(screen->r))
 		return 0;
-	if(Dx(r) < 100 || Dy(r) < 3*font->height)
+	/*
+	 * the height has to be big enough to fit one line of text.
+	 * that includes the border on each side with an extra pixel
+	 * so that the text is still drawn
+	 */
+	if(Dx(r) < 100 || Dy(r) < 2*(Borderwidth+1)+font->height)
 		return 0;
 	/* window must be on screen */
 	if(!rectXrect(screen->r, r))
--- a/sys/src/cmd/rio/wind.c
+++ b/sys/src/cmd/rio/wind.c
@@ -1420,7 +1420,7 @@
 void
 wsetpid(Window *w, int pid, int dolabel)
 {
-	char buf[64];
+	char buf[32];
 	int ofd;
 
 	ofd = w->notefd;
@@ -1428,11 +1428,11 @@
 		w->notefd = -1;
 	else {
 		if(dolabel){
-			snprint(buf, sizeof(buf), "rc %d", pid);
+			snprint(buf, sizeof(buf), "rc %lud", (ulong)pid);
 			free(w->label);
 			w->label = estrdup(buf);
 		}
-		snprint(buf, sizeof(buf), "/proc/%d/notepg", pid);
+		snprint(buf, sizeof(buf), "/proc/%lud/notepg", (ulong)pid);
 		w->notefd = open(buf, OWRITE|OCEXEC);
 	}
 	if(ofd >= 0)
--- a/sys/src/cmd/srvfs.c
+++ b/sys/src/cmd/srvfs.c
@@ -12,7 +12,7 @@
 main(int argc, char **argv)
 {
 	char *ename, *arglist[16], **argp;
-	int n, fd, pipefd[2];
+	int fd, pipefd[2];
 	char buf[64];
 	int perm = 0600;
 
@@ -39,14 +39,27 @@
 		*argp++ = "-R";
 		 break;
 	}ARGEND
-	*argp = 0;
 	if(argc != 2)
 		usage();
+	*argp++ = "-r";
+	*argp++ = argv[1];
+	*argp = 0;
 
 	if(pipe(pipefd) < 0){
 		fprint(2, "can't pipe: %r\n");
 		exits("pipe");
 	}
+	if(argv[0][0] == '/')
+		strecpy(buf, buf+sizeof buf, argv[0]);
+	else
+		snprint(buf, sizeof buf, "/srv/%s", argv[0]);
+	fd = create(buf, OWRITE|ORCLOSE, perm);
+	if(fd < 0){
+		fprint(2, "can't create %s: %r\n", buf);
+		exits("create");
+	}
+	fprint(fd, "%d", pipefd[1]);
+	close(pipefd[1]);
 
 	switch(rfork(RFPROC|RFNOWAIT|RFNOTEG|RFFDG)){
 	case -1:
@@ -56,7 +69,6 @@
 		dup(pipefd[0], 0);
 		dup(pipefd[0], 1);
 		close(pipefd[0]);
-		close(pipefd[1]);
 		exec(ename, arglist);
 		fprint(2, "can't exec exportfs: %r\n");
 		exits("exec");
@@ -63,32 +75,5 @@
 	default:
 		break;
 	}
-	close(pipefd[0]);
-	if(fprint(pipefd[1], "%s", argv[1]) < 0){
-		fprint(2, "can't write pipe: %r\n");
-		exits("write");
-	}
-	n = read(pipefd[1], buf, sizeof buf-1);
-	if(n < 0){
-		fprint(2, "can't read pipe: %r\n");
-		exits("read");
-	}
-	buf[n] = 0;
-	if(n != 2 || strcmp(buf, "OK") != 0){
-		fprint(2, "not OK (%d): %s\n", n, buf);
-		exits("OK");
-	}
-	if(argv[0][0] == '/')
-		strecpy(buf, buf+sizeof buf, argv[0]);
-	else
-		snprint(buf, sizeof buf, "/srv/%s", argv[0]);
-	fd = create(buf, OWRITE, perm);
-	if(fd < 0){
-		fprint(2, "can't create %s: %r\n", buf);
-		exits("create");
-	}
-	fprint(fd, "%d", pipefd[1]);
-	close(fd);
-	close(pipefd[1]);
 	exits(0);
 }
--- a/sys/src/cmd/vmx/dat.h
+++ b/sys/src/cmd/vmx/dat.h
@@ -11,6 +11,7 @@
 	VMDEAD,
 };
 extern int state;
+extern int debug;
 
 enum {
 	BY2PG = 4096
--- a/sys/src/cmd/vmx/exith.c
+++ b/sys/src/cmd/vmx/exith.c
@@ -1,9 +1,8 @@
 #include <u.h>
 #include <libc.h>
-#include <thread.h>
-#include <bio.h>
 #include "dat.h"
 #include "fns.h"
+#include "x86.h"
 
 int persist = 1;
 
@@ -118,109 +117,167 @@
 
 typedef struct CPUID CPUID;
 struct CPUID {
-	u32int idx;
 	u32int ax, bx, cx, dx;
 };
-static CPUID *cpuidf;
-static int ncpuidf;
+static u32int cpuidmax;
+static u32int cpuidmaxext;
+static CPUID leaf1;
+static struct {
+	uvlong miscen;
+}msr;
 
-static void
-auxcpuidproc(void *vpfd)
-{
-	int *pfd;
-	
-	pfd = vpfd;
-	close(pfd[1]);
-	close(0);
-	open("/dev/null", OREAD);
-	dup(pfd[0], 1);
-	close(pfd[0]);
-	procexecl(nil, "/bin/aux/cpuid", "cpuid", "-r", nil);
-	threadexits("exec: %r");
-}
+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;
+
 void
 cpuidinit(void)
 {
-	int pfd[2];
-	Biobuf *bp;
-	char *l, *f[5];
-	CPUID *cp;
-	
-	pipe(pfd);
-	procrfork(auxcpuidproc, pfd, 4096, RFFDG);
-	close(pfd[0]);
-	bp = Bfdopen(pfd[1], OREAD);
-	if(bp == nil) sysfatal("Bopenfd: %r");
-	for(; l = Brdstr(bp, '\n', 1), l != nil; free(l)){
-		if(tokenize(l, f, 5) < 5) continue;
-		cpuidf = realloc(cpuidf, (ncpuidf + 1) * sizeof(CPUID));
-		cp = cpuidf + ncpuidf++;
-		cp->idx = strtoul(f[0], nil, 16);
-		cp->ax = strtoul(f[1], nil, 16);
-		cp->bx = strtoul(f[2], nil, 16);
-		cp->cx = strtoul(f[3], nil, 16);
-		cp->dx = strtoul(f[4], nil, 16);
+	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);
 	}
-	Bterm(bp);
-	close(pfd[1]);
 }
 
-CPUID *
-getcpuid(ulong idx)
-{
-	CPUID *cp;
-	
-	for(cp = cpuidf; cp < cpuidf + ncpuidf; cp++)
-		if(cp->idx == idx)
-			return cp;
-	return nil;
-}
+static int xsavesz[] = {
+	[1] = 512+64,
+	[3] = 512+64,
+	[7] = 512+64+256,
+};
 
-int maxcpuid = 7;
-
 static void
 cpuid(ExitInfo *ei)
 {
 	u32int ax, bx, cx, dx;
-	CPUID *cp;
-	static CPUID def;
-	
+	CPUID cp;
+
 	ax = rget(RAX);
-	cp = getcpuid(ax);
-	if(cp == nil) cp = &def;
+	cx = rget(RCX);
+	bx = dx = 0;
+	cp = getcpuid(ax, cx);
 	switch(ax){
-	case 0: /* highest register & GenuineIntel */
-		ax = maxcpuid;
-		bx = cp->bx;
-		dx = cp->dx;
-		cx = cp->cx;
+	case 0x00: /* highest register & GenuineIntel */
+		ax = MIN(cpuidmax, 0x18);
+		bx = cp.bx;
+		dx = cp.dx;
+		cx = cp.cx;
 		break;
-	case 1: /* features */
-		ax = cp->ax;
-		bx = cp->bx & 0xffff;
-		cx = cp->cx & 0x60de2203;
-		dx = cp->dx & 0x0782a179;
+	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 2: goto literal; /* cache stuff */
-	case 3: goto zero; /* processor serial number */
-	case 4: goto zero; /* cache stuff */
-	case 5: goto zero; /* monitor/mwait */
-	case 6: goto zero; /* thermal management */
-	case 7: goto zero; /* more features */
-	case 10: goto zero; /* performance counters */
+	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 = 0x80000008;
-		bx = cx = dx = 0;
+		ax = MIN(cpuidmaxext, 0x80000008);
+		cx = 0;
 		break;
 	case 0x80000001: /* signature & ext features */
-		ax = cp->ax;
-		bx = 0;
-		cx = cp->cx & 0x121;
+		ax = cp.ax;
+		cx = cp.cx & 0x121;
 		if(sizeof(uintptr) == 8)
-			dx = cp->dx & 0x24100800;
+			dx = cp.dx & 0x24100800;
 		else
-			dx = cp->dx & 0x04100000;
+			dx = cp.dx & 0x04100000;
 		break;
 	case 0x80000002: goto literal; /* brand string */
 	case 0x80000003: goto literal; /* brand string */
@@ -230,18 +287,16 @@
 	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;
+		ax = cp.ax;
+		bx = cp.bx;
+		cx = cp.cx;
+		dx = cp.dx;
 		break;
 	default:
-		vmerror("unknown cpuid field eax=%#ux", ax);
+		if((ax & 0xf0000000) != 0x40000000)
+			vmdebug("unknown cpuid field eax=%#ux", ax);
 	zero:
-		ax = 0;
-		bx = 0;
-		cx = 0;
-		dx = 0;
+		ax = cx = 0;
 		break;
 	}
 	rset(RAX, ax);
@@ -267,12 +322,15 @@
 		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){
-			vmerror("read from unknown MSR %#ux ignored", cx);
+			vmdebug("read from unknown MSR %#ux ignored", cx);
 			val = 0;
 		}else
-			vmerror("write to unknown MSR %#ux ignored (val=%#ullx)", cx, val);
+			vmdebug("write to unknown MSR %#ux ignored (val=%#ullx)", cx, val);
 		break;
 	}
 	if(rd){
@@ -310,7 +368,7 @@
 	case 0:
 		switch(q >> 4 & 3){
 		case 0:
-			vmdebug("illegal CR0 write, value %#ux", rget(x86reg[q >> 8 & 15]));
+			vmdebug("illegal CR0 write, value %#ux", (u32int)rget(x86reg[q >> 8 & 15]));
 			rset("cr0real", rget(x86reg[q >> 8 & 15]));
 			skipinstr(ei);
 			break;
@@ -332,7 +390,7 @@
 	case 4:
 		switch(q >> 4 & 3){
 		case 0:
-			vmdebug("illegal CR4 write, value %#ux", rget(x86reg[q >> 8 & 15]));
+			vmdebug("illegal CR4 write, value %#ux", (u32int)rget(x86reg[q >> 8 & 15]));
 			rset("cr4real", rget(x86reg[q >> 8 & 15]));
 			skipinstr(ei);
 			break;
@@ -347,7 +405,7 @@
 		}
 		break;
 	default:
-		vmerror("access to unknown control register CR%d", ei->qual & 15);
+		vmerror("access to unknown control register CR%ud", q & 15);
 		postexc("#ud", NOERRC);
 	}
 }
@@ -373,6 +431,26 @@
 	irqack(ei->qual);
 }
 
+static void
+xsetbv(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;
@@ -389,6 +467,7 @@
 	{".movdr", movdr},
 	{"#db", dbgexc},
 	{"movcr", movcr},
+	{".xsetbv", xsetbv},
 };
 
 void
--- a/sys/src/cmd/vmx/fns.h
+++ b/sys/src/cmd/vmx/fns.h
@@ -1,3 +1,4 @@
+#define MIN(a,b) ((a)<(b)?(a):(b))
 void *emalloc(ulong);
 void loadkernel(char *);
 uvlong rget(char *);
@@ -10,7 +11,8 @@
 void rtcadvance(void);
 void settimer(vlong targ);
 void vmerror(char *, ...);
-#define vmdebug vmerror
+#pragma varargck argpos vmerror 1
+#define vmdebug if(!debug) {} else vmerror
 int ctl(char *, ...);
 void registermmio(uvlong, uvlong, uvlong (*)(int, uvlong, uvlong));
 void irqline(int, int);
--- a/sys/src/cmd/vmx/ide.c
+++ b/sys/src/cmd/vmx/ide.c
@@ -118,7 +118,7 @@
 	
 	addr = getlba(d);
 	if(addr < 0){
-		vmerror("ide%d: access to invalid sector address (access to CHS=(%#.4ux,%#ux,%#.2ux); geometry is (%#.4ux,%#ux,%#.2ux)", d-ide, d->cyl, d->head&0xf, d->sec, d->lcyl, d->lhead, d->lsec);
+		vmerror("ide%zd: access to invalid sector address (access to CHS=(%#.4ux,%#ux,%#.2ux); geometry is (%#.4ux,%#ux,%#.2ux)", d-ide, d->cyl, d->head&0xf, d->sec, d->lcyl, d->lhead, d->lsec);
 		postexc("#bp", NOERRC);
 		d->stat = IDEDRDY | IDEDSC | IDEDRQ | IDEERR;
 		d->err = IDEIDNF;
@@ -325,7 +325,7 @@
 				qunlock(io);
 				werrstr("eof");
 				if(getsector(a+i, p) < 0 && pread(d->fd, p, 512, (a+i)*512) < 512){
-					vmerror("ide%d: read: %r", d - ide);
+					vmerror("ide%zd: read: %r", d - ide);
 					qlock(io);
 					io->err = IDEUNC;
 					qunlock(io);
@@ -355,7 +355,7 @@
 		break;
 	default:
 		if((d->flags & IDEPRESENT) == 0){
-			vmerror("ide%d: command %#ux issued to absent drive", d-ide, cmd);
+			vmerror("ide%zd: command %#ux issued to absent drive", d-ide, cmd);
 			return;
 		}
 	}
@@ -435,7 +435,7 @@
 		case 0x66: d->flags |= IDEKEEPFEAT; break; /* retain settings */
 		case 0xcc: d->flags &= ~IDEKEEPFEAT; break; /* revert to default on reset */
 		default:
-			vmerror("ide%d: unknown feature %#ux", d-ide, d->feat);
+			vmerror("ide%zd: unknown feature %#ux", d-ide, d->feat);
 			d->stat = IDEDRDY|IDEDSC|IDEERR;
 			d->err = IDEABRT;
 			return;
@@ -443,7 +443,7 @@
 		d->stat = IDEDRDY|IDEDSC;
 		break;
 	default:
-		vmerror("ide%d: unknown command %#ux", d-ide, cmd);
+		vmerror("ide%zd: unknown command %#ux", d-ide, cmd);
 		d->stat = IDEDRDY|IDEDSC|IDEERR;
 		d->err = IDEABRT;
 	}
--- a/sys/src/cmd/vmx/io.c
+++ b/sys/src/cmd/vmx/io.c
@@ -292,7 +292,7 @@
 			p->imr = 0;
 			p->prio = 7;
 			p->flags = 0;
-			if((val & 0x0b) != 0x01) vmerror("PIC%ld ICW1 with unsupported value %#ux", p-pic, val);
+			if((val & 0x0b) != 0x01) vmerror("PIC%zd ICW1 with unsupported value %#ux", p-pic, (u32int)val);
 			p->init = 1;
 			return 0;
 		}
@@ -347,7 +347,7 @@
 	case 0xa1:
 		switch(p->init){
 		default:
-			vmerror("write to PIC%ld in init=%d state", p-pic, p->init);
+			vmerror("write to PIC%zd in init=%d state", p-pic, p->init);
 			return 0;
 		case 1:
 			p->base = val;
@@ -355,11 +355,11 @@
 			return 0;
 		case 2:
 			if(p == &pic[0] && val != 4 || p == &pic[1] && val != 2)
-				vmerror("PIC%ld ICW3 with unsupported value %#ux", p-pic, val);
+				vmerror("PIC%zd ICW3 with unsupported value %#ux", p-pic, val);
 			p->init = 3;
 			return 0;
 		case 3:
-			if((val & 0xfd) != 1) vmerror("PIC%ld ICW4 with unsupported value %#ux", p-pic, val);
+			if((val & 0xfd) != 1) vmerror("PIC%zd ICW4 with unsupported value %#ux", p-pic, val);
 			if((val & 2) != 0) p->flags |= AEOI;
 			p->init = 4;
 			picupdate(p);
@@ -726,7 +726,7 @@
 		case 0xf2: keyputc(0xfa); keyputc(0xab); keyputc(0x41); break; /* keyboard id */
 		case 0xee: keyputc(0xee); break; /* echo */
 		default:
-			vmerror("unknown kbd command %#ux", val);
+			vmdebug("unknown kbd command %#ux", val);
 			keyputc(0xfe);
 		}
 	}
@@ -1203,9 +1203,9 @@
 iowhine(int isin, u16int port, u32int val, int sz, void *mod)
 {
 	if(isin)
-		vmerror("%s%sread from unknown i/o port %#ux ignored (sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, sz, rget(RPC));
+		vmdebug("%s%sread from unknown i/o port %#ux ignored (sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, sz, rget(RPC));
 	else
-		vmerror("%s%swrite to unknown i/o port %#ux ignored (val=%#ux, sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, val, sz, rget(RPC));
+		vmdebug("%s%swrite to unknown i/o port %#ux ignored (val=%#ux, sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, val, sz, rget(RPC));
 	return -1;
 }
 
--- a/sys/src/cmd/vmx/mkfile
+++ b/sys/src/cmd/vmx/mkfile
@@ -2,6 +2,7 @@
 
 BIN=/$objtype/bin
 TARG=vmx
+CLEANFILES=$O.vmxgdb
 HFILES=dat.h fns.h
 OFILES=\
 	vmx.$O \
@@ -18,6 +19,10 @@
 	nanosec.$O \
 
 </sys/src/cmd/mkone
+
+default:V:	all
+
+all:V:	$O.out $O.vmxgdb
 
 install:V:	$BIN/vmxgdb
 
--- a/sys/src/cmd/vmx/nanosec.c
+++ b/sys/src/cmd/vmx/nanosec.c
@@ -17,14 +17,15 @@
 		return nsec() - xstart;
 
 	if(fasthz == 0){
-		if((fasthz = _tos->cyclefreq) == 0){
-			fasthz = ~0ULL;
+		if(_tos->cyclefreq){
+			cycles(&xstart);
+			fasthz = _tos->cyclefreq;
+		} else {
 			xstart = nsec();
+			fasthz = ~0ULL;
 			fprint(2, "cyclefreq not available, falling back to nsec()\n");
 			fprint(2, "you might want to disable aux/timesync\n");
 			return 0;
-		}else{
-			cycles(&xstart);
 		}
 	}
 	cycles(&x);
--- a/sys/src/cmd/vmx/vesa.c
+++ b/sys/src/cmd/vmx/vesa.c
@@ -622,7 +622,8 @@
 			if(vesaddc(&ur) < 0 || vesasetregs(sp, &ur) < 0) continue;
 			break;
 		default:
-			vmerror("vesa: unsupported function %#x", ur.ax);
+			vesasetax(sp, 0x0100);
+			vmdebug("vesa: unsupported function %#x", ur.ax);
 		}
 	}
 }
--- a/sys/src/cmd/vmx/vga.c
+++ b/sys/src/cmd/vmx/vga.c
@@ -178,7 +178,7 @@
 		switch(vga.sidx){
 		case 0: vga.seq[vga.sidx] = val & 3; return 0;
 		case 4: vga.seq[vga.sidx] = val & 0xe; return 0;
-		default: vmerror("vga: write to unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
+		default: vmdebug("vga: write to unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
 		}
 	case 0x3c6: return 0;
 	case 0x3c7: vga.rdidx = val << 2; return 0;
@@ -194,7 +194,7 @@
 		case 4: vga.graph[vga.gidx] = val & 3; break;
 		case 8: vga.graph[vga.gidx] = val; break;
 		default:
-			vmerror("vga: write to unknown graphics register %#ux (val=%#ux)", vga.gidx, val);
+			vmdebug("vga: write to unknown graphics register %#ux (val=%#ux)", vga.gidx, val);
 		}
 		return 0;
 	case 0x3d4: vga.cidx = val; return 0;
@@ -204,7 +204,7 @@
 			vga.crtc[vga.cidx] = val;
 			return 0;
 		default:
-			vmerror("vga: write to unknown CRTC register %#ux (val=%#ux)", vga.cidx, val);
+			vmdebug("vga: write to unknown CRTC register %#ux (val=%#ux)", vga.cidx, val);
 		}
 		return 0;
 	case 0x103c0: return vga.aidx & 0x3f;
@@ -215,7 +215,7 @@
 		case 0:
 		case 4:
 			return vga.seq[vga.sidx];
-		default: vmerror("vga: read from unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
+		default: vmdebug("vga: read from unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
 		}
 	case 0x103c6: return 0xff;
 	case 0x103c7: return vga.rdidx >> 2;
@@ -232,7 +232,7 @@
 		case 8:
 			return vga.graph[vga.gidx];
 		default:
-			vmerror("vga: read from unknown graphics register %#ux", vga.gidx);
+			vmdebug("vga: read from unknown graphics register %#ux", vga.gidx);
 			return 0;
 		}
 	case 0x103d4: return vga.cidx;
@@ -241,7 +241,7 @@
 		case 10: case 11: case 12: case 13: case 14: case 15:
 			return vga.crtc[vga.cidx];
 		default:
-			vmerror("vga: read from unknown CRTC register %#ux", vga.cidx);
+			vmdebug("vga: read from unknown CRTC register %#ux", vga.cidx);
 			return 0;
 		}
 	case 0x103ca:
@@ -374,7 +374,7 @@
 					nkdown[k->code >> 6] |= 1ULL<<(k->code&63);
 					break;
 				}
-			if(k == nil) vmerror("unknown key %d", r);
+			if(k == nil) vmdebug("unknown key %d", r);
 		}
 		if(mousegrab && (nkdown[0]>>29 & 1) != 0 && (nkdown[0]>>56 & 1) != 0){
 			mousegrab = 0;
@@ -737,7 +737,7 @@
 
 
 void
-vgainit(void)
+vgainit(int new)
 {
 	char buf[512];
 	int i;
@@ -760,7 +760,7 @@
 			sysfatal("got nil ptr for framebuffer");
 	}
 	snprint(buf, sizeof(buf), "-dx %d -dy %d", maxw+50, maxh+50);
-	if(newwindow(buf) < 0 || initdraw(nil, nil, "vmx") < 0)
+	if((new && newwindow(buf) < 0) || initdraw(nil, nil, "vmx") < 0)
 		sysfatal("failed to initialize graphics: %r");
 	screeninit(1);
 	flushimage(display, 1);
--- a/sys/src/cmd/vmx/vmx.c
+++ b/sys/src/cmd/vmx/vmx.c
@@ -11,7 +11,7 @@
 int ctlfd, regsfd, mapfd, waitfd;
 Channel *waitch, *sleepch, *notifch;
 enum { MSEC = 1000*1000, MinSleep = MSEC, SleeperPoll = 2000*MSEC } ;
-int getexit, state;
+int getexit, state, debug;
 typedef struct VmxNotif VmxNotif;
 struct VmxNotif {
 	void (*f)(void *);
@@ -320,9 +320,6 @@
 	return (u8int *) v + gavail(v);
 }
 
-void *tmp, *vgamem;
-uvlong tmpoff, vgamemoff;
-
 static void
 mksegment(char *sn)
 {
@@ -355,8 +352,9 @@
 		close(fd);
 		gmem = segattach(0, sn, nil, sz);
 		if(gmem == (void*)-1) sysfatal("segattach: %r");
+	}else{
+		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;
@@ -365,14 +363,12 @@
 		p += r->end - r->start;
 		r->ve = p;
 	}
-	vgamem = p;
-	vgamemoff = p - gmem;
-	regptr(0xa0000)->segoff = vgamemoff;
-	regptr(0xa0000)->v = vgamem;
+	/* vga */
+	r = regptr(0xa0000);
+	r->segoff = p - gmem;
+	r->v = p;
 	p += 256*1024;
-	regptr(0xa0000)->ve = p;
-	tmp = p;
-	tmpoff = p - gmem;
+	r->ve = p;
 
 	for(r = mmap; r != nil; r = r->next)
 		modregion(r);
@@ -504,7 +500,7 @@
 		send(notifch, &notif);
 }
 
-extern void vgainit(void);
+extern void vgainit(int);
 extern void pciinit(void);
 extern void pcibusmap(void);
 extern void cpuidinit(void);
@@ -574,7 +570,7 @@
 	for(p = blanks; *p != 0; p++)
 		*p = ' ';
 	fprint(2, "usage: %s [ -M mem ] [ -c com1rd[,com1wr] ] [ -C com2rd[,com2r] ] [ -n nic ]\n", argv0);
-	fprint(2, "       %s [ -d blockfile ] [ -m module ] [ -v vga ] [ -9 srv ] kernel [ args ... ]\n", blanks);
+	fprint(2, "       %s [ -d blockfile ] [ -m module ] [ -v|-w vga ] [ -9 srv ] kernel [ args ... ]\n", blanks);
 	threadexitsall("usage");
 }
 
@@ -590,6 +586,7 @@
 	static uvlong gmemsz = 64*1024*1024;
 	static char *srvname;
 	extern uintptr fbsz, fbaddr;
+	int newwin = 0;
 	int i;
 
 	quotefmtinstall();
@@ -598,7 +595,7 @@
 	waitch = chancreate(sizeof(char *), 32);
 	sleepch = chancreate(sizeof(ulong), 32);
 	notifch = chancreate(sizeof(VmxNotif), 16);
-	
+
 	ARGBEGIN {
 	case 'm':
 		bootmod = realloc(bootmod, (bootmodn + 1) * sizeof(char *));
@@ -633,10 +630,15 @@
 		}
 		edevn++;
 		break;
+	case 'D':
+		debug++;
+		break;
 	case 'M':
 		gmemsz = siparse(EARGF(usage()));
 		if(gmemsz != (uintptr) gmemsz) sysfatal("too much memory for address space");
 		break;
+	case 'w':
+		newwin = 1;
 	case 'v':
 		vgafbparse(EARGF(usage()));
 		break;
@@ -673,7 +675,7 @@
 	loadkernel(argv[0]);
 	pciinit();
 
-	vgainit();
+	vgainit(newwin);
 	for(i = 0; i < edevn; i++)
 		if(edev[i](edevaux[i]) < 0)
 			sysfatal("%s: %r", edevt[i]);
--- a/sys/src/cmd/vmx/x86.h
+++ b/sys/src/cmd/vmx/x86.h
@@ -22,8 +22,9 @@
 enum {
 	Cr0Pg	= 1<<31,
 	
-	Cr4Pse	= 1<<4,
-	Cr4Pae	= 1<<5,
+	Cr4Pse		= 1<<4,
+	Cr4Pae		= 1<<5,
+	Cr4Osxsave	= 1<<18,
 	
 	EferLme	= 1<<8,
 };
--- a/sys/src/lib9p/auth.c
+++ b/sys/src/lib9p/auth.c
@@ -25,7 +25,7 @@
 	Afid *afid;
 	
 	afid = emalloc9p(sizeof(Afid));
-	afid->afd = open("/mnt/factotum/rpc", ORDWR);
+	afid->afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC);
 	if(afid->afd < 0)
 		goto error;
 
--- a/sys/src/lib9p/listen.c
+++ b/sys/src/lib9p/listen.c
@@ -100,7 +100,7 @@
 
 	snprint(buf, sizeof buf, "%s/remote", ndir);
 	sys = nil;
-	fd = open(buf, OREAD);
+	fd = open(buf, OREAD|OCEXEC);
 	if(fd >= 0){
 		n = read(fd, buf, sizeof(buf)-1);
 		if(n>0){
--- a/sys/src/lib9p/queue.c
+++ b/sys/src/lib9p/queue.c
@@ -16,8 +16,8 @@
 	q = v;
 	rfork(RFNOTEG);
 
-	buf = smprint("/proc/%d/ctl", getpid());
-	fd = open(buf, OWRITE);
+	buf = smprint("/proc/%lud/ctl", (ulong)getpid());
+	fd = open(buf, OWRITE|OCEXEC);
 	free(buf);
 	
 	for(;;){
@@ -40,6 +40,8 @@
 		f(r);
 	}
 
+	if(fd >= 0)
+		close(fd);
 	free(r);
 	free(q);
 	threadexits(nil);
--- a/sys/src/libauth/auth_challenge.c
+++ b/sys/src/libauth/auth_challenge.c
@@ -23,7 +23,7 @@
 		return nil;
 	}
 
-	if((c->afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
+	if((c->afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC)) < 0){
 	Error:
 		auth_freechal(c);
 		free(p);
--- a/sys/src/libauth/auth_chuid.c
+++ b/sys/src/libauth/auth_chuid.c
@@ -16,7 +16,7 @@
 	}
 
 	/* change uid */
-	fd = open("#¤/capuse", OWRITE);
+	fd = open("#¤/capuse", OWRITE|OCEXEC);
 	if(fd < 0){
 		werrstr("opening #¤/capuse: %r");
 		return -1;
@@ -31,8 +31,8 @@
 	/* get a link to factotum as new user */
 	fd = open("/srv/factotum", ORDWR);
 	if(fd >= 0){
-		mount(fd, -1, "/mnt", MREPL, "");
-		close(fd);
+		if(mount(fd, -1, "/mnt", MREPL, "") == -1)
+			close(fd);
 	}
 
 	/* set up new namespace */
--- a/sys/src/libauth/auth_getuserpasswd.c
+++ b/sys/src/libauth/auth_getuserpasswd.c
@@ -32,12 +32,11 @@
 	UserPasswd *up;
 
 	up = nil;
-	rpc = nil;
 	params = nil;
 
-	fd = open("/mnt/factotum/rpc", ORDWR);
+	fd = open("/mnt/factotum/rpc", ORDWR|OCEXEC);
 	if(fd < 0)
-		goto out;
+		return nil;
 	rpc = auth_allocrpc(fd);
 	if(rpc == nil)
 		goto out;
@@ -69,7 +68,7 @@
 
 out:
 	free(params);
-	auth_freerpc(rpc);
 	close(fd);
+	auth_freerpc(rpc);
 	return up;
 }
--- a/sys/src/libauth/auth_proxy.c
+++ b/sys/src/libauth/auth_proxy.c
@@ -200,7 +200,7 @@
 	va_end(arg);
 
 	ai = nil;
-	afd = open("/mnt/factotum/rpc", ORDWR);
+	afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC);
 	if(afd < 0){
 		werrstr("opening /mnt/factotum/rpc: %r");
 		free(p);
--- a/sys/src/libauth/auth_respond.c
+++ b/sys/src/libauth/auth_respond.c
@@ -31,7 +31,7 @@
 	AuthRpc *rpc;
 	Attr *a;
 
-	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
+	if((afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC)) < 0)
 		return -1;
 	
 	if((rpc = auth_allocrpc(afd)) == nil){
--- a/sys/src/libauth/auth_userpasswd.c
+++ b/sys/src/libauth/auth_userpasswd.c
@@ -11,7 +11,7 @@
 	char *s;
 	int afd;
 
-	afd = open("/mnt/factotum/rpc", ORDWR);
+	afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC);
 	if(afd < 0)
 		return nil;
 	ai = nil;
--- a/sys/src/libauth/newns.c
+++ b/sys/src/libauth/newns.c
@@ -41,7 +41,7 @@
 
 	rpc = nil;
 	/* try for factotum now because later is impossible */
-	afd = open("/mnt/factotum/rpc", ORDWR);
+	afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC);
 	if(afd < 0 && newnsdebug)
 		fprint(2, "open /mnt/factotum/rpc: %r\n");
 	if(afd >= 0){
@@ -58,8 +58,8 @@
 		}
 		file = "/lib/namespace";
 	}
-	b = Bopen(file, OREAD);
-	if(b == 0){
+	b = Bopen(file, OREAD|OCEXEC);
+	if(b == nil){
 		werrstr("can't open %s: %r", file);
 		return freecloserpc(rpc);
 	}
@@ -135,6 +135,8 @@
 			auth_freeAI(ai);
 	}
 	ret = mount(fd, afd, mntpt, flags, aname);
+	if(ret == -1)
+		close(fd);
 	if(afd >= 0)
 		close(afd);
 	return ret;
@@ -151,7 +153,7 @@
 
 	cdroot = 0;
 	flags = 0;
-	argv0 = 0;
+	argv0 = nil;
 	if(newnsdebug){
 		for (i = 0; i < argc; i++)
 			fprint(2, "%s ", argv[i]);
@@ -176,7 +178,7 @@
 		flags |= MREPL;
 
 	if(strcmp(argv0, ".") == 0 && argc == 1){
-		b = Bopen(argv[0], OREAD);
+		b = Bopen(argv[0], OREAD|OCEXEC);
 		if(b == nil)
 			return 0;
 		cdroot |= nsfile(fn, b, rpc);
@@ -204,8 +206,9 @@
 		}else if(argc == 3){
 			if(famount(fd, rpc, argv[1], flags, argv[2]) == -1 && newnsdebug)
 				fprint(2, "%s: mount: %s %s %s: %r\n", fn, argv[0], argv[1], argv[2]);
+		} else {
+			close(fd);
 		}
-		close(fd);
 	}else if(strcmp(argv0, "cd") == 0 && argc == 1){
 		if(chdir(argv[0]) == 0 && *argv[0] == '/')
 			cdroot = 1;
@@ -316,7 +319,7 @@
 		strcpy(env, "#e/");
 		strncpy(env+3, p, len);
 		env[3+len] = '\0';
-		fd = open(env, OREAD);
+		fd = open(env, OREAD|OCEXEC);
 		if(fd >= 0){
 			len = read(fd, &buf[n], ANAMELEN - 1);
 			/* some singleton environment variables have trailing NULs */
@@ -345,7 +348,7 @@
 	long s;
 
 	sprint(ename, "#e/%s", name);
-	f = create(ename, OWRITE, 0664);
+	f = create(ename, OWRITE|OCEXEC, 0664);
 	if(f < 0)
 		return -1;
 	s = strlen(val);
--- a/sys/src/libauth/noworld.c
+++ b/sys/src/libauth/noworld.c
@@ -15,7 +15,7 @@
 	char *p;
 	int n;
 
-	b = Bopen("/adm/users", OREAD);
+	b = Bopen("/adm/users", OREAD|OCEXEC);
 	if(b == nil)
 		return 0;
 	while((p = Brdline(b, '\n')) != nil){
--- a/sys/src/libauthsrv/readcons.c
+++ b/sys/src/libauthsrv/readcons.c
@@ -13,13 +13,13 @@
 	s = p = nil;
 	fdout = ctl = -1;
 
-	if((fdin = open("/dev/cons", OREAD)) < 0)
+	if((fdin = open("/dev/cons", OREAD|OCEXEC)) < 0)
 		goto Out;
-	if((fdout = open("/dev/cons", OWRITE)) < 0)
+	if((fdout = open("/dev/cons", OWRITE|OCEXEC)) < 0)
 		goto Out;
 
 	if(raw){
-		if((ctl = open("/dev/consctl", OWRITE)) < 0)
+		if((ctl = open("/dev/consctl", OWRITE|OCEXEC)) < 0)
 			goto Out;
 		write(ctl, "rawon", 5);
 	}
--- a/sys/src/libauthsrv/readnvram.c
+++ b/sys/src/libauthsrv/readnvram.c
@@ -91,9 +91,9 @@
 			v[0] = "";
 			v[1] = nil;
 		}
-		fd = open(v[0], ORDWR);
+		fd = open(v[0], ORDWR|OCEXEC);
 		if (fd < 0)
-			fd = open(v[0], OREAD);
+			fd = open(v[0], OREAD|OCEXEC);
 		safelen = sizeof(Nvrsafe);
 		if(strstr(v[0], "/9fat") == nil)
 			safeoff = 0;
@@ -120,7 +120,7 @@
 		for(i=0; i<nelem(nvtab); i++){
 			if(strcmp(cputype, nvtab[i].cputype) != 0)
 				continue;
-			if((fd = open(nvtab[i].file, ORDWR)) < 0)
+			if((fd = open(nvtab[i].file, ORDWR|OCEXEC)) < 0)
 				continue;
 			safeoff = nvtab[i].off;
 			safelen = nvtab[i].len;
--- a/sys/src/libc/9sys/getenv.c
+++ b/sys/src/libc/9sys/getenv.c
@@ -18,7 +18,8 @@
 	snprint(s, HUNK, "/env/%s", name);
 	n = 0;
 	r = -1;
-	if((f = open(s, OREAD)) >= 0){
+	f = open(s, OREAD|OCEXEC);
+	if(f >= 0){
 		while((r = read(f, s+n, HUNK)) > 0){
 			n += r;
 			r = -1;
--- a/sys/src/libc/9sys/getnetconninfo.c
+++ b/sys/src/libc/9sys/getnetconninfo.c
@@ -13,7 +13,7 @@
 	sys = serv = 0;
 
 	snprint(buf, sizeof buf, "%s/%s", dir, file);
-	fd = open(buf, OREAD);
+	fd = open(buf, OREAD|OCEXEC);
 	if(fd >= 0){
 		n = read(fd, buf, sizeof(buf)-1);
 		if(n>0){
@@ -41,7 +41,6 @@
 	NetConnInfo *nci;
 	char *cp;
 	Dir *d;
-	char spec[10];
 	char path[128];
 	char netname[128], *p;
 
@@ -76,10 +75,8 @@
 
 	/* figure out bind spec */
 	d = dirstat(nci->dir);
-	if(d != nil){
-		sprint(spec, "#%C%d", d->type, d->dev);
-		nci->spec = strdup(spec);
-	}
+	if(d != nil)
+		nci->spec = smprint("#%C%d", d->type, d->dev);
 	if(nci->spec == nil)
 		nci->spec = unknown;
 	free(d);
--- a/sys/src/libc/9sys/getppid.c
+++ b/sys/src/libc/9sys/getppid.c
@@ -8,7 +8,7 @@
 	int f;
 
 	memset(b, 0, sizeof(b));
-	f = open("/dev/ppid", 0);
+	f = open("/dev/ppid", OREAD|OCEXEC);
 	if(f >= 0) {
 		read(f, b, sizeof(b));
 		close(f);
--- a/sys/src/libc/9sys/getwd.c
+++ b/sys/src/libc/9sys/getwd.c
@@ -1,14 +1,12 @@
 #include <u.h>
 #include <libc.h>
 
-static char *nsgetwd(char*, int);
-
 char*
 getwd(char *buf, int nbuf)
 {
 	int n, fd;
 
-	fd = open(".", OREAD);
+	fd = open(".", OREAD|OCEXEC);
 	if(fd < 0)
 		return nil;
 	n = fd2path(fd, buf, nbuf);
--- a/sys/src/libc/9sys/iounit.c
+++ b/sys/src/libc/9sys/iounit.c
@@ -13,7 +13,7 @@
 	char buf[128], *args[10];
 
 	snprint(buf, sizeof buf, "#d/%dctl", fd);
-	cfd = open(buf, OREAD);
+	cfd = open(buf, OREAD|OCEXEC);
 	if(cfd < 0)
 		return 0;
 	i = read(cfd, buf, sizeof buf-1);
--- a/sys/src/libc/9sys/postnote.c
+++ b/sys/src/libc/9sys/postnote.c
@@ -4,21 +4,21 @@
 int
 postnote(int group, int pid, char *note)
 {
-	char file[128];
+	char file[32];
 	int f, r;
 
 	switch(group) {
 	case PNPROC:
-		sprint(file, "/proc/%d/note", pid);
+		snprint(file, sizeof(file), "/proc/%lud/note", (ulong)pid);
 		break;
 	case PNGROUP:
-		sprint(file, "/proc/%d/notepg", pid);
+		snprint(file, sizeof(file), "/proc/%lud/notepg", (ulong)pid);
 		break;
 	default:
 		return -1;
 	}
 
-	f = open(file, OWRITE);
+	f = open(file, OWRITE|OCEXEC);
 	if(f < 0)
 		return -1;
 
--- a/sys/src/libc/9sys/procsetname.c
+++ b/sys/src/libc/9sys/procsetname.c
@@ -8,8 +8,9 @@
 	char buf[128];
 	va_list arg;
 
-	snprint(buf, sizeof buf, "#p/%lud/args", (ulong)getpid());
-	if((fd = open(buf, OWRITE)) < 0)
+	snprint(buf, sizeof buf, "/proc/%lud/args", (ulong)getpid());
+	fd = open(buf, OWRITE|OCEXEC);
+	if(fd < 0)
 		return;
 	va_start(arg, fmt);
 	n = vsnprint(buf, sizeof buf, fmt, arg);
--- a/sys/src/libc/9sys/pushssl.c
+++ b/sys/src/libc/9sys/pushssl.c
@@ -11,7 +11,7 @@
 pushssl(int fd, char *alg, char *secin, char *secout, int *cfd)
 {
 	char buf[8];
-	char dname[64];
+	char dname[32];
 	int n, data, ctl;
 
 	ctl = open("#D/ssl/clone", ORDWR);
@@ -21,7 +21,7 @@
 	if(n < 0)
 		goto error;
 	buf[n] = 0;
-	sprint(dname, "#D/ssl/%s/data", buf);
+	snprint(dname, sizeof(dname), "#D/ssl/%s/data", buf);
 	data = open(dname, ORDWR);
 	if(data < 0)
 		goto error;
--- a/sys/src/libc/9sys/pushtls.c
+++ b/sys/src/libc/9sys/pushtls.c
@@ -42,7 +42,7 @@
 pushtls(int fd, char *hashalg, char *encalg, int isclient, char *secret, char *dir)
 {
 	char buf[8];
-	char dname[64];
+	char dname[32];
 	int n, data, ctl, hand;
 
 	// open a new filter; get ctl fd
@@ -49,7 +49,7 @@
 	data = hand = -1;
 	// /net/tls uses decimal file descriptors to name channels, hence a
 	// user-level file server can't stand in for #a; may as well hard-code it.
-	ctl = open("#a/tls/clone", ORDWR);
+	ctl = open("#a/tls/clone", ORDWR|OCEXEC);
 	if(ctl < 0)
 		goto error;
 	n = read(ctl, buf, sizeof(buf)-1);
@@ -60,14 +60,14 @@
 		sprint(dir, "#a/tls/%s", buf);
 
 	// get application fd
-	sprint(dname, "#a/tls/%s/data", buf);
+	snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
 	data = open(dname, ORDWR);
 	if(data < 0)
 		goto error;
 
 	// get handshake fd
-	sprint(dname, "#a/tls/%s/hand", buf);
-	hand = open(dname, ORDWR);
+	snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
+	hand = open(dname, ORDWR|OCEXEC);
 	if(hand < 0)
 		goto error;
 
--- a/sys/src/libc/9sys/putenv.c
+++ b/sys/src/libc/9sys/putenv.c
@@ -13,7 +13,7 @@
 		return -1;
 	}
 	snprint(ename, sizeof(ename), "/env/%s", name);
-	f = create(ename, OWRITE, 0664);
+	f = create(ename, OWRITE|OCEXEC, 0664);
 	if(f < 0)
 		return -1;
 	n = strlen(val);
--- a/sys/src/libc/9sys/sysname.c
+++ b/sys/src/libc/9sys/sysname.c
@@ -10,7 +10,7 @@
 	if(b[0])
 		return b;
 
-	f = open("#c/sysname", 0);
+	f = open("/dev/sysname", OREAD|OCEXEC);
 	if(f >= 0) {
 		n = read(f, b, sizeof(b)-1);
 		if(n > 0)
--- a/sys/src/libc/port/date.c
+++ b/sys/src/libc/port/date.c
@@ -174,11 +174,12 @@
 	else
 		snprint(path, sizeof(path), "/adm/timezone/%s", name);
 	memset(buf, 0, sizeof(buf));
-	if((f = open(path, 0)) == -1)
+	f = open(path, OREAD|OCEXEC);
+	if(f < 0)
 		return -1;
 	r = read(f, buf, sizeof(buf));
 	close(f);
-	if(r == sizeof(buf) || r == -1)
+	if(r < 0 || r >= sizeof(buf))
 		return -1;
 	buf[r] = 0;
 	p = buf;
--- a/sys/src/libc/port/getuser.c
+++ b/sys/src/libc/port/getuser.c
@@ -8,7 +8,7 @@
 	int fd;
 	int n;
 
-	fd = open("/dev/user", OREAD);
+	fd = open("/dev/user", OREAD|OCEXEC);
 	if(fd < 0)
 		return "none";
 	n = read(fd, user, (sizeof user)-1);
--- a/sys/src/libc/port/malloc.c
+++ b/sys/src/libc/port/malloc.c
@@ -99,13 +99,13 @@
 {
 	int n, fd;
 	char buf[20];
-	fd = open("/env/MALLOCFD", OREAD);
+	fd = open("/env/MALLOCFD", OREAD|OCEXEC);
 	if(fd < 0)
 		return -1;
-	if((n = read(fd, buf, sizeof buf)) < 0) {
-		close(fd);
+	n = read(fd, buf, sizeof buf);
+	close(fd);
+	if(n < 0)
 		return -1;
-	}
 	if(n >= sizeof buf)
 		n = sizeof(buf)-1;
 	buf[n] = 0;
--- a/sys/src/libc/port/profile.c
+++ b/sys/src/libc/port/profile.c
@@ -137,7 +137,7 @@
 		snprint(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
 	else
 		snprint(filename, sizeof filename - 1, "prof.out");
-	f = create(filename, 1, 0666);
+	f = create(filename, OWRITE|OCEXEC, 0666);
 	if(f < 0) {
 		perror("create prof.out");
 		return;
@@ -245,7 +245,7 @@
 		khz = _tos->cyclefreq / 1000;	/* Report times in milliseconds */
 		havecycles = 1;
 	}
-	f = open("/env/profsize", OREAD);
+	f = open("/env/profsize", OREAD|OCEXEC);
 	if(f >= 0) {
 		memset(ename, 0, sizeof(ename));
 		read(f, ename, sizeof(ename)-1);
@@ -253,7 +253,7 @@
 		n = atol(ename);
 	}
 	_tos->prof.what = Profuser;
-	f = open("/env/proftype", OREAD);
+	f = open("/env/proftype", OREAD|OCEXEC);
 	if(f >= 0) {
 		memset(ename, 0, sizeof(ename));
 		read(f, ename, sizeof(ename)-1);
--- a/sys/src/libcomplete/complete.c
+++ b/sys/src/libcomplete/complete.c
@@ -53,7 +53,7 @@
 		return nil;
 	}
 
-	fd = open(dir, OREAD);
+	fd = open(dir, OREAD|OCEXEC);
 	if(fd < 0)
 		return nil;
 
--- a/sys/src/libdraw/getsubfont.c
+++ b/sys/src/libdraw/getsubfont.c
@@ -22,7 +22,7 @@
 	if(dolock)
 		unlockdisplay(d);
 
-	fd = open(name, OREAD);
+	fd = open(name, OREAD|OCEXEC);
 	if(fd < 0) {
 		fprint(2, "getsubfont: can't open %s: %r\n", name);
 		f = nil;
--- a/sys/src/libdraw/init.c
+++ b/sys/src/libdraw/init.c
@@ -49,7 +49,7 @@
 		return -1;
 	}
 	if(fontname == nil){
-		fd = open("/env/font", OREAD);
+		fd = open("/env/font", OREAD|OCEXEC);
 		if(fd >= 0){
 			n = read(fd, buf, sizeof(buf));
 			if(n>0 && n<sizeof buf-1){
@@ -82,11 +82,11 @@
 	 */
 	if(label != nil){
 		snprint(buf, sizeof buf, "%s/label", display->windir);
-		fd = open(buf, OREAD);
+		fd = open(buf, OREAD|OCEXEC);
 		if(fd >= 0){
 			read(fd, display->oldlabel, (sizeof display->oldlabel)-1);
 			close(fd);
-			fd = create(buf, OWRITE, 0666);
+			fd = create(buf, OWRITE|OCEXEC, 0666);
 			if(fd >= 0){
 				write(fd, label, strlen(label));
 				close(fd);
@@ -125,7 +125,7 @@
 
 	obuf[0] = 0;
 retry:
-	fd = open(winname, OREAD);
+	fd = open(winname, OREAD|OCEXEC);
 	if(fd<0 || (n=read(fd, buf, sizeof buf-1))<=0){
 		if(fd >= 0) close(fd);
 		strcpy(buf, "noborder");
@@ -345,7 +345,7 @@
 		display = nil;
 	if(disp->oldlabel[0]){
 		snprint(buf, sizeof buf, "%s/label", disp->windir);
-		fd = open(buf, OWRITE);
+		fd = open(buf, OWRITE|OCEXEC);
 		if(fd >= 0){
 			write(fd, disp->oldlabel, strlen(disp->oldlabel));
 			close(fd);
--- a/sys/src/libdraw/openfont.c
+++ b/sys/src/libdraw/openfont.c
@@ -12,7 +12,7 @@
 	n = 0;
 	r = -1;
 	if((s = malloc(HUNK)) != nil){
-		if((f = open(name, OREAD)) >= 0){
+		if((f = open(name, OREAD|OCEXEC)) >= 0){
 			while((r = read(f, s+n, HUNK)) > 0){
 				n += r;
 				r = -1;
--- a/sys/src/libdraw/readcolmap.c
+++ b/sys/src/libdraw/readcolmap.c
@@ -27,8 +27,8 @@
 	USED(screen);
 
 	sprint(buf, "/dev/draw/%d/colormap", d->dirno);
-	b = Bopen(buf, OREAD);
-	if(b == 0)
+	b = Bopen(buf, OREAD|OCEXEC);
+	if(b == nil)
 		drawerror(d, "rdcolmap: can't open colormap device");
 
 	for(;;) {
--- a/sys/src/libdraw/window.c
+++ b/sys/src/libdraw/window.c
@@ -90,7 +90,6 @@
 	d = s->display;
 	a = bufimage(d, 1+4);
 	if(a == nil){
-Error:
 		free(s);
 		return -1;
 	}
--- a/sys/src/libdraw/writecolmap.c
+++ b/sys/src/libdraw/writecolmap.c
@@ -16,7 +16,7 @@
 	ulong r, g, b;
 
 	sprint(buf, "/dev/draw/%d/colormap", d->dirno);
-	fd = open(buf, OWRITE);
+	fd = open(buf, OWRITE|OCEXEC);
 	if(fd < 0)
 		drawerror(d, "writecolmap: open colormap failed");
 	t = malloc(8192);
--- a/sys/src/libndb/csgetval.c
+++ b/sys/src/libndb/csgetval.c
@@ -25,10 +25,10 @@
 		snprint(line, sizeof(line), "%s/cs", netroot);
 	else
 		strcpy(line, "/net/cs");
-	fd = open(line, ORDWR);
+	fd = open(line, ORDWR|OCEXEC);
 	if(fd < 0)
 		return nil;
-	seek(fd, 0, 0);
+
 	snprint(line, sizeof(line), "!%s=%s %s=*", attr, val, rattr);
 	if(write(fd, line, strlen(line)) < 0){
 		close(fd);
--- a/sys/src/libndb/csipinfo.c
+++ b/sys/src/libndb/csipinfo.c
@@ -23,10 +23,10 @@
 		snprint(line, sizeof(line), "%s/cs", netroot);
 	else
 		strcpy(line, "/net/cs");
-	fd = open(line, ORDWR);
+	fd = open(line, ORDWR|OCEXEC);
 	if(fd < 0)
-		return 0;
-	seek(fd, 0, 0);
+		return nil;
+
 	e = line + sizeof(line);
 	p = seprint(line, e, "!ipinfo %s=%s", attr, val);
 	for(i = 0; i < n; i++){
@@ -37,11 +37,11 @@
 	
 	if(write(fd, line, strlen(line)) < 0){
 		close(fd);
-		return 0;
+		return nil;
 	}
 	seek(fd, 0, 0);
 
-	first = last = 0;
+	first = last = nil;
 	for(;;){
 		n = read(fd, line, sizeof(line)-2);
 		if(n <= 0)
@@ -50,15 +50,15 @@
 		line[n+1] = 0;
 
 		t = _ndbparseline(line);
-		if(t == 0)
+		if(t == nil)
 			continue;
-		if(first)
+		if(first != nil)
 			last->entry = t;
 		else
 			first = t;
 		last = t;
 
-		while(last->entry)
+		while(last->entry != nil)
 			last = last->entry;
 	}
 	close(fd);
--- a/sys/src/libndb/dnsquery.c
+++ b/sys/src/libndb/dnsquery.c
@@ -29,7 +29,7 @@
 		net = "/net";
 
 	snprint(buf, sizeof(buf), "%s/dns", net);
-	if((fd = open(buf, ORDWR)) < 0)
+	if((fd = open(buf, ORDWR|OCEXEC)) < 0)
 		return nil;
 
 	/* zero out the error string */
@@ -84,7 +84,6 @@
 	int n;
 	Ndbtuple *t, *first, *last;
 
-	seek(fd, 0, 0);
 	snprint(buf, sizeof(buf), "!%s %s", dn, type);
 	if(write(fd, buf, strlen(buf)) < 0)
 		return nil;
--- a/sys/src/libndb/ndbhash.c
+++ b/sys/src/libndb/ndbhash.c
@@ -57,7 +57,7 @@
 
 	/* try opening the data base if it's closed */
 	if(db->mtime==0 && ndbreopen(db) < 0)
-		return 0;
+		return nil;
 
 	/* if the database has changed, throw out hash files and reopen db */
 	if((d = dirfstat(Bfildes(&db->b))) == nil || db->qid.path != d->qid.path
@@ -64,16 +64,16 @@
 	|| db->qid.vers != d->qid.vers){
 		if(ndbreopen(db) < 0){
 			free(d);
-			return 0;
+			return nil;
 		}
 	}
 	free(d);
 
 	if(db->nohash)
-		return 0;
+		return nil;
 
 	/* see if a hash file exists for this attribute */
-	for(hf = db->hf; hf; hf= hf->next){
+	for(hf = db->hf; hf != nil; hf= hf->next){
 		if(strcmp(hf->attr, attr) == 0)
 			return hf;
 	}
@@ -80,19 +80,19 @@
 
 	/* create a new one */
 	hf = (Ndbhf*)malloc(sizeof(Ndbhf));
-	if(hf == 0)
-		return 0;
+	if(hf == nil)
+		return nil;
 	memset(hf, 0, sizeof(Ndbhf));
 
 	/* compare it to the database file */
 	strncpy(hf->attr, attr, sizeof(hf->attr)-1);
 	sprint(buf, "%s.%s", db->file, hf->attr);
-	hf->fd = open(buf, OREAD);
+	hf->fd = open(buf, OREAD|OCEXEC);
 	if(hf->fd >= 0){
 		hf->len = 0;
 		hf->off = 0;
 		p = hfread(hf, 0, 2*NDBULLEN);
-		if(p){
+		if(p != nil){
 			hf->dbmtime = NDBGETUL(p);
 			hf->hlen = NDBGETUL(p+NDBULLEN);
 			if(hf->dbmtime == db->mtime){
@@ -105,7 +105,7 @@
 	}
 
 	free(hf);
-	return 0;
+	return nil;
 }
 
 /*
@@ -142,17 +142,6 @@
 		}
 		s->ptr = NDBGETP(p);
 		s->type = Cptr1;
-	} else if(db->length > 128*1024){
-		print("Missing or out of date hash file %s.%s.\n", db->file, attr);
-		syslog(0, "ndb", "Missing or out of date hash file %s.%s.", db->file, attr);
-
-		/* advance search to next db file */
-		s->ptr = NDBNAP;
-		_ndbcacheadd(db, s, attr, val, nil);
-		if(db->next == nil)
-			return nil;
-		t = ndbsearch(db->next, s, attr, val);
-		goto out;
 	} else {
 		s->ptr = 0;
 		s->type = Dptr;
@@ -173,7 +162,7 @@
 		if(strcmp(attr, nt->attr) == 0
 		&& strcmp(val, nt->val) == 0)
 			return nt;
-	return 0;
+	return nil;
 }
 
 /*
--- a/sys/src/libndb/ndbopen.c
+++ b/sys/src/libndb/ndbopen.c
@@ -99,7 +99,7 @@
 	}
 
 	/* try the open again */
-	fd = open(db->file, OREAD);
+	fd = open(db->file, OREAD|OCEXEC);
 	if(fd < 0)
 		return -1;
 	d = dirfstat(fd);
--- a/sys/src/libsec/port/readcert.c
+++ b/sys/src/libsec/port/readcert.c
@@ -8,7 +8,7 @@
 	char *s;
 	Dir *d;
 
-	fd = open(name, OREAD);
+	fd = open(name, OREAD|OCEXEC);
 	if(fd < 0)
 		return nil;
 	if((d = dirfstat(fd)) == nil) {
--- a/sys/src/libsec/port/thumb.c
+++ b/sys/src/libsec/port/thumb.c
@@ -95,7 +95,7 @@
 	}
 	if(access(file, AEXIST) < 0)
 		return 0;	/* not an error */
-	if((bin = Bopen(file, OREAD)) == nil)
+	if((bin = Bopen(file, OREAD|OCEXEC)) == nil)
 		return -1;
 	for(; (line = Brdstr(bin, '\n', 1)) != nil; free(line)){
 		if(tokenize(line, field, nelem(field)) < 2)
--- a/sys/src/libsec/port/tlshand.c
+++ b/sys/src/libsec/port/tlshand.c
@@ -459,7 +459,7 @@
 
 	if(conn == nil)
 		return -1;
-	ctl = open("#a/tls/clone", ORDWR);
+	ctl = open("#a/tls/clone", ORDWR|OCEXEC);
 	if(ctl < 0)
 		return -1;
 	n = read(ctl, buf, sizeof(buf)-1);
@@ -470,7 +470,7 @@
 	buf[n] = 0;
 	snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
 	snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
-	hand = open(dname, ORDWR);
+	hand = open(dname, ORDWR|OCEXEC);
 	if(hand < 0){
 		close(ctl);
 		return -1;
@@ -592,7 +592,7 @@
 
 	if(conn == nil)
 		return -1;
-	ctl = open("#a/tls/clone", ORDWR);
+	ctl = open("#a/tls/clone", ORDWR|OCEXEC);
 	if(ctl < 0)
 		return -1;
 	n = read(ctl, buf, sizeof(buf)-1);
@@ -603,7 +603,7 @@
 	buf[n] = 0;
 	snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
 	snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
-	hand = open(dname, ORDWR);
+	hand = open(dname, ORDWR|OCEXEC);
 	if(hand < 0){
 		close(ctl);
 		return -1;
@@ -2178,7 +2178,7 @@
 		unlock(&ciphLock);
 		return nciphers;
 	}
-	j = open("#a/tls/encalgs", OREAD);
+	j = open("#a/tls/encalgs", OREAD|OCEXEC);
 	if(j < 0){
 		werrstr("can't open #a/tls/encalgs: %r");
 		goto out;
@@ -2202,7 +2202,7 @@
 		cipherAlgs[i].ok = ok;
 	}
 
-	j = open("#a/tls/hashalgs", OREAD);
+	j = open("#a/tls/hashalgs", OREAD|OCEXEC);
 	if(j < 0){
 		werrstr("can't open #a/tls/hashalgs: %r");
 		goto out;
@@ -2261,7 +2261,7 @@
 	AuthRpc *rpc;
 
 	// start talking to factotum
-	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
+	if((afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC)) < 0)
 		return nil;
 	if((rpc = auth_allocrpc(afd)) == nil){
 		close(afd);
--- a/sys/src/libthread/id.c
+++ b/sys/src/libthread/id.c
@@ -59,7 +59,7 @@
 threadsetname(char *fmt, ...)
 {
 	int fd;
-	char buf[128];
+	char buf[32];
 	va_list arg;
 	Proc *p;
 	Thread *t;
@@ -72,8 +72,8 @@
 	t->cmdname = vsmprint(fmt, arg);
 	va_end(arg);
 	if(t->cmdname && p->nthreads == 1){
-		snprint(buf, sizeof buf, "#p/%lud/args", _tos->pid); //getpid());
-		if((fd = open(buf, OWRITE)) >= 0){
+		snprint(buf, sizeof buf, "/proc/%lud/args", (ulong)getpid());
+		if((fd = open(buf, OWRITE|OCEXEC)) >= 0){
 			write(fd, t->cmdname, strlen(t->cmdname)+1);
 			close(fd);
 		}
--- a/sys/src/libthread/ioproc.c
+++ b/sys/src/libthread/ioproc.c
@@ -19,6 +19,15 @@
 	qunlock(io);
 }
 
+static int
+openprocctl(void)
+{
+	char buf[32];
+
+	snprint(buf, sizeof(buf), "/proc/%lud/ctl", (ulong)getpid());
+	return open(buf, OWRITE|OCEXEC);
+}
+
 static void
 xioproc(void *a)
 {
@@ -28,15 +37,11 @@
 
 	c = a;
 	if(io = mallocz(sizeof(*io), 1)){
-		char buf[128];
-
 		/*
 		 * open might fail, ignore it for programs like factotum
 		 * that don't use iointerrupt() anyway.
 		 */
-		snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
-		io->ctl = open(buf, OWRITE);
-
+		io->ctl = openprocctl();
 		if((io->creply = chancreate(sizeof(void*), 0)) == nil){
 			if(io->ctl >= 0)
 				close(io->ctl);
--- a/sys/src/libthread/kill.c
+++ b/sys/src/libthread/kill.c
@@ -76,24 +76,31 @@
 	threadxxx(id, 0);
 }
 
-static void
-tinterrupt(Proc *p, Thread *t)
+static int
+writeprocctl(int pid, char *ctl)
 {
-	char buf[64];
+	char buf[32];
 	int fd;
 
+	snprint(buf, sizeof(buf), "/proc/%lud/ctl", (ulong)pid);
+	fd = open(buf, OWRITE|OCEXEC);
+	if(fd < 0)
+		return -1;
+	if(write(fd, ctl, strlen(ctl)) < 0){
+		close(fd);
+		return -1;
+	}
+	close(fd);
+	return 0;
+}
+
+static void
+tinterrupt(Proc *p, Thread *t)
+{
 	switch(t->state){
 	case Running:
-		snprint(buf, sizeof(buf), "/proc/%d/ctl", p->pid);
-		fd = open(buf, OWRITE|OCEXEC);
-		if(fd >= 0){
-			if(write(fd, "interrupt", 9) == 9){
-				close(fd);
-				break;
-			}
-			close(fd);
-		}
-		postnote(PNPROC, p->pid, "threadint");
+		if(writeprocctl(p->pid, "interrupt") < 0)
+			postnote(PNPROC, p->pid, "threadint");
 		break;
 	case Rendezvous:
 		_threadflagrendez(t);