shithub: riscv

Download patch

ref: aab4e32ce05a26bff7386bb86ffc24589cbe601c
parent: ffde0eda463e8b908975abcabc5345cfaa1a14f7
author: cinap_lenrek <[email protected]>
date: Sun Jun 25 18:22:58 EDT 2017

pc, pc64: support for multiboot framebuffer, common bootargs and multiboot code

--- /dev/null
+++ b/sys/src/9/pc/bootargs.c
@@ -1,0 +1,189 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+#define	MAXCONF 64
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+/* screen.c */
+extern char* rgbmask2chan(char *buf, int depth, u32int rm, u32int gm, u32int bm);
+
+/* vgavesa.c */
+extern char* vesabootscreenconf(char*, char*, uchar*);
+
+static void
+multibootargs(void)
+{
+	extern ulong multibootptr;
+	ulong *multiboot;
+	char *cp, *ep;
+	ulong *m, l;
+
+	if(multibootptr == 0)
+		return;
+
+	multiboot = (ulong*)KADDR(multibootptr);
+
+	/* command line */
+	if((multiboot[0] & (1<<2)) != 0)
+		strncpy(BOOTLINE, KADDR(multiboot[4]), BOOTLINELEN-1);
+
+	cp = BOOTARGS;
+	ep = cp + BOOTARGSLEN-1;
+
+	/* memory map */
+	if((multiboot[0] & (1<<6)) != 0 && (l = multiboot[11]) >= 24){
+		cp = seprint(cp, ep, "*e820=");
+		m = KADDR(multiboot[12]);
+		while(m[0] >= 20 && m[0]+4 <= l){
+			uvlong base, size;
+			m++;
+			base = ((uvlong)m[0] | (uvlong)m[1]<<32);
+			size = ((uvlong)m[2] | (uvlong)m[3]<<32);
+			cp = seprint(cp, ep, "%.1lux %.16llux %.16llux ",
+				m[4] & 0xF, base, base+size);
+			l -= m[-1]+4;
+			m = (ulong*)((uintptr)m + m[-1]);
+		}
+		cp[-1] = '\n';
+	}
+
+	if((multiboot[0] & (1<<12)) != 0 && multiboot[22] != 0){	/* framebuffer */
+		uchar *p = (uchar*)multiboot + 112;
+		int depth = multiboot[27] & 0xFF;
+		char chan[32];
+
+		switch((multiboot[27]>>8) & 0xFF){
+		case 0:
+			snprint(chan, sizeof chan, "m%d", depth);
+			if(0){
+		case 1:
+			rgbmask2chan(chan, depth,
+				(1UL<<p[1])-1 << p[0],
+				(1UL<<p[3])-1 << p[2],
+				(1UL<<p[5])-1 << p[4]);
+			}
+			cp = seprint(cp, ep, "*bootscreen=%dx%dx%d %s %#lux\n",
+				(int)multiboot[24]*8 / depth,
+				(int)multiboot[26],
+				depth,
+				chan,
+				multiboot[22]);
+		}
+	} else
+	if((multiboot[0] & (1<<11)) != 0 && multiboot[19] != 0)		/* vbe mode info */
+		cp = vesabootscreenconf(cp, ep, KADDR(multiboot[19]));
+
+	/* plan9.ini passed as the first module */
+	if((multiboot[0] & (1<<3)) != 0 && multiboot[5] > 0){
+		m = KADDR(multiboot[6]);
+		l = m[1] - m[0];
+		m = KADDR(m[0]);
+		if(cp+l > ep)
+			l = ep - cp;
+		memmove(cp, m, l);
+		cp += l;
+	}
+	*cp = 0;
+}
+
+void
+bootargsinit(void)
+{
+	int i, j, n;
+	char *cp, *line[MAXCONF], *p, *q;
+
+	multibootargs();
+
+	/*
+	 *  parse configuration args from dos file plan9.ini
+	 */
+	cp = BOOTARGS;	/* where b.com leaves its config */
+	cp[BOOTARGSLEN-1] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+
+	n = getfields(cp, line, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(*line[i] == '#')
+			continue;
+		cp = strchr(line[i], '=');
+		if(cp == nil)
+			continue;
+		*cp++ = '\0';
+		for(j = 0; j < nconf; j++){
+			if(cistrcmp(confname[j], line[i]) == 0)
+				break;
+		}
+		confname[j] = line[i];
+		confval[j] = cp;
+		if(j == nconf)
+			nconf++;
+	}
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+setconfenv(void)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++){
+		if(confname[i][0] != '*')
+			ksetenv(confname[i], confval[i], 0);
+		ksetenv(confname[i], confval[i], 1);
+	}
+}
+
+void
+writeconf(void)
+{
+	char *p, *q;
+	int n;
+
+	p = getconfenv();
+	if(waserror()) {
+		free(p);
+		nexterror();
+	}
+
+	/* convert to name=value\n format */
+	for(q=p; *q; q++) {
+		q += strlen(q);
+		*q = '=';
+		q += strlen(q);
+		*q = '\n';
+	}
+	n = q - p + 1;
+	if(n >= BOOTARGSLEN)
+		error("kernel configuration too large");
+	memset(BOOTLINE, 0, BOOTLINELEN);
+	memmove(BOOTARGS, p, n);
+	poperror();
+	free(p);
+}
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -8,7 +8,7 @@
 int	bios32ci(BIOS32si*, BIOS32ci*);
 void	bios32close(BIOS32si*);
 BIOS32si* bios32open(char*);
