[gigaboot20x6] initial source code and docs
Change-Id: I95ed2d692472d7b816bc7d7045731e583da2393b
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..364dd01
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,85 @@
+
+ARCH := x86_64
+
+EFI_TOOLCHAIN :=
+EFI_CC := $(EFI_TOOLCHAIN)gcc
+EFI_LD := $(EFI_TOOLCHAIN)ld
+EFI_OBJCOPY := $(EFI_TOOLCHAIN)objcopy
+EFI_AR := $(EFI_TOOLCHAIN)ar
+
+EFI_PATH := external/gnu-efi
+EFI_LIB_PATHS := $(EFI_PATH)/$(ARCH)/lib $(EFI_PATH)/$(ARCH)/gnuefi out
+EFI_INC_PATHS := $(EFI_PATH)/inc $(EFI_PATH)/inc/$(ARCH) $(EFI_PATH)/inc/protocol
+
+EFI_CRT0 := $(EFI_PATH)/$(ARCH)/gnuefi/crt0-efi-$(ARCH).o
+EFI_LINKSCRIPT := $(EFI_PATH)/gnuefi/elf_$(ARCH)_efi.lds
+
+EFI_CFLAGS := -fpic -fshort-wchar -fno-stack-protector -mno-red-zone
+EFI_CFLAGS += -Wall
+EFI_CFLAGS += -std=c99
+EFI_CFLAGS += -ffreestanding -nostdinc -Iinclude -Isrc -Iexternal/edk2
+EFI_CFLAGS += $(patsubst %,-I%,$(EFI_INC_PATHS))
+EFI_CFLAGS += -DHAVE_USE_MS_ABI=1
+EFI_CFLAGS += -ggdb
+
+EFI_LDFLAGS := -nostdlib -znocombreloc -T $(EFI_LINKSCRIPT)
+EFI_LDFLAGS += -shared -Bsymbolic
+EFI_LDFLAGS += $(patsubst %,-L%,$(EFI_LIB_PATHS))
+
+EFI_LIBS := -lstuff -lefi -lgnuefi
+
+what_to_build:: all
+
+# build rules and macros
+include build/build.mk
+
+# declare applications here
+$(call efi_app, hello, hello.c)
+$(call efi_app, showmem, showmem.c)
+$(call efi_app, fileio, fileio.c)
+$(call efi_app, osboot, osboot.c netboot.c netifc.c inet6.c)
+$(call efi_app, usbtest, usbtest.c)
+
+LIB_SRCS := lib/goodies.c lib/loadfile.c lib/console-printf.c lib/printf.c lib/string.c
+
+LIB_OBJS := $(patsubst %.c,out/%.o,$(LIB_SRCS))
+DEPS += $(patsubst %.c,out/%.d,$(LIB_SRCS))
+
+out/libstuff.a: $(LIB_OBJS)
+ @mkdir -p $(dir $@)
+ @echo archiving: $@
+ $(QUIET)rm -f $@
+ $(QUIET)ar rc $@ $^
+
+# generate a small IDE disk image for qemu
+out/disk.img: $(APPS)
+ @mkdir -p $(dir $@)
+ $(QUIET)./build/mkdiskimg.sh $@
+ @echo copying: $(APPS) README.txt to disk.img
+ $(QUIET)mcopy -o -i out/disk.img@@1024K $(APPS) README.txt ::
+
+ALL += out/disk.img
+
+-include $(DEPS)
+
+# ensure gnu-efi gets built
+$(EFI_CRT0):
+ @echo building: gnu-efi
+ $(QUIET)$(MAKE) -C $(EFI_PATH)
+
+QEMU_OPTS := -cpu qemu64
+QEMU_OPTS += -bios external/ovmf/OVMF.fd
+QEMU_OPTS += -drive file=out/disk.img,format=raw,if=ide
+
+qemu:: all
+ qemu-system-x86_64 $(QEMU_OPTS)
+
+out/nbserver: src/nbserver.c
+ @mkdir -p out
+ @echo building nbserver
+ $(QUIET)gcc -o out/nbserver -Isrc -Wall src/nbserver.c
+
+all: $(ALL) out/nbserver
+
+clean::
+ rm -rf out
diff --git a/NOTES.txt b/NOTES.txt
new file mode 100644
index 0000000..465baa1
--- /dev/null
+++ b/NOTES.txt
@@ -0,0 +1,40 @@
+
+uefi_call_wrapper()
+-------------------
+gnu-efi provides this macro thing as a workaround for older compilers that
+don't support the right attributes to handle the fact that the EFI calling
+conventions are different than the standard gcc ones.
+
+If HAVE_USE_MS_ABI is defined (as the Makefile here does) and you have a
+compiler that's not a fairly ancient, everything Just Works(tm) and the
+code is much more readable.
+
+
+Intel Visual BIOS (on NUC) and netboot
+--------------------------------------
+This will netboot EFI apps, provided you have a DHCP server which is
+setup to give the BIOS the IP of a tftp server and a filename to grab
+from there.
+
+You must disable legacy boot for the EFI netboot option to appear. If
+you check the "keep retrying forever" option, when your app exits, the
+BIOS will try to download it from the tftp server again, making for a
+quick build/download/test cycle
+
+
+Making tftpd work on Ubuntu with IPv4
+-------------------------------------
+sudo apt-get install tftpd-hpa
+
+Optionally make it easy to copy files to the server without sudo:
+sudo chown `who` /var/lib/tftpdboot
+
+Edit /etc/default/tftpd-hpa so it looks more like:
+TFTP_USERNAME="tftp"
+TFTP_DIRECTORY="/var/lib/tftpboot"
+TFTP_ADDRESS=":69"
+TFTP_OPTIONS="--secure -4 -v -v -v"
+
+Removing the [::] and adding the -4 make it work reliably on IPv4 for me.
+The several -v's make it chattier in syslog which is handy if you're not
+sure the test machine is actually trying to grab files.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..c206d7b
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,52 @@
+What is This?
+-------------
+
+This project contains some experiments in software that runs on UEFI
+firmware for the purpose of exploring UEFI development and bootloader
+development.
+
+
+External Software Included
+--------------------------
+
+Local Path: external/gnu-efi/...
+Description: headers and tooling to build UEFI binaries with gcc, etc
+Project: https://sourceforge.net/projects/gnu-efi/
+Source: git://git.code.sf.net/p/gnu-efi/code
+Version: 6605c16fc8b1fd3b2085364902d1fa73aa7fad76 (post-3.0.4)
+License: BSD-ish, see gnu-efi/README.*
+
+Local Path: external/edk2/...
+Description: headers for UEFI from Tianocore EDK II
+Project: http://www.tianocore.org/edk2/
+Source: https://github.com/tianocore/edk2
+License: BSD-ish, see headers
+
+Local Path: external/ovmf/...
+Description: UEFI Firmware Suitable for use in Qemu
+Distribution: http://www.tianocore.org/ovmf/
+Version: OVMF-X64-r15214.zip
+License: BSD-ish, see ovmf/LICENSE
+
+
+External Dependencies
+---------------------
+
+qemu-system-x86_64 is needed to test in emulation
+gnu parted and mtools are needed to generate the disk.img for Qemu
+
+
+Useful Resources & Documentation
+--------------------------------
+
+ACPI & UEFI Specifications
+http://www.uefi.org/specifications
+
+Intel 64 and IA-32 Architecture Manuals
+http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
+
+Tianocore UEFI Open Source Community
+(Source for OVMF, EDK II Dev Environment, etc)
+http://www.tianocore.org/
+https://github.com/tianocore
+
diff --git a/build/build.mk b/build/build.mk
new file mode 100644
index 0000000..ad51a0e
--- /dev/null
+++ b/build/build.mk
@@ -0,0 +1,54 @@
+
+ALL :=
+APPS :=
+DEPS :=
+
+QUIET ?= @
+
+EFI_SECTIONS := .text .sdata .data .dynamic .dynsym
+EFI_SECTIONS += .rel .rela .reloc .eh_frame
+EFI_SECTIONS := $(patsubst %,-j %,$(EFI_SECTIONS))
+
+DBG_SECTIONS := .debug_info .debug_abbrev .debug_loc .debug_aranges
+DBG_SECTIONS += .debug_line .debug_macinfo .debug_str
+DBG_SECTIONS := $(EFI_SECTIONS) $(patsubst %,-j %,$(DBG_SECTIONS))
+
+ifneq ($(VERBOSE),)
+$(info CFLAGS := $(EFI_CFLAGS))
+$(info LDFLAGS := $(EFI_LDFLAGS))
+$(info SECTIONS := $(EFI_SECTIONS))
+endif
+
+out/%.o: %.c
+ @mkdir -p $(dir $@)
+ @echo compiling: $@
+ $(QUIET)$(EFI_CC) -MMD -MP -o $@ -c $(EFI_CFLAGS) $<
+
+out/%.efi: out/%.so
+ @mkdir -p $(dir $@)
+ @echo building: $@
+ $(QUIET)$(EFI_OBJCOPY) --target=efi-app-$(ARCH) $(EFI_SECTIONS) $< $@
+ $(QUIET)if [ "`nm $< | grep ' U '`" != "" ]; then echo "error: $<: undefined symbols"; nm $< | grep ' U '; rm $<; exit 1; fi
+
+out/%.dbg: out/%.so
+ @mkdir -p $(dir $@)
+ @echo building: $@
+ $(QUIET)$(EFI_OBJCOPY) --target=efi-app-$(ARCH) $(DBG_SECTIONS) $< $@
+
+
+# _efi_app <basename> <obj-files> <dep-files>
+define _efi_app
+ALL += out/$1.efi out/$1.dbg
+APPS += out/$1.efi
+DEPS += $3
+out/$1.so: $2 $(EFI_CRT0) out/libstuff.a
+ @mkdir -p $$(dir $$@)
+ @echo linking: $$@
+ $(QUIET)$(EFI_LD) -o $$@ $(EFI_LDFLAGS) $(EFI_CRT0) $2 $(EFI_LIBS)
+endef
+
+efi_app = $(eval $(call _efi_app,$(strip $1),\
+$(patsubst %.c,out/src/%.o,$2),\
+$(patsubst %.c,out/src/%.d,$2)))
+
+
diff --git a/build/mkdiskimg.sh b/build/mkdiskimg.sh
new file mode 100755
index 0000000..323aaa4
--- /dev/null
+++ b/build/mkdiskimg.sh
@@ -0,0 +1,18 @@
+#!/bin/bash -e
+
+if [ -z "$1" ]; then
+ echo usage: $0 "<diskimg>"
+ exit 1
+fi
+
+if [[ ! -f $1 ]]; then
+ echo creating: $1
+ dd if=/dev/zero of="$1" bs=512 count=93750
+
+ parted "$1" -s -a minimal mklabel gpt
+ parted "$1" -s -a minimal mkpart EFI FAT16 2048s 93716s
+ parted "$1" -s -a minimal toggle 1 boot
+
+ mformat -i "$1"@@1024K -h 32 -t 32 -n 64 -c 1
+fi
+
diff --git a/include/goodies.h b/include/goodies.h
new file mode 100644
index 0000000..06a7acc
--- /dev/null
+++ b/include/goodies.h
@@ -0,0 +1,45 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#pragma once
+
+void InitGoodies(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys);
+
+void WaitAnyKey(void);
+void Fatal(const char *msg, EFI_STATUS status);
+CHAR16 *HandleToString(EFI_HANDLE handle);
+
+// Convenience wrappers for Open/Close protocol for use by
+// UEFI app code that's not a driver model participant
+EFI_STATUS OpenProtocol(EFI_HANDLE h, EFI_GUID *guid, void **ifc);
+EFI_STATUS CloseProtocol(EFI_HANDLE h, EFI_GUID *guid);
+
+void *LoadFile(CHAR16 *filename, UINTN *size_out);
+
+// GUIDs
+extern EFI_GUID SimpleFileSystemProtocol;
+extern EFI_GUID FileInfoGUID;
+
+// Global Context
+extern EFI_HANDLE gImg;
+extern EFI_SYSTEM_TABLE *gSys;
+extern EFI_BOOT_SERVICES *gBS;
+extern SIMPLE_TEXT_OUTPUT_INTERFACE *gConOut;
diff --git a/include/stdarg.h b/include/stdarg.h
new file mode 100644
index 0000000..25d9f5e
--- /dev/null
+++ b/include/stdarg.h
@@ -0,0 +1,9 @@
+#pragma once
+
+typedef __builtin_va_list va_list;
+
+#define va_start(v,l) __builtin_va_start(v,l)
+#define va_end(v) __builtin_va_end(v)
+#define va_arg(v,l) __builtin_va_arg(v,l)
+#define va_copy(d,s) __builtin_va_copy(d,s)
+
diff --git a/include/stdint.h b/include/stdint.h
new file mode 100644
index 0000000..880c376
--- /dev/null
+++ b/include/stdint.h
@@ -0,0 +1,30 @@
+#pragma once
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+typedef __INT8_TYPE__ int8_t;
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __INT64_TYPE__ int64_t;
+
+typedef __INTMAX_TYPE__ intmax_t;
+
+typedef __UINTMAX_TYPE__ uintmax_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+typedef __SIZE_TYPE__ size_t;
+
+typedef unsigned int uint;
+
+#define INT_MAX (__INT_MAX__)
+
+typedef int bool;
+typedef int ssize_t;
+
+#define false (0)
+#define true (1)
+
diff --git a/include/stdio.h b/include/stdio.h
new file mode 100644
index 0000000..ab4accd
--- /dev/null
+++ b/include/stdio.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include <printf.h>
diff --git a/include/string.h b/include/string.h
new file mode 100644
index 0000000..2e9d71b
--- /dev/null
+++ b/include/string.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdint.h>
+
+void *memset(void *dst, int c, size_t n);
+void *memcpy(void *dst, const void *src, size_t n);
+int memcmp(const void *a, const void *b, size_t n);
+size_t strlen(const char *s);
diff --git a/lib/console-printf.c b/lib/console-printf.c
new file mode 100644
index 0000000..e36c843
--- /dev/null
+++ b/lib/console-printf.c
@@ -0,0 +1,72 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <printf.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <goodies.h>
+
+#define PCBUFMAX 126
+// buffer is two larger to leave room for a \0 and room
+// for a \r that may be added after a \n
+
+typedef struct {
+ int off;
+ CHAR16 buf[PCBUFMAX + 2];
+} _pcstate;
+
+static int _printf_console_out(const char *str, size_t len, void *_state) {
+ _pcstate *state = _state;
+ CHAR16 *buf = state->buf;
+ int i = state->off;
+ int n = len;
+ while (n > 0) {
+ if (*str == '\n') {
+ buf[i++] = '\r';
+ }
+ buf[i++] = *str++;
+ if (i >= PCBUFMAX) {
+ buf[i] = 0;
+ gConOut->OutputString(gConOut, buf);
+ i = 0;
+ }
+ n--;
+ }
+ state->off = i;
+ return len;
+}
+
+int _printf(const char *fmt, ...) {
+ va_list ap;
+ _pcstate state;
+ int r;
+ state.off = 0;
+ va_start(ap, fmt);
+ r = _printf_engine(_printf_console_out, &state, fmt, ap);
+ va_end(ap);
+ if (state.off) {
+ state.buf[state.off] = 0;
+ gConOut->OutputString(gConOut, state.buf);
+ }
+ return r;
+}
+
diff --git a/lib/goodies.c b/lib/goodies.c
new file mode 100644
index 0000000..06601fa
--- /dev/null
+++ b/lib/goodies.c
@@ -0,0 +1,74 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <goodies.h>
+#include <printf.h>
+
+// Useful GUID Constants Not Defined by -lefi
+EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
+EFI_GUID FileInfoGUID = EFI_FILE_INFO_ID;
+
+// -lefi has its own globals, but this may end up not
+// depending on that, so let's not depend on those
+EFI_SYSTEM_TABLE *gSys;
+EFI_HANDLE gImg;
+EFI_BOOT_SERVICES *gBS;
+SIMPLE_TEXT_OUTPUT_INTERFACE *gConOut;
+
+void InitGoodies(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ gSys = sys;
+ gImg = img;
+ gBS = sys->BootServices;
+ gConOut = sys->ConOut;
+}
+
+void WaitAnyKey(void) {
+ SIMPLE_INPUT_INTERFACE *sii = gSys->ConIn;
+ EFI_INPUT_KEY key;
+ while (sii->ReadKeyStroke(sii, &key) != EFI_SUCCESS) ;
+}
+
+void Fatal(const char *msg, EFI_STATUS status) {
+ printf("\nERROR: %s (%ld)\n", msg, status);
+ WaitAnyKey();
+ gBS->Exit(gImg, 1, 0, NULL);
+}
+
+CHAR16 *HandleToString(EFI_HANDLE h) {
+ EFI_DEVICE_PATH *path = DevicePathFromHandle(h);
+ if (path == NULL) return L"<NoPath>";
+ CHAR16 *str = DevicePathToStr(path);
+ if (str == NULL) return L"<NoString>";
+ return str;
+}
+
+
+EFI_STATUS OpenProtocol(EFI_HANDLE h, EFI_GUID *guid, void **ifc) {
+ return gBS->OpenProtocol(h, guid, ifc, gImg, NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
+}
+
+EFI_STATUS CloseProtocol(EFI_HANDLE h, EFI_GUID *guid) {
+ return gBS->CloseProtocol(h, guid, gImg, NULL);
+}
diff --git a/lib/loadfile.c b/lib/loadfile.c
new file mode 100644
index 0000000..691d961
--- /dev/null
+++ b/lib/loadfile.c
@@ -0,0 +1,107 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+#include <stdio.h>
+#include <goodies.h>
+
+void *LoadFile(CHAR16 *filename, UINTN *_sz) {
+ EFI_LOADED_IMAGE *loaded;
+ EFI_STATUS r;
+ void *data = NULL;
+
+ r = OpenProtocol(gImg, &LoadedImageProtocol, (void**) &loaded);
+ if (r) {
+ printf("LoadFile: Cannot open LoadedImageProtocol (%ld)\n", r);
+ goto exit0;
+ }
+
+#if 0
+ printf("Img DeviceHandle='%s'\n", HandleToString(loaded->DeviceHandle));
+ printf("Img FilePath='%s'\n", DevicePathToStr(loaded->FilePath));
+ printf("Img Base=%lx Size=%lx\n", loaded->ImageBase, loaded->ImageSize);
+#endif
+
+ EFI_FILE_IO_INTERFACE *fioi;
+ r = OpenProtocol(loaded->DeviceHandle, &SimpleFileSystemProtocol, (void **) &fioi);
+ if (r) {
+ printf("LoadFile: Cannot open SimpleFileSystemProtocol (%ld)\n", r);
+ goto exit1;
+ }
+
+ EFI_FILE_HANDLE root;
+ r = fioi->OpenVolume(fioi, &root);
+ if (r) {
+ printf("LoadFile: Cannot open root volume (%ld)\n", r);
+ goto exit2;
+ }
+
+ EFI_FILE_HANDLE file;
+ r = root->Open(root, &file, filename, EFI_FILE_MODE_READ, 0);
+ if (r) {
+ printf("LoadFile: Cannot open file (%ld)\n", r);
+ goto exit3;
+ }
+
+ char buf[512];
+ UINTN sz = sizeof(buf);
+ EFI_FILE_INFO *finfo = (void*) buf;
+ r = file->GetInfo(file, &FileInfoGUID, &sz, finfo);
+ if (r) {
+ printf("LoadFile: Cannot get FileInfo (%ld)\n", r);
+ goto exit3;
+ }
+
+ r = gBS->AllocatePool(EfiLoaderData, finfo->FileSize, (void**) &data);
+ if (r) {
+ printf("LoadFile: Cannot allocate buffer (%ld)\n", r);
+ data = NULL;
+ goto exit4;
+ }
+
+ sz = finfo->FileSize;
+ r = file->Read(file, &sz, data);
+ if (r) {
+ printf("LoadFile: Error reading file (%ld)\n", r);
+ gBS->FreePool(data);
+ data = NULL;
+ goto exit4;
+ }
+ if (sz != finfo->FileSize) {
+ printf("LoadFile: Short read\n");
+ gBS->FreePool(data);
+ data = NULL;
+ goto exit4;
+ }
+ *_sz = finfo->FileSize;
+exit4:
+ file->Close(file);
+exit3:
+ root->Close(root);
+exit2:
+ CloseProtocol(loaded->DeviceHandle, &SimpleFileSystemProtocol);
+exit1:
+ CloseProtocol(gImg, &LoadedImageProtocol);
+exit0:
+ return data;
+}
+
diff --git a/lib/string.c b/lib/string.c
new file mode 100644
index 0000000..a2c622b
--- /dev/null
+++ b/lib/string.c
@@ -0,0 +1,57 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <string.h>
+
+void *memset(void *_dst, int c, size_t n) {
+ uint8_t *dst = _dst;
+ while (n-- > 0) {
+ *dst++ = c;
+ }
+ return _dst;
+}
+
+void *memcpy(void *_dst, const void *_src, size_t n) {
+ uint8_t *dst = _dst;
+ const uint8_t *src = _src;
+ while (n-- > 0) {
+ *dst++ = *src++;
+ }
+ return _dst;
+}
+
+int memcmp(const void *_a, const void *_b, size_t n) {
+ const uint8_t *a = _a;
+ const uint8_t *b = _b;
+ while (n-- > 0) {
+ int x = *a++ - *b++;
+ if (x != 0) {
+ return x;
+ }
+ }
+ return 0;
+}
+
+size_t strlen(const char *s) {
+ size_t len = 0;
+ while (*s++) len++;
+ return len;
+}
diff --git a/src/fileio.c b/src/fileio.c
new file mode 100644
index 0000000..2f8f2e8
--- /dev/null
+++ b/src/fileio.c
@@ -0,0 +1,78 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+#include <goodies.h>
+
+EFI_STATUS efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ EFI_LOADED_IMAGE *loaded;
+ EFI_STATUS r;
+
+ InitializeLib(img, sys);
+ InitGoodies(img, sys);
+
+ Print(L"Hello, EFI World\n");
+
+ r = OpenProtocol(img, &LoadedImageProtocol, (void**) &loaded);
+ if (r) Fatal("LoadedImageProtocol", r);
+
+ Print(L"Img DeviceHandle='%s'\n", HandleToString(loaded->DeviceHandle));
+ Print(L"Img FilePath='%s'\n", DevicePathToStr(loaded->FilePath));
+ Print(L"Img Base=%lx Size=%lx\n", loaded->ImageBase, loaded->ImageSize);
+
+ EFI_FILE_IO_INTERFACE *fioi;
+ r = OpenProtocol(loaded->DeviceHandle, &SimpleFileSystemProtocol, (void **) &fioi);
+ if (r) Fatal("SimpleFileSystemProtocol", r);
+
+ EFI_FILE_HANDLE root;
+ r = fioi->OpenVolume(fioi, &root);
+ if (r) Fatal("OpenVolume", r);
+
+ EFI_FILE_HANDLE file;
+ r = root->Open(root, &file, L"README.txt", EFI_FILE_MODE_READ, 0);
+
+ if (r == EFI_SUCCESS) {
+ char buf[512];
+ UINTN sz = sizeof(buf);
+ EFI_FILE_INFO *finfo = (void*) buf;
+ r = file->GetInfo(file, &FileInfoGUID, &sz, finfo);
+ if (r) Fatal("GetInfo", r);
+ Print(L"FileSize %ld\n", finfo->FileSize);
+
+ sz = sizeof(buf) - 1;
+ r = file->Read(file, &sz, buf);
+ if (r) Fatal("Read", r);
+
+ char *x = buf;
+ while(sz-- > 0) Print(L"%c", (CHAR16) *x++);
+
+ file->Close(file);
+ }
+
+ root->Close(root);
+ CloseProtocol(loaded->DeviceHandle, &SimpleFileSystemProtocol);
+ CloseProtocol(img, &LoadedImageProtocol);
+
+ WaitAnyKey();
+
+ return EFI_SUCCESS;
+}
diff --git a/src/hello.c b/src/hello.c
new file mode 100644
index 0000000..86c02ed
--- /dev/null
+++ b/src/hello.c
@@ -0,0 +1,8 @@
+
+#include <efi.h>
+
+EFI_STATUS efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ SIMPLE_TEXT_OUTPUT_INTERFACE *ConOut = sys->ConOut;
+ ConOut->OutputString(ConOut, L"Hello, EFI World!\r\n");
+ return EFI_SUCCESS;
+}
diff --git a/src/inet6.c b/src/inet6.c
new file mode 100644
index 0000000..735aea0
--- /dev/null
+++ b/src/inet6.c
@@ -0,0 +1,418 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <inet6.h>
+
+#if 1
+#define BAD(n) do { printf("error: %s\n", n); return; } while (0)
+#else
+#define BAD(n) do { return; } while (0)
+#endif
+
+
+// useful addresses
+const ip6_addr ip6_ll_all_nodes = {
+ .x = { 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+};
+const ip6_addr ip6_ll_all_routers = {
+ .x = { 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 },
+};
+
+// Convert MAC Address to IPv6 Link Local Address
+// aa:bb:cc:dd:ee:ff => FF80::aabb:ccFF:FEdd:eeff
+// bit 2 (U/L) of the mac is inverted
+void ll6addr_from_mac(ip6_addr *_ip, const mac_addr *_mac) {
+ uint8_t *ip = _ip->x;
+ const uint8_t *mac = _mac->x;
+ memset(ip, 0, IP6_ADDR_LEN);
+ ip[0] = 0xFE;
+ ip[1] = 0x80;
+ memset(ip + 2, 0, 6);
+ ip[8] = mac[0] ^ 2;
+ ip[9] = mac[1];
+ ip[10] = mac[2];
+ ip[11] = 0xFF;
+ ip[12] = 0xFE;
+ ip[13] = mac[3];
+ ip[14] = mac[4];
+ ip[15] = mac[5];
+}
+
+// Convert MAC Address to IPv6 Solicit Neighbor Multicast Address
+// aa:bb:cc:dd:ee:ff -> FF02::1:FFdd:eeff
+void snmaddr_from_mac(ip6_addr *_ip, const mac_addr *_mac) {
+ uint8_t *ip = _ip->x;
+ const uint8_t *mac = _mac->x;
+ ip[0] = 0xFF;
+ ip[1] = 0x02;
+ memset(ip + 2, 0, 9);
+ ip[11] = 0x01;
+ ip[12] = 0xFF;
+ ip[13] = mac[3];
+ ip[14] = mac[4];
+ ip[15] = mac[5];
+}
+
+// Convert IPv6 Multicast Address to Ethernet Multicast Address
+void multicast_from_ip6(mac_addr *_mac, const ip6_addr *_ip6) {
+ const uint8_t *ip = _ip6->x;
+ uint8_t *mac = _mac->x;
+ mac[0] = 0x33;
+ mac[1] = 0x33;
+ mac[2] = ip[12];
+ mac[3] = ip[13];
+ mac[4] = ip[14];
+ mac[5] = ip[15];
+}
+
+// ip6 stack configuration
+mac_addr ll_mac_addr;
+ip6_addr ll_ip6_addr;
+mac_addr snm_mac_addr;
+ip6_addr snm_ip6_addr;
+
+// cache for the last source addresses we've seen
+static mac_addr rx_mac_addr;
+static ip6_addr rx_ip6_addr;
+
+void ip6_init(void *macaddr) {
+ char tmp[IP6TOAMAX];
+ mac_addr all;
+
+ // save our ethernet MAC and synthesize link layer addresses
+ memcpy(&ll_mac_addr, macaddr, 6);
+ ll6addr_from_mac(&ll_ip6_addr, &ll_mac_addr);
+ snmaddr_from_mac(&snm_ip6_addr, &ll_mac_addr);
+ multicast_from_ip6(&snm_mac_addr, &snm_ip6_addr);
+
+ eth_add_mcast_filter(&snm_mac_addr);
+
+ multicast_from_ip6(&all, &ip6_ll_all_nodes);
+ eth_add_mcast_filter(&all);
+
+ printf("macaddr: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ll_mac_addr.x[0], ll_mac_addr.x[1], ll_mac_addr.x[2],
+ ll_mac_addr.x[3], ll_mac_addr.x[4], ll_mac_addr.x[5]);
+ printf("ip6addr: %s\n", ip6toa(tmp, &ll_ip6_addr));
+ printf("snmaddr: %s\n", ip6toa(tmp, &snm_ip6_addr));
+}
+
+static int resolve_ip6(mac_addr *_mac, const ip6_addr *_ip) {
+ const uint8_t *ip = _ip->x;
+
+ // Multicast addresses are a simple transform
+ if (ip[0] == 0xFF) {
+ multicast_from_ip6(_mac, _ip);
+ return 0;
+ }
+
+ // Trying to send to the IP that we last received a packet from?
+ // Assume their mac address has not changed
+ if (memcmp(_ip, &rx_ip6_addr, sizeof(rx_ip6_addr)) == 0) {
+ memcpy(_mac, &rx_mac_addr, sizeof(rx_mac_addr));
+ return 0;
+ }
+
+ // We don't know how to find peers or routers yet, so give up...
+ return -1;
+}
+
+static uint16_t checksum(const void *_data, size_t len, uint16_t _sum) {
+ uint32_t sum = _sum;
+ const uint16_t *data = _data;
+ while (len > 1) {
+ sum += *data++;
+ len -= 2;
+ }
+ if (len) {
+ sum += (*data & 0xFF);
+ }
+ return sum + (sum >> 16);
+}
+
+typedef struct {
+ uint8_t eth[16];
+ ip6_hdr ip6;
+ uint8_t data[0];
+} ip6_pkt;
+
+typedef struct {
+ uint8_t eth[16];
+ ip6_hdr ip6;
+ udp_hdr udp;
+ uint8_t data[0];
+} udp_pkt;
+
+static unsigned ip6_checksum(ip6_hdr *ip, unsigned type, size_t length) {
+ uint16_t sum;
+
+ // length and protocol field for pseudo-header
+ sum = checksum(&ip->length, 2, htons(type));
+ // src/dst for pseudo-header + payload
+ sum = checksum(ip->src, 32 + length, sum);
+
+ // 0 is illegal, so 0xffff remains 0xffff
+ if (sum != 0xffff) {
+ return ~sum;
+ } else {
+ return sum;
+ }
+}
+
+static int ip6_setup(ip6_pkt *p, const ip6_addr *daddr, size_t length, uint8_t type) {
+ mac_addr dmac;
+
+ if (resolve_ip6(&dmac, daddr)) return -1;
+
+ // ethernet header
+ memcpy(p->eth + 2, &dmac, ETH_ADDR_LEN);
+ memcpy(p->eth + 8, &ll_mac_addr, ETH_ADDR_LEN);
+ p->eth[14] = (ETH_IP6 >> 8) & 0xFF;
+ p->eth[15] = ETH_IP6 & 0xFF;
+
+ // ip6 header
+ p->ip6.ver_tc_flow = 0x60; // v=6, tc=0, flow=0
+ p->ip6.length = htons(length);
+ p->ip6.next_header = type;
+ p->ip6.hop_limit = 255;
+ memcpy(p->ip6.src, &ll_ip6_addr, sizeof(ip6_addr));
+ memcpy(p->ip6.dst, daddr, sizeof(ip6_addr));
+
+ return 0;
+}
+
+#define UDP6_MAX_PAYLOAD (ETH_MTU - ETH_HDR_LEN - IP6_HDR_LEN - UDP_HDR_LEN)
+
+int udp6_send(const void *data, size_t dlen, const ip6_addr *daddr, uint16_t dport, uint16_t sport) {
+ size_t length = dlen + UDP_HDR_LEN;
+ udp_pkt *p = eth_get_buffer(ETH_MTU + 2);
+
+ if (p == 0) return -1;
+ if (dlen > UDP6_MAX_PAYLOAD) goto fail;
+ if (ip6_setup((void*) p, daddr, length, HDR_UDP)) goto fail;
+
+ // udp header
+ p->udp.src_port = htons(sport);
+ p->udp.dst_port = htons(dport);
+ p->udp.length = htons(length);
+ p->udp.checksum = 0;
+
+ memcpy(p->data, data, dlen);
+ p->udp.checksum = ip6_checksum(&p->ip6, HDR_UDP, length);
+ return eth_send(p->eth + 2, ETH_HDR_LEN + IP6_HDR_LEN + length);
+
+fail:
+ eth_put_buffer(p);
+ return -1;
+}
+
+#define ICMP6_MAX_PAYLOAD (ETH_MTU - ETH_HDR_LEN - IP6_HDR_LEN)
+
+static int icmp6_send(const void *data, size_t length, const ip6_addr *daddr) {
+ ip6_pkt *p;
+ icmp6_hdr *icmp;
+
+ p = eth_get_buffer(ETH_MTU + 2);
+ if (p == 0) return -1;
+ if (length > ICMP6_MAX_PAYLOAD) goto fail;
+ if (ip6_setup(p, daddr, length, HDR_ICMP6)) goto fail;
+
+ icmp = (void*) p->data;
+ memcpy(icmp, data, length);
+ icmp->checksum = ip6_checksum(&p->ip6, HDR_ICMP6, length);
+ return eth_send(p->eth + 2, ETH_HDR_LEN + IP6_HDR_LEN + length);
+
+fail:
+ eth_put_buffer(p);
+ return -1;
+}
+
+void _udp6_recv(ip6_hdr *ip, void *_data, size_t len) {
+ udp_hdr *udp = _data;
+ uint16_t sum, n;
+
+ if (len < UDP_HDR_LEN) BAD("Bogus Header Len");
+ if (udp->checksum == 0) BAD("Checksum Invalid");
+ if (udp->checksum == 0xFFFF) udp->checksum = 0;
+
+ sum = checksum(&ip->length, 2, htons(HDR_UDP));
+ sum = checksum(ip->src, 32 + len, sum);
+ if (sum != 0xFFFF) BAD("Checksum Incorrect");
+
+ n = ntohs(udp->length);
+ if (n < UDP_HDR_LEN) BAD("Bogus Header Len");
+ if (n > len) BAD("Packet Too Short");
+ len = n - UDP_HDR_LEN;
+
+ udp6_recv((uint8_t*) _data + UDP_HDR_LEN, len,
+ (void*) ip->dst, ntohs(udp->dst_port),
+ (void*) ip->src, ntohs(udp->src_port));
+}
+
+void icmp6_recv(ip6_hdr *ip, void *_data, size_t len) {
+ icmp6_hdr *icmp = _data;
+ uint16_t sum;
+
+ if (icmp->checksum == 0) BAD("Checksum Invalid");
+ if (icmp->checksum == 0xFFFF) icmp->checksum = 0;
+
+ sum = checksum(&ip->length, 2, htons(HDR_ICMP6));
+ sum = checksum(ip->src, 32 + len, sum);
+ if (sum != 0xFFFF) BAD("Checksum Incorrect");
+
+ if (icmp->type == ICMP6_NDP_N_SOLICIT) {
+ ndp_n_hdr *ndp = _data;
+ struct {
+ ndp_n_hdr hdr;
+ uint8_t opt[8];
+ } msg;
+
+ if (len < sizeof(ndp_n_hdr)) BAD("Bogus NDP Message");
+ if (ndp->code != 0) BAD("Bogus NDP Code");
+ if (memcmp(ndp->target, &ll_ip6_addr, IP6_ADDR_LEN)) BAD("NDP Not For Me");
+
+ msg.hdr.type = ICMP6_NDP_N_ADVERTISE;
+ msg.hdr.code = 0;
+ msg.hdr.checksum = 0;
+ msg.hdr.flags = 0x60; // (S)olicited and (O)verride flags
+ memcpy(msg.hdr.target, &ll_ip6_addr, IP6_ADDR_LEN);
+ msg.opt[0] = NDP_N_TGT_LL_ADDR;
+ msg.opt[1] = 1;
+ memcpy(msg.opt + 2, &ll_mac_addr, ETH_ADDR_LEN);
+
+ icmp6_send(&msg, sizeof(msg), (void*) ip->src);
+ return;
+ }
+
+ if (icmp->type == ICMP6_ECHO_REQUEST) {
+ icmp->checksum = 0;
+ icmp->type = ICMP6_ECHO_REPLY;
+ icmp6_send(_data, len, (void*) ip->src);
+ return;
+ }
+
+ BAD("ICMP6 Unhandled");
+}
+
+void eth_recv(void *_data, size_t len) {
+ uint8_t *data = _data;
+ ip6_hdr *ip;
+ uint32_t n;
+
+ if (len < (ETH_HDR_LEN + IP6_HDR_LEN)) BAD("Bogus Header Len");
+ if (data[12] != (ETH_IP6 >> 8)) return;
+ if (data[13] != (ETH_IP6 & 0xFF)) return;
+
+ ip = (void*) (data + ETH_HDR_LEN);
+ data += (ETH_HDR_LEN + IP6_HDR_LEN);
+ len -= (ETH_HDR_LEN + IP6_HDR_LEN);
+
+ // require v6
+ if ((ip->ver_tc_flow & 0xF0) != 0x60) BAD("Unknown IP6 Version");
+
+ // ensure length is sane
+ n = ntohs(ip->length);
+ if (n > len) BAD("IP6 Length Mismatch");
+
+ // ignore any trailing data in the ethernet frame
+ len = n;
+
+ // require that we are the destination
+ if (memcmp(&ll_ip6_addr, ip->dst, IP6_ADDR_LEN) &&
+ memcmp(&snm_ip6_addr, ip->dst, IP6_ADDR_LEN)) {
+ return;
+ }
+
+ // stash the sender's info to simplify replies
+ memcpy(&rx_mac_addr, (uint8_t*) _data + 6, ETH_ADDR_LEN);
+ memcpy(&rx_ip6_addr, ip->src, IP6_ADDR_LEN);
+
+ if (ip->next_header == HDR_ICMP6) {
+ icmp6_recv(ip, data, len);
+ return;
+ }
+
+ if (ip->next_header == HDR_UDP) {
+ _udp6_recv(ip, data, len);
+ return;
+ }
+
+ BAD("Unhandled IP6");
+}
+
+char *ip6toa(char *_out, void *ip6addr) {
+ const uint8_t *x = ip6addr;
+ const uint8_t *end = x + 16;
+ char *out = _out;
+ uint16_t n;
+
+ n = (x[0] << 8) | x[1];
+ while ((n == 0) && (x < end)) {
+ x += 2;
+ n = (x[0] << 8) | x[1];
+ }
+
+ if ((end - x) < 16) {
+ if (end == x) {
+ // all 0s - special case
+ sprintf(out, "::");
+ return _out;
+ }
+ // we consumed some number of leading 0s
+ out += sprintf(out, ":");
+ while (x < end) {
+ out += sprintf(out, ":%x", n);
+ x += 2;
+ n = (x[0] << 8) | x[1];
+ }
+ return _out;
+ }
+
+ while (x < (end - 2)) {
+ out += sprintf(out, "%x:", n);
+ x += 2;
+ n = (x[0] << 8) | x[1];
+ if (n == 0) goto middle_zeros;
+ }
+ out += sprintf(out, "%x", n);
+ return _out;
+
+middle_zeros:
+ while ((n == 0) && (x < end)) {
+ x += 2;
+ n = (x[0] << 8) | x[1];
+ }
+ if (x == end) {
+ out += sprintf(out, ":");
+ return _out;
+ }
+ while (x < end) {
+ out += sprintf(out, ":%x", n);
+ x += 2;
+ n = (x[0] << 8) | x[1];
+ }
+ return _out;
+}
+
diff --git a/src/inet6.h b/src/inet6.h
new file mode 100644
index 0000000..12d7cf6
--- /dev/null
+++ b/src/inet6.h
@@ -0,0 +1,184 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#pragma once
+
+typedef struct mac_addr_t mac_addr;
+typedef struct ip6_addr_t ip6_addr;
+typedef struct ip6_hdr_t ip6_hdr;
+typedef struct udp_hdr_t udp_hdr;
+typedef struct icmp6_hdr_t icmp6_hdr;
+typedef struct ndp_n_hdr_t ndp_n_hdr;
+
+#define ETH_ADDR_LEN 6
+#define ETH_HDR_LEN 14
+#define ETH_MTU 1514
+
+#define IP6_ADDR_LEN 16
+#define IP6_HDR_LEN 40
+
+#define IP6_MIN_MTU 1280
+
+#define UDP_HDR_LEN 8
+
+
+struct mac_addr_t {
+ uint8_t x[ETH_ADDR_LEN];
+} __attribute__((packed));
+
+struct ip6_addr_t {
+ uint8_t x[IP6_ADDR_LEN];
+} __attribute__((packed));
+
+extern const ip6_addr ip6_ll_all_nodes;
+extern const ip6_addr ip6_ll_all_routers;
+
+#define ETH_IP4 0x0800
+#define ETH_ARP 0x0806
+#define ETH_IP6 0x86DD
+
+#define HDR_HNH_OPT 0
+#define HDR_TCP 6
+#define HDR_UDP 17
+#define HDR_ROUTING 43
+#define HDR_FRAGMENT 44
+#define HDR_ICMP6 58
+#define HDR_NONE 59
+#define HDR_DST_OPT 60
+
+struct ip6_hdr_t {
+ uint32_t ver_tc_flow;
+ uint16_t length;
+ uint8_t next_header;
+ uint8_t hop_limit;
+ uint8_t src[IP6_ADDR_LEN];
+ uint8_t dst[IP6_ADDR_LEN];
+} __attribute__((packed));
+
+struct udp_hdr_t {
+ uint16_t src_port;
+ uint16_t dst_port;
+ uint16_t length;
+ uint16_t checksum;
+} __attribute__((packed));
+
+
+#define ICMP6_DEST_UNREACHABLE 1
+#define ICMP6_PACKET_TOO_BIG 2
+#define ICMP6_TIME_EXCEEDED 3
+#define ICMP6_PARAMETER_PROBLEM 4
+
+#define ICMP6_ECHO_REQUEST 128
+#define ICMP6_ECHO_REPLY 129
+
+#define ICMP6_NDP_N_SOLICIT 135
+#define ICMP6_NDP_N_ADVERTISE 136
+
+struct icmp6_hdr_t {
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+} __attribute__((packed));
+
+struct ndp_n_hdr_t {
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+ uint32_t flags;
+ uint8_t target[IP6_ADDR_LEN];
+ uint8_t options[0];
+} __attribute__((packed));
+
+#define NDP_N_SRC_LL_ADDR 1
+#define NDP_N_TGT_LL_ADDR 2
+#define NDP_N_PREFIX_INFO 3
+#define NDP_N_REDIRECTED_HDR 4
+#define NDP_N_MTU 5
+
+#ifndef ntohs
+#define ntohs(n) _swap16(n)
+#define htons(n) _swap16(n)
+static inline uint16_t _swap16(uint16_t n) {
+ return (n >> 8) | (n << 8);
+}
+#endif
+
+#ifndef ntohl
+#define ntohl(n) _swap32(n)
+#define htonl(n) _swap32(n)
+static inline uint32_t _swap32(uint32_t n) {
+ return (n >> 24) | ((n >> 8) & 0xFF00) |
+ ((n & 0xFF00) << 8) | (n << 24);
+}
+#endif
+
+// Formats an IP6 address into the provided buffer (which must be
+// at least IP6TOAMAX bytes in size), and returns the buffer address.
+char *ip6toa(char *_out, void *ip6addr);
+#define IP6TOAMAX 40
+
+// provided by inet6.c
+void ip6_init(void *macaddr);
+void eth_recv(void *data, size_t len);
+
+// provided by interface driver
+void *eth_get_buffer(size_t len);
+void eth_put_buffer(void *ptr);
+int eth_send(void *data, size_t len);
+int eth_add_mcast_filter(const mac_addr *addr);
+
+// call to transmit a UDP packet
+int udp6_send(const void *data, size_t len,
+ const ip6_addr *daddr, uint16_t dport,
+ uint16_t sport);
+
+// implement to recive UDP packets
+void udp6_recv(void *data, size_t len,
+ const ip6_addr *daddr, uint16_t dport,
+ const ip6_addr *saddr, uint16_t sport);
+
+// NOTES
+//
+// This is an extremely minimal IPv6 stack, supporting just enough
+// functionality to talk to link local hosts over UDP.
+//
+// It responds to ICMPv6 Neighbor Solicitations for its link local
+// address, which is computed from the mac address provided by the
+// ethernet interface driver.
+//
+// It responds to PINGs.
+//
+// It can only transmit to multicast addresses or to the address it
+// last received a packet from (general usecase is to reply to a UDP
+// packet from the UDP callback, which this supports)
+//
+// It does not currently do duplicate address detection, which is
+// probably the most severe bug.
+//
+// It does not support any IPv6 options and will drop packets with
+// options.
+//
+// It expects the network stack to provide transmit buffer allocation
+// and free functionality. It will allocate a single transmit buffer
+// from udp6_send() or icmp6_send() to fill out and either pass to the
+// network stack via eth_send() or, in the event of an error, release
+// via eth_put_buffer().
+//
diff --git a/src/nbserver.c b/src/nbserver.c
new file mode 100644
index 0000000..27bb82e
--- /dev/null
+++ b/src/nbserver.c
@@ -0,0 +1,242 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <netboot.h>
+
+static uint32_t cookie = 1;
+
+static int io(int s, nbmsg *msg, size_t len, nbmsg *ack) {
+ int retries = 5;
+ int r;
+
+ msg->magic = NB_MAGIC;
+ msg->cookie = cookie++;
+
+ for (;;) {
+ r = write(s, msg, len);
+ if (r < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+ continue;
+ }
+ fprintf(stderr, "\nnbserver: socket write error %d\n", errno);
+ return -1;
+ }
+ r = read(s, ack, 2048);
+ if (r < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+ retries--;
+ if (retries > 0) {
+ fprintf(stderr, "T");
+ continue;
+ }
+ fprintf(stderr, "\nnbserver: timed out\n");
+ } else {
+ fprintf(stderr, "\nnbserver: socket read error %d\n", errno);
+ }
+ return -1;
+ }
+ if (r < sizeof(nbmsg)) {
+ fprintf(stderr, "Z");
+ continue;
+ }
+ if (ack->magic != NB_MAGIC) {
+ fprintf(stderr, "?");
+ continue;
+ }
+ if (ack->cookie != msg->cookie) {
+ fprintf(stderr, "C");
+ continue;
+ }
+ if (ack->arg != msg->arg) {
+ fprintf(stderr, "A");
+ continue;
+ }
+ if (ack->cmd == NB_ACK) return 0;
+ return -1;
+ }
+}
+
+static void xfer(struct sockaddr_in6 *addr, const char *fn) {
+ char msgbuf[2048];
+ char ackbuf[2048];
+ char tmp[INET6_ADDRSTRLEN];
+ struct timeval tv;
+ nbmsg *msg = (void*) msgbuf;
+ nbmsg *ack = (void*) ackbuf;
+ FILE *fp;
+ int s, r;
+ int count = 0;
+
+ if ((fp = fopen(fn, "rb")) == NULL) {
+ return;
+ }
+ if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ fprintf(stderr, "nbserver: cannot create socket %d\n", errno);
+ goto done;
+ }
+ tv.tv_sec = 0;
+ tv.tv_usec = 250 * 1000;
+ setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (connect(s, (void*) addr, sizeof(*addr)) < 0) {
+ fprintf(stderr, "nbserver: cannot connect to [%s]%d\n",
+ inet_ntop(AF_INET6, &addr->sin6_addr, tmp, sizeof(tmp)),
+ ntohs(addr->sin6_port));
+ goto done;
+ }
+
+ msg->cmd = NB_SEND_FILE;
+ msg->arg = 0;
+ strcpy((void*) msg->data, "kernel.bin");
+ if (io(s, msg, sizeof(nbmsg) + sizeof("kernel.bin"), ack)) {
+ fprintf(stderr, "nbserver: failed to start transfer\n");
+ goto done;
+ }
+
+ msg->cmd = NB_DATA;
+ msg->arg = 0;
+ do {
+ r = fread(msg->data, 1, 1024, fp);
+ if (r == 0) {
+ if (ferror(fp)) {
+ fprintf(stderr, "\nnbserver: error: reading '%s'\n", fn);
+ goto done;
+ }
+ break;
+ }
+ count += r;
+ if (count >= (32*1024)) {
+ count = 0;
+ fprintf(stderr, "#");
+ }
+ if (io(s, msg, sizeof(nbmsg) + r, ack)) {
+ fprintf(stderr, "\nnbserver: error: sending '%s'\n", fn);
+ goto done;
+ }
+ msg->arg += r;
+ } while (r != 0);
+
+ msg->cmd = NB_BOOT;
+ msg->arg = 0;
+ if (io(s, msg, sizeof(nbmsg), ack)) {
+ fprintf(stderr, "\nnbserver: failed to send boot command\n");
+ } else {
+ fprintf(stderr, "\nnbserver: sent boot command\n");
+ }
+done:
+ if (s >= 0) close(s);
+ if (fp != NULL) fclose(fp);
+}
+
+void usage(void) {
+ fprintf(stderr,
+ "usage: nbserver [ <option> ]* <filename>\n"
+ "\n"
+ "options: -1 only boot once, then exit\n"
+ );
+ exit(1);
+}
+
+int main(int argc, char **argv) {
+ struct sockaddr_in6 addr;
+ char tmp[INET6_ADDRSTRLEN];
+ int r, s, n = 1;
+ const char *fn = NULL;
+ int once = 0;
+
+ while (argc > 1) {
+ if (argv[1][0] != '-') {
+ if (fn != NULL) usage();
+ fn = argv[1];
+ } else if (!strcmp(argv[1], "-1")) {
+ once = 1;
+ } else {
+ usage();
+ }
+ argc--;
+ argv++;
+ }
+ if (fn == NULL) {
+ usage();
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(NB_ADVERT_PORT);
+
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0) {
+ fprintf(stderr, "nbserver: cannot create socket %d\n", s);
+ return -1;
+ }
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+ if ((r = bind(s, (void*) &addr, sizeof(addr))) < 0) {
+ fprintf(stderr, "nbserver: cannot bind to [%s]%d %d\n",
+ inet_ntop(AF_INET6, &addr.sin6_addr, tmp, sizeof(tmp)),
+ ntohs(addr.sin6_port), r);
+ return -1;
+ }
+
+ fprintf(stderr, "nbserver: listening on [%s]%d\n",
+ inet_ntop(AF_INET6, &addr.sin6_addr, tmp, sizeof(tmp)),
+ ntohs(addr.sin6_port));
+ for (;;) {
+ struct sockaddr_in6 ra;
+ socklen_t rlen;
+ char buf[4096];
+ nbmsg *msg = (void*) buf;
+ rlen = sizeof(ra);
+ r = recvfrom(s, buf, 4096, 0, (void*) &ra, &rlen);
+ if (r < 0) {
+ fprintf(stderr, "nbserver: socket read error %d\n", r);
+ break;
+ }
+ if (r < sizeof(nbmsg)) continue;
+ if ((ra.sin6_addr.s6_addr[0] != 0xFE) || (ra.sin6_addr.s6_addr[1] != 0x80)) {
+ fprintf(stderr, "ignoring non-link-local message\n");
+ continue;
+ }
+ if (msg->magic != NB_MAGIC) continue;
+ if (msg->cmd != NB_ADVERTISE) continue;
+ fprintf(stderr, "nbserver: got beacon from [%s]%d\n",
+ inet_ntop(AF_INET6, &ra.sin6_addr, tmp, sizeof(tmp)),
+ ntohs(ra.sin6_port));
+ fprintf(stderr, "nbserver: sending '%s'...\n", fn);
+ xfer(&ra, fn);
+ if (once) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/netboot.c b/src/netboot.c
new file mode 100644
index 0000000..93e9832
--- /dev/null
+++ b/src/netboot.c
@@ -0,0 +1,188 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <stdio.h>
+#include <string.h>
+
+#include <netboot.h>
+#include <netifc.h>
+#include <inet6.h>
+
+static uint32_t last_cookie = 0;
+static uint32_t last_cmd = 0;
+static uint32_t last_arg = 0;
+static uint32_t last_ack_cmd = 0;
+static uint32_t last_ack_arg = 0;
+
+static uint8_t *nb_buffer;
+static uint32_t nb_bufsize = 0;
+static uint32_t nb_offset = 0;
+
+static int nb_boot_now = 0;
+static int nb_active = 0;
+
+void udp6_recv(void *data, size_t len,
+ const ip6_addr *daddr, uint16_t dport,
+ const ip6_addr *saddr, uint16_t sport) {
+ nbmsg *msg = data;
+ nbmsg ack;
+
+ if (dport != NB_SERVER_PORT) return;
+
+ if (len < sizeof(nbmsg)) return;
+ len -= sizeof(nbmsg);
+
+ //printf("netboot: MSG %08x %08x %08x %08x datalen %d\n",
+ // msg->magic, msg->cookie, msg->cmd, msg->arg, len);
+
+ if ((last_cookie == msg->cookie) &&
+ (last_cmd == msg->cmd) && (last_arg = msg->arg)) {
+ // host must have missed the ack. resend
+ ack.magic = NB_MAGIC;
+ ack.cookie = last_cookie;
+ ack.cmd = last_ack_cmd;
+ ack.arg = last_ack_arg;
+ goto transmit;
+ }
+
+ ack.cmd = NB_ACK;
+ ack.arg = 0;
+
+ switch (msg->cmd) {
+ case NB_COMMAND:
+ if (len == 0) return;
+ msg->data[len - 1] = 0;
+ break;
+ case NB_SEND_FILE:
+ if (len == 0) return;
+ msg->data[len - 1] = 0;
+ nb_offset = 0;
+ printf("netboot: Receive File...\n");
+ break;
+ case NB_DATA:
+ if (msg->arg != nb_offset) return;
+ if (nb_buffer == 0) return;
+ ack.arg = msg->arg;
+ if ((nb_offset + len) > nb_bufsize) {
+ ack.cmd = NB_ERROR_TOO_LARGE;
+ } else {
+ memcpy(nb_buffer + nb_offset, msg->data, len);
+ nb_offset += len;
+ ack.cmd = NB_ACK;
+ }
+ break;
+ case NB_BOOT:
+ nb_boot_now = 1;
+ printf("netboot: Boot Kernel...\n");
+ break;
+ default:
+ ack.cmd = NB_ERROR_BAD_CMD;
+ ack.arg = 0;
+ }
+
+ last_cookie = msg->cookie;
+ last_cmd = msg->cmd;
+ last_arg = msg->arg;
+ last_ack_cmd = ack.cmd;
+ last_ack_arg = ack.arg;
+
+ ack.cookie = msg->cookie;
+ ack.magic = NB_MAGIC;
+transmit:
+ nb_active = 1;
+ udp6_send(&ack, sizeof(ack), saddr, sport, NB_SERVER_PORT);
+}
+
+static char advertise_data[] =
+ "version\00.1\0"
+ "serialno\0unknown\0"
+ "board\0unknown\0";
+
+static void advertise(void) {
+ uint8_t buffer[256];
+ nbmsg *msg = (void*) buffer;
+ msg->magic = NB_MAGIC;
+ msg->cookie = 0;
+ msg->cmd = NB_ADVERTISE;
+ msg->arg = 0;
+ memcpy(msg->data, advertise_data, sizeof(advertise_data));
+ udp6_send(buffer, sizeof(nbmsg) + sizeof(advertise_data),
+ &ip6_ll_all_nodes, NB_ADVERT_PORT, NB_SERVER_PORT);
+}
+
+#define FAST_TICK 100
+#define SLOW_TICK 1000
+
+int netboot_init(void *buf, size_t len) {
+ if (netifc_open()) {
+ printf("netboot: Failed to open network interface\n");
+ return -1;
+ }
+
+ nb_buffer = buf;
+ nb_bufsize = len;
+ return 0;
+}
+
+static int nb_fastcount = 0;
+static int nb_online = 0;
+
+int netboot_poll(void) {
+ if (netifc_active()) {
+ if (nb_online == 0) {
+ printf("netboot: interface online\n");
+ nb_online = 1;
+ nb_fastcount = 20;
+ netifc_set_timer(FAST_TICK);
+ advertise();
+ }
+ } else {
+ if (nb_online == 1) {
+ printf("netboot: interface offline\n");
+ nb_online = 0;
+ }
+ return 0;
+ }
+ if (netifc_timer_expired()) {
+ if (nb_fastcount) {
+ nb_fastcount--;
+ netifc_set_timer(FAST_TICK);
+ } else {
+ netifc_set_timer(SLOW_TICK);
+ }
+ if (nb_active) {
+ // don't advertise if we're in a transfer
+ nb_active = 0;
+ } else {
+ advertise();
+ }
+ }
+
+ netifc_poll();
+
+ if (nb_boot_now) {
+ nb_boot_now = 0;
+ return nb_offset;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/src/netboot.h b/src/netboot.h
new file mode 100644
index 0000000..8019b78
--- /dev/null
+++ b/src/netboot.h
@@ -0,0 +1,52 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#pragma once
+
+#define NB_MAGIC 0xAA774217
+
+#define NB_SERVER_PORT 33330
+#define NB_ADVERT_PORT 33331
+
+#define NB_COMMAND 1 // arg=0, data=command
+#define NB_SEND_FILE 2 // arg=0, data=filename
+#define NB_DATA 3 // arg=blocknum, data=data
+#define NB_BOOT 4 // arg=0
+
+#define NB_ACK 0
+
+#define NB_ADVERTISE 0x77777777
+
+#define NB_ERROR 0x80000000
+#define NB_ERROR_BAD_CMD 0x80000001
+#define NB_ERROR_BAD_PARAM 0x80000002
+#define NB_ERROR_TOO_LARGE 0x80000003
+
+typedef struct nbmsg_t {
+ uint32_t magic;
+ uint32_t cookie;
+ uint32_t cmd;
+ uint32_t arg;
+ uint8_t data[0];
+} nbmsg;
+
+int netboot_init(void *buf, size_t len);
+int netboot_poll(void);
diff --git a/src/netifc.c b/src/netifc.c
new file mode 100644
index 0000000..2760043
--- /dev/null
+++ b/src/netifc.c
@@ -0,0 +1,270 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <goodies.h>
+
+#include <netifc.h>
+#include <inet6.h>
+
+static EFI_SIMPLE_NETWORK *snp;
+
+#define MAX_FILTER 8
+static EFI_MAC_ADDRESS mcast_filters[MAX_FILTER];
+static unsigned mcast_filter_count = 0;
+
+#define NUM_BUFFER_PAGES 8
+#define ETH_BUFFER_SIZE 1516
+#define ETH_HEADER_SIZE 16
+#define ETH_BUFFER_MAGIC 0x424201020304A7A7UL
+
+typedef struct eth_buffer_t eth_buffer;
+struct eth_buffer_t {
+ uint64_t magic;
+ eth_buffer *next;
+ uint8_t data[0];
+};
+
+static EFI_PHYSICAL_ADDRESS eth_buffers_base = 0;
+static eth_buffer *eth_buffers = NULL;
+
+void *eth_get_buffer(size_t sz) {
+ eth_buffer *buf;
+ if (sz > ETH_BUFFER_SIZE) {
+ return NULL;
+ }
+ if (eth_buffers == NULL) {
+ return NULL;
+ }
+ buf = eth_buffers;
+ eth_buffers = buf->next;
+ buf->next = NULL;
+ return buf->data;
+}
+
+void eth_put_buffer(void *data) {
+ eth_buffer *buf = (void*) (((uint64_t) data) & (~2047));
+
+ if (buf->magic != ETH_BUFFER_MAGIC) {
+ printf("fatal: eth buffer %p (from %p) bad magic %lx\n", buf, data, buf->magic);
+ for (;;) ;
+ }
+ buf->next = eth_buffers;
+ eth_buffers = buf;
+}
+
+int eth_send(void *data, size_t len) {
+ EFI_STATUS r;
+
+ if ((r = snp->Transmit(snp, 0, len, (void*) data, NULL, NULL, NULL))) {
+ eth_put_buffer(data);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+void eth_dump_status(void) {
+ printf("State/HwAdSz/HdrSz/MaxSz %d %d %d %d\n",
+ snp->Mode->State, snp->Mode->HwAddressSize,
+ snp->Mode->MediaHeaderSize, snp->Mode->MaxPacketSize);
+ printf("RcvMask/RcvCfg/MaxMcast/NumMcast %d %d %d %d\n",
+ snp->Mode->ReceiveFilterMask, snp->Mode->ReceiveFilterSetting,
+ snp->Mode->MaxMCastFilterCount, snp->Mode->MCastFilterCount);
+ UINT8 *x = snp->Mode->CurrentAddress.Addr;
+ printf("MacAddr %02x:%02x:%02x:%02x:%02x:%02x\n",
+ x[0], x[1], x[2], x[3], x[4], x[5]);
+ printf("SetMac/MultiTx/LinkDetect/Link %d %d %d %d\n",
+ snp->Mode->MacAddressChangeable, snp->Mode->MultipleTxSupported,
+ snp->Mode->MediaPresentSupported, snp->Mode->MediaPresent);
+}
+
+int eth_add_mcast_filter(const mac_addr *addr) {
+ if (mcast_filter_count >= MAX_FILTER) return -1;
+ if (mcast_filter_count >= snp->Mode->MaxMCastFilterCount) return -1;
+ memcpy(mcast_filters + mcast_filter_count, addr, ETH_ADDR_LEN);
+ mcast_filter_count++;
+ return 0;
+}
+
+static EFI_EVENT net_timer = NULL;
+
+#define TIMER_MS(n) (((uint64_t) (n)) * 10000UL)
+
+void netifc_set_timer(uint32_t ms) {
+ if (net_timer == 0) {
+ return;
+ }
+ gBS->SetTimer(net_timer, TimerRelative, TIMER_MS(ms));
+}
+
+int netifc_timer_expired(void) {
+ if (net_timer == 0) {
+ return 0;
+ }
+ if (gBS->CheckEvent(net_timer) == EFI_SUCCESS) {
+ return 1;
+ }
+ return 0;
+}
+
+int netifc_open(void) {
+ EFI_BOOT_SERVICES *bs = gSys->BootServices;
+ EFI_HANDLE h[32];
+ EFI_STATUS r;
+ int i, j;
+ UINTN sz;
+
+ bs->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &net_timer);
+
+ sz = sizeof(h);
+ r = bs->LocateHandle(ByProtocol, &SimpleNetworkProtocol, NULL, &sz, h);
+ if (r != EFI_SUCCESS) {
+ printf("Failed to locate SNP handle(s) %ld\n", r);
+ return -1;
+ }
+
+ r = bs->OpenProtocol(h[0], &SimpleNetworkProtocol, (void**) &snp, gImg, NULL,
+ EFI_OPEN_PROTOCOL_EXCLUSIVE);
+ if (r) {
+ printf("Failed to open SNP exclusively %ld\n", r);
+ return -1;
+ }
+
+ if (snp->Mode->State != EfiSimpleNetworkStarted) {
+ snp->Start(snp);
+ if (snp->Mode->State != EfiSimpleNetworkStarted) {
+ printf("Failed to start SNP\n");
+ return -1;
+ }
+ r = snp->Initialize(snp, 32768, 32768);
+ if (r) {
+ printf("Failed to initialize SNP\n");
+ return -1;
+ }
+ }
+
+ if (bs->AllocatePages(AllocateAnyPages, EfiLoaderData, NUM_BUFFER_PAGES, ð_buffers_base)) {
+ printf("Failed to allocate net buffers\n");
+ return -1;
+ }
+
+ uint8_t *ptr = (void*) eth_buffers_base;
+ for (r = 0; r < (NUM_BUFFER_PAGES * 2); r++) {
+ eth_buffer *buf = (void*) ptr;
+ buf->magic = ETH_BUFFER_MAGIC;
+ eth_put_buffer(buf);
+ ptr += 2048;
+ }
+
+ ip6_init(snp->Mode->CurrentAddress.Addr);
+
+ r = snp->ReceiveFilters(snp,
+ EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST,
+ 0, 0, mcast_filter_count, (void*) mcast_filters);
+ if (r) {
+ printf("Failed to install multicast filters %lx\n", r);
+ return -1;
+ }
+
+ eth_dump_status();
+
+ if (snp->Mode->MCastFilterCount != mcast_filter_count) {
+ printf("OOPS: expected %d filters, found %d\n",
+ mcast_filter_count, snp->Mode->MCastFilterCount);
+ goto force_promisc;
+ }
+ for (i = 0; i < mcast_filter_count; i++) {
+ //uint8_t *m = (void*) &mcast_filters[i];
+ //printf("i=%d %02x %02x %02x %02x %02x %02x\n", i, m[0], m[1], m[2], m[3], m[4], m[5]);
+ for (j = 0; j < mcast_filter_count; j++) {
+ //m = (void*) &snp->Mode->MCastFilter[j];
+ //printf("j=%d %02x %02x %02x %02x %02x %02x\n", j, m[0], m[1], m[2], m[3], m[4], m[5]);
+ if (!memcmp(mcast_filters + i, &snp->Mode->MCastFilter[j], 6)) {
+ goto found_it;
+ }
+ }
+ printf("OOPS: filter #%d missing\n", i);
+ goto force_promisc;
+ found_it:
+ ;
+ }
+
+ return 0;
+
+force_promisc:
+ r = snp->ReceiveFilters(snp,
+ EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS |
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST,
+ 0, 0, 0, NULL);
+ if (r) {
+ printf("Failed to set promiscuous mode %lx\n", r);
+ return -1;
+ }
+ return 0;
+}
+
+void netifc_close(void) {
+}
+
+int netifc_active(void) {
+ return (snp != 0);
+}
+
+void netifc_poll(void) {
+ UINT8 data[1514];
+ EFI_STATUS r;
+ UINTN hsz, bsz;
+ UINT32 irq;
+ VOID *txdone;
+
+ if ((r = snp->GetStatus(snp, &irq, &txdone))) {
+ return;
+ }
+
+ if (txdone) {
+ eth_put_buffer(txdone);
+ }
+
+ if (irq & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT) {
+ hsz = 0;
+ bsz = sizeof(data);
+ r = snp->Receive(snp, &hsz, &bsz, data, NULL, NULL, NULL);
+ if (r != EFI_SUCCESS) {
+ return;
+ }
+#if TRACE
+ printf("RX %02x:%02x:%02x:%02x:%02x:%02x < %02x:%02x:%02x:%02x:%02x:%02x %02x%02x %d\n",
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6], data[7], data[8], data[9], data[10], data[11],
+ data[12], data[13], (int) (bsz - hsz));
+#endif
+ eth_recv(data, bsz);
+ }
+}
+
+
diff --git a/src/netifc.h b/src/netifc.h
new file mode 100644
index 0000000..1d1c55b
--- /dev/null
+++ b/src/netifc.h
@@ -0,0 +1,41 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#pragma once
+
+// setup networking
+int netifc_open(void);
+
+// process inbound packet(s)
+void netifc_poll(void);
+
+// return nonzero if interface exists
+int netifc_active(void);
+
+// shut down networking
+void netifc_close(void);
+
+// set a timer to expire after ms milliseconds
+void netifc_set_timer(uint32_t ms);
+
+// returns true once the timer has expired
+int netifc_timer_expired(void);
+
diff --git a/src/osboot.c b/src/osboot.c
new file mode 100644
index 0000000..887441a
--- /dev/null
+++ b/src/osboot.c
@@ -0,0 +1,450 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <goodies.h>
+#include <netboot.h>
+
+#define E820_IGNORE 0
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3
+#define E820_NVS 4
+#define E820_UNUSABLE 5
+
+const char *e820name[] = {
+ "IGNORE",
+ "RAM",
+ "RESERVED",
+ "ACPI",
+ "NVS",
+ "UNUSABLE",
+};
+
+struct e820entry {
+ UINT64 addr;
+ UINT64 size;
+ UINT32 type;
+} __attribute__((packed));
+
+unsigned e820type(unsigned uefi_mem_type) {
+ switch (uefi_mem_type) {
+ case EfiReservedMemoryType:
+ case EfiPalCode:
+ return E820_RESERVED;
+ case EfiRuntimeServicesCode:
+ case EfiRuntimeServicesData:
+#if WITH_RUNTIME_SERVICES
+ return E820_RESERVED;
+#else
+ return E820_RAM;
+#endif
+ case EfiACPIReclaimMemory:
+ return E820_ACPI;
+ case EfiACPIMemoryNVS:
+ return E820_NVS;
+ case EfiLoaderCode:
+ case EfiLoaderData:
+ case EfiBootServicesCode:
+ case EfiBootServicesData:
+ case EfiConventionalMemory:
+ return E820_RAM;
+ case EfiMemoryMappedIO:
+ case EfiMemoryMappedIOPortSpace:
+ return E820_IGNORE;
+ default:
+ if (uefi_mem_type >= 0x80000000) {
+ return E820_RAM;
+ }
+ return E820_UNUSABLE;
+ }
+}
+
+static unsigned char scratch[32768];
+static struct e820entry e820table[128];
+
+int process_memory_map(EFI_SYSTEM_TABLE *sys, UINTN *_key, int silent) {
+ EFI_MEMORY_DESCRIPTOR *mmap;
+ struct e820entry *entry = e820table;
+ UINTN msize, off;
+ UINTN mkey, dsize;
+ UINT32 dversion;
+ unsigned n, type;
+ EFI_STATUS r;
+
+ msize = sizeof(scratch);
+ mmap = (EFI_MEMORY_DESCRIPTOR*) scratch;
+ mkey = dsize = dversion = 0;
+ r = sys->BootServices->GetMemoryMap(&msize, mmap, &mkey, &dsize, &dversion);
+ if (!silent) printf("r=%lx msz=%lx key=%lx dsz=%lx dvn=%x\n", r, msize, mkey, dsize, dversion);
+ if (r != EFI_SUCCESS) {
+ return -1;
+ }
+ if (msize > sizeof(scratch)) {
+ if (!silent) printf("Memory Table Too Large (%ld entries)\n", (msize / dsize));
+ return -1;
+ }
+ for (off = 0, n = 0; off < msize; off += dsize) {
+ mmap = (EFI_MEMORY_DESCRIPTOR*) (scratch + off);
+ type = e820type(mmap->Type);
+ if (type == E820_IGNORE) {
+ continue;
+ }
+ if ((n > 0) && (entry[n-1].type == type)) {
+ if ((entry[n-1].addr + entry[n-1].size) == mmap->PhysicalStart) {
+ entry[n-1].size += mmap->NumberOfPages * 4096UL;
+ continue;
+ }
+ }
+ entry[n].addr = mmap->PhysicalStart;
+ entry[n].size = mmap->NumberOfPages * 4096UL;
+ entry[n].type = type;
+ n++;
+ if (n == 128) {
+ if (!silent) printf("E820 Table Too Large (%ld raw entries)\n", (msize / dsize));
+ return -1;
+ }
+ }
+ *_key = mkey;
+ return n;
+}
+
+#define ZP_E820_COUNT 0x1E8 // byte
+#define ZP_SETUP 0x1F1 // start of setup structure
+#define ZP_SETUP_SECTS 0x1F1 // byte (setup_size/512-1)
+#define ZP_JUMP 0x200 // jump instruction
+#define ZP_HEADER 0x202 // word "HdrS"
+#define ZP_VERSION 0x206 // half 0xHHLL
+#define ZP_LOADER_TYPE 0x210 // byte
+#define ZP_RAMDISK_BASE 0x218 // word (ptr or 0)
+#define ZP_RAMDISK_SIZE 0x21C // word (bytes)
+#define ZP_EXTRA_MAGIC 0x220 // word
+#define ZP_CMDLINE 0x228 // word (ptr)
+#define ZP_SYSSIZE 0x1F4 // word (size/16)
+#define ZP_XLOADFLAGS 0x236 // half
+#define ZP_E820_TABLE 0x2D0 // 128 entries
+
+#define ZP_ACPI_RSD 0x080 // word phys ptr
+#define ZP_FB_BASE 0x090
+#define ZP_FB_WIDTH 0x094
+#define ZP_FB_HEIGHT 0x098
+#define ZP_FB_STRIDE 0x09C
+#define ZP_FB_FORMAT 0x0A0
+#define ZP_FB_REGBASE 0x0A4
+#define ZP_FB_SIZE 0x0A8
+
+#define ZP_MAGIC_VALUE 0xDBC64323
+
+#define ZP8(p,off) (*((UINT8*)((p) + (off))))
+#define ZP16(p,off) (*((UINT16*)((p) + (off))))
+#define ZP32(p,off) (*((UINT32*)((p) + (off))))
+
+typedef struct {
+ UINT8 *zeropage;
+ UINT8 *cmdline;
+ void *image;
+ UINT32 pages;
+} kernel_t;
+
+void install_memmap(kernel_t *k, struct e820entry *memmap, unsigned count) {
+ memcpy(memmap, k->zeropage + ZP_E820_TABLE, sizeof(*memmap) * count);
+ ZP8(k->zeropage, ZP_E820_COUNT) = count;
+}
+
+void start_kernel(kernel_t *k) {
+ // 64bit entry is at offset 0x200
+ UINT64 entry = (UINT64) (k->image + 0x200);
+
+ // ebx = 0, ebp = 0, edi = 0, esi = zeropage
+ __asm__ __volatile__ (
+ "movl $0, %%ebp \n"
+ "cli \n"
+ "jmp *%[entry] \n"
+ :: [entry]"a"(entry),
+ [zeropage] "S"(k->zeropage),
+ "b"(0), "D"(0)
+ );
+ for (;;) ;
+}
+
+int load_kernel(EFI_BOOT_SERVICES *bs, uint8_t *image, size_t sz, kernel_t *k) {
+ UINT32 setup_sz;
+ UINT32 image_sz;
+ UINT32 setup_end;
+ EFI_PHYSICAL_ADDRESS mem;
+
+ k->zeropage = NULL;
+ k->cmdline = NULL;
+ k->image = NULL;
+ k->pages = 0;
+
+ if (sz < 1024) {
+ // way too small to be a kernel
+ goto fail;
+ }
+
+ if (ZP32(image, ZP_HEADER) != 0x53726448) {
+ printf("kernel: invalid setup magic %08x\n", ZP32(image, ZP_HEADER));
+ goto fail;
+ }
+ if (ZP16(image, ZP_VERSION) < 0x020B) {
+ printf("kernel: unsupported setup version %04x\n", ZP16(image, ZP_VERSION));
+ goto fail;
+ }
+ setup_sz = (ZP8(image, ZP_SETUP_SECTS) + 1) * 512;
+ image_sz = (ZP16(image, ZP_SYSSIZE) * 16);
+ setup_end = ZP_JUMP + ZP8(image, ZP_JUMP+1);
+
+ printf("setup %d image %d hdr %04x-%04x\n", setup_sz, image_sz, ZP_SETUP, setup_end);
+ // image size may be rounded up, thus +15
+ if ((setup_sz < 1024) || ((setup_sz + image_sz) > (sz + 15))) {
+ printf("kernel: invalid image size\n");
+ goto fail;
+ }
+
+ mem = 0xFF000;
+ if (bs->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1, &mem)) {
+ printf("kernel: cannot allocate 'zero page'\n");
+ goto fail;
+ }
+ k->zeropage = (void*) mem;
+
+ mem = 0xFF000;
+ if (bs->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1, &mem)) {
+ printf("kernel: cannot allocate commandline\n");
+ goto fail;
+ }
+ k->cmdline = (void*) mem;
+
+ mem = 0x100000;
+ k->pages = (image_sz + 4095) / 4096;
+ if (bs->AllocatePages(AllocateAddress, EfiLoaderData, k->pages + 1, &mem)) {
+ printf("kernel: cannot allocate kernel\n");
+ goto fail;
+ }
+ k->image = (void*) mem;
+
+ // setup zero page, copy setup header from kernel binary
+ ZeroMem(k->zeropage, 4096);
+ CopyMem(k->zeropage + ZP_SETUP, image + ZP_SETUP, setup_end - ZP_SETUP);
+
+ CopyMem(k->image, image + setup_sz, image_sz);
+
+ // empty commandline for now
+ ZP32(k->zeropage, ZP_CMDLINE) = (uint64_t) k->cmdline;
+ k->cmdline[0] = 0;
+
+ // no ramdisk for now
+ ZP32(k->zeropage, ZP_RAMDISK_BASE) = 0;
+ ZP32(k->zeropage, ZP_RAMDISK_SIZE) = 0;
+
+ // undefined bootloader
+ ZP8(k->zeropage, ZP_LOADER_TYPE) = 0xFF;
+
+ printf("kernel @%p, zeropage @%p, cmdline @%p\n",
+ k->image, k->zeropage, k->cmdline);
+
+ return 0;
+fail:
+ if (k->image) {
+ bs->FreePages((EFI_PHYSICAL_ADDRESS) k->image, k->pages);
+ }
+ if (k->cmdline) {
+ bs->FreePages((EFI_PHYSICAL_ADDRESS) k->cmdline, 1);
+ }
+ if (k->zeropage) {
+ bs->FreePages((EFI_PHYSICAL_ADDRESS) k->zeropage, 1);
+ }
+
+ return -1;
+}
+
+static EFI_GUID GraphicsOutputProtocol = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+
+void dump_graphics_modes(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop) {
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
+ UINTN sz;
+ UINT32 num;
+ for (num = 0; num < gop->Mode->MaxMode; num++) {
+ if (gop->QueryMode(gop, num, &sz, &info)) {
+ continue;
+ }
+ printf("Mode %d %d x %d (stride %d) fmt %d\n",
+ num, info->HorizontalResolution, info->VerticalResolution,
+ info->PixelsPerScanLine, info->PixelFormat);
+ if (info->PixelFormat == PixelBitMask) {
+ printf("Mode %d R:%08x G:%08x B:%08x X:%08x\n", num,
+ info->PixelInformation.RedMask,
+ info->PixelInformation.GreenMask,
+ info->PixelInformation.BlueMask,
+ info->PixelInformation.ReservedMask);
+ }
+ }
+}
+
+static EFI_GUID AcpiTableGUID = ACPI_TABLE_GUID;
+static EFI_GUID Acpi2TableGUID = ACPI_20_TABLE_GUID;
+
+static UINT8 ACPI_RSD_PTR[8] = "RSD PTR ";
+
+uint32_t find_acpi_root(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ EFI_CONFIGURATION_TABLE *cfgtab = sys->ConfigurationTable;
+ int i;
+
+ for (i = 0; i < sys->NumberOfTableEntries; i++) {
+ if (!CompareGuid(&cfgtab[i].VendorGuid, &AcpiTableGUID) &&
+ !CompareGuid(&cfgtab[i].VendorGuid, &Acpi2TableGUID)) {
+ // not an ACPI table
+ continue;
+ }
+ if (CompareMem(cfgtab[i].VendorTable, ACPI_RSD_PTR, 8)) {
+ // not the Root Description Pointer
+ continue;
+ }
+ return (uint64_t) cfgtab[i].VendorTable;
+ }
+ return 0;
+}
+
+static EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
+
+int boot_kernel(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys, void *image, size_t sz) {
+ kernel_t kernel;
+ EFI_STATUS r;
+ UINTN key;
+ int n;
+
+ printf("boot_kernel() from %p (%ld bytes)\n", image, sz);
+
+ if (load_kernel(sys->BootServices, image, sz, &kernel)) {
+ printf("Failed to load kernel image\n");
+ goto fail;
+ }
+
+ ZP32(kernel.zeropage, ZP_EXTRA_MAGIC) = ZP_MAGIC_VALUE;
+ ZP32(kernel.zeropage, ZP_ACPI_RSD) = find_acpi_root(img, sys);
+
+ ZP32(kernel.zeropage, ZP_FB_BASE) = (UINT32) gop->Mode->FrameBufferBase;
+ ZP32(kernel.zeropage, ZP_FB_WIDTH) = (UINT32) gop->Mode->Info->HorizontalResolution;
+ ZP32(kernel.zeropage, ZP_FB_HEIGHT) = (UINT32) gop->Mode->Info->VerticalResolution;
+ ZP32(kernel.zeropage, ZP_FB_STRIDE) = (UINT32) gop->Mode->Info->PixelsPerScanLine;
+ ZP32(kernel.zeropage, ZP_FB_FORMAT) = 4; // XRGB32
+ ZP32(kernel.zeropage, ZP_FB_REGBASE) = 0;
+ ZP32(kernel.zeropage, ZP_FB_SIZE) = 256*1024*1024;
+
+ n = process_memory_map(sys, &key, 0);
+ if (n > 0) {
+ struct e820entry *e = e820table;
+ while (n > 0) {
+ printf("%016lx %016lx %s\n", e->addr, e->size, e820name[e->type]);
+ e++;
+ n--;
+ }
+ }
+
+ r = sys->BootServices->ExitBootServices(img, key);
+ if (r == EFI_INVALID_PARAMETER) {
+ n = process_memory_map(sys, &key, 1);
+ r = sys->BootServices->ExitBootServices(img, key);
+ if (r) {
+ printf("Cannot Exit Services! %ld\n", r);
+ } else {
+ install_memmap(&kernel, e820table, n);
+ start_kernel(&kernel);
+ }
+ } else if (r) {
+ printf("Cannot Exit! %ld\n", r);
+ } else {
+ for (;;) ;
+ }
+fail:
+ return -1;
+}
+
+EFI_STATUS efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ EFI_BOOT_SERVICES *bs = sys->BootServices;
+ EFI_PHYSICAL_ADDRESS mem;
+ void *image;
+ UINTN sz;
+
+ InitializeLib(img, sys);
+ InitGoodies(img, sys);
+
+ printf("\nOSBOOT v0.2\n\n");
+
+ bs->LocateProtocol(&GraphicsOutputProtocol, NULL, (void**) &gop);
+ printf("Framebuffer base is at %lx\n\n", gop->Mode->FrameBufferBase);
+
+ image = LoadFile(L"lk.bin", &sz);
+ if (image != NULL) {
+ boot_kernel(img, sys, image, sz);
+ goto fail;
+ }
+ printf("Failed to load 'lk.bin' from boot media\n\n");
+
+ if (bs->AllocatePages(AllocateAnyPages, EfiLoaderData, 1024, &mem)) {
+ printf("Failed to allocate network io buffer\n");
+ goto fail;
+ }
+ image = (void*) mem;
+ if (netboot_init(image, 1024 * 4096)) {
+ printf("Failed to initialize NetBoot\n");
+ goto fail;
+ }
+ printf("\nNetBoot Server Started...\n\n");
+ for (;;) {
+ int n = netboot_poll();
+ if (n < 1024) continue;
+
+ uint8_t *x = image;
+ if ((x[0]=='M') && (x[1]=='Z') && (x[0x80]=='P') && (x[0x81]=='E')) {
+ UINTN exitdatasize;
+ EFI_STATUS r;
+ EFI_HANDLE h;
+ printf("Attempting to run EFI binary...\n");
+ r = bs->LoadImage(FALSE, img, NULL, image, n, &h);
+ if (r != EFI_SUCCESS) {
+ printf("LoadImage Failed %ld\n", r);
+ continue;
+ }
+ r = bs->StartImage(h, &exitdatasize, NULL);
+ if (r != EFI_SUCCESS) {
+ printf("StartImage Failed %ld\n", r);
+ continue;
+ }
+ printf("\nNetBoot Server Resuming...\n");
+ continue;
+ }
+
+ // maybe it's a kernel image?
+ boot_kernel(img, sys, image, n);
+ goto fail;
+ }
+
+fail:
+ printf("\nBoot Failure\n");
+ WaitAnyKey();
+ return EFI_SUCCESS;
+}
diff --git a/src/showmem.c b/src/showmem.c
new file mode 100644
index 0000000..b442ece
--- /dev/null
+++ b/src/showmem.c
@@ -0,0 +1,87 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <printf.h>
+
+static const char *MemTypeName(UINT32 type, char *buf) {
+ switch (type) {
+ case EfiReservedMemoryType: return "Reserved";
+ case EfiLoaderCode: return "LoaderCode";
+ case EfiLoaderData: return "LoaderData";
+ case EfiBootServicesCode: return "BootSvcsCode";
+ case EfiBootServicesData: return "BootSvcsData";
+ case EfiRuntimeServicesCode: return "RunTimeCode";
+ case EfiRuntimeServicesData: return "RunTimeData";
+ case EfiConventionalMemory: return "Conventional";
+ case EfiUnusableMemory: return "Unusable";
+ case EfiACPIReclaimMemory: return "ACPIReclaim";
+ case EfiACPIMemoryNVS: return "ACPINonVolMem";
+ case EfiMemoryMappedIO: return "MemMappedIO";
+ case EfiMemoryMappedIOPortSpace: return "MemMappedPort";
+ case EfiPalCode: return "PalCode";
+ default:
+ sprintf(buf, "0x%08x", type);
+ return buf;
+ }
+}
+
+static unsigned char scratch[4096];
+
+static void dump_memmap(EFI_SYSTEM_TABLE *systab) {
+ EFI_STATUS r;
+ UINTN msize, off;
+ EFI_MEMORY_DESCRIPTOR *mmap;
+ UINTN mkey, dsize;
+ UINT32 dversion;
+ char tmp[32];
+
+ msize = sizeof(scratch);
+ mmap = (EFI_MEMORY_DESCRIPTOR*) scratch;
+ mkey = dsize = dversion;
+ r = systab->BootServices->GetMemoryMap(&msize, mmap, &mkey, &dsize, &dversion);
+ printf("r=%lx msz=%lx key=%lx dsz=%lx dvn=%x\n",
+ r, msize, mkey, dsize, dversion);
+ if (r != EFI_SUCCESS) {
+ return;
+ }
+ for (off = 0; off < msize; off += dsize) {
+ mmap = (EFI_MEMORY_DESCRIPTOR*) (scratch + off);
+ printf("%016lx %016lx %08lx %c %04lx %s\n",
+ mmap->PhysicalStart, mmap->VirtualStart,
+ mmap->NumberOfPages,
+ mmap->Attribute & EFI_MEMORY_RUNTIME ? 'R' : '-',
+ mmap->Attribute & 0xFFFF,
+ MemTypeName(mmap->Type, tmp));
+ }
+}
+
+#include <goodies.h>
+
+EFI_STATUS efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ InitializeLib(img, sys);
+ InitGoodies(img, sys);
+ dump_memmap(sys);
+ WaitAnyKey();
+ return EFI_SUCCESS;
+}
diff --git a/src/usbtest.c b/src/usbtest.c
new file mode 100644
index 0000000..4026024
--- /dev/null
+++ b/src/usbtest.c
@@ -0,0 +1,161 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <stdio.h>
+#include <goodies.h>
+
+#include <Protocol/UsbIo.h>
+
+EFI_GUID UsbIoProtocol = EFI_USB_IO_PROTOCOL_GUID;
+
+EFIAPI EFI_STATUS MyDriverSupported(
+ EFI_DRIVER_BINDING *self, EFI_HANDLE ctlr,
+ EFI_DEVICE_PATH *path) {
+
+ EFI_USB_DEVICE_DESCRIPTOR dev;
+ EFI_USB_IO_PROTOCOL *usbio;
+ EFI_STATUS r;
+
+ r = gBS->OpenProtocol(ctlr, &UsbIoProtocol,
+ (void**) &usbio, self->DriverBindingHandle,
+ ctlr, EFI_OPEN_PROTOCOL_BY_DRIVER);
+
+ if (r == 0) {
+ if (usbio->UsbGetDeviceDescriptor(usbio, &dev)) {
+ return EFI_UNSUPPORTED;
+ }
+ printf("Supported? ctlr=%p vid=%04x pid=%04x\n",
+ ctlr, dev.IdVendor, dev.IdProduct);
+ gBS->CloseProtocol(ctlr, &UsbIoProtocol,
+ self->DriverBindingHandle, ctlr);
+ return EFI_SUCCESS;
+ }
+ return EFI_UNSUPPORTED;
+}
+
+EFIAPI EFI_STATUS MyDriverStart(
+ EFI_DRIVER_BINDING *self, EFI_HANDLE ctlr,
+ EFI_DEVICE_PATH *path) {
+ EFI_STATUS r;
+
+ EFI_USB_IO_PROTOCOL *usbio;
+
+ printf("Start! ctlr=%p\n", ctlr);
+
+ r = gBS->OpenProtocol(ctlr, &UsbIoProtocol,
+ (void**) &usbio, self->DriverBindingHandle,
+ ctlr, EFI_OPEN_PROTOCOL_BY_DRIVER);
+
+ // alloc device state, stash usbio with it
+ // probably attached to a protocol installed on a child handle
+
+ if (r) {
+ printf("OpenProtocol Failed %lx\n", r);
+ return EFI_DEVICE_ERROR;
+ }
+ return EFI_SUCCESS;
+}
+
+EFIAPI EFI_STATUS MyDriverStop(
+ EFI_DRIVER_BINDING *self, EFI_HANDLE ctlr,
+ UINTN count, EFI_HANDLE *children) {
+
+ printf("Stop! ctlr=%p\n", ctlr);
+
+ // recover device state, tear down
+
+ gBS->CloseProtocol(ctlr, &UsbIoProtocol,
+ self->DriverBindingHandle, ctlr);
+ return EFI_SUCCESS;
+}
+
+static EFI_DRIVER_BINDING MyDriver = {
+ .Supported = MyDriverSupported,
+ .Start = MyDriverStart,
+ .Stop = MyDriverStop,
+ .Version = 32,
+ .ImageHandle = NULL,
+ .DriverBindingHandle = NULL,
+};
+
+void InstallMyDriver(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ EFI_BOOT_SERVICES *bs = sys->BootServices;
+ EFI_HANDLE *list;
+ UINTN count, i;
+ EFI_STATUS r;
+
+ MyDriver.ImageHandle = img;
+ MyDriver.DriverBindingHandle = img;
+ r = bs->InstallProtocolInterface(&img, &DriverBindingProtocol,
+ EFI_NATIVE_INTERFACE, &MyDriver);
+ if (r) {
+ Print(L"DriverBinding failed %lx\n", r);
+ return;
+ }
+
+ // For every Handle that supports UsbIoProtocol, try to connect the driver
+ r = bs->LocateHandleBuffer(ByProtocol, &UsbIoProtocol, NULL, &count, &list);
+ if (r == 0) {
+ for (i = 0; i < count; i++) {
+ r = bs->ConnectController(list[i], NULL, NULL, FALSE);
+ }
+ bs->FreePool(list);
+ }
+
+}
+
+void RemoveMyDriver(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ EFI_BOOT_SERVICES *bs = sys->BootServices;
+ EFI_HANDLE *list;
+ UINTN count, i;
+ EFI_STATUS r;
+
+ // Disconnect the driver
+ r = bs->LocateHandleBuffer(ByProtocol, &UsbIoProtocol, NULL, &count, &list);
+ if (r == 0) {
+ for (i = 0; i < count; i++) {
+ r = bs->DisconnectController(list[i], img, NULL);
+ }
+ bs->FreePool(list);
+ }
+
+ // Unregister so we can safely exit
+ r = bs->UninstallProtocolInterface(img, &DriverBindingProtocol, &MyDriver);
+ if (r) printf("UninstallProtocol failed %lx\n", r);
+}
+
+EFI_STATUS efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *sys) {
+ InitializeLib(img, sys);
+ InitGoodies(img, sys);
+
+ Print(L"Hello, EFI World\n");
+
+ InstallMyDriver(img, sys);
+
+ // do stuff
+
+ RemoveMyDriver(img, sys);
+
+ return EFI_SUCCESS;
+}