shithub: riscv

Download patch

ref: 16e08adb32778864035cc582c3a9163d3032ca4b
parent: e81e1a4aeddad2bc612c9c5243573250b6ff33a4
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu Oct 23 19:11:49 EDT 2014

efi: add initial pxe support (v4 only)

--- a/sys/src/boot/efi/efi.c
+++ b/sys/src/boot/efi/efi.c
@@ -2,15 +2,14 @@
 #include "fns.h"
 #include "efi.h"
 
-enum {
-	MAXPATH = 128,
-};
-
-UINTN MK;
+UINTN MK;
 EFI_HANDLE IH;
 EFI_SYSTEM_TABLE *ST;
-EFI_FILE_PROTOCOL *root;
 
+void* (*open)(char *name);
+int (*read)(void *f, void *data, int len);
+void (*close)(void *f);
+
 void
 putc(int c)
 {
@@ -18,7 +17,7 @@
 
 	w[0] = c;
 	w[1] = 0;
-	eficall(2, ST->ConOut->OutputString, ST->ConOut, w);
+	eficall(ST->ConOut->OutputString, ST->ConOut, w);
 }
 
 int
@@ -26,7 +25,7 @@
 {
 	EFI_INPUT_KEY k;
 
-	if(eficall(2, ST->ConIn->ReadKeyStroke, ST->ConIn, &k))
+	if(eficall(ST->ConIn->ReadKeyStroke, ST->ConIn, &k))
 		return 0;
 	return k.UnicodeChar;
 }
@@ -34,79 +33,16 @@
 void
 usleep(int us)
 {
-	eficall(1, ST->BootServices->Stall, (UINTN)us);
+	eficall(ST->BootServices->Stall, (UINTN)us);
 }
 
 void
 unload(void)
 {
-	eficall(2, ST->BootServices->ExitBootServices, IH, MK);
+	eficall(ST->BootServices->ExitBootServices, IH, MK);
 }
 
-void
-fsinit(void)
-{
-	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
-
-	fs = nil;
-	root = nil;
-	if(eficall(3, ST->BootServices->LocateProtocol,
-		&EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, nil, &fs))
-		return;
-	if(eficall(2, fs->OpenVolume, fs, &root)){
-		root = nil;
-		return;
-	}
-}
-
 static void