-void	bootargs(void*);
+void	bootargsinit(void);
 ulong	cankaddr(ulong);
 int	checksum(void *, int);
 void	clockintr(Ureg*, void*);
@@ -176,6 +176,7 @@
 void	realmode(Ureg*);
 void	screeninit(void);
 void	(*screenputs)(char*, int);
+void	setconfenv(void);
 void*	sigsearch(char*);
 void	syncclock(void);
 void*	tmpmap(Page*);
@@ -198,6 +199,7 @@
 int	vmapsync(ulong);
 void	vunmap(void*, int);
 void	wbinvd(void);
+void	writeconf(void);
 int	wrmsr(int, vlong);
 int	xchgw(ushort*, int);
 void	rdrandbuf(void*, ulong);
--- a/sys/src/9/pc/l.s
+++ b/sys/src/9/pc/l.s
@@ -42,8 +42,8 @@
  */
 TEXT _multibootheader(SB), $0
 	LONG	$0x1BADB002			/* magic */
-	LONG	$0x00010003			/* flags */
-	LONG	$-(0x1BADB002 + 0x00010003)	/* checksum */
+	LONG	$0x00010007			/* flags */
+	LONG	$-(0x1BADB002 + 0x00010007)	/* checksum */
 	LONG	$_multibootheader-KZERO(SB)	/* header_addr */
 	LONG	$_startKADDR-KZERO(SB)		/* load_addr */
 	LONG	$edata-KZERO(SB)		/* load_end_addr */
@@ -52,7 +52,7 @@
 	LONG	$0				/* mode_type */
 	LONG	$0				/* width */
 	LONG	$0				/* height */
-	LONG	$0				/* depth */
+	LONG	$32				/* depth */
 
 /* 
  * the kernel expects the data segment to be page-aligned
@@ -70,14 +70,13 @@
 	INCL	CX	/* one more for post decrement */
 	STD
 	REP; MOVSB
-	ADDL	$KZERO, BX
-	MOVL	BX, multiboot-KZERO(SB)
+	MOVL	BX, multibootptr-KZERO(SB)
 	MOVL	$_startPADDR(SB), AX
 	ANDL	$~KZERO, AX
 	JMP*	AX
 
-/* multiboot structure pointer */
-TEXT multiboot(SB), $0
+/* multiboot structure pointer (physical address) */
+TEXT multibootptr(SB), $0
 	LONG	$0
 
 /*
--- a/sys/src/9/pc/main.c
+++ b/sys/src/9/pc/main.c
@@ -11,124 +11,21 @@
 #include	"reboot.h"
 
 Mach *m;
-
-/*
- * Where configuration info is left for the loaded programme.
- * This will turn into a structure as more is done by the boot loader
- * (e.g. why parse the .ini file twice?).
- * There are 3584 bytes available at CONFADDR.
- */
-#define BOOTLINE	((char*)CONFADDR)
-#define BOOTLINELEN	64
-#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
-#define	BOOTARGSLEN	(4096-0x200-BOOTLINELEN)
-#define	MAXCONF		64
-
 Conf conf;
-char *confname[MAXCONF];
-char *confval[MAXCONF];
-int nconf;
+
 char *sp;	/* user stack of init proc */
 int delaylink;
 int idle_spin;
 
-static void
-multibootargs(void)
-{
-	char *cp, *ep;
-	ulong *m, l;
-
-	extern ulong *multiboot;
-
-	if(multiboot == nil)
-		return;
-
-	/* command line */
-	if((multiboot[0] & (1<<2)) != 0)
-		strncpy(BOOTLINE, KADDR(multiboot[4]), BOOTLINELEN-1);
-
-	cp = BOOTARGS;
-	ep = cp + BOOTARGSLEN-1;
-
-	/* memory map */
-	if((multiboot[0] & (1<<6)) != 0 && (l = multiboot[11]) >= 24){
-		cp = seprint(cp, ep, "*e820=");
-		m = KADDR(multiboot[12]);
-		while(m[0] >= 20 && m[0]+4 <= l){
-			uvlong base, size;
-			m++;
-			base = ((uvlong)m[0] | (uvlong)m[1]<<32);
-			size = ((uvlong)m[2] | (uvlong)m[3]<<32);
-			cp = seprint(cp, ep, "%.1lux %.16llux %.16llux ",
-				m[4] & 0xF, base, base+size);
-			l -= m[-1]+4;
-			m = (ulong*)((ulong)m + m[-1]);
-		}
-		cp[-1] = '\n';
-	}
-
-	/* plan9.ini passed as the first module */
-	if((multiboot[0] & (1<<3)) != 0 && multiboot[5] > 0){
-		m = KADDR(multiboot[6]);
-		l = m[1] - m[0];
-		m = KADDR(m[0]);
-		if(cp+l > ep)
-			l = ep - cp;
-		memmove(cp, m, l);
-		cp += l;
-	}
-	*cp = 0;
-}
-
-static void
-options(void)
-{
-	long i, n;
-	char *cp, *line[MAXCONF], *p, *q;
-
-	multibootargs();
-
-	/*
-	 *  parse configuration args from dos file plan9.ini
-	 */
-	cp = BOOTARGS;	/* where b.com leaves its config */
-	cp[BOOTARGSLEN-1] = 0;
-
-	/*
-	 * Strip out '\r', change '\t' -> ' '.
-	 */
-	p = cp;
-	for(q = cp; *q; q++){
-		if(*q == '\r')
-			continue;
-		if(*q == '\t')
-			*q = ' ';
-		*p++ = *q;
-	}
-	*p = 0;
-
-	n = getfields(cp, line, MAXCONF, 1, "\n");
-	for(i = 0; i < n; i++){
-		if(*line[i] == '#')
-			continue;
-		cp = strchr(line[i], '=');
-		if(cp == nil)
-			continue;
-		*cp++ = '\0';
-		confname[nconf] = line[i];
-		confval[nconf] = cp;
-		nconf++;
-	}
-}
-
 extern void (*i8237alloc)(void);
 extern void bootscreeninit(void);
+extern void multibootdebug(void);
 
 void
 main(void)
 {
 	mach0init();
-	options();
+	bootargsinit();
 	ioinit();
 	i8250console();
 	quotefmtinstall();
@@ -135,7 +32,7 @@
 	screeninit();
 
 	print("\nPlan 9\n");
-
+	
 	trapinit0();
 	i8253init();
 	cpuidentify();
@@ -213,7 +110,6 @@
 void
 init0(void)
 {
-	int i;
 	char buf[2*KNAMELEN];
 
 	up->nerrlab = 0;
@@ -239,11 +135,7 @@
 			ksetenv("service", "cpu", 0);
 		else
 			ksetenv("service", "terminal", 0);
-		for(i = 0; i < nconf; i++){
-			if(confname[i][0] != '*')
-				ksetenv(confname[i], confval[i], 0);
-			ksetenv(confname[i], confval[i], 1);
-		}
+		setconfenv();
 		poperror();
 	}
 	kproc("alarm", alarmkproc, 0);
@@ -251,6 +143,42 @@
 }
 
 void