-towpath(CHAR16 *w, int nw, char *s)
-{
-	int i;
-
-	for(i=0; *s && i<nw-1; i++){
-		*w = *s++;
-		if(*w == '/')
-			*w = '\\';
-		w++;
-	}
-	*w = 0;
-}
-
-EFI_FILE_PROTOCOL*
-fswalk(EFI_FILE_PROTOCOL *dir, char *name)
-{
-	CHAR16 wname[MAXPATH];
-	EFI_FILE_PROTOCOL *fp;
-
-	towpath(wname, MAXPATH, name);
-
-	fp = nil;
-	if(eficall(5, dir->Open, dir, &fp, wname, (UINT64)1, (UINT64)1))
-		return nil;
-	return fp;
-}
-
-
-int
-read(void *f, void *data, int len)
-{
-	UINTN size;
-
-	size = len;
-	if(eficall(3, ((EFI_FILE_PROTOCOL*)f)->Read, f, &size, data))
-		return 0;
-	return (int)size;
-}
-
-void
-close(void *f)
-{
-	eficall(1, ((EFI_FILE_PROTOCOL*)f)->Close, f);
-}
-
-
-static void
 memconf(char **cfg)
 {
 	static uchar memtype[EfiMaxMemoryType] = {
@@ -134,7 +70,7 @@
 	mapsize = sizeof(mapbuf);
 	entsize = sizeof(EFI_MEMORY_DESCRIPTOR);
 	entvers = 1;
-	if(eficall(5, ST->BootServices->GetMemoryMap, &mapsize, mapbuf, &MK, &entsize, &entvers))
+	if(eficall(ST->BootServices->GetMemoryMap, &mapsize, mapbuf, &MK, &entsize, &entvers))
 		return;
 
 	s = *cfg;
@@ -165,6 +101,16 @@
 static void
 acpiconf(char **cfg)
 {
+	static EFI_GUID ACPI_20_TABLE_GUID = {
+		0x8868e871, 0xe4f1, 0x11d3,
+		0xbc, 0x22, 0x00, 0x80,
+		0xc7, 0x3c, 0x88, 0x81,
+	};
+	static EFI_GUID ACPI_10_TABLE_GUID = {
+		0xeb9d2d30, 0x2d88, 0x11d3,
+		0x9a, 0x16, 0x00, 0x90,
+		0x27, 0x3f, 0xc1, 0x4d,
+	};
 	EFI_CONFIGURATION_TABLE *t;
 	uintptr pa;
 	char *s;
@@ -220,6 +166,11 @@
 static void
 screenconf(char **cfg)
 {
+	static EFI_GUID EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = {
+		0x9042a9de, 0x23dc, 0x4a38,
+		0x96, 0xfb, 0x7a, 0xde,
+		0xd0, 0x80, 0x51, 0x6a,
+	};
 	EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
 	EFI_HANDLE *Handles;
 	UINTN Count;
@@ -231,13 +182,13 @@
 
 	Count = 0;
 	Handles = nil;
-	if(eficall(5, ST->BootServices->LocateHandleBuffer,
+	if(eficall(ST->BootServices->LocateHandleBuffer,
 		ByProtocol, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, nil, &Count, &Handles))
 		return;
 
 	for(i=0; i<Count; i++){
 		gop = nil;
-		if(eficall(3, ST->BootServices->HandleProtocol,
+		if(eficall(ST->BootServices->HandleProtocol,
 			Handles[i], &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, &gop))
 			continue;
 
@@ -331,7 +282,7 @@
 }
 
 EFI_STATUS
-main(EFI_HANDLE ih, EFI_SYSTEM_TABLE *st)
+efimain(EFI_HANDLE ih, EFI_SYSTEM_TABLE *st)
 {
 	char path[MAXPATH], *kern;
 	void *f;
@@ -339,12 +290,13 @@
 	IH = ih;
 	ST = st;
 
-	fsinit();
+	f = pxeinit();
+	if(f == nil)
+		f = fsinit();
 
-	f = fswalk(root, "/plan9.ini");
 	for(;;){
 		kern = configure(f, path);
-		f = fswalk(root, kern);
+		f = open(kern);
 		if(f == nil){
 			print("not found\n");
 			continue;
--- a/sys/src/boot/efi/efi.h
+++ b/sys/src/boot/efi/efi.h
@@ -4,6 +4,7 @@
 typedef ushort	UINT16;
 typedef ulong	UINT32;
 typedef uvlong	UINT64;
+typedef UINT8	BOOLEAN;
 
 typedef uintptr	UINTN;
 
@@ -87,41 +88,6 @@
 	EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
 } EFI_GRAPHICS_OUTPUT_PROTOCOL;
 
-EFI_GUID EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = {
-	0x9042a9de, 0x23dc, 0x4a38,
-	0x96, 0xfb, 0x7a, 0xde,
-	0xd0, 0x80, 0x51, 0x6a,
-};
-
-typedef struct {
-	UINT64		Revision;
-	void		*Open;
-	void		*Close;
-	void		*Delete;
-	void		*Read;
-	void		*Write;
-	void		*GetPosition;
-	void		*SetPosition;
-	void		*GetInfo;
-	void		*SetInfo;
-	void		*Flush;
-	void		*OpenEx;
-	void		*ReadEx;
-	void		*WriteEx;
-	void		*FlushEx;
-} EFI_FILE_PROTOCOL;
-
-typedef struct {
-	UINT64		Revision;
-	void		*OpenVolume;
-} EFI_SIMPLE_FILE_SYSTEM_PROTOCOL;
-
-EFI_GUID EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = {
-	0x0964e5b22, 0x6459, 0x11d2,
-	0x8e, 0x39, 0x00, 0xa0,
-	0xc9, 0x69, 0x72, 0x3b,
-};
-
 enum {
 	EfiReservedMemoryType,
 	EfiLoaderCode,
@@ -243,19 +209,6 @@
 	void		*QueryVariableInfo;
 } EFI_RUNTIME_SERVICES;
 
-
-EFI_GUID ACPI_20_TABLE_GUID = {
-	0x8868e871, 0xe4f1, 0x11d3,
-	0xbc, 0x22, 0x00, 0x80,
-	0xc7, 0x3c, 0x88, 0x81,
-};
-
-EFI_GUID ACPI_10_TABLE_GUID = {
-	0xeb9d2d30, 0x2d88, 0x11d3,
-	0x9a, 0x16, 0x00, 0x90,
-	0x27, 0x3f, 0xc1, 0x4d,
-};
-
 typedef struct {
 	EFI_GUID	VendorGuid;
 	void		*VendorTable;
@@ -282,3 +235,6 @@
 	UINTN			NumberOfTableEntries;
 	EFI_CONFIGURATION_TABLE	*ConfigurationTable;
 } EFI_SYSTEM_TABLE;
+
+extern EFI_SYSTEM_TABLE *ST;
+extern EFI_HANDLE IH;
--- a/sys/src/boot/efi/fns.h
+++ b/sys/src/boot/efi/fns.h
@@ -1,11 +1,20 @@
+enum {
+	MAXPATH = 128,
+};
+
 extern char hex[];
 
 void usleep(int t);
 void jump(void *pc);
 
-int read(void *f, void *data, int len);
+void* pxeinit(void);
+void* fsinit(void);
+
+void* (*open)(char *name);
+int (*read)(void *f, void *data, int len);
+void (*close)(void *f);
+
 int readn(void *f, void *data, int len);
-void close(void *f);
 void unload(void);
 
 int getc(void);
@@ -25,5 +34,5 @@
 char *hexfmt(char *s, int i, uvlong a);
 char *decfmt(char *s, int i, ulong a);
 
-long eficall(long narg, void *proc, ...);
+uintptr eficall(void *proc, ...);
 void eficonfig(char **cfg);
--- /dev/null
+++ b/sys/src/boot/efi/fs.c
@@ -1,0 +1,106 @@
+#include <u.h>
+#include "fns.h"
+#include "efi.h"
+
+typedef struct {
+	UINT64		Revision;
+	void		*Open;
+	void		*Close;
+	void		*Delete;
+	void		*Read;
+	void		*Write;
+	void		*GetPosition;
+	void		*SetPosition;
+	void		*GetInfo;
+	void		*SetInfo;
+	void		*Flush;
+	void		*OpenEx;
+	void		*ReadEx;
+	void		*WriteEx;
+	void		*FlushEx;
+} EFI_FILE_PROTOCOL;
+
+typedef struct {
+	UINT64		Revision;
+	void		*OpenVolume;
+} EFI_SIMPLE_FILE_SYSTEM_PROTOCOL;
+
+static
+EFI_GUID EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = {
+	0x0964e5b22, 0x6459, 0x11d2,
+	0x8e, 0x39, 0x00, 0xa0,
+	0xc9, 0x69, 0x72, 0x3b,
+};
+
+static
+EFI_FILE_PROTOCOL *fsroot;
+
+static void
+towpath(CHAR16 *w, int nw, char *s)
+{
+	int i;
+
+	for(i=0; *s && i<nw-1; i++){
+		*w = *s++;
+		if(*w == '/')
+			*w = '\\';
+		w++;
+	}
+	*w = 0;
+}
+
+static void*
+fsopen(char *name)
+{
+	CHAR16 wname[MAXPATH];
+	EFI_FILE_PROTOCOL *fp;
+
+	if(fsroot == nil)
+		return nil;
+
+	towpath(wname, MAXPATH, name);
+
+	fp = nil;
+	if(eficall(fsroot->Open, fsroot, &fp, wname, (UINT64)1, (UINT64)1))
+		return nil;
+	return fp;
+}
+
+static int
+fsread(void *f, void *data, int len)
+{
+	UINTN size;
+
+	size = len;
+	if(eficall(((EFI_FILE_PROTOCOL*)f)->Read, f, &size, data))
+		return 0;
+	return (int)size;
+}
+
+static void
+fsclose(void *f)
+{
+	eficall(((EFI_FILE_PROTOCOL*)f)->Close, f);
+}
+
+void*
+fsinit(void)
+{
+	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
+
+	fs = nil;
+	fsroot = nil;
+	if(eficall(ST->BootServices->LocateProtocol,
+		&EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, nil, &fs))
+		return nil;
+	if(eficall(fs->OpenVolume, fs, &fsroot)){
+		fsroot = nil;
+		return nil;
+	}
+
+	read = fsread;
+	close = fsclose;
+	open = fsopen;
+
+	return fsopen("/plan9.ini");
+}
--- a/sys/src/boot/efi/mkfile
+++ b/sys/src/boot/efi/mkfile
@@ -13,7 +13,7 @@
 clean:V:
 	rm -f *.[68] *.out $TARG
 
-bootia32.efi:	pe32.8 efi.8 sub.8
+bootia32.efi:	pe32.8 efi.8 fs.8 pxe.8 sub.8
 	8l -l -H3 -T$IMAGEBASE -o $target $prereq
 
 pe32.8:	pe32.s
@@ -22,6 +22,12 @@
 efi.8:	efi.c efi.h
 	8c $CFLAGS efi.c
 
+fs.8:	fs.c efi.h
+	8c $CFLAGS fs.c
+
+pxe.8:	pxe.c efi.h
+	8c $CFLAGS pxe.c
+
 sub.8:	sub.c
 	8c $CFLAGS sub.c
 
@@ -28,7 +34,7 @@
 %.8:	$HFILES
 
 
-bootx64.efi:	pe64.6 efi.6 sub.6
+bootx64.efi:	pe64.6 efi.6 fs.6 pxe.6 sub.6
 	6l -l -s -R1 -T$IMAGEBASE -o bootx64.out $prereq
 	dd -if bootx64.out -bs 1 -iseek 40 >$target
 
@@ -37,6 +43,12 @@
 
 efi.6:	efi.c efi.h
 	6c $CFLAGS efi.c
+
+fs.6:	fs.c efi.h
+	6c $CFLAGS fs.c
+
+pxe.6:	pxe.c efi.h
+	6c $CFLAGS pxe.c
 
 sub.6:	sub.c
 	6c $CFLAGS sub.c
--- a/sys/src/boot/efi/pe32.s
+++ b/sys/src/boot/efi/pe32.s
@@ -129,7 +129,7 @@
 	MOVL $edata-IMAGEBASE(SB), CX
 	CLD
 	REP; MOVSB
-	MOVL $main(SB), DI
+	MOVL $efimain(SB), DI
 	MOVL DI, (SP)
 	RET
 
@@ -138,12 +138,22 @@
 	MOVL 4(SP), AX
 	JMP *AX
 
-
 TEXT eficall(SB), 1, $0
-	MOVL 0(SP), DI	/* saved by callee */
-	MOVL 8(SP), AX
-	ADDL $12, SP
+	MOVL SP, SI
+	MOVL SP, DI
+	MOVL $(4*16), CX
+	SUBL CX, DI
+	ANDL $~15ULL, DI
+	SUBL $8, DI
+
+	MOVL 4(SI), AX
+	LEAL 8(DI), SP
+
+	CLD
+	REP; MOVSB
+	SUBL $(4*16), SI
+
 	CALL AX
-	SUBL $12, SP
-	MOVL DI, 0(SP)
+
+	MOVL SI, SP
 	RET
--- a/sys/src/boot/efi/pe64.s
+++ b/sys/src/boot/efi/pe64.s
@@ -136,25 +136,28 @@
 	REP; MOVSB
 
 	MOVQ 16(SP), BP
-	MOVQ $main(SB), DI
+	MOVQ $efimain(SB), DI
 	MOVQ DI, (SP)
 	RET
 
 TEXT eficall(SB), 1, $-4
-	MOVQ 0(SP), DI	/* saved by callee */
-	MOVQ 16(SP), AX
-	ADDQ $24, SP
+	MOVQ SP, SI
+	MOVQ SP, DI
+	MOVL $(8*16), CX
+	SUBQ CX, DI
+	ANDQ $~15ULL, DI
+	LEAQ 16(DI), SP
+	CLD
+	REP; MOVSB
+	SUBQ $(8*16), SI
 
-	/* arguments in regisyers */
 	MOVQ 0(SP), CX
 	MOVQ 8(SP), DX
 	MOVQ 16(SP), R8
 	MOVQ 24(SP), R9
+	CALL BP
 
-	CALL AX
-
-	SUBQ $24, SP
-	MOVQ DI, 0(SP)
+	MOVQ SI, SP
 	RET
 
 #include "mem.h"
--- /dev/null
+++ b/sys/src/boot/efi/pxe.c
@@ -1,0 +1,359 @@
+#include <u.h>
+#include "fns.h"
+#include "efi.h"
+
+typedef UINT16	EFI_PXE_BASE_CODE_UDP_PORT;
+
+typedef struct {
+	UINT8		Addr[4];
+} EFI_IPv4_ADDRESS;
+
+typedef struct {
+	UINT8		Addr[16];
+} EFI_IPv6_ADDRESS;
+
+typedef union {
+	UINT32			Addr[4];
+	EFI_IPv4_ADDRESS	v4;
+	EFI_IPv6_ADDRESS	v6;
+} EFI_IP_ADDRESS;
+
+typedef struct {
+	UINT8		Addr[32];
+} EFI_MAC_ADDRESS;
+
+typedef struct {
+	UINT8		BootpOpcode;
+	UINT8		BootpHwType;
+	UINT8		BootpHwAddrLen;
+	UINT8		BootpGateHops;
+	UINT32		BootpIdent;
+	UINT16		BootpSeconds;
+	UINT16		BootpFlags;
+	UINT8		BootpCiAddr[4];
+	UINT8		BootpYiAddr[4];
+	UINT8		BootpSiAddr[4];
+	UINT8		BootpGiAddr[4];
+	UINT8		BootpHwAddr[16];
+	UINT8		BootpSrvName[64];
+	UINT8		BootpBootFile[128];
+	UINT32		DhcpMagik;
+	UINT8		DhcpOptions[56];		
+} EFI_PXE_BASE_CODE_DHCPV4_PACKET;
+
+typedef struct {
+	BOOLEAN		Started;
+	BOOLEAN		Ipv6Available;
+	BOOLEAN		Ipv6Supported;
+	BOOLEAN		UsingIpv6;
+	BOOLEAN		BisSupported;
+	BOOLEAN		BisDetected;
+	BOOLEAN		AutoArp;
+	BOOLEAN		SendGUID;
+	BOOLEAN		DhcpDiscoverValid;
+	BOOLEAN		DhcpAckReceived;
+	BOOLEAN		ProxyOfferReceived;
+	BOOLEAN		PxeDiscoverValid;
+	BOOLEAN		PxeReplyReceived;
+	BOOLEAN		PxeBisReplyReceived;
+	BOOLEAN		IcmpErrorReceived;
+	BOOLEAN		TftpErrorReceived;
+	BOOLEAN		MakeCallbacks;
+
+	UINT8		TTL;
+	UINT8		ToS;
+
+	UINT8		Reserved;
+
+	UINT8		StationIp[16];
+	UINT8		SubnetMask[16];
+
+	UINT8		DhcpDiscover[1472];
+	UINT8		DhcpAck[1472];
+} EFI_PXE_BASE_CODE_MODE;
+
+typedef struct {
+	UINT64		Revision;
+	void		*Start;
+	void		*Stop;
+	void		*Dhcp;
+	void		*Discover;
+	void		*Mtftp;
+	void		*UdpWrite;
+	void		*UdpRead;
+	void		*SetIpFilter;
+	void		*Arp;
+	void		*SetParameters;
+	void		*SetStationIp;
+	void		*SetPackets;
+	EFI_PXE_BASE_CODE_MODE	*Mode;
+} EFI_PXE_BASE_CODE_PROTOCOL;
+
+
+enum {
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Tftp_OACK	= 6,
+
+	TftpPort	= 69,
+
+	Segsize		= 512,
+};
+
+static
+EFI_GUID EFI_PXE_BASE_CODE_PROTOCOL_GUID = {
+	0x03C4E603, 0xAC28, 0x11D3,
+	0x9A, 0x2D, 0x00, 0x90,
+	0x27, 0x3F, 0xC1, 0x4D,
+};
+
+static
+EFI_PXE_BASE_CODE_PROTOCOL *pxe;
+
+static
+EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp;
+
+typedef struct Tftp Tftp;
+struct Tftp
+{
+	EFI_IP_ADDRESS sip;
+	EFI_IP_ADDRESS dip;
+
+	EFI_PXE_BASE_CODE_UDP_PORT sport;
+	EFI_PXE_BASE_CODE_UDP_PORT dport;
+
+	char *rp;
+	char *ep;
+
+	int seq;
+	int eof;
+	
+	char pkt[2+2+Segsize];
+	char nul;
+};
+
+static void
+puts(void *x, ushort v)
+{
+	uchar *p = x;
+
+	p[1] = (v>>8) & 0xFF;
+	p[0] = v & 0xFF;
+}
+
+static ushort
+gets(void *x)
+{
+	uchar *p = x;
+
+	return p[1]<<8 | p[0];
+}
+
+static void
+hnputs(void *x, ushort v)
+{
+	uchar *p = x;
+
+	p[0] = (v>>8) & 0xFF;
+	p[1] = v & 0xFF;
+}
+
+static ushort
+nhgets(void *x)
+{
+	uchar *p = x;
+
+	return p[0]<<8 | p[1];
+}
+
+enum {
+	ANY_SRC_IP	= 0x0001,
+	ANY_SRC_PORT	= 0x0002,
+	ANY_DEST_IP	= 0x0004,
+	ANY_DEST_PORT	= 0x0008,
+	USE_FILTER	= 0x0010,
+	MAY_FRAGMENT	= 0x0020,
+};
+
+static int
+udpread(EFI_IP_ADDRESS *sip, EFI_IP_ADDRESS *dip, 
+	EFI_PXE_BASE_CODE_UDP_PORT *sport, 
+	EFI_PXE_BASE_CODE_UDP_PORT dport, 
+	int *len, void *data)
+{
+	UINTN size;
+
+	size = *len;
+	if(eficall(pxe->UdpRead, pxe, (UINTN)ANY_SRC_PORT, dip, &dport, sip, sport, nil, nil, &size, data))
+		return -1;
+
+	*len = size;
+	return 0;
+}
+
+static int
+udpwrite(EFI_IP_ADDRESS *dip,
+	EFI_PXE_BASE_CODE_UDP_PORT sport, 
+	EFI_PXE_BASE_CODE_UDP_PORT dport,
+	int len, void *data)
+{
+	UINTN size;
+
+	size = len;
+	if(eficall(pxe->UdpWrite, pxe, (UINTN)MAY_FRAGMENT, dip, &dport, nil, nil, &sport, nil, nil, &size, data))
+		return -1;
+
+	return 0;
+}
+
+static int
+pxeread(void *f, void *data, int len)
+{
+	Tftp *t = f;
+	int seq, n;
+
+	while(!t->eof && t->rp >= t->ep){
+		for(;;){
+			n = sizeof(t->pkt);
+			if(udpread(&t->dip, &t->sip, &t->dport, t->sport, &n, t->pkt))
+				continue;
+			if(n >= 4)
+				break;
+		}
+		switch(nhgets(t->pkt)){
+		case Tftp_DATA:
+			seq = nhgets(t->pkt+2);
+			if(seq > t->seq){
+				putc('?');
+				continue;
+			}
+			hnputs(t->pkt, Tftp_ACK);
+			while(udpwrite(&t->dip, t->sport, t->dport, 4, t->pkt))
+				putc('!');
+			if(seq < t->seq){
+				putc('@');
+				continue;
+			}
+			t->seq = seq+1;
+			n -= 4;
+			t->rp = t->pkt + 4;
+			t->ep = t->rp + n;
+			t->eof = n < Segsize;
+			break;
+		case Tftp_ERROR:
+			print(t->pkt+4);
+			print("\n");
+		default:
+			t->eof = 1;
+			return -1;
+		}
+		break;
+	}
+	n = t->ep - t->rp;
+	if(len > n)
+		len = n;
+	memmove(data, t->rp, len);
+	t->rp += len;
+	return len;
+}
+
+static void
+pxeclose(void *f)
+{
+	Tftp *t = f;
+	t->eof = 1;
+}
+
+
+static int
+tftpopen(Tftp *t, char *path)
+{
+	static EFI_PXE_BASE_CODE_UDP_PORT xport = 6666;
+	int r, n;
+	char *p;
+
+	t->sport = xport++;
+	t->dport = 0;
+	t->rp = t->ep = 0;
+	t->seq = 1;
+	t->eof = 0;
+	t->nul = 0;
+	p = t->pkt;
+	hnputs(p, Tftp_READ); p += 2;
+	n = strlen(path)+1;
+	memmove(p, path, n); p += n;
+	memmove(p, "octet", 6); p += 6;
+	n = p - t->pkt;
+	for(;;){
+		if(r = udpwrite(&t->dip, t->sport, TftpPort, n, t->pkt))
+			break;
+		if(r = pxeread(t, 0, 0))
+			break;
+		return 0;
+	}
+	pxeclose(t);
+	return r;
+}
+
+static void*
+pxeopen(char *name)
+{
+	static Tftp t[1];
+
+	memset(t, 0, sizeof(Tftp));
+
+	memmove(&t->dip, dhcp->BootpSiAddr, 4);
+	memmove(&t->sip, pxe->Mode->StationIp, 4);
+
+	if(tftpopen(t, name) == 0)
+		return t;
+	return nil;
+}
+
+void*
+pxeinit(void)
+{
+	EFI_HANDLE *Handles;
+	UINTN Count;
+	char ini[24];
+	uchar *mac;
+	void *f;
+	int i;
+
+	pxe = nil;
+	dhcp = nil;
+	Count = 0;
+	Handles = nil;
+	if(eficall(ST->BootServices->LocateHandleBuffer,
+		ByProtocol, &EFI_PXE_BASE_CODE_PROTOCOL_GUID, nil, &Count, &Handles))
+		return nil;
+
+	for(i=0; i<Count; i++){
+		pxe = nil;
+		if(eficall(ST->BootServices->HandleProtocol,
+			Handles[i], &EFI_PXE_BASE_CODE_PROTOCOL_GUID, &pxe))
+			continue;
+		if(pxe->Mode != nil && pxe->Mode->Started)
+			break;
+	}
+	dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET*)pxe->Mode->DhcpAck;
+
+	mac = dhcp->BootpHwAddr;
+	memmove(ini, "/cfg/pxe/", 9);
+	for(i=0; i<6; i++){
+		ini[9+i*2+0] = hex[mac[i] >> 4];
+		ini[9+i*2+1] = hex[mac[i] & 0xF];
+	}
+	ini[9+12] = '\0';
+
+	open = pxeopen;
+	read = pxeread;
+	close = pxeclose;
+
+	if((f = pxeopen(ini)) != nil)
+		return f;
+	return pxeopen("/cfg/pxe/default");
+}