+userbootargs(void *base)
+{
+	char *argv[8];
+	int i, argc;
+
+#define UA(ka)	((char*)(ka) + ((uintptr)(USTKTOP - BY2PG) - (uintptr)base))
+	sp = (char*)base + BY2PG - sizeof(Tos);
+
+	/* push boot command line onto the stack */
+	sp -= BOOTLINELEN;
+	sp[BOOTLINELEN-1] = '\0';
+	memmove(sp, BOOTLINE, BOOTLINELEN-1);
+
+	/* parse boot command line */
+	argc = tokenize(sp, argv, nelem(argv));
+	if(argc < 1){
+		strcpy(sp, "boot");
+		argc = 0;
+		argv[argc++] = sp;
+	}
+
+	/* 4 byte word align stack */
+	sp = (char*)((uintptr)sp & ~3);
+
+	/* build argv on stack */
+	sp -= (argc+1)*BY2WD;
+	for(i=0; i<argc; i++)
+		((char**)sp)[i] = UA(argv[i]);
+	((char**)sp)[i] = nil;
+
+	sp = UA(sp);
+#undef UA
+	sp -= BY2WD;
+}
+
+void
 userinit(void)
 {
 	void *v;
@@ -294,7 +222,7 @@
 	v = tmpmap(pg);
 	memset(v, 0, BY2PG);
 	segpage(s, pg);
-	bootargs(v);
+	userbootargs(v);
 	tmpunmap(v);
 
 	/*
@@ -312,82 +240,6 @@
 	tmpunmap(v);
 
 	ready(p);
-}
-
-void
-bootargs(void *base)
-{
-	char *argv[8];
-	int i, argc;
-
-#define UA(ka)	((char*)(ka) + ((uintptr)(USTKTOP - BY2PG) - (uintptr)base))
-	sp = (char*)base + BY2PG - sizeof(Tos);
-
-	/* push boot command line onto the stack */
-	sp -= BOOTLINELEN;
-	sp[BOOTLINELEN-1] = '\0';
-	memmove(sp, BOOTLINE, BOOTLINELEN-1);
-
-	/* parse boot command line */
-	argc = tokenize(sp, argv, nelem(argv));
-	if(argc < 1){
-		strcpy(sp, "boot");
-		argc = 0;
-		argv[argc++] = sp;
-	}
-
-	/* 4 byte word align stack */
-	sp = (char*)((uintptr)sp & ~3);
-
-	/* build argv on stack */
-	sp -= (argc+1)*BY2WD;
-	for(i=0; i<argc; i++)
-		((char**)sp)[i] = UA(argv[i]);
-	((char**)sp)[i] = nil;
-
-	sp = UA(sp);
-#undef UA
-	sp -= BY2WD;
-}
-
-char*
-getconf(char *name)
-{
-	int i;
-
-	for(i = 0; i < nconf; i++)
-		if(cistrcmp(confname[i], name) == 0)
-			return confval[i];
-	return 0;
-}
-
-static void
-writeconf(void)
-{
-	char *p, *q;
-	int n;
-
-	p = getconfenv();
-
-	if(waserror()) {
-		free(p);
-		nexterror();
-	}
-
-	/* convert to name=value\n format */
-	for(q=p; *q; q++) {
-		q += strlen(q);
-		*q = '=';
-		q += strlen(q);
-		*q = '\n';
-	}
-	n = q - p + 1;
-	if(n >= BOOTARGSLEN)
-		error("kernel configuration too large");
-	memset(BOOTLINE, 0, BOOTLINELEN);
-	memmove(BOOTARGS, p, n);
-	poperror();
-	free(p);
 }
 
 void
--- a/sys/src/9/pc/mem.h
+++ b/sys/src/9/pc/mem.h
@@ -78,6 +78,17 @@
  */
 
 /*
+ * Where configuration info is left for the loaded programme.
+ * This will turn into a structure as more is done by the boot loader
+ * (e.g. why parse the .ini file twice?).
+ * There are 3584 bytes available at CONFADDR.
+ */
+#define BOOTLINE	((char*)CONFADDR)
+#define BOOTLINELEN	64
+#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
+#define BOOTARGSLEN	(4096-0x200-BOOTLINELEN)
+
+/*
  *  known x86 segments (in GDT) and their selectors
  */
 #define	NULLSEG	0	/* null segment */
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -53,6 +53,7 @@
 	memory.$O\
 	mmu.$O\
 	trap.$O\
+	bootargs.$O\
 	$CONF.root.$O\
 	$CONF.rootc.$O\
 	$DEVS\
--- a/sys/src/9/pc/screen.c
+++ b/sys/src/9/pc/screen.c
@@ -587,6 +587,36 @@
 	return vgalinearaddr0(scr, pa, sz);
 }
 
+char*
+rgbmask2chan(char *buf, int depth, u32int rm, u32int gm, u32int bm)
+{
+	u32int m[4], dm;	/* r,g,b,x */
+	char tmp[32];
+	int c, n;
+
+	dm = 1<<depth-1;
+	dm |= dm-1;
+
+	m[0] = rm & dm;
+	m[1] = gm & dm;
+	m[2] = bm & dm;
+	m[3] = (~(m[0] | m[1] | m[2])) & dm;
+
+	buf[0] = 0;
+Next:
+	for(c=0; c<4; c++){
+		for(n = 0; m[c] & (1<<n); n++)
+			;
+		if(n){
+			m[0] >>= n, m[1] >>= n, m[2] >>= n, m[3] >>= n;
+			snprint(tmp, sizeof tmp, "%c%d%s", "rgbx"[c], n, buf);
+			strcpy(buf, tmp);
+			goto Next;
+		}
+	}
+	return buf;
+}
+
 /*
  * called early on boot to attach to framebuffer
  * setup by bootloader/firmware or plan9.
@@ -682,11 +712,10 @@
 	char conf[100], chan[30];
 
 	conf[0] = '\0';
-	if(scr != nil && scr->paddr != 0)
+	if(scr != nil && scr->paddr != 0 && scr->gscreen != nil)
 		snprint(conf, sizeof(conf), "%dx%dx%d %s %#p %d\n",
 			scr->gscreen->r.max.x, scr->gscreen->r.max.y,
 			scr->gscreen->depth, chantostr(chan, scr->gscreen->chan),
 			scr->paddr, scr->apsize);
-
 	ksetenv("*bootscreen", conf, 1);
 }
--- a/sys/src/9/pc/screen.h
+++ b/sys/src/9/pc/screen.h
@@ -147,6 +147,7 @@
 extern int	screenaperture(int, int);
 extern Rectangle physgscreenr;	/* actual monitor size */
 extern void	blankscreen(int);
+extern char*	rgbmask2chan(char *buf, int depth, u32int rm, u32int gm, u32int bm);
 
 extern void	bootscreeninit(void);
 extern void	bootscreenconf(VGAscr*);
--- a/sys/src/9/pc/vgavesa.c
+++ b/sys/src/9/pc/vgavesa.c
@@ -31,6 +31,17 @@
 #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
 #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
 
+typedef struct Vmode Vmode;
+struct Vmode
+{
+	char	chan[32];
+	int	attr;	/* flags */
+	int	bpl;
+	int	dx, dy;
+	int	depth;
+	ulong	paddr;
+};
+
 static uchar*
 vbesetup(Ureg386 *u, int ax)
 {
@@ -95,13 +106,37 @@
 	return p;
 }
 
+static char*
+vmode(Vmode *m, uchar *p)
+{
+	m->attr = WORD(p);
+	if(!(m->attr & (1<<4)))
+		return "not in VESA graphics mode";
+	if(!(m->attr & (1<<7)))
+		return "not in linear graphics mode";
+	m->bpl = WORD(p+16);
+	m->dx = WORD(p+18);
+	m->dy = WORD(p+20);
+	m->depth = p[25];
+	m->paddr = LONG(p+40);
+	if(m->depth <= 8)
+		snprint(m->chan, sizeof m->chan, "%c%d", 
+			(m->attr & (1<<3)) ? 'm' : 'k', m->depth);
+	else
+		rgbmask2chan(m->chan, m->depth,
+			(1UL<<p[31])-1 << p[32],
+			(1UL<<p[33])-1 << p[34],
+			(1UL<<p[35])-1 << p[36]);
+	return nil;
+}
+
 static void
 vesalinear(VGAscr *scr, int, int)
 {
 	int i, mode, size, havesize;
-	ulong paddr;
 	Pcidev *pci;
-	uchar *p;
+	char *err;
+	Vmode m;
 
 	vbecheck();
 	mode = vbegetmode();
@@ -111,14 +146,10 @@
 		error("not in linear graphics mode");
 	 */
 	mode &= 0x3FFF;
-	p = vbemodeinfo(mode);
-	if(!(WORD(p+0) & (1<<4)))
-		error("not in VESA graphics mode");
-	if(!(WORD(p+0) & (1<<7)))
-		error("not in linear graphics mode");
+	if((err = vmode(&m, vbemodeinfo(mode))) != nil)
+		error(err);
 
-	paddr = LONG(p+40);
-	size = WORD(p+20)*WORD(p+16);
+	size = m.dy * m.bpl;
 
 	/*
 	 * figure out max size of memory so that we have
@@ -136,8 +167,8 @@
 				continue;
 			a = pci->mem[i].bar & ~0xF;
 			e = a + pci->mem[i].size;
-			if(paddr >= a && (paddr+size) <= e){
-				size = e - paddr;
+			if(m.paddr >= a && (m.paddr+size) <= e){
+				size = e - m.paddr;
 				havesize = 1;
 				break;
 			}
@@ -151,7 +182,7 @@
 		else
 			size = ROUND(size, 1024*1024);
 
-	vgalinearaddr(scr, paddr, size);
+	vgalinearaddr(scr, m.paddr, size);
 	if(scr->apsize)
 		addvgaseg("vesascreen", scr->paddr, scr->apsize);
 
@@ -222,3 +253,19 @@
 	vesalinear,
 	vesadrawinit,
 };
+
+/*
+ * called from multibootargs() to convert
+ * vbe mode info (passed from bootloader)
+ * to *bootscreen= parameter
+ */
+char*
+vesabootscreenconf(char *s, char *e, uchar *p)
+{
+	Vmode m;
+
+	if(vmode(&m, p) != nil)
+		return s;
+	return seprint(s, e, "*bootscreen=%dx%dx%d %s %#lux\n",
+		m.bpl * 8 / m.depth, m.dy, m.depth, m.chan, m.paddr);
+}
--- a/sys/src/9/pc64/fns.h
+++ b/sys/src/9/pc64/fns.h
@@ -8,7 +8,7 @@
 int	bios32ci(BIOS32si*, BIOS32ci*);
 void	bios32close(BIOS32si*);
 BIOS32si* bios32open(char*);
-void	bootargs(void*);
+void	bootargsinit(void);
 uintptr	cankaddr(uintptr);
 int	checksum(void *, int);
 void	clockintr(Ureg*, void*);
@@ -171,6 +171,7 @@
 void	realmode(Ureg*);
 void	screeninit(void);
 void	(*screenputs)(char*, int);
+void	setconfenv(void);
 void*	sigsearch(char*);
 void	syncclock(void);
 void	syscallentry(void);
@@ -191,6 +192,7 @@
 void*	vmap(uintptr, int);
 void	vunmap(void*, int);
 void	wbinvd(void);
+void	writeconf(void);
 int	wrmsr(int, vlong);
 int	xchgw(ushort*, int);
 void	rdrandbuf(void*, ulong);
--- a/sys/src/9/pc64/l.s
+++ b/sys/src/9/pc64/l.s
@@ -37,8 +37,8 @@
  */
 TEXT _multibootheader<>(SB), 1, $-4
 	LONG	$0x1BADB002			/* magic */
-	LONG	$0x00010003			/* flags */
-	LONG	$-(0x1BADB002 + 0x00010003)	/* checksum */
+	LONG	$0x00010007			/* flags */
+	LONG	$-(0x1BADB002 + 0x00010007)	/* checksum */
 	LONG	$_multibootheader<>-KZERO(SB)	/* header_addr */
 	LONG	$_protected<>-KZERO(SB)		/* load_addr */
 	LONG	$edata-KZERO(SB)		/* load_end_addr */
@@ -47,7 +47,7 @@
 	LONG	$0				/* mode_type */
 	LONG	$0				/* width */
 	LONG	$0				/* height */
-	LONG	$0				/* depth */
+	LONG	$32				/* depth */
 
 /* 
  * the kernel expects the data segment to be page-aligned
--- a/sys/src/9/pc64/main.c
+++ b/sys/src/9/pc64/main.c
@@ -10,22 +10,7 @@
 #include	"pool.h"
 #include	"reboot.h"
 
-/*
- * Where configuration info is left for the loaded programme.
- * This will turn into a structure as more is done by the boot loader
- * (e.g. why parse the .ini file twice?).
- * There are 3584 bytes available at CONFADDR.
- */
-#define BOOTLINE	((char*)CONFADDR)
-#define BOOTLINELEN	64
-#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
-#define	BOOTARGSLEN	(4096-0x200-BOOTLINELEN)
-#define	MAXCONF		64
-
 Conf conf;
-char *confname[MAXCONF];
-char *confval[MAXCONF];
-int nconf;
 int delaylink;
 int idle_spin;
 
@@ -34,136 +19,6 @@
 extern void (*i8237alloc)(void);
 extern void bootscreeninit(void);
 
-static void
-multibootargs(void)
-{
-	extern ulong multibootptr;
-	ulong *multiboot;
-	char *cp, *ep;
-	ulong *m, l;
-
-	if(multibootptr == 0)
-		return;
-
-	multiboot = (ulong*)KADDR(multibootptr);
-	/* command line */
-	if((multiboot[0] & (1<<2)) != 0)
-		strncpy(BOOTLINE, KADDR(multiboot[4]), BOOTLINELEN-1);
-
-	cp = BOOTARGS;
-	ep = cp + BOOTARGSLEN-1;
-
-	/* memory map */
-	if((multiboot[0] & (1<<6)) != 0 && (l = multiboot[11]) >= 24){
-		cp = seprint(cp, ep, "*e820=");
-		m = KADDR(multiboot[12]);
-		while(m[0] >= 20 && m[0]+4 <= l){
-			uvlong base, size;
-			m++;
-			base = ((uvlong)m[0] | (uvlong)m[1]<<32);
-			size = ((uvlong)m[2] | (uvlong)m[3]<<32);
-			cp = seprint(cp, ep, "%.1lux %.16llux %.16llux ",
-				m[4] & 0xF, base, base+size);
-			l -= m[-1]+4;
-			m = (ulong*)((uintptr)m + m[-1]);
-		}
-		cp[-1] = '\n';
-	}
-
-	/* plan9.ini passed as the first module */
-	if((multiboot[0] & (1<<3)) != 0 && multiboot[5] > 0){
-		m = KADDR(multiboot[6]);
-		l = m[1] - m[0];
-		m = KADDR(m[0]);
-		if(cp+l > ep)
-			l = ep - cp;
-		memmove(cp, m, l);
-		cp += l;
-	}
-	*cp = 0;
-}
-
-static void
-options(void)
-{
-	long i, n;
-	char *cp, *line[MAXCONF], *p, *q;
-
-	multibootargs();
-
-	/*
-	 *  parse configuration args from dos file plan9.ini
-	 */
-	cp = BOOTARGS;	/* where b.com leaves its config */
-	cp[BOOTARGSLEN-1] = 0;
-
-	/*
-	 * Strip out '\r', change '\t' -> ' '.
-	 */
-	p = cp;
-	for(q = cp; *q; q++){
-		if(*q == '\r')
-			continue;
-		if(*q == '\t')
-			*q = ' ';
-		*p++ = *q;
-	}
-	*p = 0;
-
-	n = getfields(cp, line, MAXCONF, 1, "\n");
-	for(i = 0; i < n; i++){
-		if(*line[i] == '#')
-			continue;
-		cp = strchr(line[i], '=');
-		if(cp == nil)
-			continue;
-		*cp++ = '\0';
-		confname[nconf] = line[i];
-		confval[nconf] = cp;
-		nconf++;
-	}
-}
-
-char*
-getconf(char *name)
-{
-	int i;
-
-	for(i = 0; i < nconf; i++)
-		if(cistrcmp(confname[i], name) == 0)
-			return confval[i];
-	return 0;
-}
-
-static void
-writeconf(void)
-{
-	char *p, *q;
-	int n;
-
-	p = getconfenv();
-
-	if(waserror()) {
-		free(p);
-		nexterror();
-	}
-
-	/* convert to name=value\n format */
-	for(q=p; *q; q++) {
-		q += strlen(q);
-		*q = '=';
-		q += strlen(q);
-		*q = '\n';
-	}
-	n = q - p + 1;
-	if(n >= BOOTARGSLEN)
-		error("kernel configuration too large");
-	memset(BOOTLINE, 0, BOOTLINELEN);
-	memmove(BOOTARGS, p, n);
-	poperror();
-	free(p);
-}
-
 void
 confinit(void)
 {
@@ -332,45 +187,8 @@
 }
 
 void
-bootargs(void *base)
-{
-	char *argv[8];
-	int i, argc;
-
-#define UA(ka)	((char*)(ka) + ((uintptr)(USTKTOP - BY2PG) - (uintptr)base))
-	sp = (char*)base + BY2PG - sizeof(Tos);
-
-	/* push boot command line onto the stack */
-	sp -= BOOTLINELEN;
-	sp[BOOTLINELEN-1] = '\0';
-	memmove(sp, BOOTLINE, BOOTLINELEN-1);
-
-	/* parse boot command line */
-	argc = tokenize(sp, argv, nelem(argv));
-	if(argc < 1){
-		strcpy(sp, "boot");
-		argc = 0;
-		argv[argc++] = sp;
-	}
-
-	/* 8 byte word align stack */
-	sp = (char*)((uintptr)sp & ~7);
-
-	/* build argv on stack */
-	sp -= (argc+1)*BY2WD;
-	for(i=0; i<argc; i++)
-		((char**)sp)[i] = UA(argv[i]);
-	((char**)sp)[i] = nil;
-
-	sp = UA(sp);
-#undef UA
-	sp -= BY2WD;
-}
-
-void
 init0(void)
 {
-	int i;
 	char buf[2*KNAMELEN];
 
 	up->nerrlab = 0;
@@ -396,11 +214,7 @@
 			ksetenv("service", "cpu", 0);
 		else
 			ksetenv("service", "terminal", 0);
-		for(i = 0; i < nconf; i++){
-			if(confname[i][0] != '*')
-				ksetenv(confname[i], confval[i], 0);
-			ksetenv(confname[i], confval[i], 1);
-		}
+		setconfenv();
 		poperror();
 	}
 	kproc("alarm", alarmkproc, 0);
@@ -409,6 +223,42 @@
 }
 
 void
+userbootargs(void *base)
+{
+	char *argv[8];
+	int i, argc;
+
+#define UA(ka)	((char*)(ka) + ((uintptr)(USTKTOP - BY2PG) - (uintptr)base))
+	sp = (char*)base + BY2PG - sizeof(Tos);
+
+	/* push boot command line onto the stack */
+	sp -= BOOTLINELEN;
+	sp[BOOTLINELEN-1] = '\0';
+	memmove(sp, BOOTLINE, BOOTLINELEN-1);
+
+	/* parse boot command line */
+	argc = tokenize(sp, argv, nelem(argv));
+	if(argc < 1){
+		strcpy(sp, "boot");
+		argc = 0;
+		argv[argc++] = sp;
+	}
+
+	/* 8 byte word align stack */
+	sp = (char*)((uintptr)sp & ~7);
+
+	/* build argv on stack */
+	sp -= (argc+1)*BY2WD;
+	for(i=0; i<argc; i++)
+		((char**)sp)[i] = UA(argv[i]);
+	((char**)sp)[i] = nil;
+
+	sp = UA(sp);
+#undef UA
+	sp -= BY2WD;
+}
+
+void
 userinit(void)
 {
 	void *v;
@@ -452,7 +302,7 @@
 	v = kmap(pg);
 	memset(v, 0, BY2PG);
 	segpage(s, pg);
-	bootargs(v);
+	userbootargs(v);
 	kunmap(v);
 
 	/*
@@ -480,7 +330,7 @@
 main()
 {
 	mach0init();
-	options();
+	bootargsinit();
 	ioinit();
 	i8250console();
 	quotefmtinstall();
--- a/sys/src/9/pc64/mem.h
+++ b/sys/src/9/pc64/mem.h
@@ -81,6 +81,17 @@
 #define	MACHSIZE	(2*KSTACK)
 
 /*
+ * Where configuration info is left for the loaded programme.
+ * This will turn into a structure as more is done by the boot loader
+ * (e.g. why parse the .ini file twice?).
+ * There are 3584 bytes available at CONFADDR.
+ */
+#define BOOTLINE	((char*)CONFADDR)
+#define BOOTLINELEN	64
+#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
+#define BOOTARGSLEN	(4096-0x200-BOOTLINELEN)
+
+/*
  *  known x86 segments (in GDT) and their selectors
  */
 #define	NULLSEG	0	/* null segment */
--- a/sys/src/9/pc64/mkfile
+++ b/sys/src/9/pc64/mkfile
@@ -50,6 +50,7 @@
 	memory.$O\
 	mmu.$O\
 	trap.$O\
+	bootargs.$O\
 	$CONF.root.$O\
 	$CONF.rootc.$O\
 	$DEVS\