Merge "gitignore: Adding rule to ignore cscope files."
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8a4af05
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index 9739379..b6c60f2 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -45,6 +45,7 @@
 #include <baseband.h>
 #include <target.h>
 #include <mmc.h>
+#include <partition_parser.h>
 #include <platform.h>
 
 #include "recovery.h"
@@ -290,6 +291,7 @@
 	unsigned long long ptn = 0;
 	unsigned n = 0;
 	const char *cmdline;
+	int index = INVALID_PTN;
 
 	uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR;
 	if (!memcmp(uhdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
@@ -298,14 +300,17 @@
 		goto unified_boot;
 	}
 	if (!boot_into_recovery) {
-		ptn = mmc_ptn_offset((unsigned char *) "boot");
-		if (ptn == 0) {
+		index = partition_get_index("boot");
+		ptn = partition_get_offset(index);
+		if(ptn == 0) {
 			dprintf(CRITICAL, "ERROR: No boot partition found\n");
                     return -1;
 		}
-	} else {
-		ptn = mmc_ptn_offset((unsigned char *) "recovery");
-		if (ptn == 0) {
+	}
+	else {
+		index = partition_get_index("recovery");
+		ptn = partition_get_offset(index);
+		if(ptn == 0) {
 			dprintf(CRITICAL, "ERROR: No recovery partition found\n");
                     return -1;
 		}
@@ -529,14 +534,15 @@
 {
 	unsigned long long ptn = 0;
 	unsigned int out[512] = {0};
+	int index = INVALID_PTN;
 
-	ptn = mmc_ptn_offset((unsigned char *) arg);
-	if (ptn == 0) {
+	index = partition_get_index(arg);
+	ptn = partition_get_offset(index);
+	if(ptn == 0) {
 		fastboot_fail("partition table doesn't exist");
 		return;
 	}
 
-
 	/* Simple inefficient version of erase. Just writing
 	   0 in first block */
 	if (mmc_write(ptn , 512, (unsigned int *)out)) {
@@ -551,6 +557,7 @@
 {
 	unsigned long long ptn = 0;
 	unsigned long long size = 0;
+	int index = INVALID_PTN;
 
 	if (!strcmp(arg, "partition"))
 	{
@@ -562,7 +569,8 @@
 	}
 	else
 	{
-		ptn = mmc_ptn_offset((unsigned char *) arg);
+		index = partition_get_index(arg);
+		ptn = partition_get_offset(index);
 		if(ptn == 0) {
 			fastboot_fail("partition table doesn't exist");
 			return;
@@ -575,7 +583,7 @@
 			}
 		}
 
-		size = mmc_ptn_size((unsigned char *) arg);
+		size = partition_get_size(index);
 		if (ROUND_TO_PAGE(sz,511) > size) {
 			fastboot_fail("size too large");
 			return;
@@ -599,9 +607,11 @@
 	chunk_header_t *chunk_header;
 	uint32_t total_blocks = 0;
 	unsigned long long ptn = 0;
+	int index = INVALID_PTN;
 
-	ptn = mmc_ptn_offset((unsigned char *) arg);
-	if (ptn == 0) {
+	index = partition_get_index(arg);
+	ptn = partition_get_offset(index);
+	if(ptn == 0) {
 		fastboot_fail("partition table doesn't exist");
 		return;
 	}
@@ -919,7 +929,7 @@
 	fastboot_register("reboot-bootloader", cmd_reboot_bootloader);
 	fastboot_publish("product", TARGET(BOARD));
 	fastboot_publish("kernel", "lk");
-	mmc_dump_partition_info();
+	partition_dump();
 	sz = target_get_max_flash_size();
 	fastboot_init(target_get_scratch_address(), sz);
 	udc_start();
diff --git a/app/aboot/recovery.c b/app/aboot/recovery.c
index 7b9141c..2ff6804 100644
--- a/app/aboot/recovery.c
+++ b/app/aboot/recovery.c
@@ -37,6 +37,7 @@
 #include <lib/ptable.h>
 #include <dev/keys.h>
 #include <platform.h>
+#include <partition_parser.h>
 #include <mmc.h>
 
 #include "recovery.h"
@@ -380,8 +381,10 @@
 	unsigned long long ptn = 0;
 	unsigned int size = ROUND_TO_PAGE(sizeof(*out),511);
 	unsigned char data[size];
+	int index = INVALID_PTN;
 
-	ptn = mmc_ptn_offset((unsigned char *) ptn_name);
+	index = partition_get_index((unsigned char *) ptn_name);
+	ptn = partition_get_offset(index);
 	if(ptn == 0) {
 		dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name);
 		return -1;
@@ -400,8 +403,10 @@
 	unsigned long long ptn = 0;
 	unsigned int size = ROUND_TO_PAGE(sizeof(*in),511);
 	unsigned char data[size];
+	int index = INVALID_PTN;
 
-	ptn = mmc_ptn_offset((unsigned char *) ptn_name);
+	index = partition_get_index((unsigned char *) ptn_name);
+	ptn = partition_get_offset(index);
 	if(ptn == 0) {
 		dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name);
 		return -1;
diff --git a/app/pcitests/pci_tests.c b/app/pcitests/pci_tests.c
new file mode 100644
index 0000000..86d2b67
--- /dev/null
+++ b/app/pcitests/pci_tests.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <app.h>
+#include <debug.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <compiler.h>
+#include <platform.h>
+#include <dev/pci.h>
+
+#if defined(WITH_LIB_CONSOLE)
+#include <lib/console.h>
+
+/*
+ * enumerates pci devices
+ */
+static void pci_list(void)
+{
+	pci_location_t state;
+	uint16_t device_id, vendor_id;
+	uint8_t header_type;
+	int busses = 0, devices = 0, lines = 0, devfn, ret;
+	char c;
+	
+	printf("Scanning...\n");
+	
+	for (state.bus = 0; state.bus <= pci_get_last_bus(); state.bus++) {
+		busses++;
+		
+		for (devfn = 0; devfn < 256; devfn++) {
+			state.dev_fn = devfn;
+			
+			ret = pci_read_config_half(&state, PCI_CONFIG_VENDOR_ID, &vendor_id);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+			
+			ret = pci_read_config_half(&state, PCI_CONFIG_DEVICE_ID, &device_id);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+			
+			ret = pci_read_config_byte(&state, PCI_CONFIG_HEADER_TYPE, &header_type);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+			
+			if (vendor_id != 0xffff) {
+				printf("%02x:%02x vendor_id=%04x device_id=%04x, header_type=%02x\n", state.bus, state.dev_fn,
+						vendor_id, device_id, header_type);
+				devices++;
+				lines++;
+			}
+			
+			if (~header_type & PCI_HEADER_TYPE_MULTI_FN) {
+				// this is not a multi-function device, so advance to the next device
+				devfn |= 7;
+			}
+			
+			if (lines == 23) {
+				printf("... press any key to continue, q to quit ...");
+				while(getc(&c) < 0);
+				printf("\n");
+				lines = 0;
+				
+				if (c == 'q' || c == 'Q') goto quit;
+			}
+		}
+	}
+
+	printf("... done. Scanned %d busses, %d device/functions\n", busses, devices);
+quit:
+	return;
+	
+error:
+	printf("Error while reading PCI config space: %02x\n", ret);
+}
+
+/*
+ * a somewhat fugly pci config space examine/modify command. this should probably
+ * be broken up a bit.
+ */
+static int pci_config(int argc, const cmd_args *argv)
+{
+	pci_location_t loc;
+	pci_config_t config;
+	uint32_t offset;
+	unsigned int i;
+	int ret;
+	
+	if (argc < 5) {
+		return -1;
+	}
+	
+	if (!strcmp(argv[2].str, "dump")) {
+		loc.bus = atoui(argv[3].str);
+		loc.dev_fn = atoui(argv[4].str);
+		
+		for (i=0; i < sizeof(pci_config_t); i++) {
+			ret = pci_read_config_byte(&loc, i, (uint8_t *) &config + i);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+		}
+		
+		printf("Device at %02x:%02x vendor id=%04x device id=%04x\n", loc.bus,
+			loc.dev_fn, config.vendor_id, config.device_id);
+		printf("command=%04x status=%04x pi=%02x sub cls=%02x base cls=%02x\n",
+			config.command, config.status, config.program_interface,
+			config.sub_class, config.base_class);
+		
+		for (i=0; i < 6; i+=2) {
+			printf("bar%d=%08x  bar%d=%08x\n", i, config.base_addresses[i],
+				i+1, config.base_addresses[i+1]);
+		}
+	} else if (!strcmp(argv[2].str, "rb") || !strcmp(argv[2].str, "rh") || !strcmp(argv[2].str, "rw")) {
+		if (argc != 6) {
+			return -1;
+		}
+		
+		loc.bus = atoui(argv[3].str);
+		loc.dev_fn = atoui(argv[4].str);
+		offset = atoui(argv[5].str);
+		
+		switch (argv[2].str[1]) {
+			case 'b': {
+				uint8_t value;
+				ret = pci_read_config_byte(&loc, offset, &value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("byte at device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'h': {
+				uint16_t value;
+				ret = pci_read_config_half(&loc, offset, &value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("half at device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'w': {
+				uint32_t value;
+				ret = pci_read_config_word(&loc, offset, &value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("word at device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+		}
+	} else if (!strcmp(argv[2].str, "mb") || !strcmp(argv[2].str, "mh") || !strcmp(argv[2].str, "mw")) {
+		if (argc != 7) {
+			return -1;
+		}
+		
+		loc.bus = atoui(argv[3].str);
+		loc.dev_fn = atoui(argv[4].str);
+		offset = atoui(argv[5].str);
+		
+		switch (argv[2].str[1]) {
+			case 'b': {
+				uint8_t value = atoui(argv[6].str);
+				ret = pci_write_config_byte(&loc, offset, value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("byte to device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'h': {
+				uint16_t value = atoui(argv[6].str);
+				ret = pci_write_config_half(&loc, offset, value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("half to device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'w': {
+				uint32_t value = atoui(argv[6].str);
+				ret = pci_write_config_word(&loc, offset, value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("word to device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+		}
+	} else {
+		return -1;
+	}
+	
+	return 0;
+
+error:
+	printf("Error while reading PCI config space: %02x\n", ret);
+	return -2;
+}
+
+static int pci_cmd(int argc, const cmd_args *argv)
+{
+	if (argc < 2) {
+		printf("pci commands:\n");
+usage:
+		printf("%s list\n", argv[0].str);
+		printf("%s config dump <bus> <devfn>\n", argv[0].str);
+		printf("%s config <rb|rh|rw> <bus> <devfn> <offset>\n", argv[0].str);
+		printf("%s config <mb|mh|mw> <bus> <devfn> <offset> <value>\n", argv[0].str);
+		goto out;
+	}
+	
+	if (!strcmp(argv[1].str, "list")) {
+		pci_list();
+	} else if (!strcmp(argv[1].str, "config")) {
+		if (pci_config(argc, argv)) {
+			goto usage;
+		}
+	} else {
+		goto usage;
+	}
+
+out:
+	return 0;
+}
+
+STATIC_COMMAND_START
+{ "pci", "pci toolbox", &pci_cmd },
+STATIC_COMMAND_END(pcitests);
+
+#endif
+
+APP_START(pcitests)
+APP_END
+
diff --git a/app/pcitests/rules.mk b/app/pcitests/rules.mk
new file mode 100644
index 0000000..a72aa49
--- /dev/null
+++ b/app/pcitests/rules.mk
@@ -0,0 +1,6 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+#INCLUDES += -I$(LOCAL_DIR)/include
+
+OBJS += \
+	$(LOCAL_DIR)/pci_tests.o 
diff --git a/app/tests/tests.c b/app/tests/tests.c
index 1a8130b..1a41f6f 100644
--- a/app/tests/tests.c
+++ b/app/tests/tests.c
@@ -29,8 +29,8 @@
 #include <lib/console.h>
 
 STATIC_COMMAND_START
-		{ "printf_tests", NULL, (console_cmd)&printf_tests },
-		{ "thread_tests", NULL, (console_cmd)&thread_tests },
+STATIC_COMMAND("printf_tests", NULL, (console_cmd)&printf_tests)
+STATIC_COMMAND("thread_tests", NULL, (console_cmd)&thread_tests)
 STATIC_COMMAND_END(tests);
 
 #endif
diff --git a/app/tests/thread_tests.c b/app/tests/thread_tests.c
index acbdfbd..908abc7 100644
--- a/app/tests/thread_tests.c
+++ b/app/tests/thread_tests.c
@@ -221,11 +221,11 @@
 
 	event_wait(&context_switch_event);
 
-	uint count = debug_cycle_count();
+	uint count = arch_cycle_count();
 	for (i = 0; i < iter; i++) {
 		thread_yield();
 	}
-	total_count += debug_cycle_count() - count;
+	total_count += arch_cycle_count() - count;
 	thread_sleep(1000);
 	printf("took %u cycles to yield %d times, %u per yield, %u per yield per thread\n", 
 		total_count, iter, total_count / iter, total_count / iter / thread_count);
diff --git a/arch/arm/arch.c b/arch/arm/arch.c
index 37b557c..7a5f85b 100644
--- a/arch/arm/arch.c
+++ b/arch/arm/arch.c
@@ -64,6 +64,19 @@
 	val = (1<<30);
 	__asm__ volatile("mcr  p10, 7, %0, c8, c0, 0" :: "r" (val));
 #endif
+
+#if ARM_CPU_CORTEX_A8
+	/* enable the cycle count register */
+	uint32_t en;
+	__asm__ volatile("mrc	p15, 0, %0, c9, c12, 0" : "=r" (en));
+	en &= ~(1<<3); /* cycle count every cycle */
+	en |= 1; /* enable all performance counters */
+	__asm__ volatile("mcr	p15, 0, %0, c9, c12, 0" :: "r" (en));
+
+	/* enable cycle counter */
+	en = (1<<31);
+	__asm__ volatile("mcr	p15, 0, %0, c9, c12, 1" :: "r" (en));
+#endif
 }
 
 void arch_init(void)
diff --git a/arch/arm/cache-ops.S b/arch/arm/cache-ops.S
index 8a545dc..b169a77 100644
--- a/arch/arm/cache-ops.S
+++ b/arch/arm/cache-ops.S
@@ -342,6 +342,17 @@
 	mcr		p15, 0, r0, c7, c10, 4		// data sync barrier (formerly drain write buffer)
 
 	bx		lr
+
+	/* void arch_sync_cache_range(addr_t start, size_t len); */
+FUNCTION(arch_sync_cache_range)
+	push    { r14 }
+	bl      arch_clean_cache_range
+
+	mov     r0, #0
+	mcr     p15, 0, r0, c7, c5, 0       // invalidate icache to PoU
+
+	pop     { pc }
+
 #else
 #error unhandled cpu
 #endif
@@ -362,5 +373,8 @@
 FUNCTION(arch_clean_invalidate_cache_range)
 	bx		lr
 
+FUNCTION(arch_sync_cache_range)
+	bx		lr
+
 #endif // ARM_WITH_CACHE
 
diff --git a/arch/arm/crt0.S b/arch/arm/crt0.S
index 68dbc67..ce823c1 100644
--- a/arch/arm/crt0.S
+++ b/arch/arm/crt0.S
@@ -24,7 +24,7 @@
 #define DSB .byte 0x4f, 0xf0, 0x7f, 0xf5
 #define ISB .byte 0x6f, 0xf0, 0x7f, 0xf5
 
-.text
+.section ".text.boot"
 .globl _start
 _start:
 	b	reset
@@ -187,3 +187,14 @@
 abort_stack:
 	.skip 1024
 abort_stack_top:
+
+.rodata:
+.align 2
+
+/* define the heap end as read-only data containing the end defined in the
+ * linker script. other archs that use dynamic memory length discovery can make
+ * this read-write and update it during init.
+ */
+.global _heap_end
+_heap_end:
+	.int _end_of_ram
diff --git a/arch/arm/ops.S b/arch/arm/ops.S
index 96b7445..c9660e7 100644
--- a/arch/arm/ops.S
+++ b/arch/arm/ops.S
@@ -221,3 +221,15 @@
 	mcr		p15, 0, r0, c7, c10, 4
 #endif
 	bx		lr
+
+/* uint32_t arm_read_cycle_count(void); */
+FUNCTION(arm_read_cycle_count)
+
+/* uint32_t arch_cycle_count(void); */
+FUNCTION(arch_cycle_count)
+#if ARM_CPU_CORTEX_A8
+	mrc		p15, 0, r0, c9, c13, 0
+#else
+	mov		r0, #0
+#endif
+	bx		lr
diff --git a/arch/arm/system-onesegment.ld b/arch/arm/system-onesegment.ld
index 9f0e7fc..f2c62ac 100644
--- a/arch/arm/system-onesegment.ld
+++ b/arch/arm/system-onesegment.ld
@@ -6,6 +6,10 @@
 {
 	. = %MEMBASE%;
 
+	/* text/read-only data */
+	.text.boot : { *(.text.boot) }
+	.text :	{ *(.text .text.* .glue_7* .gnu.linkonce.t.*) } =0x9090
+
 	.interp : { *(.interp) }
 	.hash : { *(.hash) }
 	.dynsym : { *(.dynsym) }
@@ -33,9 +37,6 @@
 	.init : { *(.init) } =0x9090
 	.plt : { *(.plt) }
 
-	/* text/read-only data */
-	.text :	{ *(.text .text.* .glue_7* .gnu.linkonce.t.*) } =0x9090
-
 	.rodata : { 
 		*(.rodata .rodata.* .gnu.linkonce.r.*)
 		. = ALIGN(4);
diff --git a/arch/arm/system-twosegment.ld b/arch/arm/system-twosegment.ld
index 3a1c04c..d636296 100644
--- a/arch/arm/system-twosegment.ld
+++ b/arch/arm/system-twosegment.ld
@@ -6,6 +6,10 @@
 {
 	. = %ROMBASE%;
 
+	/* text/read-only data */
+	.text.boot : { *(.text.boot) }
+	.text :	{ *(.text .text.* .glue_7* .gnu.linkonce.t.*) } =0x9090
+
 	.interp : { *(.interp) }
 	.hash : { *(.hash) }
 	.dynsym : { *(.dynsym) }
@@ -33,9 +37,6 @@
 	.init : { *(.init) } =0x9090
 	.plt : { *(.plt) }
 
-	/* text/read-only data */
-	.text :	{ *(.text .text.* .glue_7* .gnu.linkonce.t.*) } =0x9090
-
 	.rodata : { 
 		*(.rodata .rodata.* .gnu.linkonce.r.*) 
 		. = ALIGN(4);
diff --git a/arch/x86/arch.c b/arch/x86/arch.c
new file mode 100644
index 0000000..bb6a17b
--- /dev/null
+++ b/arch/x86/arch.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <debug.h>
+#include <arch.h>
+#include <arch/ops.h>
+#include <arch/x86.h>
+#include <arch/x86/mmu.h>
+#include <arch/x86/descriptor.h>
+#include <platform.h>
+#include <sys/types.h>
+#include <string.h>
+
+static tss_t system_tss;
+
+void arch_early_init(void)
+{
+	x86_mmu_init();
+
+	platform_init_mmu_mappings();
+	
+	/* enable caches here for now */
+	clear_in_cr0(X86_CR0_NW | X86_CR0_CD);
+	
+	memset(&system_tss, 0, sizeof(tss_t));
+	
+	system_tss.esp0 = 0;
+	system_tss.ss0 = DATA_SELECTOR;
+	system_tss.ss1 = 0;
+	system_tss.ss2 = 0;
+	system_tss.eflags = 0x00003002;
+	system_tss.bitmap = offsetof(tss_t, tss_bitmap);
+	system_tss.trace = 1; // trap on hardware task switch
+	
+	set_global_desc(TSS_SELECTOR, &system_tss, sizeof(tss_t), 1, 0, 0, SEG_TYPE_TSS, 0, 0);
+
+	x86_ltr(TSS_SELECTOR);
+}
+
+void arch_init(void)
+{
+}
+
+uint32_t arch_cycle_count(void)
+{
+	uint32_t timestamp;
+	rdtscl(timestamp);
+	
+	return timestamp;
+}
+
diff --git a/arch/x86/asm.S b/arch/x86/asm.S
new file mode 100644
index 0000000..8916243
--- /dev/null
+++ b/arch/x86/asm.S
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <asm.h>
+
diff --git a/arch/x86/cache-ops.S b/arch/x86/cache-ops.S
new file mode 100644
index 0000000..9ad7c71
--- /dev/null
+++ b/arch/x86/cache-ops.S
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <asm.h>
+#include <arch/ops.h>
+#include <arch/defines.h>
+
+.text
+
+/* stubs */
+
+FUNCTION(arch_disable_cache)
+	ret
+
+FUNCTION(arch_enable_cache)
+	ret
+
+FUNCTION(arch_clean_cache_range)
+	ret
+
+FUNCTION(arch_clean_invalidate_cache_range)
+	ret
+
diff --git a/arch/x86/cache.c b/arch/x86/cache.c
new file mode 100644
index 0000000..b00d6cc
--- /dev/null
+++ b/arch/x86/cache.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
diff --git a/arch/x86/compile.mk b/arch/x86/compile.mk
new file mode 100644
index 0000000..271db17
--- /dev/null
+++ b/arch/x86/compile.mk
@@ -0,0 +1,16 @@
+
+$(BUILDDIR)/%.o: %.c $(SRCDEPS)
+	@$(MKDIR)
+	@echo compiling $<
+	$(NOECHO)$(CC) $(CFLAGS) --std=c99 $(INCLUDES) -c $< -MD -MT $@ -MF $(@:%o=%d) -o $@
+
+$(BUILDDIR)/%.o: %.cpp $(SRCDEPS)
+	@$(MKDIR)
+	@echo compiling $<
+	$(NOECHO)$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -c $< -MD -MT $@ -MF $(@:%o=%d) -o $@
+
+$(BUILDDIR)/%.o: %.S $(SRCDEPS)
+	@$(MKDIR)
+	@echo compiling $<
+	$(NOECHO)$(CC) $(CFLAGS) $(ASMFLAGS) $(INCLUDES) -c $< -MD -MT $@ -MF $(@:%o=%d) -o $@
+
diff --git a/arch/x86/crt0.S b/arch/x86/crt0.S
new file mode 100644
index 0000000..9cc9ab0
--- /dev/null
+++ b/arch/x86/crt0.S
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+
+/* The magic number for the Multiboot header. */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/* The flags for the Multiboot header. */
+#if defined(__ELF__) && 0
+#define MULTIBOOT_HEADER_FLAGS 0x00000002
+#else
+#define MULTIBOOT_HEADER_FLAGS 0x00010002
+#endif
+
+/* The magic number passed by a Multiboot-compliant boot loader. */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+#define NUM_INT 0x31
+#define NUM_EXC 0x14
+
+.text
+.global _start
+_start:
+	jmp real_start
+
+.align 4
+
+multiboot_header:
+	/* magic */
+	.int MULTIBOOT_HEADER_MAGIC
+	/* flags */
+	.int MULTIBOOT_HEADER_FLAGS
+	/* checksum */
+	.int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
+
+#if !defined(__ELF__) || 1
+	/* header_addr */
+	.int multiboot_header
+	/* load_addr */
+	.int _start
+	/* load_end_addr */
+	.int __bss_start
+	/* bss_end_addr */
+	.int __bss_end
+	/* entry_addr */
+	.int real_start
+#endif
+
+real_start:
+	cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
+	jne 0f
+	movl %ebx, (_multiboot_info)
+0:
+	/* setup isr stub descriptors in the idt */
+	movl $_isr, %esi
+	movl $_idt, %edi
+	movl $NUM_INT, %ecx
+	
+.Lloop:
+	movl %esi, %ebx
+	movw %bx, (%edi)		/* low word in IDT(n).low */
+	shrl $16, %ebx
+	movw %bx, 6(%edi)		/* high word in IDT(n).high */
+	
+	addl $isr_stub_len, %esi/* index the next ISR stub */
+	addl $8, %edi			/* index the next IDT entry */
+	
+	loop .Lloop
+	
+	lidt _idtr
+	xorl %eax, %eax
+	movl %eax, %cr3
+
+	lgdt _gdtr
+		
+	movw $datasel, %ax
+	movw %ax, %ds
+	movw %ax, %es
+	movw %ax, %fs
+	movw %ax, %ss
+	movw %ax, %gs
+	movw %ax, %ss
+	
+	movl $_kstack, %esp
+	
+	/* zero the bss section */
+	movl $__bss_start, %edi	/* starting address of the bss */
+	movl $__bss_end, %ecx	/* find the length of the bss in bytes */
+	subl %edi, %ecx
+	shrl $2, %ecx			/* convert to 32 bit words, since the bss is aligned anyway */
+2:
+	movl $0, (%edi)
+	addl $4, %edi
+	loop 2b
+	
+	/* call the main module */
+	call kmain
+	
+0:							/* just sit around waiting for interrupts */
+	hlt						/* interrupts will unhalt the processor */
+	pause
+	jmp 0b					/* so jump back to halt to conserve power */
+
+/* interrupt service routine stubs */
+_isr:
+
+.set i, 0
+.rept NUM_INT
+
+.set isr_stub_start, .
+
+.if i == 8 || (i >= 10 && i <= 14) || i == 17
+	nop						/* error code pushed by exception */
+	nop						/* 2 nops are the same length as push byte */
+	pushl $i				/* interrupt number */
+	jmp interrupt_common
+.else
+	pushl $0				/* fill in error code in iframe */
+	pushl $i				/* interrupt number */
+	jmp interrupt_common
+.endif
+
+/* figure out the length of a single isr stub (usually 6 or 9 bytes) */
+.set isr_stub_len, . - isr_stub_start
+
+.set i, i + 1
+.endr
+
+/* annoying, but force AS to use the same (longer) encoding of jmp for all of the stubs */
+.fill 256
+
+interrupt_common:
+	pushl %gs				/* save segment registers */
+	pushl %fs
+	pushl %es
+	pushl %ds
+	pusha					/* save general purpose registers */
+	movl $datasel, %eax		/* put known good value in segment registers */
+	movl %eax, %gs
+	movl %eax, %fs
+	movl %eax, %es
+	movl %eax, %ds
+	movl %esp, %eax			/* store stack switch pivot. push esp has errata on some cpus, so use mov/push */
+	pushl %eax
+	movl %esp, %eax			/* store pointer to iframe, using same method */
+	pushl %eax
+	
+	incl critical_section_count
+
+	call platform_irq
+	
+	cmpl $0,%eax
+	je 0f
+	call thread_preempt
+
+0:
+	decl critical_section_count
+
+	popl %eax				/* drop pointer to iframe */
+	popl %eax				/* restore task_esp, stack switch can occur here if task_esp is modified */
+	movl %eax, %esp
+	popa					/* restore general purpose registers */
+	popl %ds				/* restore segment registers */
+	popl %es
+	popl %fs
+	popl %gs
+	addl $8, %esp			/* drop exception number and error code */
+	iret
+
+.data
+.align 4
+
+/* define the heap end as read-write data containing the default end of the
+ * heap. dynamic memory length discovery can update this value during init.
+ * other archs can define this statically based on the memory layout of the
+ * platform.
+ */
+.global _heap_end
+_heap_end:
+	.int 4096*1024	/* default to 4MB total */
+
+.global _multiboot_info
+_multiboot_info:
+	.int 0
+
+.global _gdtr
+_gdtr:
+	.short _gdt_end - _gdt - 1
+	.int _gdt
+
+.global _gdt
+_gdt:
+	.int 0
+	.int 0
+
+/* ring 0 descriptors */
+.set codesel, . - _gdt
+_code_gde:
+	.short 0xffff			/* limit 15:00 */
+	.short 0x0000			/* base 15:00 */
+	.byte  0x00				/* base 23:16 */
+	.byte  0b10011010		/* P(1) DPL(00) S(1) 1 C(0) R(1) A(0) */
+	.byte  0b11001111		/* G(1) D(1) 0 0 limit 19:16 */
+	.byte  0x0				/* base 31:24 */
+	
+.set datasel, . - _gdt
+_data_gde:
+	.short 0xffff			/* limit 15:00 */
+	.short 0x0000			/* base 15:00 */
+	.byte  0x00				/* base 23:16 */
+	.byte  0b10010010		/* P(1) DPL(00) S(1) 0 E(0) W(1) A(0) */
+	.byte  0b11001111		/* G(1) B(1) 0 0 limit 19:16 */
+	.byte  0x0				/* base 31:24 */
+	
+.set videosel, . - _gdt
+_video_gde:
+	.short 0xffff			/* limit 15:00 */
+	.short 0x8000			/* base 15:00 */
+	.byte  0x0b				/* base 23:16 */
+	.byte  0b10010010		/* P(1) DPL(00) S(1) 0 E(0) W(1) A(0) */
+	.byte  0b11001111		/* G(1) B(1) 0 0 limit 19:16 */
+	.byte  0x0				/* base 31:24 */
+	
+.if 1
+/* ring 3 descriptors */
+.set user_codesel, . - _gdt
+_user_code_gde:
+	.short 0xffff			/* limit 15:00 */
+	.short 0x0000			/* base 15:00 */
+	.byte  0x00				/* base 23:16 */
+	.byte  0b11111010		/* P(1) DPL(11) S(1) 1 C(0) R(1) A(0) */
+	.byte  0b11001111		/* G(1) D(1) 0 0 limit 19:16 */
+	.byte  0x0				/* base 31:24 */
+	
+.set user_datasel, . - _gdt
+_user_data_gde:
+	.short 0xffff			/* limit 15:00 */
+	.short 0x0000			/* base 15:00 */
+	.byte  0x00				/* base 23:16 */
+	.byte  0b11110010		/* P(1) DPL(11) S(1) 0 E(0) W(1) A(0) */
+	.byte  0b11001111		/* G(1) B(1) 0 0 limit 19:16 */
+	.byte  0x0				/* base 31:24 */
+.endif
+
+/* TSS descriptor */
+.if 1
+.set tsssel, . - _gdt
+_tss_gde:
+	.short 0				/* limit 15:00 */
+	.short 0				/* base 15:00 */
+	.byte  0				/* base 23:16 */
+	.byte  0xe9				/* P(1) DPL(11) 0 10 B(0) 1 */
+	.byte  0x00				/* G(0) 0 0 AVL(0) limit 19:16 */
+	.short 0				/* base 31:24 */
+.endif
+
+.global _gdt_end
+_gdt_end:
+
+.global _idtr
+_idtr:
+	.short _idt_end - _idt - 1	/* IDT limit */
+	.int _idt
+
+/* interrupt descriptor table (IDT) */
+.global _idt
+_idt:
+
+.set i, 0
+.rept NUM_INT-1
+	.short 0				/* low 16 bits of ISR offset (_isr#i & 0FFFFh) */
+	.short codesel			/* selector */
+	.byte  0
+	.byte  0x8e				/* present, ring 0, 32-bit interrupt gate */
+	.short 0				/* high 16 bits of ISR offset (_isr#i / 65536) */
+	
+.set i, i + 1
+.endr
+
+/* syscall int (ring 3) */
+_idt30:
+	.short 0				/* low 16 bits of ISR offset (_isr#i & 0FFFFh) */
+	.short codesel			/* selector */
+	.byte  0
+	.byte  0xee				/* present, ring 3, 32-bit interrupt gate */
+	.short 0				/* high 16 bits of ISR offset (_isr#i / 65536) */
+
+.global _idt_end
+_idt_end:
+
+.bss
+.align 4096
+
+.global _kstack
+.fill 4096
+_kstack:
diff --git a/arch/x86/descriptor.c b/arch/x86/descriptor.c
new file mode 100644
index 0000000..27ee37e
--- /dev/null
+++ b/arch/x86/descriptor.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <compiler.h>
+#include <arch/x86/descriptor.h>
+
+/* not the best way to do this, but easy for now */
+typedef struct {
+	uint16_t limit_15_0;
+	uint16_t base_15_0;
+	uint8_t base_23_16;
+	
+	uint8_t type : 4;
+	uint8_t s : 1;
+	uint8_t dpl : 2;
+	uint8_t p : 1;
+	
+	uint8_t limit_19_16 : 4;
+	uint8_t avl : 1;
+	uint8_t reserved_0 : 1;
+	uint8_t d_b : 1;
+	uint8_t g : 1;
+	
+	uint8_t base_31_24;
+} __PACKED seg_desc_t;
+
+extern seg_desc_t _gdt[];
+
+void set_global_desc(seg_sel_t sel, void *base, uint32_t limit,
+	uint8_t present, uint8_t ring, uint8_t sys, uint8_t type, uint8_t gran, uint8_t bits)
+{
+	// convert selector into index
+	uint16_t index = sel >> 3;
+
+	_gdt[index].limit_15_0 = limit & 0x0000ffff;
+	_gdt[index].limit_19_16 = (limit & 0x000f0000) >> 16;
+
+	_gdt[index].base_15_0 = ((uint32_t) base) & 0x0000ffff;
+	_gdt[index].base_23_16 = (((uint32_t) base) & 0x00ff0000) >> 16;
+	_gdt[index].base_31_24 = ((uint32_t) base) >> 24;
+
+	_gdt[index].type = type & 0x0f;	// segment type
+	_gdt[index].p = present != 0;	// present
+	_gdt[index].dpl = ring & 0x03;	// descriptor privilege level
+	_gdt[index].g = gran != 0;		// granularity
+	_gdt[index].s = sys != 0;		// system / non-system
+	_gdt[index].d_b = bits != 0;	// 16 / 32 bit
+}
diff --git a/arch/x86/faults.c b/arch/x86/faults.c
new file mode 100644
index 0000000..3d4c8d2
--- /dev/null
+++ b/arch/x86/faults.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <debug.h>
+#include <arch/x86.h>
+#include <kernel/thread.h>
+
+static void dump_fault_frame(struct x86_iframe *frame)
+{
+	dprintf(CRITICAL, " CS:     %04x EIP: %08x EFL: %08x CR2: %08x\n",
+		frame->cs, frame->eip, frame->eflags, x86_get_cr2());
+	dprintf(CRITICAL, "EAX: %08x ECX: %08x EDX: %08x EBX: %08x\n",
+		frame->eax, frame->ecx, frame->edx, frame->ebx);
+	dprintf(CRITICAL, "ESP: %08x EBP: %08x ESI: %08x EDI: %08x\n",
+		frame->esp, frame->ebp, frame->esi, frame->edi);
+	dprintf(CRITICAL, " DS:     %04x  ES:     %04x  FS:     %04x  GS:     %04x\n",
+		frame->ds, frame->es, frame->fs, frame->gs);
+
+	// dump the bottom of the current stack
+	addr_t stack = (addr_t) frame; //(addr_t) (((uint32_t *) frame) + (sizeof(struct x86_iframe) / sizeof(uint32_t) - 1));
+
+	if (stack != 0) {
+		dprintf(CRITICAL, "bottom of stack at 0x%08x:\n", (unsigned int)stack);
+		hexdump((void *)stack, 192);
+	}
+}
+
+static void exception_die(struct x86_iframe *frame, const char *msg)
+{
+	inc_critical_section();
+	dprintf(CRITICAL, msg);
+	dump_fault_frame(frame);
+	
+	for (;;) {
+		x86_cli();
+		x86_hlt();
+	}
+}
+
+void x86_syscall_handler(struct x86_iframe *frame)
+{
+	exception_die(frame, "unhandled syscall, halting\n");
+}
+
+void x86_gpf_handler(struct x86_iframe *frame)
+{
+	exception_die(frame, "unhandled gpf, halting\n");	
+}
+
+void x86_invop_handler(struct x86_iframe *frame)
+{
+	exception_die(frame, "unhandled invalid op, halting\n");	
+}
+
+void x86_unhandled_exception(struct x86_iframe *frame)
+{
+	exception_die(frame, "unhandled exception, halting\n");	
+}
diff --git a/arch/x86/include/arch/arch_thread.h b/arch/x86/include/arch/arch_thread.h
new file mode 100644
index 0000000..9347bcd
--- /dev/null
+++ b/arch/x86/include/arch/arch_thread.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __X86_ARCH_THREAD_H
+#define __X86_ARCH_THREAD_H
+
+struct arch_thread {
+	vaddr_t esp;
+	
+	// TODO: fpu context
+};
+
+#endif
+
diff --git a/arch/x86/include/arch/defines.h b/arch/x86/include/arch/defines.h
new file mode 100644
index 0000000..814fcc9
--- /dev/null
+++ b/arch/x86/include/arch/defines.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __ARCH_CPU_H
+#define __ARCH_CPU_H
+
+#define PAGE_SIZE 4096
+
+// TODO: define to resolve to platform setup discovered value
+#define CACHE_LINE 32
+
+#endif
+
diff --git a/arch/x86/include/arch/x86.h b/arch/x86/include/arch/x86.h
new file mode 100644
index 0000000..4cf65d9
--- /dev/null
+++ b/arch/x86/include/arch/x86.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __ARCH_X86_H
+#define __ARCH_X86_H
+
+#include <compiler.h>
+#include <sys/types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void x86_mmu_init(void);
+
+struct x86_iframe {
+	uint32_t pivot;										// stack switch pivot
+	uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;	// pushed by common handler using pusha
+	uint32_t ds, es, fs, gs;							// pushed by common handler
+	uint32_t vector;									// pushed by stub
+	uint32_t err_code;									// pushed by interrupt or stub
+	uint32_t eip, cs, eflags;							// pushed by interrupt
+	uint32_t user_esp, user_ss;							// pushed by interrupt if priv change occurs
+};
+
+/*
+ * x86 TSS structure
+ */
+typedef struct {
+	uint16_t	backlink, __blh;
+	uint32_t	esp0;
+	uint16_t	ss0, __ss0h;
+	uint32_t	esp1;
+	uint16_t	ss1, __ss1h;
+	uint32_t	esp2;
+	uint16_t	ss2, __ss2h;
+	uint32_t	cr3;
+	uint32_t	eip;
+	uint32_t	eflags;
+	uint32_t	eax, ecx, edx, ebx;
+	uint32_t	esp, ebp, esi, edi;
+	uint16_t	es, __esh;
+	uint16_t	cs, __csh;
+	uint16_t	ss, __ssh;
+	uint16_t	ds, __dsh;
+	uint16_t	fs, __fsh;
+	uint16_t	gs, __gsh;
+	uint16_t	ldt, __ldth;
+	uint16_t	trace, bitmap;
+	
+	uint8_t tss_bitmap[8192];
+} __PACKED tss_t;
+
+#define X86_CR0_PE		0x00000001 /* protected mode enable */
+#define X86_CR0_MP		0x00000002 /* monitor coprocessor */
+#define X86_CR0_EM		0x00000004 /* emulation */
+#define X86_CR0_TS		0x00000008 /* task switched */
+#define X86_CR0_WP		0x00010000 /* supervisor write protect */
+#define X86_CR0_NW		0x20000000 /* not write-through */
+#define X86_CR0_CD		0x40000000 /* cache disable */
+#define X86_CR0_PG		0x80000000 /* enable paging */
+
+static inline void set_in_cr0(uint32_t mask) {
+	__asm__ __volatile__ (
+		"movl %%cr0,%%eax	\n\t"
+		"orl %0,%%eax		\n\t"
+		"movl %%eax,%%cr0	\n\t"
+		: : "irg" (mask)
+		:"ax");
+}
+
+static inline void clear_in_cr0(uint32_t mask) {
+	__asm__ __volatile__ (
+		"movl %%cr0, %%eax	\n\t"
+		"andl %0, %%eax		\n\t"
+		"movl %%eax, %%cr0	\n\t"
+		: : "irg" (~mask)
+		: "ax");
+}
+
+static inline void x86_clts(void) {__asm__ __volatile__ ("clts"); }
+static inline void x86_hlt(void) {__asm__ __volatile__ ("hlt"); }
+static inline void x86_sti(void) {__asm__ __volatile__ ("sti"); }
+static inline void x86_cli(void) {__asm__ __volatile__ ("cli"); }
+static inline void x86_ltr(uint16_t sel) {
+	__asm__ __volatile__ ("ltr %%ax" :: "a" (sel));
+}
+
+static inline uint32_t x86_get_cr2(void) {
+	uint32_t rv;
+	
+	__asm__ __volatile__ (
+		"movl %%cr2, %0"
+		: "=r" (rv)
+	);
+	
+	return rv;
+}
+
+#define rdtsc(low,high) \
+     __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdtscl(low) \
+     __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx")
+
+#define rdtscll(val) \
+     __asm__ __volatile__("rdtsc" : "=A" (val))
+
+static inline uint8_t inp(uint16_t _port) {
+    uint8_t rv;
+    __asm__ __volatile__ ("inb %1, %0"
+	  : "=a" (rv)
+	  : "d" (_port));
+    return(rv);
+}
+
+static inline uint16_t inpw (uint16_t _port) {
+    uint16_t rv;
+    __asm__ __volatile__ ("inw %1, %0"
+	  : "=a" (rv)
+	  : "d" (_port));
+    return(rv);
+}
+
+static inline uint32_t inpd(uint16_t _port) {
+    uint32_t rv;
+    __asm__ __volatile__ ("inl %1, %0"
+	  : "=a" (rv)
+	  : "d" (_port));
+    return(rv);
+}
+
+static inline void outp(uint16_t _port, uint8_t _data) {
+    __asm__ __volatile__ ("outb %1, %0"
+	  :
+	  : "d" (_port),
+	    "a" (_data));
+}
+
+static inline void outpw(uint16_t _port, uint16_t _data) {
+    __asm__ __volatile__ ("outw %1, %0"
+	  :
+	  : "d" (_port),
+	    "a" (_data));
+}
+
+static inline void outpd(uint16_t _port, uint32_t _data) {
+    __asm__ __volatile__ ("outl %1, %0"
+	  :
+	  : "d" (_port),
+	    "a" (_data));
+}
+
+static inline void inprep(uint16_t _port, uint8_t *_buffer, uint32_t _reads) {
+	__asm__ __volatile__ ("pushal \n\t"	
+		"pushfl \n\t"	
+		"cli \n\t"	
+		"cld \n\t"	
+		"rep insb \n\t"	
+		"popfl \n\t"	
+		"popal"	
+		:
+		: "d" (_port),
+		  "D" (_buffer),
+		  "c" (_reads));
+}
+
+static inline void outprep(uint16_t _port, uint8_t *_buffer, uint32_t _writes) {
+	__asm__ __volatile__ ("pushal \n\t"
+		"pushfl \n\t"
+		"cli \n\t"
+		"cld \n\t"
+		"rep outsb \n\t"
+		"popfl \n\t"
+		"popal"
+		:
+		: "d" (_port),
+		  "S" (_buffer),
+		  "c" (_writes));
+}
+
+static inline void inpwrep(uint16_t _port, uint16_t *_buffer, uint32_t _reads) {
+	__asm__ __volatile__ ("pushal \n\t"	
+		"pushfl \n\t"	
+		"cli \n\t"	
+		"cld \n\t"	
+		"rep insw \n\t"	
+		"popfl \n\t"	
+		"popal"	
+		:
+		: "d" (_port),
+		  "D" (_buffer),
+		  "c" (_reads));
+}
+
+static inline void outpwrep(uint16_t _port, uint16_t *_buffer,
+	uint32_t _writes) {
+	__asm__ __volatile__ ("pushal \n\t"
+		"pushfl \n\t"
+		"cli \n\t"
+		"cld \n\t"
+		"rep outsw \n\t"
+		"popfl \n\t"
+		"popal"
+		:
+		: "d" (_port),
+		  "S" (_buffer),
+		  "c" (_writes));
+}
+
+static inline void inpdrep(uint16_t _port, uint32_t *_buffer,
+	uint32_t _reads) {
+	__asm__ __volatile__ ("pushal \n\t"	
+		"pushfl \n\t"	
+		"cli \n\t"	
+		"cld \n\t"	
+		"rep insl \n\t"	
+		"popfl \n\t"	
+		"popal"	
+		:
+		: "d" (_port),
+		  "D" (_buffer),
+		  "c" (_reads));
+}
+
+static inline void outpdrep(uint16_t _port, uint32_t *_buffer,
+	uint32_t _writes) {
+	__asm__ __volatile__ ("pushal \n\t"
+		"pushfl \n\t"
+		"cli \n\t"
+		"cld \n\t"
+		"rep outsl \n\t"
+		"popfl \n\t"
+		"popal"
+		:
+		: "d" (_port),
+		  "S" (_buffer),
+		  "c" (_writes));
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/arch/x86/include/arch/x86/descriptor.h b/arch/x86/include/arch/x86/descriptor.h
new file mode 100644
index 0000000..1b2580d
--- /dev/null
+++ b/arch/x86/include/arch/x86/descriptor.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __ARCH_DESCRIPTOR_H
+#define __ARCH_DESCRIPTOR_H
+
+#include <sys/types.h>
+
+/*
+ * System Selectors
+ */
+#define CODE_SELECTOR	0x08
+#define DATA_SELECTOR	0x10
+#define VIDEO_SELECTOR	0x18
+#define TSS_SELECTOR	0x30
+
+#define USER_CODE_SELECTOR 0x23
+#define USER_DATA_SELECTOR 0x2b
+
+/*
+ * Descriptor Types
+ */
+#define SEG_TYPE_TSS		0x9
+#define SEG_TYPE_TSS_BUSY	0xb
+#define SEG_TYPE_TASK_GATE	0x5
+#define SEG_TYPE_INT_GATE	0xe		// 32 bit
+#define SEG_TYPE_DATA_RW	0x2
+#define SEG_TYPE_CODE_RW	0xa
+
+typedef uint16_t seg_sel_t;
+
+void set_global_desc(seg_sel_t sel, void *base, uint32_t limit,
+	uint8_t present, uint8_t ring, uint8_t sys, uint8_t type, uint8_t gran, uint8_t bits);
+
+#endif
diff --git a/arch/x86/include/arch/x86/mmu.h b/arch/x86/include/arch/x86/mmu.h
new file mode 100644
index 0000000..719710c
--- /dev/null
+++ b/arch/x86/include/arch/x86/mmu.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __ARCH_ARM_MMU_H
+#define __ARCH_ARM_MMU_H
+
+#include <sys/types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void arm_mmu_init(void);
+
+#define MMU_FLAG_CACHED 0x1
+#define MMU_FLAG_BUFFERED 0x2
+#define MMU_FLAG_READWRITE 0x4
+void arm_mmu_map_section(addr_t paddr, addr_t vaddr, uint flags);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/arch/x86/include/arch/x86/ops.h b/arch/x86/include/arch/x86/ops.h
new file mode 100644
index 0000000..d2bbc24
--- /dev/null
+++ b/arch/x86/include/arch/x86/ops.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __ARCH_X86_OPS_H
+#define __ARHC_X86_OPS_H
+
+#if 0
+#include <compiler.h>
+
+#ifndef ASSEMBLY
+
+// override of some routines
+__GNU_INLINE __ALWAYS_INLINE extern inline void arch_enable_ints(void)
+{
+	__asm__ __volatile__ ("sti");
+}
+
+__GNU_INLINE __ALWAYS_INLINE extern inline void arch_disable_ints(void)
+{
+	__asm__ __volatile__ ("cli");
+}
+
+#endif
+#endif
+
+#endif
+
diff --git a/arch/x86/kernel.ld b/arch/x86/kernel.ld
new file mode 100644
index 0000000..1e3abba
--- /dev/null
+++ b/arch/x86/kernel.ld
@@ -0,0 +1,83 @@
+/*

+ * Copyright (c) 2009 Corey Tabaka

+ *

+ * 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.

+ */

+

+ENTRY(_start)

+SECTIONS

+{

+	.text 0x0200000 : {

+		*(.text)

+		*(SORT(.text$*))

+		*(.gnu.linkonce.t.*)

+		

+		__ctor_list = .;

+		*(SORT(.ctor*))

+		__ctor_end = .;

+		__dtor_list = .;

+		*(SORT(.dtor*))

+		__dtor_end = .;

+		

+		. = ALIGN(4096);

+	}

+	

+	__data_start = .;

+	.data		: { *(.data .data.* .gnu.linkonce.d.*) }

+	.stab		: { *(.stab) }

+	.stabst		: { *(.stabstr) }

+	

+	. = ALIGN(4096);

+	__data_end = .;

+	

+	.rdata		: {

+		*(.rdata)

+		*(SORT(.rdata$*))

+		*(.rodata*)

+		*(.gnu.linkonce.r.*)

+		__commands_start = .;

+		KEEP (*(.commands))

+		__commands_end = .;

+		. = ALIGN(4);

+		__apps_start = .;

+		KEEP (*(.apps))

+		__apps_end = .;

+		

+		. = ALIGN(4096);

+	}

+	

+	__bss_start = .;

+	.bss		: {

+		*(.bss*)

+		*(.gnu.linkonce.b.*)

+		*(COMMON)

+		

+		. = ALIGN(4096);

+	}

+	__bss_end = .;

+	

+	/*. += 0x2000;

+	_kstack = .;

+	. += 0x1000;*/

+	

+	_end = .;

+	

+	/DISCARD/ : { *(.comment .note .eh_frame) }

+}

diff --git a/arch/x86/mmu.c b/arch/x86/mmu.c
new file mode 100644
index 0000000..6cefe67
--- /dev/null
+++ b/arch/x86/mmu.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <debug.h>
+#include <sys/types.h>
+#include <compiler.h>
+#include <arch.h>
+#include <arch/x86.h>
+#include <arch/x86/mmu.h>
+
+void x86_mmu_map_section(addr_t paddr, addr_t vaddr, uint flags)
+{
+	// TODO: stuff
+	//x86_invalidate_tlb();
+}
+
+void x86_mmu_init(void)
+{
+	// TODO: stuff
+}
+
+void arch_disable_mmu(void)
+{
+	// TODO: stuff
+}
diff --git a/arch/x86/ops.S b/arch/x86/ops.S
new file mode 100644
index 0000000..9a97948
--- /dev/null
+++ b/arch/x86/ops.S
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <asm.h>
+
+.text
+
+/* void arch_enable_ints(void); */
+FUNCTION(arch_enable_ints)
+	sti
+	ret
+
+/* void arch_disable_ints(void); */
+FUNCTION(arch_disable_ints)
+	cli
+	ret
+
+/* int atomic_swap(int *ptr, int val); */
+FUNCTION(atomic_swap)
+	movl 4(%esp), %edx
+	movl 8(%esp), %eax
+	xchgl %eax, (%edx)
+	ret
+
+/* int atomic_add(int *ptr, int val); */
+FUNCTION(atomic_add)
+	movl 4(%esp), %edx
+	movl 8(%esp), %eax
+	lock
+	xadd %eax, (%edx)
+	ret
+	
+/* int atomic_and(int *ptr, int val); */
+FUNCTION(atomic_and)
+	movl 4(%esp), %edx
+	movl (%edx), %eax
+0:
+	movl %eax, %ecx
+	andl 8(%esp), %ecx
+	lock
+	cmpxchgl %ecx, (%edx)
+	jnz 1f					/* static prediction: branch forward not taken */
+	ret
+1:
+	jmp 0b
+	
+	
+/* int atomic_or(int *ptr, int val); */
+FUNCTION(atomic_or)
+movl 4(%esp), %edx
+	movl (%edx), %eax
+0:
+	movl %eax, %ecx
+	orl 8(%esp), %ecx
+	lock
+	cmpxchgl %ecx, (%edx)
+	jnz 1f					/* static prediction: branch forward not taken */
+	ret
+1:
+	jmp 0b
+
+/* void arch_idle(); */
+FUNCTION(arch_idle)
+	pushf
+	popl %eax
+	andl $0x200, %eax
+	test %eax, %eax
+	je 1f					/* don't halt if local interrupts are disabled */
+	hlt
+1:
+	ret
+
+/* void arch_switch_stacks_and_call(addr_t call, addr_t stack) */
+FUNCTION(arch_switch_stacks_and_call)
+	movl 4(%esp), %eax
+	movl 8(%esp), %edx
+	movl %edx, %esp
+	call *%eax			/* perhaps this should be a jmp? it's not used anywhere so I don't know. */
diff --git a/arch/x86/rules.mk b/arch/x86/rules.mk
new file mode 100644
index 0000000..d929cb6
--- /dev/null
+++ b/arch/x86/rules.mk
@@ -0,0 +1,41 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+INCLUDES += \
+	-I$(LOCAL_DIR)/include
+
+BOOTOBJS += \
+	$(LOCAL_DIR)/crt0.o
+
+OBJS += \
+	$(LOCAL_DIR)/arch.o \
+	$(LOCAL_DIR)/asm.o \
+	$(LOCAL_DIR)/cache.o \
+	$(LOCAL_DIR)/cache-ops.o \
+	$(LOCAL_DIR)/ops.o \
+	$(LOCAL_DIR)/thread.o \
+	$(LOCAL_DIR)/mmu.o \
+	$(LOCAL_DIR)/faults.o \
+	$(LOCAL_DIR)/descriptor.o
+
+# set the default toolchain to x86 elf and set a #define
+TOOLCHAIN_PREFIX ?= i386-elf-
+
+LIBGCC := $(shell $(TOOLCHAIN_PREFIX)gcc $(CFLAGS) -print-libgcc-file-name)
+#$(info LIBGCC = $(LIBGCC))
+
+cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`"; \
+	then echo "$(2)"; else echo "$(3)"; fi ;)
+
+# disable SSP if the compiler supports it; it will break stuff
+CFLAGS += $(call cc-option,$(CC),-fno-stack-protector,)
+
+# potentially generated files that should be cleaned out with clean make rule
+GENERATED += \
+	$(BUILDDIR)/kernel.ld
+
+# rules for generating the linker scripts
+
+$(BUILDDIR)/kernel.ld: $(LOCAL_DIR)/kernel.ld
+	@echo generating $@
+	@$(MKDIR)
+	$(NOECHO)cp $< $@
diff --git a/arch/x86/thread.c b/arch/x86/thread.c
new file mode 100644
index 0000000..f2c2f84
--- /dev/null
+++ b/arch/x86/thread.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <kernel/thread.h>
+#include <arch/x86.h>
+#include <arch/x86/descriptor.h>
+
+/*struct context_switch_frame {
+	uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
+	uint32_t ds, es, fs, gs;
+	uint32_t eip, cs, eflags;
+};*/
+struct context_switch_frame {
+	uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
+	uint32_t eflags;
+	uint32_t eip;
+};
+
+extern void x86_context_switch(addr_t *old_sp, addr_t new_sp);
+
+static void initial_thread_func(void) __NO_RETURN;
+static void initial_thread_func(void)
+{
+	int ret;
+
+//	dprintf("initial_thread_func: thread %p calling %p with arg %p\n", current_thread, current_thread->entry, current_thread->arg);
+//	dump_thread(current_thread);
+
+	/* exit the implicit critical section we're within */
+	exit_critical_section();
+
+	ret = current_thread->entry(current_thread->arg);
+
+//	dprintf("initial_thread_func: thread %p exiting with %d\n", current_thread, ret);
+
+	thread_exit(ret);
+}
+
+void arch_thread_initialize(thread_t *t)
+{
+	// create a default stack frame on the stack
+	vaddr_t stack_top = (vaddr_t)t->stack + t->stack_size;
+
+	// make sure the top of the stack is 8 byte aligned for EABI compliance
+	stack_top = ROUNDDOWN(stack_top, 8);
+
+	struct context_switch_frame *frame = (struct context_switch_frame *)(stack_top);
+	frame--;
+
+	// fill it in
+	memset(frame, 0, sizeof(*frame));
+	
+	frame->eip = (vaddr_t) &initial_thread_func;
+	frame->eflags = 0x3002; // IF = 0, NT = 0, IOPL = 3
+	//frame->cs = CODE_SELECTOR;
+	//frame->fs = DATA_SELECTOR;
+	//frame->gs = DATA_SELECTOR;
+	//frame->es = DATA_SELECTOR;
+	//frame->ds = DATA_SELECTOR;
+	
+	// set the stack pointer
+	t->arch.esp = (vaddr_t)frame;
+}
+
+void arch_context_switch(thread_t *oldthread, thread_t *newthread)
+{
+	//dprintf(DEBUG, "arch_context_switch: old %p (%s), new %p (%s)\n", oldthread, oldthread->name, newthread, newthread->name);
+	
+	__asm__ __volatile__ (
+		"pushl $1f			\n\t"
+		"pushf				\n\t"
+		"pusha				\n\t"
+		"movl %%esp,(%%edx)	\n\t"
+		"movl %%eax,%%esp	\n\t"
+		"popa				\n\t"
+		"popf				\n\t"
+		"ret				\n\t"
+		"1:					\n\t"
+		
+		:
+		: "d" (&oldthread->arch.esp), "a" (newthread->arch.esp)
+	);
+	
+	/*__asm__ __volatile__ (
+		"pushf				\n\t"
+		"pushl %%cs			\n\t"
+		"pushl $1f			\n\t"
+		"pushl %%gs			\n\t"
+		"pushl %%fs			\n\t"
+		"pushl %%es			\n\t"
+		"pushl %%ds			\n\t"
+		"pusha				\n\t"
+		"movl %%esp,(%%edx)	\n\t"
+		"movl %%eax,%%esp	\n\t"
+		"popa				\n\t"
+		"popl %%ds			\n\t"
+		"popl %%es			\n\t"
+		"popl %%fs			\n\t"
+		"popl %%gs			\n\t"
+		"iret				\n\t"
+		"1:	"
+		:
+		: "d" (&oldthread->arch.esp), "a" (newthread->arch.esp)
+	);*/
+}
+
diff --git a/include/arch/ops.h b/include/arch/ops.h
index d5bafd0..26d0642 100644
--- a/include/arch/ops.h
+++ b/include/arch/ops.h
@@ -51,6 +51,8 @@
 
 void arch_clean_cache_range(addr_t start, size_t len);
 void arch_clean_invalidate_cache_range(addr_t start, size_t len);
+void arch_invalidate_cache_range(addr_t start, size_t len);
+void arch_sync_cache_range(addr_t start, size_t len);
 	
 void arch_idle(void);
 
@@ -58,6 +60,8 @@
 
 void arch_switch_stacks_and_call(addr_t call, addr_t stack) __NO_RETURN;
 
+uint32_t arch_cycle_count(void);
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/include/compiler.h b/include/compiler.h
index cd4b243..c3095a8 100644
--- a/include/compiler.h
+++ b/include/compiler.h
@@ -41,6 +41,19 @@
 #define __WEAK __attribute__((weak))
 #define __GNU_INLINE __attribute__((gnu_inline))
 #define __GET_CALLER(x) __builtin_return_address(0)
+#define __GET_FRAME(x) __builtin_frame_address(0)
+
+#define INCBIN(symname, sizename, filename, section)					\
+	__asm__ (".section " section "; .align 4; .globl "#symname);		\
+	__asm__ (""#symname ":\n.incbin \"" filename "\"");					\
+	__asm__ (".section " section "; .align 1;");						\
+	__asm__ (""#symname "_end:");										\
+	__asm__ (".section " section "; .align 4; .globl "#sizename);		\
+	__asm__ (""#sizename ": .long "#symname "_end - "#symname " - 1");	\
+	extern unsigned char symname[];										\
+	extern unsigned int sizename
+
+#define INCFILE(symname, sizename, filename) INCBIN(symname, sizename, filename, ".rodata")
 
 /* look for gcc 3.0 and above */
 #if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 0)
@@ -106,4 +119,7 @@
 
 #endif
 
+/* TODO: add type check */
+#define countof(a) (sizeof(a) / sizeof((a)[0]))
+
 #endif
diff --git a/include/debug.h b/include/debug.h
index e5ebc19..2944cb7 100644
--- a/include/debug.h
+++ b/include/debug.h
@@ -57,7 +57,7 @@
 #define dvprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dvprintf(x); } } while (0)
 
 /* input */
-int dgetc(char *c);
+int dgetc(char *c, bool wait);
 
 /* systemwide halts */
 void halt(void) __NO_RETURN;
diff --git a/include/dev/display.h b/include/dev/display.h
new file mode 100644
index 0000000..aa0bff6
--- /dev/null
+++ b/include/dev/display.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __DEV_DISPLAY_H
+#define __DEV_DISPLAY_H
+
+#include <sys/types.h>
+#include <lib/gfx.h>
+
+int display_init(void *framebuffer);
+int display_enable(bool enable);
+void display_pre_freq_change(void);
+void display_post_freq_change(void);
+
+struct display_info {
+	void *framebuffer;
+	gfx_format format;
+	uint width;
+	uint height;
+	uint stride;
+
+	// Update function	
+	void (*flush)(uint starty, uint endy);
+};
+
+void display_get_info(struct display_info *info);
+
+#endif
+
diff --git a/include/dev/pci.h b/include/dev/pci.h
new file mode 100644
index 0000000..4ba644e
--- /dev/null
+++ b/include/dev/pci.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __PCI_H
+#define __PCI_H
+
+#include <sys/types.h>
+#include <compiler.h>
+
+/*
+ * PCI access return codes
+ */
+#define _PCI_SUCCESSFUL				0x00
+#define _PCI_FUNC_NOT_SUPPORTED		0x81
+#define _PCI_BAD_VENDOR_ID			0x83
+#define _PCI_DEVICE_NOT_FOUND		0x86
+#define _PCI_BAD_REGISTER_NUMBER	0x87
+#define _PCI_SET_FAILED				0x88
+#define _PCI_BUFFER_TOO_SMALL		0x89
+
+/*
+ * PCI configuration space offsets
+ */
+#define PCI_CONFIG_VENDOR_ID		0x00
+#define PCI_CONFIG_DEVICE_ID		0x02
+#define PCI_CONFIG_COMMAND			0x04
+#define PCI_CONFIG_STATUS			0x06
+#define PCI_CONFIG_REVISION_ID		0x08
+#define PCI_CONFIG_CLASS_CODE		0x09
+#define PCI_CONFIG_CACHE_LINE_SIZE	0x0c
+#define PCI_CONFIG_LATENCY_TIMER	0x0d
+#define PCI_CONFIG_HEADER_TYPE		0x0e
+#define PCI_CONFIG_BIST				0x0f
+#define PCI_CONFIG_BASE_ADDRESSES	0x10
+#define PCI_CONFIG_CARDBUS_CIS_PTR	0x28
+#define PCI_CONFIG_SUBSYS_VENDOR_ID	0x2c
+#define PCI_CONFIG_SUBSYS_ID		0x2e
+#define PCI_CONFIG_EXP_ROM_ADDRESS	0x30
+#define PCI_CONFIG_CAPABILITIES		0x34
+#define PCI_CONFIG_INTERRUPT_LINE	0x3c
+#define PCI_CONFIG_INTERRUPT_PIN	0x3d
+#define PCI_CONFIG_MIN_GRANT		0x3e
+#define PCI_CONFIG_MAX_LATENCY		0x3f
+
+/*
+ * PCI header type register bits
+ */
+#define PCI_HEADER_TYPE_MASK		0x7f
+#define PCI_HEADER_TYPE_MULTI_FN	0x80
+
+/*
+ * PCI header types
+ */
+#define PCI_HEADER_TYPE_STANDARD	0x00
+#define PCI_HEADER_TYPE_PCI_BRIDGE	0x01
+#define PCI_HEADER_TYPE_CARD_BUS	0x02
+
+/*
+ * PCI command register bits
+ */
+#define PCI_COMMAND_IO_EN			0x0001
+#define PCI_COMMAND_MEM_EN			0x0002
+#define PCI_COMMAND_BUS_MASTER_EN	0x0004
+#define PCI_COMMAND_SPECIAL_EN		0x0008
+#define PCI_COMMAND_MEM_WR_INV_EN	0x0010
+#define PCI_COMMAND_PAL_SNOOP_EN	0x0020
+#define PCI_COMMAND_PERR_RESP_EN	0x0040
+#define PCI_COMMAND_AD_STEP_EN		0x0080
+#define PCI_COMMAND_SERR_EN			0x0100
+#define PCI_COMMAND_FAST_B2B_EN		0x0200
+
+/*
+ * PCI status register bits
+ */
+#define PCI_STATUS_NEW_CAPS			0x0010
+#define PCI_STATUS_66_MHZ			0x0020
+#define PCI_STATUS_FAST_B2B			0x0080
+#define PCI_STATUS_MSTR_PERR		0x0100
+#define PCI_STATUS_DEVSEL_MASK		0x0600
+#define PCI_STATUS_TARG_ABORT_SIG	0x0800
+#define PCI_STATUS_TARG_ABORT_RCV	0x1000
+#define PCI_STATUS_MSTR_ABORT_RCV	0x2000
+#define PCI_STATUS_SERR_SIG			0x4000
+#define PCI_STATUS_PERR				0x8000
+
+typedef struct {
+	uint16_t vendor_id;
+	uint16_t device_id;
+	uint16_t command;
+	uint16_t status;
+	uint8_t revision_id_0;
+	uint8_t program_interface;
+	uint8_t sub_class;
+	uint8_t base_class;
+	uint8_t cache_line_size;
+	uint8_t latency_timer;
+	uint8_t header_type;
+	uint8_t bist;
+	uint32_t base_addresses[6];
+	uint32_t cardbus_cis_ptr;
+	uint16_t subsystem_vendor_id;
+	uint16_t subsystem_id;
+	uint32_t expansion_rom_address;
+	uint8_t capabilities_ptr;
+	uint8_t reserved_0[3];
+	uint32_t reserved_1;
+	uint8_t interrupt_line;
+	uint8_t interrupt_pin;
+	uint8_t min_grant;
+	uint8_t max_latency;
+} __PACKED pci_config_t;
+
+/*
+ * PCI address structure
+ */
+typedef struct
+{
+	uint8_t bus;
+	uint8_t dev_fn;
+} pci_location_t;
+
+typedef struct {
+	uint8_t id;
+	uint8_t next;
+} __PACKED pci_capability_t;
+
+typedef struct {
+	uint8_t bus;
+	uint8_t device;
+	uint8_t link_int_a;
+	uint16_t irq_int_a;
+	uint8_t link_int_b;
+	uint16_t irq_int_b;
+	uint8_t link_int_c;
+	uint16_t irq_int_c;
+	uint8_t link_int_d;
+	uint16_t irq_int_d;
+	uint8_t slot;
+	uint8_t reserved;
+} __PACKED irq_routing_entry;
+
+void pci_init(void);
+
+int pci_get_last_bus(void);
+
+int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
+int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index);
+ 
+int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value);
+int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value);
+int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value);
+
+int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value);
+int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value);
+int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value);
+	
+int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs);
+int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
+
+#endif
diff --git a/include/endian.h b/include/endian.h
index 30623b2..34bef6e 100644
--- a/include/endian.h
+++ b/include/endian.h
@@ -40,6 +40,10 @@
 #define BYTE_ORDER LITTLE_ENDIAN
 #endif
 
+#if defined(__i386__) || defined(_X86_)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
 #ifndef BYTE_ORDER
 #error "need to get the BYTE_ORDER define from somewhere"
 #endif
diff --git a/include/err.h b/include/err.h
index 3b8b401..265c19c 100644
--- a/include/err.h
+++ b/include/err.h
@@ -23,6 +23,8 @@
 #ifndef __ERR_H
 #define __ERR_H
 
+#include <sys/types.h> // for status_t
+
 #define NO_ERROR 0
 #define ERROR -1
 #define ERR_NOT_FOUND -2
@@ -37,5 +39,17 @@
 #define ERR_OBJECT_DESTROYED -11
 #define ERR_NOT_BLOCKED -12
 #define ERR_TIMED_OUT -13
+#define ERR_ALREADY_EXISTS -14
+#define ERR_CHANNEL_CLOSED -15
+#define ERR_OFFLINE -16
+#define ERR_NOT_ALLOWED -17
+#define ERR_BAD_PATH -18
+#define ERR_ALREADY_MOUNTED -19
+#define ERR_IO -20
+#define ERR_NOT_DIR -21
+#define ERR_NOT_FILE -22
+#define ERR_RECURSE_TOO_DEEP -23
+#define ERR_NOT_SUPPORTED -24
+#define ERR_TOO_BIG -25
 
 #endif
diff --git a/include/kernel/event.h b/include/kernel/event.h
index 8666737..d0ce7f5 100644
--- a/include/kernel/event.h
+++ b/include/kernel/event.h
@@ -58,6 +58,7 @@
 status_t event_wait_timeout(event_t *, time_t); /* wait on the event with a timeout */
 status_t event_signal(event_t *, bool reschedule);
 status_t event_unsignal(event_t *);
+#define event_initialized(e)	((e)->magic == EVENT_MAGIC)
 
 #endif
 
diff --git a/include/kernel/thread.h b/include/kernel/thread.h
index 8f76291..6c25712 100644
--- a/include/kernel/thread.h
+++ b/include/kernel/thread.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -205,7 +205,11 @@
 status_t thread_unblock_from_wait_queue(thread_t *t, bool reschedule, status_t wait_queue_error);
 
 /* thread level statistics */
+#if DEBUGLEVEL > 1
+#define THREAD_STATS 1
+#else
 #define THREAD_STATS 0
+#endif
 #if THREAD_STATS
 struct thread_stats {
 	bigtime_t idle_time;
diff --git a/include/kernel/timer.h b/include/kernel/timer.h
index 98332fd..2111d94 100644
--- a/include/kernel/timer.h
+++ b/include/kernel/timer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
diff --git a/include/lib/bcache.h b/include/lib/bcache.h
new file mode 100644
index 0000000..167cfbc
--- /dev/null
+++ b/include/lib/bcache.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2007 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __LIB_BCACHE_H
+#define __LIB_BCACHE_H
+
+#include <lib/bio.h>
+
+typedef void * bcache_t;
+
+bcache_t bcache_create(bdev_t *dev, size_t block_size, int block_count);
+void bcache_destroy(bcache_t);
+
+int bcache_read_block(bcache_t, void *, uint block);
+
+// get and put a pointer directly to the block
+int bcache_get_block(bcache_t, void **, uint block);
+int bcache_put_block(bcache_t, uint block);
+
+#endif
+
diff --git a/include/lib/bio.h b/include/lib/bio.h
new file mode 100644
index 0000000..a8d145c
--- /dev/null
+++ b/include/lib/bio.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __LIB_BIO_H
+#define __LIB_BIO_H
+
+#include <sys/types.h>
+#include <list.h>
+
+typedef uint32_t bnum_t;
+
+typedef struct bdev {
+	struct list_node node;
+	volatile int ref;
+
+	/* info about the block device */
+	char *name;
+	off_t size;
+	size_t block_size;
+	bnum_t block_count;
+
+	/* function pointers */
+	ssize_t (*read)(struct bdev *, void *buf, off_t offset, size_t len);
+	ssize_t (*read_block)(struct bdev *, void *buf, bnum_t block, uint count);
+	ssize_t (*write)(struct bdev *, const void *buf, off_t offset, size_t len);
+	ssize_t (*write_block)(struct bdev *, const void *buf, bnum_t block, uint count);
+	ssize_t (*erase)(struct bdev *, off_t offset, size_t len);
+	int (*ioctl)(struct bdev *, int request, void *argp);
+	void (*close)(struct bdev *);
+} bdev_t;
+
+/* user api */
+bdev_t *bio_open(const char *name);
+void bio_close(bdev_t *dev);
+ssize_t bio_read(bdev_t *dev, void *buf, off_t offset, size_t len);
+ssize_t bio_read_block(bdev_t *dev, void *buf, bnum_t block, uint count);
+ssize_t bio_write(bdev_t *dev, const void *buf, off_t offset, size_t len);
+ssize_t bio_write_block(bdev_t *dev, const void *buf, bnum_t block, uint count);
+ssize_t bio_erase(bdev_t *dev, off_t offset, size_t len);
+int bio_ioctl(bdev_t *dev, int request, void *argp);
+
+/* intialize the block device layer */
+void bio_init(void);
+
+/* register a block device */
+void bio_register_device(bdev_t *dev);
+void bio_unregister_device(bdev_t *dev);
+
+/* used during bdev construction */
+void bio_initialize_bdev(bdev_t *dev, const char *name, size_t block_size, bnum_t block_count);
+
+/* debug stuff */
+void bio_dump_devices(void);
+
+/* subdevice support */
+status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, size_t len);
+
+/* memory based block device */
+int create_membdev(const char *name, void *ptr, size_t len);
+
+#endif
+
diff --git a/include/lib/cbuf.h b/include/lib/cbuf.h
new file mode 100644
index 0000000..d71e308
--- /dev/null
+++ b/include/lib/cbuf.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __LIB_CBUF_H
+#define __LIB_CBUF_H
+
+#include <sys/types.h>
+#include <kernel/event.h>
+
+typedef struct cbuf {
+	uint head; 
+	uint tail;
+	uint len_pow2;
+	char *buf;
+	event_t event;
+} cbuf_t;
+
+void cbuf_initialize(cbuf_t *cbuf, size_t len);
+size_t cbuf_read(cbuf_t *cbuf, void *_buf, size_t buflen, bool block);
+size_t cbuf_write(cbuf_t *cbuf, const void *_buf, size_t len, bool canreschedule);
+
+#endif
+
diff --git a/include/lib/font.h b/include/lib/font.h
new file mode 100644
index 0000000..c209c61
--- /dev/null
+++ b/include/lib/font.h
@@ -0,0 +1,12 @@
+#ifndef __LIB_FONT_H
+#define __LIB_FONT_H
+
+#include <lib/gfx.h>
+
+#define FONT_X	6
+#define FONT_Y	12
+
+void font_draw_char(gfx_surface *surface, unsigned char c, int x, int y, uint32_t color);
+
+#endif
+
diff --git a/include/lib/fs.h b/include/lib/fs.h
new file mode 100644
index 0000000..05390cc
--- /dev/null
+++ b/include/lib/fs.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __LIB_FS_H
+#define __LIB_FS_H
+
+void fs_init(void);
+
+struct file_stat {
+	bool is_dir;
+	off_t size;
+};
+
+typedef void *filecookie;
+typedef void *fscookie;
+
+int fs_mount(const char *path, const char *device);
+int fs_unmount(const char *path);
+
+/* file api */
+int fs_open_file(const char *path, filecookie *fcookie);
+int fs_read_file(filecookie fcookie, void *buf, off_t offset, size_t len);
+int fs_close_file(filecookie fcookie);
+int fs_stat_file(filecookie fcookie, struct file_stat *);
+
+/* convenience routines */
+ssize_t fs_load_file(const char *path, void *ptr, size_t maxlen);
+
+/* walk through a path string, removing duplicate path seperators, flattening . and .. references */
+void fs_normalize_path(char *path);
+
+#endif
+
diff --git a/include/lib/gfx.h b/include/lib/gfx.h
new file mode 100644
index 0000000..2f251e4
--- /dev/null
+++ b/include/lib/gfx.h
@@ -0,0 +1,86 @@
+#ifndef __LIB_GFX_H
+#define __LIB_GFX_H
+
+#include <sys/types.h>
+
+// gfx library
+
+// different graphics formats
+typedef enum {
+	GFX_FORMAT_RGB_565,
+	GFX_FORMAT_ARGB_8888,
+	GFX_FORMAT_RGB_x888,
+
+	GFX_FORMAT_MAX
+} gfx_format;
+
+#define MAX_ALPHA 255
+
+/**
+ * @brief  Describe a graphics drawing surface
+ *
+ * The gfx_surface object represents a framebuffer that can be rendered
+ * to.  Elements include a pointer to the actual pixel memory, its size, its
+ * layout, and pointers to basic drawing functions.
+ *
+ * @ingroup graphics
+ */
+typedef struct gfx_surface {
+	void *ptr;
+	bool free_on_destroy;
+	gfx_format format;
+	uint width;
+	uint height;
+	uint stride;
+	uint pixelsize;
+	size_t len;
+	uint alpha;
+
+	// function pointers
+	void (*copyrect)(struct gfx_surface *, uint x, uint y, uint width, uint height, uint x2, uint y2);
+	void (*fillrect)(struct gfx_surface *, uint x, uint y, uint width, uint height, uint color);
+	void (*putpixel)(struct gfx_surface *, uint x, uint y, uint color);
+	void (*flush)(uint starty, uint endy);
+} gfx_surface;
+
+// copy a rect from x,y with width x height to x2, y2
+void gfx_copyrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2);
+
+// fill a rect within the surface with a color
+void gfx_fillrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color);
+
+// draw a pixel at x, y in the surface
+void gfx_putpixel(gfx_surface *surface, uint x, uint y, uint color);
+
+// clear the entire surface with a color
+static inline void gfx_clear(gfx_surface *surface, uint color)
+{
+	surface->fillrect(surface, 0, 0, surface->width, surface->height, color);
+
+	if (surface->flush)
+		surface->flush(0, surface->height-1);
+}
+
+// blend between two surfaces
+void gfx_surface_blend(struct gfx_surface *target, struct gfx_surface *source, uint destx, uint desty);
+
+void gfx_flush(struct gfx_surface *surface);
+
+void gfx_flush_rows(struct gfx_surface *surface, uint start, uint end);
+
+// surface setup
+gfx_surface *gfx_create_surface(void *ptr, uint width, uint height, uint stride, gfx_format format);
+
+// utility routine to make a surface out of a display info
+struct display_info;
+gfx_surface *gfx_create_surface_from_display(struct display_info *);
+
+// free the surface
+// optionally frees the buffer if the free bit is set
+void gfx_surface_destroy(struct gfx_surface *surface);
+
+// utility routine to fill the display with a little moire pattern
+void gfx_draw_pattern(void);
+
+#endif
+
diff --git a/include/lib/gfxconsole.h b/include/lib/gfxconsole.h
new file mode 100644
index 0000000..4425f50
--- /dev/null
+++ b/include/lib/gfxconsole.h
@@ -0,0 +1,10 @@
+#ifndef __LIB_GFXCONSOLE_H
+#define __LIB_GFXCONSOLE_H
+
+#include <lib/gfx.h>
+
+void gfxconsole_start_on_display(void);
+void gfxconsole_start(gfx_surface *surface);
+
+#endif
+
diff --git a/include/lib/partition.h b/include/lib/partition.h
new file mode 100644
index 0000000..576bc97
--- /dev/null
+++ b/include/lib/partition.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __LIB_PARTITION_H
+#define __LIB_PARTITION_H
+
+#include <sys/types.h>
+
+/* examine and try to publish partitions on a particular device at a particular offset */
+int partition_publish(const char *device, off_t offset);
+
+/* remove any published subdevices on this device */
+int partition_unpublish(const char *device);
+
+#endif
+
diff --git a/include/lib/text.h b/include/lib/text.h
new file mode 100644
index 0000000..ad7b1fd
--- /dev/null
+++ b/include/lib/text.h
@@ -0,0 +1,14 @@
+#ifndef __LIB_TEXT_H
+#define __LIB_TEXT_H
+
+#include <lib/font.h>
+
+/* super cheezy mechanism to stick lines of text up on top of whatever is being drawn */
+/* XXX replace with something more generic later */
+void text_draw(int x, int y, const char *string);
+
+/* super dumb, someone has to call this to refresh everything */
+void text_update(void);
+
+#endif
+
diff --git a/include/lib/tga.h b/include/lib/tga.h
new file mode 100644
index 0000000..62ffb43
--- /dev/null
+++ b/include/lib/tga.h
@@ -0,0 +1,10 @@
+#ifndef __LIB_TGA_H
+#define __LIB_TGA_H
+
+#include <lib/gfx.h>
+#include <sys/types.h>
+
+gfx_surface *tga_decode(const void *ptr, size_t len, gfx_format format);
+
+#endif
+
diff --git a/include/platform/debug.h b/include/platform/debug.h
index 4ab460a..785cd42 100644
--- a/include/platform/debug.h
+++ b/include/platform/debug.h
@@ -32,7 +32,6 @@
 #endif
 
 void debug_dump_regs(void);
-uint32_t debug_cycle_count(void);
 
 void debug_dump_memory_bytes(void *mem, int len);
 void debug_dump_memory_halfwords(void *mem, int len);
diff --git a/include/pow2.h b/include/pow2.h
new file mode 100644
index 0000000..1ab8ae0
--- /dev/null
+++ b/include/pow2.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __POW2_H
+#define __POW2_H
+
+#include <sys/types.h>
+#include <compiler.h>
+
+/* routines for dealing with power of 2 values for efficiency */
+static inline __ALWAYS_INLINE bool ispow2(uint val)
+{
+	return ((val - 1) & val) == 0;
+}
+
+static inline __ALWAYS_INLINE uint log2(uint val)
+{
+	if (!ispow2(val))
+		return 0; // undefined
+
+	return __builtin_ctz(val);
+}
+
+static inline __ALWAYS_INLINE uint valpow2(uint valp2)
+{
+	return 1 << valp2;
+}
+
+static inline __ALWAYS_INLINE uint divpow2(uint val, uint divp2)
+{
+	return val >> divp2;
+}
+
+static inline __ALWAYS_INLINE uint modpow2(uint val, uint modp2)
+{
+	return val & ((1UL << modp2) - 1);
+}
+
+
+#endif
+
diff --git a/include/sys/types.h b/include/sys/types.h
index 95059fa..9b26eeb 100644
--- a/include/sys/types.h
+++ b/include/sys/types.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -62,6 +62,11 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
+#define TIME_GTE(a, b) ((long)((a) - (b)) >= 0)
+#define TIME_LTE(a, b) ((long)((a) - (b)) <= 0)
+#define TIME_GT(a, b) ((long)((a) - (b)) > 0)
+#define TIME_LT(a, b) ((long)((a) - (b)) < 0)
+
 enum handler_return {
 	INT_NO_RESCHEDULE = 0,
 	INT_RESCHEDULE,
diff --git a/kernel/debug.c b/kernel/debug.c
index c3d7220..ec4b987 100644
--- a/kernel/debug.c
+++ b/kernel/debug.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -21,12 +21,22 @@
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
+/**
+ * @defgroup debug  Debug
+ * @{
+ */
+
+/**
+ * @file
+ * @brief  Debug console functions.
+ */
+
 #include <debug.h>
 #include <kernel/thread.h>
 #include <kernel/timer.h>
 #include <platform.h>
 
-#if defined(WITH_LIB_CONSOLE)
+#if WITH_LIB_CONSOLE
 #include <lib/console.h>
 
 static int cmd_threads(int argc, const cmd_args *argv);
@@ -35,11 +45,11 @@
 
 STATIC_COMMAND_START
 #if DEBUGLEVEL > 1
-	{ "threads", "list kernel threads", &cmd_threads },
+STATIC_COMMAND("threads", "list kernel threads", &cmd_threads)
 #endif
 #if THREAD_STATS
-	{ "threadstats", "thread level statistics", &cmd_threadstats },
-	{ "threadload", "toggle thread load display", &cmd_threadload },
+STATIC_COMMAND("threadstats", "thread level statistics", &cmd_threadstats)
+STATIC_COMMAND("threadload", "toggle thread load display", &cmd_threadload)
 #endif
 STATIC_COMMAND_END(kernel);
 
@@ -75,8 +85,6 @@
 	static struct thread_stats old_stats;
 	static bigtime_t last_idle_time;
 
-	timer_set_oneshot(t, 1000, &threadload, NULL);
-
 	bigtime_t idle_time = thread_stats.idle_time;
 	if (current_thread == idle_thread) {
 		idle_time += current_time_hires() - thread_stats.last_idle_timestamp;
@@ -108,7 +116,7 @@
 	if (showthreadload == false) {
 		// start the display
 		timer_initialize(&tltimer);
-		timer_set_oneshot(&tltimer, 1000, &threadload, NULL);
+		timer_set_periodic(&tltimer, 1000, &threadload, NULL);
 		showthreadload = true;
 	} else {
 		timer_cancel(&tltimer);
diff --git a/kernel/event.c b/kernel/event.c
index 09e485a..bdb6ec2 100644
--- a/kernel/event.c
+++ b/kernel/event.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -20,6 +20,26 @@
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
+
+/**
+ * @file
+ * @brief  Event wait and signal functions for threads.
+ * @defgroup event Events
+ *
+ * An event is a subclass of a wait queue.
+ *
+ * Threads wait for events, with optional timeouts.
+ *
+ * Events are "signaled", releasing waiting threads to continue.
+ * Signals may be one-shot signals (EVENT_FLAG_AUTOUNSIGNAL), in which
+ * case one signal releases only one thread, at which point it is
+ * automatically cleared. Otherwise, signals release all waiting threads
+ * to continue immediately until the signal is manually cleared with
+ * event_unsignal().
+ *
+ * @{
+ */
+
 #include <debug.h>
 #include <err.h>
 #include <kernel/event.h>
@@ -28,6 +48,13 @@
 #define EVENT_CHECK 1
 #endif
 
+/**
+ * @brief  Initialize an event object
+ *
+ * @param e        Event object to initialize
+ * @param initial  Initial value for "signaled" state
+ * @param flags    0 or EVENT_FLAG_AUTOUNSIGNAL
+ */
 void event_init(event_t *e, bool initial, uint flags)
 {
 #if EVENT_CHECK
@@ -40,6 +67,15 @@
 	wait_queue_init(&e->wait);
 }
 
+/**
+ * @brief  Destroy an event object.
+ *
+ * Event's resources are freed and it may no longer be
+ * used until event_init() is called again.  Any threads
+ * still waiting on the event will be resumed.
+ *
+ * @param e        Event object to initialize
+ */
 void event_destroy(event_t *e)
 {
 	enter_critical_section();
@@ -56,6 +92,21 @@
 	exit_critical_section();
 }
 
+/**
+ * @brief  Wait for event to be signaled
+ *
+ * If the event has already been signaled, this function
+ * returns immediately.  Otherwise, the current thread
+ * goes to sleep until the event object is signaled,
+ * the timeout is reached, or the event object is destroyed
+ * by another thread.
+ *
+ * @param e        Event object
+ * @param timeout  Timeout value, in ms
+ *
+ * @return  0 on success, ERR_TIMED_OUT on timeout,
+ *         other values on other errors.
+ */
 status_t event_wait_timeout(event_t *e, time_t timeout)
 {
 	status_t ret = NO_ERROR;
@@ -85,11 +136,31 @@
 	return ret;
 }
 
+/**
+ * @brief  Same as event_wait_timeout(), but without a timeout.
+ */
 status_t event_wait(event_t *e)
 {
 	return event_wait_timeout(e, INFINITE_TIME);
 }
 
+/**
+ * @brief  Signal an event
+ *
+ * Signals an event.  If EVENT_FLAG_AUTOUNSIGNAL is set in the event
+ * object's flags, only one waiting thread is allowed to proceed.  Otherwise,
+ * all waiting threads are allowed to proceed until such time as
+ * event_unsignal() is called.
+ *
+ * @param e	          Event object
+ * @param reschedule  If true, waiting thread(s) are executed immediately,
+ *                    and the current thread resumes only after the
+ *                    waiting threads have been satisfied. If false,
+ *                    waiting threads are placed at the end of the run
+ *                    queue.
+ *
+ * @return  Returns NO_ERROR on success.
+ */
 status_t event_signal(event_t *e, bool reschedule)
 {
 	enter_critical_section();
@@ -121,6 +192,18 @@
 	return NO_ERROR;
 }
 
+/**
+ * @brief  Clear the "signaled" property of an event
+ *
+ * Used mainly for event objects without the EVENT_FLAG_AUTOUNSIGNAL
+ * flag.  Once this function is called, threads that call event_wait()
+ * functions will once again need to wait until the event object
+ * is signaled.
+ *
+ * @param e  Event object
+ *
+ * @return  Returns NO_ERROR on success.
+ */
 status_t event_unsignal(event_t *e)
 {
 	enter_critical_section();
diff --git a/kernel/main.c b/kernel/main.c
index f48ea55..efbb9e9 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -121,6 +121,14 @@
 
 	arch_init();
 
+	// XXX put this somewhere else
+#if WITH_LIB_BIO
+	bio_init();
+#endif
+#if WITH_LIB_FS
+	fs_init();
+#endif
+
 	// initialize the rest of the platform
 	dprintf(SPEW, "initializing platform\n");
 	platform_init();
diff --git a/kernel/mutex.c b/kernel/mutex.c
index 4998b6a..07d2b19 100644
--- a/kernel/mutex.c
+++ b/kernel/mutex.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -20,6 +20,15 @@
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
+
+/**
+ * @file
+ * @brief  Mutex functions
+ *
+ * @defgroup mutex Mutex
+ * @{
+ */
+
 #include <debug.h>
 #include <err.h>
 #include <kernel/mutex.h>
@@ -29,6 +38,9 @@
 #define MUTEX_CHECK 1
 #endif
 
+/**
+ * @brief  Initialize a mutex_t
+ */
 void mutex_init(mutex_t *m)
 {
 #if MUTEX_CHECK
@@ -41,6 +53,12 @@
 	wait_queue_init(&m->wait);
 }
 
+/**
+ * @brief  Destroy a mutex_t
+ *
+ * This function frees any resources that were allocated
+ * in mutex_init().  The mutex_t object itself is not freed.
+ */
 void mutex_destroy(mutex_t *m)
 {
 	enter_critical_section();
@@ -49,9 +67,9 @@
 	ASSERT(m->magic == MUTEX_MAGIC);
 #endif
 
-	if (m->holder != 0 && current_thread != m->holder)
-		panic("mutex_destroy: thread %p (%s) tried to release mutex %p it doesn't own. owned by %p (%s)\n", 
-				current_thread, current_thread->name, m, m->holder, m->holder ? m->holder->name : "none");
+//	if (m->holder != 0 && current_thread != m->holder)
+//		panic("mutex_destroy: thread %p (%s) tried to release mutex %p it doesn't own. owned by %p (%s)\n", 
+//				current_thread, current_thread->name, m, m->holder, m->holder ? m->holder->name : "none");
 
 	m->magic = 0;
 	m->count = 0;
@@ -59,6 +77,14 @@
 	exit_critical_section();
 }
 
+/**
+ * @brief  Acquire a mutex; wait if needed.
+ *
+ * This function waits for a mutex to become available.  It
+ * may wait forever if the mutex never becomes free.
+ *
+ * @return  NO_ERROR on success, other values on error
+ */
 status_t mutex_acquire(mutex_t *m)
 {
 	status_t ret = NO_ERROR;
@@ -94,6 +120,16 @@
 	return ret;
 }
 
+/**
+ * @brief  Mutex wait with timeout
+ *
+ * This function waits up to \a timeout ms for the mutex to become available.
+ * Timeout may be zero, in which case this function returns immediately if
+ * the mutex is not free.
+ *
+ * @return  NO_ERROR on success, ERR_TIMED_OUT on timeout,
+ * other values on error
+ */
 status_t mutex_acquire_timeout(mutex_t *m, time_t timeout)
 {
 	status_t ret = NO_ERROR;
@@ -140,6 +176,9 @@
 	return ret;
 }
 
+/**
+ * @brief  Release mutex
+ */
 status_t mutex_release(mutex_t *m)
 {
 	if (current_thread != m->holder)
diff --git a/kernel/thread.c b/kernel/thread.c
index eeccdd6..58dff8d 100644
--- a/kernel/thread.c
+++ b/kernel/thread.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -20,6 +20,16 @@
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
+
+/**
+ * @file
+ * @brief  Kernel threading
+ *
+ * This file is the core kernel threading interface.
+ *
+ * @defgroup thread Threads
+ * @{
+ */
 #include <debug.h>
 #include <list.h>
 #include <malloc.h>
@@ -61,6 +71,11 @@
 static void thread_resched(void);
 static void idle_thread_routine(void) __NO_RETURN;
 
+#if PLATFORM_HAS_DYNAMIC_TIMER
+/* preemption timer */
+static timer_t preempt_timer;
+#endif
+
 /* run queue manipulation */
 static void insert_in_run_queue_head(thread_t *t)
 {
@@ -95,6 +110,33 @@
 	strlcpy(t->name, name, sizeof(t->name));
 }
 
+/**
+ * @brief  Create a new thread
+ *
+ * This function creates a new thread.  The thread is initially suspended, so you
+ * need to call thread_resume() to execute it.
+ *
+ * @param  name        Name of thread
+ * @param  entry       Entry point of thread
+ * @param  arg         Arbitrary argument passed to entry()
+ * @param  priority    Execution priority for the thread.
+ * @param  stack_size  Stack size for the thread.
+ *
+ * Thread priority is an integer from 0 (lowest) to 31 (highest).  Some standard
+ * prioritys are defined in <kernel/thread.h>:
+ *
+ *	HIGHEST_PRIORITY
+ *	DPC_PRIORITY
+ *	HIGH_PRIORITY
+ *	DEFAULT_PRIORITY
+ *	LOW_PRIORITY
+ *	IDLE_PRIORITY
+ *	LOWEST_PRIORITY
+ *
+ * Stack size is typically set to DEFAULT_STACK_SIZE
+ *
+ * @return  Pointer to thread object, or NULL on failure.
+ */
 thread_t *thread_create(const char *name, thread_start_routine entry, void *arg, int priority, size_t stack_size)
 {
 	thread_t *t;
@@ -138,6 +180,16 @@
 	return t;
 }
 
+/**
+ * @brief  Make a suspended thread executable.
+ *
+ * This function is typically called to start a thread which has just been
+ * created with thread_create()
+ *
+ * @param t  Thread to resume
+ *
+ * @return NO_ERROR on success, ERR_NOT_SUSPENDED if thread was not suspended.
+ */
 status_t thread_resume(thread_t *t)
 {
 #if THREAD_CHECKS
@@ -181,6 +233,13 @@
 	free(t);
 }
 
+/**
+ * @brief  Terminate the current thread
+ *
+ * Current thread exits with the specified return code.
+ *
+ * This function does not return.
+ */
 void thread_exit(int retcode)
 {
 #if THREAD_CHECKS
@@ -211,10 +270,15 @@
 		arch_idle();
 }
 
-/* 
+/**
+ * @brief  Cause another thread to be executed.
+ *
  * Internal reschedule routine. The current thread needs to already be in whatever
  * state and queues it needs to be in. This routine simply picks the next thread and
  * switches to it.
+ *
+ * This is probably not the function you're looking for. See
+ * thread_yield() instead.
  */
 void thread_resched(void)
 {
@@ -294,6 +358,17 @@
 	ASSERT(newthread->saved_critical_section_count > 0);
 #endif
 
+#if PLATFORM_HAS_DYNAMIC_TIMER
+	/* if we're switching from idle to a real thread, set up a periodic
+	 * timer to run our preemption tick.
+	 */
+	if (oldthread == idle_thread) {
+		timer_set_periodic(&preempt_timer, 10, (timer_callback)thread_timer_tick, NULL);
+	} else if (newthread == idle_thread) {
+		timer_cancel(&preempt_timer);
+	}
+#endif
+
 	/* do the switch */
 	oldthread->saved_critical_section_count = critical_section_count;
 	current_thread = newthread;
@@ -301,6 +376,15 @@
 	arch_context_switch(oldthread, newthread);
 }
 
+/**
+ * @brief Yield the cpu to another thread
+ *
+ * This function places the current thread at the end of the run queue
+ * and yields the cpu to another waiting thread (if any.)
+ *
+ * This function will return at some later time. Possibly immediately if
+ * no other threads are waiting to execute.
+ */
 void thread_yield(void)
 {
 #if THREAD_CHECKS
@@ -323,6 +407,21 @@
 	exit_critical_section();
 }
 
+/**
+ * @brief  Briefly yield cpu to another thread
+ *
+ * This function is similar to thread_yield(), except that it will
+ * restart more quickly.
+ *
+ * This function places the current thread at the head of the run
+ * queue and then yields the cpu to another thread.
+ *
+ * Exception:  If the time slice for this thread has expired, then
+ * the thread goes to the end of the run queue.
+ *
+ * This function will return at some later time. Possibly immediately if
+ * no other threads are waiting to execute.
+ */
 void thread_preempt(void)
 {
 #if THREAD_CHECKS
@@ -348,6 +447,16 @@
 	exit_critical_section();
 }
 
+/**
+ * @brief  Suspend thread until woken.
+ *
+ * This function schedules another thread to execute.  This function does not
+ * return until the thread is made runable again by some other module.
+ *
+ * You probably don't want to call this function directly; it's meant to be called
+ * from other modules, such as mutex, which will presumably set the thread's
+ * state to blocked and add it to some queue or another.
+ */
 void thread_block(void)
 {
 #if THREAD_CHECKS
@@ -391,6 +500,16 @@
 	return INT_RESCHEDULE;
 }
 
+/**
+ * @brief  Put thread to sleep; delay specified in ms
+ *
+ * This function puts the current thread to sleep until the specified
+ * delay in ms has expired.
+ *
+ * Note that this function could sleep for longer than the specified delay if
+ * other threads are running.  When the timer expires, this thread will
+ * be placed at the head of the run queue.
+ */
 void thread_sleep(time_t delay)
 {
 	timer_t timer;
@@ -409,6 +528,11 @@
 	exit_critical_section();
 }
 
+/**
+ * @brief  Initialize threading system
+ *
+ * This function is called once, from kmain()
+ */
 void thread_init_early(void)
 {
 	int i;
@@ -432,15 +556,31 @@
 	current_thread = t;
 }
 
+/**
+ * @brief Complete thread initialization
+ *
+ * This function is called once at boot time
+ */
 void thread_init(void)
 {
+#if PLATFORM_HAS_DYNAMIC_TIMER
+	timer_initialize(&preempt_timer);
+#endif
 }
 
+/**
+ * @brief Change name of current thread
+ */
 void thread_set_name(const char *name)
 {
 	strlcpy(current_thread->name, name, sizeof(current_thread->name));
 }
 
+/**
+ * @brief Change priority of current thread
+ *
+ * See thread_create() for a discussion of priority values.
+ */
 void thread_set_priority(int priority)
 {
 	if (priority < LOWEST_PRIORITY)
@@ -450,6 +590,13 @@
 	current_thread->priority = priority;
 }
 
+/**
+ * @brief  Become an idle thread
+ *
+ * This function marks the current thread as the idle thread -- the one which
+ * executes when there is nothing else to do.  This function does not return.
+ * This function is called once at boot time.
+ */
 void thread_become_idle(void)
 {
 	thread_set_name("idle");
@@ -458,6 +605,9 @@
 	idle_thread_routine();
 }
 
+/**
+ * @brief  Dump debugging info about the specified thread.
+ */
 void dump_thread(thread_t *t)
 {
 	dprintf(INFO, "dump_thread: t %p (%s)\n", t, t->name);
@@ -473,6 +623,9 @@
 	dprintf(INFO, "\n");
 }
 
+/**
+ * @brief  Dump debugging info about all threads
+ */
 void dump_all_threads(void)
 {
 	thread_t *t;
@@ -484,7 +637,17 @@
 	exit_critical_section();
 }
 
-/* wait queue */
+/** @} */
+
+
+/**
+ * @defgroup  wait  Wait Queue
+ * @{
+ */
+
+/**
+ * @brief  Initialize a wait queue
+ */
 void wait_queue_init(wait_queue_t *wait)
 {
 	wait->magic = WAIT_QUEUE_MAGIC;
@@ -506,6 +669,24 @@
 	return INT_NO_RESCHEDULE;
 }
 
+/**
+ * @brief  Block until a wait queue is notified.
+ *
+ * This function puts the current thread at the end of a wait
+ * queue and then blocks until some other thread wakes the queue
+ * up again.
+ *
+ * @param  wait     The wait queue to enter
+ * @param  timeout  The maximum time, in ms, to wait
+ *
+ * If the timeout is zero, this function returns immediately with
+ * ERR_TIMED_OUT.  If the timeout is INFINITE_TIME, this function
+ * waits indefinitely.  Otherwise, this function returns with
+ * ERR_TIMED_OUT at the end of the timeout period.
+ *
+ * @return ERR_TIMED_OUT on timeout, else returns the return
+ * value specified when the queue was woken by wait_queue_wake_one().
+ */
 status_t wait_queue_block(wait_queue_t *wait, time_t timeout)
 {
 	timer_t timer;
@@ -541,6 +722,20 @@
 	return current_thread->wait_queue_block_ret;
 }
 
+/**
+ * @brief  Wake up one thread sleeping on a wait queue
+ *
+ * This function removes one thread (if any) from the head of the wait queue and
+ * makes it executable.  The new thread will be placed at the head of the
+ * run queue.
+ *
+ * @param wait  The wait queue to wake
+ * @param reschedule  If true, the newly-woken thread will run immediately.
+ * @param wait_queue_error  The return value which the new thread will receive
+ * from wait_queue_block().
+ *
+ * @return  The number of threads woken (zero or one)
+ */
 int wait_queue_wake_one(wait_queue_t *wait, bool reschedule, status_t wait_queue_error)
 {
 	thread_t *t;
@@ -578,6 +773,21 @@
 	return ret;
 }
 
+
+/**
+ * @brief  Wake all threads sleeping on a wait queue
+ *
+ * This function removes all threads (if any) from the wait queue and
+ * makes them executable.  The new threads will be placed at the head of the
+ * run queue.
+ *
+ * @param wait  The wait queue to wake
+ * @param reschedule  If true, the newly-woken threads will run immediately.
+ * @param wait_queue_error  The return value which the new thread will receive
+ * from wait_queue_block().
+ *
+ * @return  The number of threads woken (zero or one)
+ */
 int wait_queue_wake_all(wait_queue_t *wait, bool reschedule, status_t wait_queue_error)
 {
 	thread_t *t;
@@ -621,6 +831,11 @@
 	return ret;
 }
 
+/**
+ * @brief  Free all resources allocated in wait_queue_init()
+ *
+ * If any threads were waiting on this queue, they are all woken.
+ */
 void wait_queue_destroy(wait_queue_t *wait, bool reschedule)
 {
 #if THREAD_CHECKS
@@ -631,6 +846,19 @@
 	wait->magic = 0;
 }
 
+/**
+ * @brief  Wake a specific thread in a wait queue
+ *
+ * This function extracts a specific thread from a wait queue, wakes it, and
+ * puts it at the head of the run queue.
+ *
+ * @param t  The thread to wake
+ * @param reschedule  If true, the newly-woken threads will run immediately.
+ * @param wait_queue_error  The return value which the new thread will receive
+ *   from wait_queue_block().
+ *
+ * @return ERR_NOT_BLOCKED if thread was not in any wait queue.
+ */
 status_t thread_unblock_from_wait_queue(thread_t *t, bool reschedule, status_t wait_queue_error)
 {
 	enter_critical_section();
diff --git a/kernel/timer.c b/kernel/timer.c
index 71bc4fb..dd5cf9f 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -20,6 +20,20 @@
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
+
+/**
+ * @file
+ * @brief  Kernel timer subsystem
+ * @defgroup timer Timers
+ *
+ * The timer subsystem allows functions to be scheduled for later
+ * execution.  Each timer object is used to cause one function to
+ * be executed at a later time.
+ *
+ * Timer callback functions are called in interrupt context.
+ *
+ * @{
+ */
 #include <debug.h>
 #include <list.h>
 #include <kernel/thread.h>
@@ -29,6 +43,11 @@
 
 static struct list_node timer_queue;
 
+static enum handler_return timer_tick(void *arg, time_t now);
+
+/**
+ * @brief  Initialize a timer object
+ */
 void timer_initialize(timer_t *timer)
 {
 	timer->magic = TIMER_MAGIC;
@@ -43,8 +62,10 @@
 {
 	timer_t *entry;
 
+//	TRACEF("timer %p, scheduled %d, periodic %d\n", timer, timer->scheduled_time, timer->periodic_time);
+
 	list_for_every_entry(&timer_queue, entry, timer_t, node) {
-		if (entry->scheduled_time > timer->scheduled_time) {
+		if (TIME_GT(entry->scheduled_time, timer->scheduled_time)) {
 			list_add_before(&entry->node, &timer->node);
 			return;
 		}
@@ -54,11 +75,11 @@
 	list_add_tail(&timer_queue, &timer->node);
 }
 
-void timer_set_oneshot(timer_t *timer, time_t delay, timer_callback callback, void *arg)
+static void timer_set(timer_t *timer, time_t delay, time_t period, timer_callback callback, void *arg)
 {
 	time_t now;
 
-//	TRACEF("delay %d, callback %p, arg %p\n", delay, callback, arg);
+//	TRACEF("timer %p, delay %d, period %d, callback %p, arg %p, now %d\n", timer, delay, period, callback, arg);
 
 	DEBUG_ASSERT(timer->magic == TIMER_MAGIC);	
 
@@ -68,7 +89,7 @@
 
 	now = current_time();
 	timer->scheduled_time = now + delay;
-	timer->periodic_time = 0;
+	timer->periodic_time = period;
 	timer->callback = callback;
 	timer->arg = arg;
 
@@ -78,18 +99,102 @@
 
 	insert_timer_in_queue(timer);
 
+#if PLATFORM_HAS_DYNAMIC_TIMER
+	if (list_peek_head_type(&timer_queue, timer_t, node) == timer) {
+		/* we just modified the head of the timer queue */
+//		TRACEF("setting new timer for %u msecs\n", (uint)delay);
+		platform_set_oneshot_timer(timer_tick, NULL, delay);
+	}
+#endif
+
 	exit_critical_section();
 }
 
+/**
+ * @brief  Set up a timer that executes once
+ *
+ * This function specifies a callback function to be called after a specified
+ * delay.  The function will be called one time.
+ *
+ * @param  timer The timer to use
+ * @param  delay The delay, in ms, before the timer is executed
+ * @param  callback  The function to call when the timer expires
+ * @param  arg  The argument to pass to the callback
+ *
+ * The timer function is declared as:
+ *   enum handler_return callback(struct timer *, time_t now, void *arg) { ... }
+ */
+void timer_set_oneshot(timer_t *timer, time_t delay, timer_callback callback, void *arg)
+{
+	if (delay == 0)
+		delay = 1;
+	timer_set(timer, delay, 0, callback, arg);
+}
+
+/**
+ * @brief  Set up a timer that executes repeatedly
+ *
+ * This function specifies a callback function to be called after a specified
+ * delay.  The function will be called repeatedly.
+ *
+ * @param  timer The timer to use
+ * @param  delay The delay, in ms, before the timer is executed
+ * @param  callback  The function to call when the timer expires
+ * @param  arg  The argument to pass to the callback
+ *
+ * The timer function is declared as:
+ *   enum handler_return callback(struct timer *, time_t now, void *arg) { ... }
+ */
+void timer_set_periodic(timer_t *timer, time_t period, timer_callback callback, void *arg)
+{
+	if (period == 0)
+		period = 1;
+	timer_set(timer, period, period, callback, arg);
+}
+
+/**
+ * @brief  Cancel a pending timer
+ */
 void timer_cancel(timer_t *timer)
 {
 	DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
 
 	enter_critical_section();
 
+#if PLATFORM_HAS_DYNAMIC_TIMER
+	timer_t *oldhead = list_peek_head_type(&timer_queue, timer_t, node);
+#endif
+
 	if (list_in_list(&timer->node))
 		list_delete(&timer->node);
 
+	/* to keep it from being reinserted into the queue if called from 
+	 * periodic timer callback.
+	 */
+	timer->periodic_time = 0;
+	timer->callback = NULL;
+	timer->arg = NULL;
+
+#if PLATFORM_HAS_DYNAMIC_TIMER
+	/* see if we've just modified the head of the timer queue */
+	timer_t *newhead = list_peek_head_type(&timer_queue, timer_t, node);
+	if (newhead == NULL) {
+//		TRACEF("clearing old hw timer, nothing in the queue\n");
+		platform_stop_timer();
+	} else if (newhead != oldhead) {
+		time_t delay;
+		time_t now = current_time();
+
+		if (TIME_LT(newhead->scheduled_time, now))
+			delay = 0;
+		else
+			delay = newhead->scheduled_time - now;
+
+//		TRACEF("setting new timer to %d\n", delay);
+		platform_set_oneshot_timer(timer_tick, NULL, delay);
+	}
+#endif
+
 	exit_critical_section();
 }
 
@@ -103,10 +208,12 @@
 	thread_stats.timer_ints++;
 #endif
 
+//	TRACEF("now %d\n", now);
+
 	for (;;) {
 		/* see if there's an event to process */
 		timer = list_peek_head_type(&timer_queue, timer_t, node);
-		if (likely(!timer || now < timer->scheduled_time))
+		if (likely(!timer || TIME_LT(now, timer->scheduled_time)))
 			break;
 
 		/* process it */
@@ -115,19 +222,48 @@
 //		timer = list_remove_head_type(&timer_queue, timer_t, node);
 //		ASSERT(timer);
 
+//		TRACEF("dequeued timer %p, scheduled %d periodic %d\n", timer, timer->scheduled_time, timer->periodic_time);
+
 #if THREAD_STATS
 		thread_stats.timers++;
 #endif
 
-//		TRACEF("firing callback %p, arg %p\n", timer->callback, timer->arg);
+		bool periodic = timer->periodic_time > 0;
+
+//		TRACEF("timer %p firing callback %p, arg %p\n", timer, timer->callback, timer->arg);
 		if (timer->callback(timer, now, timer->arg) == INT_RESCHEDULE)
 			ret = INT_RESCHEDULE;
+
+		/* if it was a periodic timer and it hasn't been requeued
+		 * by the callback put it back in the list
+		 */
+		if (periodic && !list_in_list(&timer->node) && timer->periodic_time > 0) {
+//			TRACEF("periodic timer, period %u\n", (uint)timer->periodic_time);
+			timer->scheduled_time = now + timer->periodic_time;
+			insert_timer_in_queue(timer);
+		}
 	}
 
+#if PLATFORM_HAS_DYNAMIC_TIMER
+	/* reset the timer to the next event */
+	timer = list_peek_head_type(&timer_queue, timer_t, node);
+	if (timer) {
+		/* has to be the case or it would have fired already */
+		ASSERT(TIME_GT(timer->scheduled_time, now));
+
+		time_t delay = timer->scheduled_time - now;
+
+//		TRACEF("setting new timer for %u msecs for event %p\n", (uint)delay, timer);
+		platform_set_oneshot_timer(timer_tick, NULL, delay);
+	}
+#else
 	/* let the scheduler have a shot to do quantum expiration, etc */
+	/* in case of dynamic timer, the scheduler will set up a periodic timer */
 	if (thread_timer_tick() == INT_RESCHEDULE)
 		ret = INT_RESCHEDULE;
+#endif
 
+	// XXX fix this, should return ret
 	return INT_RESCHEDULE;
 }
 
diff --git a/lib/bcache/bcache.c b/lib/bcache/bcache.c
new file mode 100644
index 0000000..8ac5a49
--- /dev/null
+++ b/lib/bcache/bcache.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2007 Travis Geiselbrecht
+ *
+ * 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 <list.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <debug.h>
+#include <lib/bcache.h>
+#include <lib/bio.h>
+
+#define LOCAL_TRACE 0
+
+struct bcache_block {
+	struct list_node node;
+	bnum_t blocknum;
+	int ref_count;
+	bool is_dirty;
+	void *ptr;
+};
+
+struct bcache_stats {
+	uint32_t hits;
+	uint32_t depth;
+	uint32_t misses;
+	uint32_t reads;
+	uint32_t writes;
+};
+
+struct bcache {
+	bdev_t *dev;
+	size_t block_size;
+	int count;
+	struct bcache_stats stats;
+
+	struct list_node free_list;
+	struct list_node lru_list;
+
+	struct bcache_block *blocks;
+};
+
+bcache_t bcache_create(bdev_t *dev, size_t block_size, int block_count)
+{
+	struct bcache *cache;
+
+	cache = malloc(sizeof(struct bcache));
+	
+	cache->dev = dev;
+	cache->block_size = block_size;
+	cache->count = block_count;
+	memset(&cache->stats, 0, sizeof(cache->stats));
+
+	list_initialize(&cache->free_list);
+	list_initialize(&cache->lru_list);
+
+	cache->blocks = malloc(sizeof(struct bcache_block) * block_count);
+	int i;
+	for (i=0; i < block_count; i++) {
+		cache->blocks[i].ref_count = 0;
+		cache->blocks[i].is_dirty = false;
+		cache->blocks[i].ptr = malloc(block_size);
+		// add to the free list
+		list_add_head(&cache->free_list, &cache->blocks[i].node);	
+	}
+
+	return (bcache_t)cache;
+}
+
+static int flush_block(struct bcache *cache, struct bcache_block *block)
+{
+	int rc;
+
+	rc = bio_write(cache->dev, block->ptr,
+			(off_t)block->blocknum * cache->block_size,
+			cache->block_size);
+	if (rc < 0)
+		goto exit;
+
+	block->is_dirty = false;
+	cache->stats.writes++;
+	rc = 0;
+exit:
+	return (rc);
+}
+
+void bcache_destroy(bcache_t _cache)
+{
+	struct bcache *cache = _cache;
+	int i;
+
+	for (i=0; i < cache->count; i++) {
+		DEBUG_ASSERT(cache->blocks[i].ref_count == 0);
+
+		if (cache->blocks[i].is_dirty)
+			printf("warning: freeing dirty block %u\n",
+				cache->blocks[i].blocknum);
+
+		free(cache->blocks[i].ptr);
+	}
+
+	free(cache);
+}
+
+/* find a block if it's already present */
+static struct bcache_block *find_block(struct bcache *cache, uint blocknum)
+{
+	uint32_t depth = 0;
+	struct bcache_block *block;
+
+	LTRACEF("num %u\n", blocknum);
+
+	block = NULL;
+	list_for_every_entry(&cache->lru_list, block, struct bcache_block, node) {
+		LTRACEF("looking at entry %p, num %u\n", block, block->blocknum);
+		depth++;
+
+		if (block->blocknum == blocknum) {
+			list_delete(&block->node);
+			list_add_tail(&cache->lru_list, &block->node);
+			cache->stats.hits++;
+			cache->stats.depth += depth;
+			return block;
+		}
+	}
+
+	cache->stats.misses++;
+	return NULL;
+}
+
+/* allocate a new block */
+static struct bcache_block *alloc_block(struct bcache *cache)
+{
+	int err;
+	struct bcache_block *block;
+
+	/* pop one off the free list if it's present */
+	block = list_remove_head_type(&cache->free_list, struct bcache_block, node);
+	if (block) {
+		block->ref_count = 0;
+		list_add_tail(&cache->lru_list, &block->node);
+		LTRACEF("found block %p on free list\n", block);
+		return block;
+	}
+
+	/* walk the lru, looking for a free block */
+	list_for_every_entry(&cache->lru_list, block, struct bcache_block, node) {
+		LTRACEF("looking at %p, num %u\n", block, block->blocknum);
+		if (block->ref_count == 0) {
+			if (block->is_dirty) {
+				err = flush_block(cache, block);
+				if (err)
+					return NULL;
+			}
+
+			// add it to the tail of the lru
+			list_delete(&block->node);
+			list_add_tail(&cache->lru_list, &block->node);
+			return block;
+		}
+	}
+
+	return NULL;
+}
+
+static struct bcache_block *find_or_fill_block(struct bcache *cache, uint blocknum)
+{
+	int err;
+
+	LTRACEF("block %u\n", blocknum);
+
+	/* see if it's already in the cache */
+	struct bcache_block *block = find_block(cache, blocknum);
+	if (block == NULL) {
+		LTRACEF("wasn't allocated\n");
+
+		/* allocate a new block and fill it */
+		block = alloc_block(cache);
+		DEBUG_ASSERT(block);
+
+		LTRACEF("wasn't allocated, new block %p\n", block);
+
+		block->blocknum = blocknum;
+		err = bio_read(cache->dev, block->ptr, (off_t)blocknum * cache->block_size, cache->block_size);
+		if (err < 0) {
+			/* free the block, return an error */
+			list_add_tail(&cache->free_list, &block->node);
+			return NULL;
+		}
+
+		cache->stats.reads++;
+	}
+
+	DEBUG_ASSERT(block->blocknum == blocknum);
+
+	return block;
+}
+
+int bcache_read_block(bcache_t _cache, void *buf, uint blocknum)
+{
+	struct bcache *cache = _cache;
+
+	LTRACEF("buf %p, blocknum %u\n", buf, blocknum);
+
+	struct bcache_block *block = find_or_fill_block(cache, blocknum);
+	if (block == NULL) {
+		/* error */
+		return -1;
+	}
+
+	memcpy(buf, block->ptr, cache->block_size);
+	return 0;
+}
+
+int bcache_get_block(bcache_t _cache, void **ptr, uint blocknum)
+{
+	struct bcache *cache = _cache;
+
+	LTRACEF("ptr %p, blocknum %u\n", ptr, blocknum);
+
+	DEBUG_ASSERT(ptr);
+
+	struct bcache_block *block = find_or_fill_block(cache, blocknum);
+	if (block == NULL) {
+		/* error */
+		return -1;
+	}
+
+	/* increment the ref count to keep it from being freed */
+	block->ref_count++;
+	*ptr = block->ptr;
+
+	return 0;
+}
+
+int bcache_put_block(bcache_t _cache, uint blocknum)
+{
+	struct bcache *cache = _cache;
+
+	LTRACEF("blocknum %u\n", blocknum);
+
+	struct bcache_block *block = find_block(cache, blocknum);
+
+	/* be pretty hard on the caller for now */
+	DEBUG_ASSERT(block);
+	DEBUG_ASSERT(block->ref_count > 0);
+
+	block->ref_count--;
+
+	return 0;
+}
+
+int bcache_mark_block_dirty(bcache_t priv, uint blocknum)
+{
+	int err;
+	struct bcache *cache = priv;
+	struct bcache_block *block;
+
+	block = find_block(cache, blocknum);
+	if (!block) {
+		err = -1;
+		goto exit;
+	}
+
+	block->is_dirty = true;
+	err = 0;
+exit:
+	return (err);
+}
+
+int bcache_zero_block(bcache_t priv, uint blocknum)
+{
+	int err;
+	struct bcache *cache = priv;
+	struct bcache_block *block;
+
+	block = find_block(cache, blocknum);
+	if (!block) {
+		block = alloc_block(cache);
+		if (!block) {
+			err = -1;
+			goto exit;
+		}
+
+		block->blocknum = blocknum;
+	}
+
+	memset(block->ptr, 0, cache->block_size);
+	block->is_dirty = true;
+	err = 0;
+exit:
+	return (err);
+}
+
+int bcache_flush(bcache_t priv)
+{
+	int err;
+	struct bcache *cache = priv;
+	struct bcache_block *block;
+
+	list_for_every_entry(&cache->lru_list, block, struct bcache_block, node) {
+		if (block->is_dirty) {
+			err = flush_block(cache, block);
+			if (err)
+				goto exit;
+		}
+	}
+
+	err = 0;
+exit:
+	return (err);
+}
+
+void bcache_dump(bcache_t priv, const char *name)
+{
+	uint32_t finds;
+	struct bcache *cache = priv;
+
+	finds = cache->stats.hits + cache->stats.misses;
+
+	printf("%s: hits=%u(%u%%) depth=%u misses=%u(%u%%) reads=%u writes=%u\n",
+		name,
+		cache->stats.hits,
+		finds ? (cache->stats.hits * 100) / finds : 0,
+		cache->stats.hits ? cache->stats.depth / cache->stats.hits : 0,
+		cache->stats.misses,
+		finds ? (cache->stats.misses * 100) / finds : 0,
+		cache->stats.reads,
+		cache->stats.writes);
+}
diff --git a/lib/bcache/rules.mk b/lib/bcache/rules.mk
new file mode 100644
index 0000000..9927f1b
--- /dev/null
+++ b/lib/bcache/rules.mk
@@ -0,0 +1,6 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES += lib/bio
+
+OBJS += \
+	$(LOCAL_DIR)/bcache.o
diff --git a/lib/bio/bio.c b/lib/bio/bio.c
new file mode 100644
index 0000000..3c35400
--- /dev/null
+++ b/lib/bio/bio.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <stdlib.h>
+#include <debug.h>
+#include <err.h>
+#include <string.h>
+#include <list.h>
+#include <lib/bio.h>
+#include <kernel/mutex.h>
+
+#define LOCAL_TRACE 0
+
+struct bdev_struct {
+	struct list_node list;
+	mutex_t lock;
+};
+
+static struct bdev_struct *bdevs;
+
+/* default implementation is to use the read_block hook to 'deblock' the device */
+static ssize_t bio_default_read(struct bdev *dev, void *_buf, off_t offset, size_t len)
+{
+	uint8_t *buf = (uint8_t *)_buf;
+	ssize_t bytes_read = 0;
+	bnum_t block;
+	int err = 0;
+	STACKBUF_DMA_ALIGN(temp, dev->block_size); // temporary buffer for partial block transfers
+
+	/* find the starting block */
+	block = offset / dev->block_size;
+
+	LTRACEF("buf %p, offset %lld, block %u, len %zd\n", buf, offset, block, len);
+	/* handle partial first block */
+	if ((offset % dev->block_size) != 0) {
+		/* read in the block */
+		err = bio_read_block(dev, temp, block, 1);
+		if (err < 0)
+			goto err;
+
+		/* copy what we need */
+		size_t block_offset = offset % dev->block_size;
+		size_t tocopy = MIN(dev->block_size - block_offset, len);
+		memcpy(buf, temp + block_offset, tocopy);
+
+		/* increment our buffers */
+		buf += tocopy;
+		len -= tocopy;
+		bytes_read += tocopy;
+		block++;
+	}
+
+	LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
+	/* handle middle blocks */
+	if (len >= dev->block_size) {
+		/* do the middle reads */
+		size_t block_count = len / dev->block_size;
+		err = bio_read_block(dev, buf, block, block_count);
+		if (err < 0)
+			goto err;
+
+		/* increment our buffers */
+		size_t bytes = block_count * dev->block_size;
+		DEBUG_ASSERT(bytes <= len);
+
+		buf += bytes;
+		len -= bytes;
+		bytes_read += bytes;
+		block += block_count;
+	}
+
+	LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
+	/* handle partial last block */
+	if (len > 0) {
+		/* read the block */
+		err = bio_read_block(dev, temp, block, 1);
+		if (err < 0)
+			goto err;
+
+		/* copy the partial block from our temp buffer */
+		memcpy(buf, temp, len);
+
+		bytes_read += len;
+	}
+
+err:
+	/* return error or bytes read */
+	return (err >= 0) ? bytes_read : err;
+}
+
+static ssize_t bio_default_write(struct bdev *dev, const void *_buf, off_t offset, size_t len)
+{
+	const uint8_t *buf = (const uint8_t *)_buf;
+	ssize_t bytes_written = 0;
+	bnum_t block;
+	int err = 0;
+	STACKBUF_DMA_ALIGN(temp, dev->block_size); // temporary buffer for partial block transfers
+
+	/* find the starting block */
+	block = offset / dev->block_size;
+
+	LTRACEF("buf %p, offset %lld, block %u, len %zd\n", buf, offset, block, len);
+	/* handle partial first block */
+	if ((offset % dev->block_size) != 0) {
+		/* read in the block */
+		err = bio_read_block(dev, temp, block, 1);
+		if (err < 0)
+			goto err;
+
+		/* copy what we need */
+		size_t block_offset = offset % dev->block_size;
+		size_t tocopy = MIN(dev->block_size - block_offset, len);
+		memcpy(temp + block_offset, buf, tocopy);
+
+		/* write it back out */
+		err = bio_write_block(dev, temp, block, 1);
+		if (err < 0)
+			goto err;
+
+		/* increment our buffers */
+		buf += tocopy;
+		len -= tocopy;
+		bytes_written += tocopy;
+		block++;
+	}
+
+	LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
+	/* handle middle blocks */
+	if (len >= dev->block_size) {
+		/* do the middle writes */
+		size_t block_count = len / dev->block_size;
+		err = bio_write_block(dev, buf, block, block_count);
+		if (err < 0)
+			goto err;
+
+		/* increment our buffers */
+		size_t bytes = block_count * dev->block_size;
+		DEBUG_ASSERT(bytes <= len);
+
+		buf += bytes;
+		len -= bytes;
+		bytes_written += bytes;
+		block += block_count;
+	}
+
+	LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
+	/* handle partial last block */
+	if (len > 0) {
+		/* read the block */
+		err = bio_read_block(dev, temp, block, 1);
+		if (err < 0)
+			goto err;
+
+		/* copy the partial block from our temp buffer */
+		memcpy(temp, buf, len);
+
+		/* write it back out */
+		err = bio_write_block(dev, temp, block, 1);
+		if (err < 0)
+			goto err;
+
+		bytes_written += len;
+	}
+
+err:
+	/* return error or bytes written */
+	return (err >= 0) ? bytes_written : err;
+}
+
+static ssize_t bio_default_erase(struct bdev *dev, off_t offset, size_t len)
+{
+	/* default erase operation is to just write zeros over the device */
+#define ERASE_BUF_SIZE 4096
+	uint8_t *zero_buf;
+
+	zero_buf = calloc(1, ERASE_BUF_SIZE);
+
+	size_t remaining = len;
+	off_t pos = offset;
+	while (remaining > 0) {
+		ssize_t towrite = MIN(remaining, ERASE_BUF_SIZE);
+
+		ssize_t written = bio_write(dev, zero_buf, pos, towrite);
+		if (written < 0)
+			return pos;
+
+		pos += written;
+		remaining -= written;
+
+		if (written < towrite)
+			return pos;
+	}
+
+	return len;
+}
+
+static ssize_t bio_default_read_block(struct bdev *dev, void *buf, bnum_t block, uint count)
+{
+	panic("%s no reasonable default operation\n", __PRETTY_FUNCTION__);
+}
+
+static ssize_t bio_default_write_block(struct bdev *dev, const void *buf, bnum_t block, uint count)
+{
+	panic("%s no reasonable default operation\n", __PRETTY_FUNCTION__);
+}
+
+static void bdev_inc_ref(bdev_t *dev)
+{
+	atomic_add(&dev->ref, 1);
+}
+
+static void bdev_dec_ref(bdev_t *dev)
+{
+	int oldval = atomic_add(&dev->ref, -1);
+	if (oldval == 1) {
+		// last ref, remove it
+		DEBUG_ASSERT(!list_in_list(&dev->node));
+
+		TRACEF("last ref, removing (%s)\n", dev->name);
+
+		// call the close hook if it exists
+		if (dev->close)
+			dev->close(dev);
+
+		free(dev->name);
+		free(dev);
+	}
+}
+
+bdev_t *bio_open(const char *name)
+{
+	bdev_t *bdev = NULL;
+
+	/* see if it's in our list */
+	bdev_t *entry;
+	mutex_acquire(&bdevs->lock);
+	list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
+		DEBUG_ASSERT(entry->ref > 0);
+		if (!strcmp(entry->name, name)) {
+			bdev = entry;
+			bdev_inc_ref(bdev);
+			break;
+		}
+	}
+	mutex_release(&bdevs->lock);
+
+	return bdev;
+}
+
+void bio_close(bdev_t *dev)
+{
+	DEBUG_ASSERT(dev);
+
+	bdev_dec_ref(dev);
+}
+
+ssize_t bio_read(bdev_t *dev, void *buf, off_t offset, size_t len)
+{
+	LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset, len);
+
+	DEBUG_ASSERT(dev->ref > 0);	
+
+	/* range check */
+	if (offset < 0)
+		return -1;
+	if (offset >= dev->size)
+		return 0;
+	if (len == 0)
+		return 0;
+	if (offset + len > dev->size)
+		len = dev->size - offset;
+
+	return dev->read(dev, buf, offset, len);
+}
+
+ssize_t bio_read_block(bdev_t *dev, void *buf, bnum_t block, uint count)
+{
+	LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);
+		
+	DEBUG_ASSERT(dev->ref > 0);
+
+	/* range check */
+	if (block > dev->block_count)
+		return 0;
+	if (count == 0)
+		return 0;
+	if (block + count > dev->block_count)
+		count = dev->block_count - block;
+
+	return dev->read_block(dev, buf, block, count);
+}
+
+ssize_t bio_write(bdev_t *dev, const void *buf, off_t offset, size_t len)
+{
+	LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset, len);
+		
+	DEBUG_ASSERT(dev->ref > 0);
+
+	/* range check */
+	if (offset < 0)
+		return -1;
+	if (offset >= dev->size)
+		return 0;
+	if (len == 0)
+		return 0;
+	if (offset + len > dev->size)
+		len = dev->size - offset;
+
+	return dev->write(dev, buf, offset, len);
+}
+
+ssize_t bio_write_block(bdev_t *dev, const void *buf, bnum_t block, uint count)
+{
+	LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);
+
+	DEBUG_ASSERT(dev->ref > 0);
+
+	/* range check */
+	if (block > dev->block_count)
+		return 0;
+	if (count == 0)
+		return 0;
+	if (block + count > dev->block_count)
+		count = dev->block_count - block;
+
+	return dev->write_block(dev, buf, block, count);
+}
+
+ssize_t bio_erase(bdev_t *dev, off_t offset, size_t len)
+{
+	LTRACEF("dev '%s', offset %lld, len %zd\n", dev->name, offset, len);
+		
+	DEBUG_ASSERT(dev->ref > 0);
+
+	/* range check */
+	if (offset < 0)
+		return -1;
+	if (offset >= dev->size)
+		return 0;
+	if (len == 0)
+		return 0;
+	if (offset + len > dev->size)
+		len = dev->size - offset;
+
+	return dev->erase(dev, offset, len);
+}
+
+int bio_ioctl(bdev_t *dev, int request, void *argp)
+{
+	LTRACEF("dev '%s', request %08x, argp %p\n", dev->name, request, argp);
+
+	if (dev->ioctl == NULL) {
+		return ERR_NOT_SUPPORTED;
+	} else {
+		return dev->ioctl(dev, request, argp);
+	}
+}
+
+void bio_initialize_bdev(bdev_t *dev, const char *name, size_t block_size, bnum_t block_count)
+{
+	DEBUG_ASSERT(dev);
+	DEBUG_ASSERT(name);
+	DEBUG_ASSERT(block_size == 512); // XXX can only deal with 512 for now
+
+	list_clear_node(&dev->node);
+	dev->name = strdup(name);
+	dev->block_size = block_size;
+	dev->block_count = block_count;
+	dev->size = (off_t)block_count * block_size;
+	dev->ref = 0;
+
+	/* set up the default hooks, the sub driver should override the block operations at least */
+	dev->read = bio_default_read;
+	dev->read_block = bio_default_read_block;
+	dev->write = bio_default_write;
+	dev->write_block = bio_default_write_block;
+	dev->erase = bio_default_erase;
+	dev->close = NULL;
+}
+
+void bio_register_device(bdev_t *dev)
+{
+	DEBUG_ASSERT(dev);
+
+	LTRACEF(" '%s'\n", dev->name);
+
+	bdev_inc_ref(dev);
+
+	mutex_acquire(&bdevs->lock);
+	list_add_head(&bdevs->list, &dev->node);
+	mutex_release(&bdevs->lock);
+}
+
+void bio_unregister_device(bdev_t *dev)
+{
+	DEBUG_ASSERT(dev);
+
+	LTRACEF(" '%s'\n", dev->name);
+
+	// remove it from the list
+	mutex_acquire(&bdevs->lock);
+	list_delete(&dev->node);
+	mutex_release(&bdevs->lock);
+
+	bdev_dec_ref(dev); // remove the ref the list used to have
+}
+
+void bio_dump_devices(void)
+{
+	printf("block devices:\n");
+	bdev_t *entry;
+	mutex_acquire(&bdevs->lock);
+	list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
+		printf("\t%s, size %lld, bsize %zd, ref %d\n", entry->name, entry->size, entry->block_size, entry->ref);
+	}
+	mutex_release(&bdevs->lock);
+}
+
+void bio_init(void)
+{
+	bdevs = malloc(sizeof(*bdevs));
+
+	list_initialize(&bdevs->list);
+	mutex_init(&bdevs->lock);
+}
+
diff --git a/lib/bio/debug.c b/lib/bio/debug.c
new file mode 100644
index 0000000..90bfc11
--- /dev/null
+++ b/lib/bio/debug.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <debug.h>
+#include <stdlib.h>
+#include <string.h>
+#include <lib/console.h>
+#include <lib/bio.h>
+#include <lib/partition.h>
+#include <platform.h>
+
+#if defined(WITH_LIB_CONSOLE)
+
+#if DEBUGLEVEL > 0
+static int cmd_bio(int argc, const cmd_args *argv);
+
+STATIC_COMMAND_START
+STATIC_COMMAND("bio", "block io debug commands", &cmd_bio)
+STATIC_COMMAND_END(bio);
+
+static int cmd_bio(int argc, const cmd_args *argv)
+{
+	int rc = 0;
+
+	if (argc < 2) {
+		printf("not enough arguments:\n");
+usage:
+		printf("%s list\n", argv[0].str);
+		printf("%s read <device> <address> <offset> <len>\n", argv[0].str);
+		printf("%s write <device> <address> <offset> <len>\n", argv[0].str);
+		printf("%s erase <device> <offset> <len>\n", argv[0].str);
+		printf("%s ioctl <device> <request> <arg>\n", argv[0].str);
+		printf("%s remove <device>\n", argv[0].str);
+#if WITH_LIB_PARTITION
+		printf("%s partscan <device> [offset]\n", argv[0].str);
+#endif
+		return -1;
+	}
+
+	if (!strcmp(argv[1].str, "list")) {
+		bio_dump_devices();
+	} else if (!strcmp(argv[1].str, "read")) {
+		if (argc < 6) {
+			printf("not enough arguments:\n");
+			goto usage;
+		}
+
+		addr_t address = argv[3].u;
+		off_t offset = argv[4].u; // XXX use long
+		size_t len = argv[5].u;
+
+		bdev_t *dev = bio_open(argv[2].str);
+		if (!dev) {
+			printf("error opening block device\n");
+			return -1;
+		}
+
+		time_t t = current_time();
+		ssize_t err = bio_read(dev, (void *)address, offset, len);
+		t = current_time() - t;
+		dprintf(INFO, "bio_read returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t));
+
+		bio_close(dev);
+
+		rc = err;
+	} else if (!strcmp(argv[1].str, "write")) {
+		if (argc < 6) {
+			printf("not enough arguments:\n");
+			goto usage;
+		}
+
+		addr_t address = argv[3].u;
+		off_t offset = argv[4].u; // XXX use long
+		size_t len = argv[5].u;
+
+		bdev_t *dev = bio_open(argv[2].str);
+		if (!dev) {
+			printf("error opening block device\n");
+			return -1;
+		}
+
+		time_t t = current_time();
+		ssize_t err = bio_write(dev, (void *)address, offset, len);
+		t = current_time() - t;
+		dprintf(INFO, "bio_write returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t));
+
+		bio_close(dev);
+
+		rc = err;
+	} else if (!strcmp(argv[1].str, "erase")) {
+		if (argc < 5) {
+			printf("not enough arguments:\n");
+			goto usage;
+		}
+
+		off_t offset = argv[3].u; // XXX use long
+		size_t len = argv[4].u;
+
+		bdev_t *dev = bio_open(argv[2].str);
+		if (!dev) {
+			printf("error opening block device\n");
+			return -1;
+		}
+
+		time_t t = current_time();
+		ssize_t err = bio_erase(dev, offset, len);
+		t = current_time() - t;
+		dprintf(INFO, "bio_erase returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t));
+
+		bio_close(dev);
+
+		rc = err;
+	} else if (!strcmp(argv[1].str, "ioctl")) {
+		if (argc < 4) {
+			printf("not enough arguments:\n");
+			goto usage;
+		}
+
+		int request = argv[3].u;
+		int arg = (argc == 5) ? argv[4].u : 0;
+
+		bdev_t *dev = bio_open(argv[2].str);
+		if (!dev) {
+			printf("error opening block device\n");
+			return -1;
+		}
+
+		int err = bio_ioctl(dev, request, (void *)arg);
+		dprintf(INFO, "bio_ioctl returns %d\n", err);
+
+		bio_close(dev);
+
+		rc = err;
+	} else if (!strcmp(argv[1].str, "remove")) {
+		if (argc < 3) {
+			printf("not enough arguments:\n");
+			goto usage;
+		}
+
+		bdev_t *dev = bio_open(argv[2].str);
+		if (!dev) {
+			printf("error opening block device\n");
+			return -1;
+		}
+
+		bio_unregister_device(dev);
+		bio_close(dev);
+#if WITH_LIB_PARTITION
+	} else if (!strcmp(argv[1].str, "partscan")) {
+		if (argc < 3) {
+			printf("not enough arguments:\n");
+			goto usage;
+		}
+
+		off_t offset = 0;
+		if (argc > 3)
+			offset = argv[3].u;
+
+		rc = partition_publish(argv[2].str, offset);
+		dprintf(INFO, "partition_publish returns %d\n", rc);
+#endif
+	} else {
+		printf("unrecognized subcommand\n");
+		goto usage;
+	}
+
+	return rc;
+}
+
+#endif
+
+#endif
+
diff --git a/lib/bio/mem.c b/lib/bio/mem.c
new file mode 100644
index 0000000..9ea2f4d
--- /dev/null
+++ b/lib/bio/mem.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <debug.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lib/bio.h>
+
+#define LOCAL_TRACE 0
+
+#define BLOCKSIZE 512
+
+typedef struct mem_bdev {
+	bdev_t dev; // base device
+
+	void *ptr;
+} mem_bdev_t;
+
+static ssize_t mem_bdev_read(bdev_t *bdev, void *buf, off_t offset, size_t len)
+{
+	mem_bdev_t *mem = (mem_bdev_t *)bdev;
+
+	LTRACEF("bdev %s, buf %p, offset %lld, len %zu\n", bdev->name, buf, offset, len);
+
+	memcpy(buf, (uint8_t *)mem->ptr + offset, len);
+
+	return len;
+}
+
+static ssize_t mem_bdev_read_block(struct bdev *bdev, void *buf, bnum_t block, uint count)
+{
+	mem_bdev_t *mem = (mem_bdev_t *)bdev;
+
+	LTRACEF("bdev %s, buf %p, block %u, count %u\n", bdev->name, buf, block, count);
+
+	memcpy(buf, (uint8_t *)mem->ptr + block * BLOCKSIZE, count * BLOCKSIZE);
+
+	return count * BLOCKSIZE;
+}
+
+static ssize_t mem_bdev_write(bdev_t *bdev, const void *buf, off_t offset, size_t len)
+{
+	mem_bdev_t *mem = (mem_bdev_t *)bdev;
+
+	LTRACEF("bdev %s, buf %p, offset %lld, len %zu\n", bdev->name, buf, offset, len);
+
+	memcpy((uint8_t *)mem->ptr + offset, buf, len);
+
+	return len;
+}
+
+static ssize_t mem_bdev_write_block(struct bdev *bdev, const void *buf, bnum_t block, uint count)
+{
+	mem_bdev_t *mem = (mem_bdev_t *)bdev;
+
+	LTRACEF("bdev %s, buf %p, block %u, count %u\n", bdev->name, buf, block, count);
+
+	memcpy((uint8_t *)mem->ptr + block * BLOCKSIZE, buf, count * BLOCKSIZE);
+
+	return count * BLOCKSIZE;
+}
+
+int create_membdev(const char *name, void *ptr, size_t len)
+{
+	mem_bdev_t *mem = malloc(sizeof(mem_bdev_t));
+
+	/* set up the base device */
+	bio_initialize_bdev(&mem->dev, name, BLOCKSIZE, len / BLOCKSIZE);
+
+	/* our bits */
+	mem->ptr = ptr;
+	mem->dev.read = mem_bdev_read;
+	mem->dev.read_block = mem_bdev_read_block;
+	mem->dev.write = mem_bdev_write;
+	mem->dev.write_block = mem_bdev_write_block;
+
+	/* register it */
+	bio_register_device(&mem->dev);
+
+	return 0;
+}
+
diff --git a/lib/bio/rules.mk b/lib/bio/rules.mk
new file mode 100644
index 0000000..aaeb026
--- /dev/null
+++ b/lib/bio/rules.mk
@@ -0,0 +1,9 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES +=
+
+OBJS += \
+	$(LOCAL_DIR)/bio.o \
+	$(LOCAL_DIR)/debug.o \
+	$(LOCAL_DIR)/mem.o \
+	$(LOCAL_DIR)/subdev.o 
diff --git a/lib/bio/subdev.c b/lib/bio/subdev.c
new file mode 100644
index 0000000..3512f02
--- /dev/null
+++ b/lib/bio/subdev.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <debug.h>
+#include <stdlib.h>
+#include <lib/bio.h>
+
+#define LOCAL_TRACE 0
+
+typedef struct {
+	// inheirit the usual bits
+	bdev_t dev;
+
+	// we're a subdevice of this
+	bdev_t *parent;
+
+	// we're this many blocks into it
+	bnum_t offset;
+} subdev_t;
+
+static ssize_t subdev_read(struct bdev *_dev, void *buf, off_t offset, size_t len)
+{
+	subdev_t *subdev = (subdev_t *)_dev;
+
+	return bio_read(subdev->parent, buf, offset + subdev->offset * subdev->dev.block_size, len);
+}
+
+static ssize_t subdev_read_block(struct bdev *_dev, void *buf, bnum_t block, uint count)
+{
+	subdev_t *subdev = (subdev_t *)_dev;
+
+	return bio_read_block(subdev->parent, buf, block + subdev->offset, count);
+}
+
+static ssize_t subdev_write(struct bdev *_dev, const void *buf, off_t offset, size_t len)
+{
+	subdev_t *subdev = (subdev_t *)_dev;
+
+	return bio_write(subdev->parent, buf, offset + subdev->offset * subdev->dev.block_size, len);
+}
+
+static ssize_t subdev_write_block(struct bdev *_dev, const void *buf, bnum_t block, uint count)
+{
+	subdev_t *subdev = (subdev_t *)_dev;
+
+	return bio_write_block(subdev->parent, buf, block + subdev->offset, count);
+}
+
+static ssize_t subdev_erase(struct bdev *_dev, off_t offset, size_t len)
+{
+	subdev_t *subdev = (subdev_t *)_dev;
+
+	return bio_erase(subdev->parent, offset + subdev->offset * subdev->dev.block_size, len);
+}
+
+static void subdev_close(struct bdev *_dev)
+{
+	subdev_t *subdev = (subdev_t *)_dev;
+
+	bio_close(subdev->parent);
+	subdev->parent = NULL;
+}
+
+status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, size_t len)
+{
+	LTRACEF("parent %s, sub %s, startblock %u, len %zd\n", parent_dev, subdev, startblock, len);
+
+	bdev_t *parent = bio_open(parent_dev);
+	if (!parent)
+		return -1;
+
+	/* make sure we're able to do this */
+	if (startblock + len > parent->block_count)
+		return -1;
+
+	subdev_t *sub = malloc(sizeof(subdev_t));
+	bio_initialize_bdev(&sub->dev, subdev, parent->block_size, len);
+
+	sub->parent = parent;
+	sub->offset = startblock;
+
+	sub->dev.read = &subdev_read;
+	sub->dev.read_block = &subdev_read_block;
+	sub->dev.write = &subdev_write;
+	sub->dev.write_block = &subdev_write_block;
+	sub->dev.erase = &subdev_erase;
+	sub->dev.close = &subdev_close;
+
+	bio_register_device(&sub->dev);
+
+	return 0;
+}
+
diff --git a/lib/cbuf/cbuf.c b/lib/cbuf/cbuf.c
new file mode 100644
index 0000000..4c31295
--- /dev/null
+++ b/lib/cbuf/cbuf.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * 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 <stdlib.h>
+#include <debug.h>
+#include <pow2.h>
+#include <string.h>
+#include <lib/cbuf.h>
+#include <kernel/event.h>
+
+#define LOCAL_TRACE 0
+
+#define INC_POINTER(cbuf, ptr, inc) \
+	modpow2(((ptr) + (inc)), (cbuf)->len_pow2)
+
+void cbuf_initialize(cbuf_t *cbuf, size_t len)
+{
+	DEBUG_ASSERT(cbuf);
+	DEBUG_ASSERT(len > 0);
+	DEBUG_ASSERT(ispow2(len));
+
+	cbuf->head = 0;
+	cbuf->tail = 0;
+	cbuf->len_pow2 = log2(len);
+	cbuf->buf = malloc(len);
+	event_init(&cbuf->event, false, 0);
+
+	LTRACEF("len %zd, len_pow2 %u\n", len, cbuf->len_pow2);
+}
+
+static size_t cbuf_space_avail(cbuf_t *cbuf)
+{
+	return (cbuf->head + valpow2(cbuf->len_pow2) - cbuf->tail - 1);
+}
+
+size_t cbuf_write(cbuf_t *cbuf, const void *_buf, size_t len, bool canreschedule)
+{
+	const char *buf = (const char *)_buf;
+
+	LTRACEF("len %zd\n", len);
+
+	DEBUG_ASSERT(cbuf);
+	DEBUG_ASSERT(_buf);
+	DEBUG_ASSERT(len < valpow2(cbuf->len_pow2));
+
+	enter_critical_section();
+
+	size_t write_len;
+	size_t pos = 0;
+
+	while (pos < len && cbuf_space_avail(cbuf) > 0) {
+		if (cbuf->head >= cbuf->tail) {
+			write_len = MIN(valpow2(cbuf->len_pow2) - cbuf->head, len - pos);
+		} else {
+			write_len = MIN(cbuf->tail - cbuf->head - 1, len - pos);
+		}
+
+		// if it's full, abort and return how much we've written
+		if (write_len == 0) {
+			break;
+		}
+
+		memcpy(cbuf->buf + cbuf->head, buf + pos, write_len);
+
+		cbuf->head = INC_POINTER(cbuf, cbuf->head, write_len);
+		pos += write_len;
+	}
+
+	if (cbuf->head != cbuf->tail)
+		event_signal(&cbuf->event, canreschedule);
+
+	exit_critical_section();
+
+	return pos;
+}
+
+size_t cbuf_read(cbuf_t *cbuf, void *_buf, size_t buflen, bool block)
+{
+	size_t ret;
+	char *buf = (char *)_buf;
+
+	DEBUG_ASSERT(cbuf);
+	DEBUG_ASSERT(_buf);
+
+	enter_critical_section();
+
+	if (block)
+		event_wait(&cbuf->event);
+
+	// see if there's data available
+	if (cbuf->tail != cbuf->head) {
+		size_t pos = 0;
+
+		// loop until we've read everything we need
+		// at most this will make two passes to deal with wraparound
+		while (pos < buflen && cbuf->tail != cbuf->head) {
+			size_t read_len;
+			if (cbuf->head > cbuf->tail) {
+				// simple case where there is no wraparound
+				read_len = MIN(cbuf->head - cbuf->tail, buflen - pos);
+			} else {
+				// read to the end of buffer in this pass
+				read_len = MIN(valpow2(cbuf->len_pow2) - cbuf->tail, buflen - pos);
+			}
+
+			memcpy(buf + pos, cbuf->buf + cbuf->tail, read_len);
+
+			cbuf->tail = INC_POINTER(cbuf, cbuf->tail, read_len);
+			pos += read_len;
+		}
+
+		if (cbuf->tail == cbuf->head) {
+			// we've emptied the buffer, unsignal the event
+			event_unsignal(&cbuf->event);
+		}
+
+		ret = pos;
+	} else {
+		DEBUG_ASSERT(!block);
+		ret = 0;
+	}
+
+	exit_critical_section();
+
+	return ret;
+}
+
diff --git a/lib/cbuf/rules.mk b/lib/cbuf/rules.mk
new file mode 100644
index 0000000..2c57d48
--- /dev/null
+++ b/lib/cbuf/rules.mk
@@ -0,0 +1,4 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+OBJS += \
+	$(LOCAL_DIR)/cbuf.o
diff --git a/lib/font/font.c b/lib/font/font.c
new file mode 100644
index 0000000..2caa8ac
--- /dev/null
+++ b/lib/font/font.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ * @brief  Font display
+ *
+ * This file contains functions to render fonts onto the graphics drawing
+ * surface.
+ *
+ * @ingroup graphics
+ */
+
+#include <debug.h>
+#include <lib/gfx.h>
+#include <lib/font.h>
+
+#include "font.h"
+
+/**
+ * @brief Draw one character from the built-in font
+ *
+ * @ingroup graphics
+ */
+void font_draw_char(gfx_surface *surface, unsigned char c, int x, int y, uint32_t color)
+{
+	uint i,j;
+	uint line;
+
+	// draw this char into a buffer
+	for (i = 0; i < FONT_Y; i++) {
+		line = FONT[c * FONT_Y + i];
+		for (j = 0; j < FONT_X; j++) {
+			if (line & 0x1)
+				gfx_putpixel(surface, x + j, y + i, color);
+			line = line >> 1;
+		}
+	}
+	gfx_flush_rows(surface, y, y + FONT_Y);
+}
+
+
diff --git a/lib/font/font.h b/lib/font/font.h
new file mode 100644
index 0000000..4a5a15f
--- /dev/null
+++ b/lib/font/font.h
@@ -0,0 +1,136 @@
+#ifndef __FONT_H
+#define __FONT_H
+
+static const unsigned char FONT[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
+0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, /* '!' */
+0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '"' */
+0x00, 0x00, 0x14, 0x14, 0x3e, 0x14, 0x3e, 0x14, 0x14, 0x00, 0x00, 0x00, /* '#' */
+0x00, 0x00, 0x08, 0x3c, 0x0a, 0x1c, 0x28, 0x1e, 0x08, 0x00, 0x00, 0x00, /* '$' */
+0x00, 0x00, 0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30, 0x00, 0x00, 0x00, /* '%' */
+0x00, 0x00, 0x1c, 0x02, 0x02, 0x04, 0x2a, 0x12, 0x2c, 0x00, 0x00, 0x00, /* '&' */
+0x00, 0x18, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ''' */
+0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x00, /* '(' */
+0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x02, 0x00, /* ')' */
+0x00, 0x00, 0x00, 0x08, 0x2a, 0x1c, 0x2a, 0x08, 0x00, 0x00, 0x00, 0x00, /* '*' */
+0x00, 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* '+' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x04, 0x00, /* ',' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '-' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* '.' */
+0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, /* '/' */
+0x00, 0x1c, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* '0' */
+0x00, 0x08, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, /* '1' */
+0x00, 0x1c, 0x22, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x00, 0x00, 0x00, /* '2' */
+0x00, 0x1c, 0x22, 0x20, 0x18, 0x20, 0x20, 0x22, 0x1c, 0x00, 0x00, 0x00, /* '3' */
+0x00, 0x10, 0x18, 0x18, 0x14, 0x14, 0x3e, 0x10, 0x38, 0x00, 0x00, 0x00, /* '4' */
+0x00, 0x3e, 0x02, 0x02, 0x1e, 0x20, 0x20, 0x22, 0x1c, 0x00, 0x00, 0x00, /* '5' */
+0x00, 0x18, 0x04, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* '6' */
+0x00, 0x3e, 0x22, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x00, 0x00, 0x00, /* '7' */
+0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* '8' */
+0x00, 0x1c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x10, 0x0c, 0x00, 0x00, 0x00, /* '9' */
+0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* ':' */
+0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x08, 0x04, 0x00, /* ';' */
+0x00, 0x00, 0x00, 0x30, 0x0c, 0x03, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, /* '<' */
+0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, /* '=' */
+0x00, 0x00, 0x00, 0x03, 0x0c, 0x30, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, /* '>' */
+0x00, 0x1c, 0x22, 0x20, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, /* '?' */
+0x00, 0x00, 0x1c, 0x22, 0x3a, 0x3a, 0x1a, 0x02, 0x1c, 0x00, 0x00, 0x00, /* '@' */
+0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x00, 0x00, 0x00, /* 'A' */
+0x00, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e, 0x00, 0x00, 0x00, /* 'B' */
+0x00, 0x00, 0x1c, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1c, 0x00, 0x00, 0x00, /* 'C' */
+0x00, 0x00, 0x0e, 0x12, 0x22, 0x22, 0x22, 0x12, 0x0e, 0x00, 0x00, 0x00, /* 'D' */
+0x00, 0x00, 0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x3e, 0x00, 0x00, 0x00, /* 'E' */
+0x00, 0x00, 0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, /* 'F' */
+0x00, 0x00, 0x1c, 0x22, 0x02, 0x32, 0x22, 0x22, 0x3c, 0x00, 0x00, 0x00, /* 'G' */
+0x00, 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, /* 'H' */
+0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, /* 'I' */
+0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* 'J' */
+0x00, 0x00, 0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22, 0x00, 0x00, 0x00, /* 'K' */
+0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e, 0x00, 0x00, 0x00, /* 'L' */
+0x00, 0x00, 0x22, 0x36, 0x2a, 0x2a, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, /* 'M' */
+0x00, 0x00, 0x22, 0x26, 0x26, 0x2a, 0x32, 0x32, 0x22, 0x00, 0x00, 0x00, /* 'N' */
+0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* 'O' */
+0x00, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, /* 'P' */
+0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x30, 0x00, 0x00, /* 'Q' */
+0x00, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x0a, 0x12, 0x22, 0x00, 0x00, 0x00, /* 'R' */
+0x00, 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c, 0x00, 0x00, 0x00, /* 'S' */
+0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, /* 'T' */
+0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* 'U' */
+0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, 0x00, /* 'V' */
+0x00, 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22, 0x00, 0x00, 0x00, /* 'W' */
+0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, 0x00, /* 'X' */
+0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, /* 'Y' */
+0x00, 0x00, 0x3e, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x00, 0x00, 0x00, /* 'Z' */
+0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, /* '[' */
+0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, /* '\' */
+0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e, 0x00, /* ']' */
+0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '^' */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, /* '_' */
+0x00, 0x0c, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '`' */
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x32, 0x2c, 0x00, 0x00, 0x00, /* 'a' */
+0x00, 0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x00, 0x00, 0x00, /* 'b' */
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, /* 'c' */
+0x00, 0x20, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x00, 0x00, 0x00, /* 'd' */
+0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x3e, 0x02, 0x1c, 0x00, 0x00, 0x00, /* 'e' */
+0x00, 0x38, 0x04, 0x04, 0x1e, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, /* 'f' */
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x1c, /* 'g' */
+0x00, 0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, /* 'h' */
+0x00, 0x08, 0x08, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, /* 'i' */
+0x00, 0x10, 0x10, 0x00, 0x1c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e, /* 'j' */
+0x00, 0x02, 0x02, 0x02, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x00, 0x00, 0x00, /* 'k' */
+0x00, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, /* 'l' */
+0x00, 0x00, 0x00, 0x00, 0x16, 0x2a, 0x2a, 0x2a, 0x22, 0x00, 0x00, 0x00, /* 'm' */
+0x00, 0x00, 0x00, 0x00, 0x1a, 0x26, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, /* 'n' */
+0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, /* 'o' */
+0x00, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02, /* 'p' */
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x20, /* 'q' */
+0x00, 0x00, 0x00, 0x00, 0x1a, 0x06, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, /* 'r' */
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x1c, 0x20, 0x1e, 0x00, 0x00, 0x00, /* 's' */
+0x00, 0x08, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, 0x00, /* 't' */
+0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2c, 0x00, 0x00, 0x00, /* 'u' */
+0x00, 0x00, 0x00, 0x00, 0x36, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, 0x00, /* 'v' */
+0x00, 0x00, 0x00, 0x00, 0x22, 0x2a, 0x2a, 0x2a, 0x14, 0x00, 0x00, 0x00, /* 'w' */
+0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, /* 'x' */
+0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x1c, /* 'y' */
+0x00, 0x00, 0x00, 0x00, 0x3e, 0x10, 0x08, 0x04, 0x3e, 0x00, 0x00, 0x00, /* 'z' */
+0x20, 0x10, 0x10, 0x10, 0x10, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00, /* '{' */
+0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, /* '|' */
+0x02, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x02, 0x00, /* '}' */
+0x00, 0x04, 0x2a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '~' */
+0x00, 0x00, 0x00, 0x08, 0x08, 0x14, 0x14, 0x22, 0x3e, 0x00, 0x00, 0x00, /* '' */
+};
+
+#endif
+
diff --git a/lib/font/rules.mk b/lib/font/rules.mk
new file mode 100644
index 0000000..71678d1
--- /dev/null
+++ b/lib/font/rules.mk
@@ -0,0 +1,6 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES += lib/gfx
+
+OBJS += \
+	$(LOCAL_DIR)/font.o
diff --git a/lib/fs/debug.c b/lib/fs/debug.c
new file mode 100644
index 0000000..6d9b433
--- /dev/null
+++ b/lib/fs/debug.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <debug.h>
+#include <string.h>
+#include <lib/console.h>
+#include <lib/fs.h>
+#include <stdlib.h>
+#include <platform.h>
+
+#if defined(WITH_LIB_CONSOLE)
+
+#if DEBUGLEVEL > 1
+static int cmd_fs(int argc, const cmd_args *argv);
+
+STATIC_COMMAND_START
+STATIC_COMMAND("fs", "fs debug commands", &cmd_fs)
+STATIC_COMMAND_END(fs);
+
+extern int fs_mount_type(const char *path, const char *device, const char *name);
+extern int fs_create_file(const char *path, filecookie *fcookie);
+extern int fs_make_dir(const char *path);
+extern int fs_write_file(filecookie fcookie, const void *buf, off_t offset, size_t len);
+
+static int cmd_fs(int argc, const cmd_args *argv)
+{
+	int rc = 0;
+
+	if (argc < 2) {
+notenoughargs:
+		printf("not enough arguments:\n");
+usage:
+		printf("%s mount <path> <device> [<type>]\n", argv[0].str);
+		printf("%s unmount <path>\n", argv[0].str);
+		printf("%s create <path>\n", argv[0].str);
+		printf("%s mkdir <path>\n", argv[0].str);
+		printf("%s read <path> [<offset>] [<len>]\n", argv[0].str);
+		printf("%s write <path> <string> [<offset>]\n", argv[0].str);
+		printf("%s stat <file>\n", argv[0].str);
+		return -1;
+	}
+
+	if (!strcmp(argv[1].str, "mount")) {
+		int err;
+
+		if (argc < 4)
+			goto notenoughargs;
+
+		if (argc < 5)
+			err = fs_mount(argv[2].str, argv[3].str);
+
+		else
+			err = fs_mount_type(argv[2].str, argv[3].str, argv[4].str);
+
+		if (err < 0) {
+			printf("error %d mounting device\n", err);
+			return err;
+		}
+	} else if (!strcmp(argv[1].str, "unmount")) {
+		int err;
+
+		if (argc < 3)
+			goto notenoughargs;
+
+		err = fs_unmount(argv[2].str);
+		if (err < 0) {
+			printf("error %d unmounting device\n", err);
+			return err;
+		}
+	} else if (!strcmp(argv[1].str, "create")) {
+		int err;
+		filecookie cookie;
+
+		if (argc < 3)
+			goto notenoughargs;
+
+		err = fs_create_file(argv[2].str, &cookie);
+		if (err < 0) {
+			printf("error %d creating file\n", err);
+			return err;
+		}
+
+		fs_close_file(cookie);
+	} else if (!strcmp(argv[1].str, "mkdir")) {
+		int err;
+
+		if (argc < 3)
+			goto notenoughargs;
+
+		err = fs_make_dir(argv[2].str);
+		if (err < 0) {
+			printf("error %d making directory\n", err);
+			return err;
+		}
+	} else if (!strcmp(argv[1].str, "read")) {
+		int err;
+		char *buf;
+		off_t off;
+		size_t len;
+		filecookie cookie;
+		struct file_stat stat;
+
+		if (argc < 3)
+			goto notenoughargs;
+
+		err = fs_open_file(argv[2].str, &cookie);
+		if (err < 0) {
+			printf("error %d opening file\n", err);
+			return err;
+		}
+
+		err = fs_stat_file(cookie, &stat);
+		if (err < 0) {
+			printf("error %d stat'ing file\n", err);
+			fs_close_file(cookie);
+			return err;
+		}
+
+		if (argc < 4)
+			off = 0;
+
+		else
+			off = argv[3].u;
+
+		if (argc < 5)
+			len = stat.size - off;
+
+		else
+			len = argv[4].u;
+
+		buf = malloc(len + 1);
+
+		err = fs_read_file(cookie, buf, off, len);
+		if (err < 0) {
+			printf("error %d reading file\n", err);
+			free(buf);
+			fs_close_file(cookie);
+			return err;
+		}
+
+		buf[len] = '\0';
+		printf("%s\n", buf);
+		free(buf);
+		fs_close_file(cookie);
+	} else if (!strcmp(argv[1].str, "write")) {
+		int err;
+		off_t off;
+		filecookie cookie;
+		struct file_stat stat;
+
+		if (argc < 3)
+			goto notenoughargs;
+
+		err = fs_open_file(argv[2].str, &cookie);
+		if (err < 0) {
+			printf("error %d opening file\n", err);
+			return err;
+		}
+
+		err = fs_stat_file(cookie, &stat);
+		if (err < 0) {
+			printf("error %d stat'ing file\n", err);
+			fs_close_file(cookie);
+			return err;
+		}
+
+		if (argc < 5)
+			off = stat.size;
+
+		else
+			off = argv[4].u;
+
+		err = fs_write_file(cookie, argv[3].str, off, strlen(argv[3].str));
+		if (err < 0) {
+			printf("error %d writing file\n", err);
+			fs_close_file(cookie);
+			return err;
+		}
+
+		fs_close_file(cookie);
+	} else if (!strcmp(argv[1].str, "stat")) {
+		int err;
+		struct file_stat stat;
+		filecookie cookie;
+
+		if (argc < 3)
+			goto notenoughargs;
+
+		err = fs_open_file(argv[2].str, &cookie);
+		if (err < 0) {
+			printf("error %d opening file\n", err);
+			return err;
+		}
+
+		err = fs_stat_file(cookie, &stat);
+		if (err < 0) {
+			printf("error %d statting file\n", err);
+			fs_close_file(cookie);
+			return err;
+		}
+
+		printf("stat successful:\n");		
+		printf("\tis_dir: %d\n", stat.is_dir ? 1 : 0);
+		printf("\tsize: %lld\n", stat.size);
+
+		fs_close_file(cookie);
+	} else {
+		printf("unrecognized subcommand\n");
+		goto usage;
+	}
+
+	return rc;
+}
+
+#endif
+
+#endif
+
diff --git a/lib/fs/fs.c b/lib/fs/fs.c
new file mode 100644
index 0000000..8389a9d
--- /dev/null
+++ b/lib/fs/fs.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <debug.h>
+#include <list.h>
+#include <err.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lib/fs.h>
+#include <lib/bio.h>
+
+#if WITH_LIB_FS_EXT2
+#include <lib/fs/ext2.h>
+#endif
+#if WITH_LIB_FS_FAT32
+#include <lib/fs/fat32.h>
+#endif
+
+#define LOCAL_TRACE 0
+
+struct fs_type {
+	const char *name;
+	int (*mount)(bdev_t *, fscookie *);
+	int (*unmount)(fscookie);
+	int (*open)(fscookie, const char *, filecookie *);
+	int (*create)(fscookie, const char *, filecookie *);
+	int (*mkdir)(fscookie, const char *);
+	int (*stat)(filecookie, struct file_stat *);
+	int (*read)(filecookie, void *, off_t, size_t);
+	int (*write)(filecookie, const void *, off_t, size_t);
+	int (*close)(filecookie);
+};
+
+struct fs_mount {
+	struct list_node node;
+	char *path;
+	bdev_t *dev;
+	fscookie cookie;
+	int refs;
+	struct fs_type *type;
+};
+
+struct fs_file {
+	filecookie cookie;
+	struct fs_mount *mount;
+};
+
+static struct list_node mounts;
+
+static struct fs_type types[] = {
+#if WITH_LIB_FS_EXT2
+	{
+		.name = "ext2",
+		.mount = ext2_mount,
+		.unmount = ext2_unmount,
+		.open = ext2_open_file,
+		.stat = ext2_stat_file,
+		.read = ext2_read_file,
+		.close = ext2_close_file,
+	},
+#endif
+#if WITH_LIB_FS_FAT32
+	{
+		.name = "fat32",
+		.mount = fat32_mount,
+		.unmount = fat32_unmount,
+		.open = fat32_open_file,
+		.create = fat32_create_file,
+		.mkdir = fat32_make_dir,
+		.stat = fat32_stat_file,
+		.read = fat32_read_file,
+		.write = fat32_write_file,
+		.close = fat32_close_file,
+	},
+#endif
+};
+
+static void test_normalize(const char *in);
+static struct fs_mount *find_mount(const char *path, const char **trimmed_path);
+
+void fs_init(void)
+{
+	list_initialize(&mounts);
+#if 0
+	test_normalize("/");
+	test_normalize("/test");
+	test_normalize("/test/");
+	test_normalize("test/");
+	test_normalize("test");
+	test_normalize("/test//");
+	test_normalize("/test/foo");
+	test_normalize("/test/foo/");
+	test_normalize("/test/foo/bar");
+	test_normalize("/test/foo/bar//");
+	test_normalize("/test//foo/bar//");
+	test_normalize("/test//./foo/bar//");
+	test_normalize("/test//./.foo/bar//");
+	test_normalize("/test//./..foo/bar//");
+	test_normalize("/test//./../foo/bar//");
+	test_normalize("/test/../foo");
+	test_normalize("/test/bar/../foo");
+	test_normalize("../foo");
+	test_normalize("../foo/");
+	test_normalize("/../foo");
+	test_normalize("/../foo/");
+	test_normalize("/../../foo");
+	test_normalize("/bleh/../../foo");
+	test_normalize("/bleh/bar/../../foo");
+	test_normalize("/bleh/bar/../../foo/..");
+	test_normalize("/bleh/bar/../../foo/../meh");
+#endif
+}
+
+static struct fs_mount *find_mount(const char *path, const char **trimmed_path)
+{
+	struct fs_mount *mount;
+	size_t pathlen = strlen(path);
+
+	list_for_every_entry(&mounts, mount, struct fs_mount, node) {
+		size_t mountpathlen = strlen(mount->path);
+		if (pathlen < mountpathlen)
+			continue;
+
+		LTRACEF("comparing %s with %s\n", path, mount->path);
+
+		if (memcmp(path, mount->path, mountpathlen) == 0) {
+			if (trimmed_path)
+				*trimmed_path = &path[mountpathlen];
+
+			return mount;
+		}
+	}
+
+	return NULL;
+}
+
+static int mount(const char *path, const char *device, struct fs_type *type)
+{
+	char temppath[512];
+
+	strlcpy(temppath, path, sizeof(temppath));
+	fs_normalize_path(temppath);
+
+	if(temppath[0] != '/')
+		return ERR_BAD_PATH;
+
+	if (find_mount(temppath, NULL))
+		return ERR_ALREADY_MOUNTED;
+
+	bdev_t *dev = bio_open(device);
+	if (!dev)
+		return ERR_NOT_FOUND;
+
+	fscookie cookie;
+	int err = type->mount(dev, &cookie);
+	if (err < 0) {
+		bio_close(dev);
+		return err;
+	}
+
+	/* create the mount structure and add it to the list */
+	struct fs_mount *mount = malloc(sizeof(struct fs_mount));
+	mount->path = strdup(temppath);
+	mount->dev = dev;
+	mount->cookie = cookie;
+	mount->refs = 1;
+	mount->type = type;
+
+	list_add_head(&mounts, &mount->node);
+
+	return 0;
+}
+
+int fs_mount(const char *path, const char *device)
+{
+	return mount(path, device, &types[0]);
+}
+
+int fs_mount_type(const char *path, const char *device, const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < countof(types); i++) {
+		if (!strcmp(name, types[i].name))
+			return mount(path, device, &types[i]);
+	}
+
+	return ERR_NOT_FOUND;
+}
+
+static void put_mount(struct fs_mount *mount)
+{
+	if (!(--mount->refs)) {
+		list_delete(&mount->node);
+		mount->type->unmount(mount->cookie);
+		free(mount->path);
+		bio_close(mount->dev);
+		free(mount);
+	}
+}
+
+int fs_unmount(const char *path)
+{
+	char temppath[512];
+
+	strlcpy(temppath, path, sizeof(temppath));
+	fs_normalize_path(temppath);
+
+	struct fs_mount *mount = find_mount(temppath, NULL);
+	if (!mount)
+		return ERR_NOT_FOUND;
+
+	put_mount(mount);
+
+	return 0;
+}
+
+
+int fs_open_file(const char *path, filecookie *fcookie)
+{
+	int err;
+	char temppath[512];
+	filecookie cookie;
+
+	strlcpy(temppath, path, sizeof(temppath));
+	fs_normalize_path(temppath);
+
+	LTRACEF("path %s temppath %s\n", path, temppath);
+
+	const char *newpath;
+	struct fs_mount *mount = find_mount(temppath, &newpath);
+	if (!mount)
+		return ERR_NOT_FOUND;
+
+	LTRACEF("path %s temppath %s newpath %s\n", path, temppath, newpath);
+
+	err = mount->type->open(mount->cookie, newpath, &cookie);
+	if (err < 0)
+		return err;
+
+	struct fs_file *f = malloc(sizeof(*f));
+	f->cookie = cookie;
+	f->mount = mount;
+	mount->refs++;
+	*fcookie = f;
+
+	return 0;
+}
+
+int fs_create_file(const char *path, filecookie *fcookie)
+{
+	int err;
+	char temppath[512];
+	filecookie cookie;
+
+	strlcpy(temppath, path, sizeof(temppath));
+	fs_normalize_path(temppath);
+
+	const char *newpath;
+	struct fs_mount *mount = find_mount(temppath, &newpath);
+	if (!mount)
+		return ERR_NOT_FOUND;
+
+	if (!mount->type->create)
+		return ERR_NOT_SUPPORTED;
+
+	err = mount->type->create(mount->cookie, newpath, &cookie);
+	if (err < 0)
+		return err;
+
+	struct fs_file *f = malloc(sizeof(*f));
+	f->cookie = cookie;
+	f->mount = mount;
+	mount->refs++;
+	*fcookie = f;
+
+	return 0;
+}
+
+int fs_make_dir(const char *path)
+{
+	char temppath[512];
+
+	strlcpy(temppath, path, sizeof(temppath));
+	fs_normalize_path(temppath);
+
+	const char *newpath;
+	struct fs_mount *mount = find_mount(temppath, &newpath);
+	if (!mount)
+		return ERR_NOT_FOUND;
+
+	if (!mount->type->mkdir)
+		return ERR_NOT_SUPPORTED;
+
+	return mount->type->mkdir(mount->cookie, newpath);
+}
+
+int fs_read_file(filecookie fcookie, void *buf, off_t offset, size_t len)
+{
+	struct fs_file *f = fcookie;
+
+	return f->mount->type->read(f->cookie, buf, offset, len);
+}
+
+int fs_write_file(filecookie fcookie, const void *buf, off_t offset, size_t len)
+{
+	struct fs_file *f = fcookie;
+
+	if (!f->mount->type->write)
+		return ERR_NOT_SUPPORTED;
+
+	return f->mount->type->write(f->cookie, buf, offset, len);
+}
+
+int fs_close_file(filecookie fcookie)
+{
+	int err;
+	struct fs_file *f = fcookie;
+
+	err = f->mount->type->close(f->cookie);
+	if (err < 0)
+		return err;
+
+	put_mount(f->mount);
+	free(f);
+	return 0;
+}
+
+int fs_stat_file(filecookie fcookie, struct file_stat *stat)
+{
+	struct fs_file *f = fcookie;
+
+	return f->mount->type->stat(f->cookie, stat);
+}
+
+ssize_t fs_load_file(const char *path, void *ptr, size_t maxlen)
+{
+	int err;
+	filecookie cookie;
+
+	/* open the file */
+	err = fs_open_file(path, &cookie);
+	if (err < 0)
+		return err;
+
+	/* stat it for size, see how much we need to read */
+	struct file_stat stat;
+	fs_stat_file(cookie, &stat);
+
+	err = fs_read_file(cookie, ptr, 0, MIN(maxlen, stat.size));
+
+	fs_close_file(cookie);
+
+	return err;
+}
+
+static void test_normalize(const char *in)
+{
+	char path[1024];
+
+	strlcpy(path, in, sizeof(path));
+	fs_normalize_path(path);
+	printf("'%s' -> '%s'\n", in, path);
+}
+
+void fs_normalize_path(char *path)
+{
+	int outpos;
+	int pos;
+	char c;
+	bool done;
+	enum {
+		INITIAL,
+		FIELD_START,
+		IN_FIELD,
+		SEP,
+		SEEN_SEP,
+		DOT,
+		SEEN_DOT,
+		DOTDOT,
+		SEEN_DOTDOT,
+	} state;
+
+	state = INITIAL;
+	pos = 0;
+	outpos = 0;
+	done = false;
+
+	/* remove duplicate path seperators, flatten empty fields (only composed of .), backtrack fields with .., remove trailing slashes */
+	while (!done) {
+		c = path[pos];
+		switch (state) {
+			case INITIAL:
+				if (c == '/') {
+					state = SEP;
+				} else if (c == '.') {
+					state = DOT;
+				} else {
+					state = FIELD_START;
+				}
+				break;
+			case FIELD_START:
+				if (c == '.') {
+					state = DOT;
+				} else if (c == 0) {
+					done = true;
+				} else {
+					state = IN_FIELD;
+				}
+				break;
+			case IN_FIELD:
+				if (c == '/') {
+					state = SEP;
+				} else if (c == 0) {
+					done = true;
+				} else {
+					path[outpos++] = c;
+					pos++;
+				}
+				break;
+			case SEP:
+				pos++;
+				path[outpos++] = '/';
+				state = SEEN_SEP;
+				break;
+			case SEEN_SEP:
+				if (c == '/') {
+					// eat it
+					pos++;
+				} else if (c == 0) {
+					done = true;
+				} else {
+					state = FIELD_START;
+				}
+				break;
+			case DOT:
+				pos++; // consume the dot
+				state = SEEN_DOT;
+				break;
+			case SEEN_DOT:
+				if (c == '.') {
+					// dotdot now
+					state = DOTDOT;
+				} else if (c == '/') {
+					// a field composed entirely of a .
+					// consume the / and move directly to the SEEN_SEP state
+					pos++;
+					state = SEEN_SEP;
+				} else if (c == 0) {
+					done = true;
+				} else {
+					// a field prefixed with a .
+					// emit a . and move directly into the IN_FIELD state
+					path[outpos++] = '.';
+					state = IN_FIELD;
+				}
+				break;
+			case DOTDOT:
+				pos++; // consume the dot
+				state = SEEN_DOTDOT;
+				break;
+			case SEEN_DOTDOT:
+				if (c == '/' || c == 0) {
+					// a field composed entirely of '..'
+					// search back and consume a field we've already emitted
+					if (outpos > 0) {
+						// we have already consumed at least one field
+						outpos--;
+
+						// walk backwards until we find the next field boundary
+						while (outpos > 0) {
+							if (path[outpos - 1] == '/') {
+								break;
+							}
+							outpos--;
+						}
+					}
+					pos++;
+					state = SEEN_SEP;
+					if (c == 0)
+						done = true;
+				} else {
+					// a field prefixed with ..
+					// emit the .. and move directly to the IN_FIELD state
+					path[outpos++] = '.';
+					path[outpos++] = '.';
+					state = IN_FIELD;
+				}
+				break;
+		}
+	}
+
+	/* dont end with trailing slashes */
+	if (outpos > 0 && path[outpos - 1] == '/')
+		outpos--;
+
+	path[outpos++] = 0;
+}
+
diff --git a/lib/fs/rules.mk b/lib/fs/rules.mk
new file mode 100644
index 0000000..c1aa7f4
--- /dev/null
+++ b/lib/fs/rules.mk
@@ -0,0 +1,9 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES += \
+
+#	lib/fs/ext2
+
+OBJS += \
+	$(LOCAL_DIR)/fs.o \
+	$(LOCAL_DIR)/debug.o
diff --git a/lib/gfx/gfx.c b/lib/gfx/gfx.c
new file mode 100644
index 0000000..9296be4
--- /dev/null
+++ b/lib/gfx/gfx.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup graphics Graphics
+ *
+ * @{
+ */
+
+/**
+ * @file
+ * @brief  Graphics drawing library
+ */
+
+#include <debug.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arch/ops.h>
+#include <sys/types.h>
+#include <lib/gfx.h>
+#include <dev/display.h>
+
+#define LOCAL_TRACE 0
+
+static uint16_t ARGB8888_to_RGB565(uint32_t in)
+{
+	uint16_t out;
+
+	out = (in >> 3) & 0x1f;    // b
+	out |= ((in >> 10) & 0x3f) << 5;  // g
+	out |= ((in >> 19) & 0x1f) << 11;  // r
+
+	return out;
+}
+
+/**
+ * @brief  Copy a rectangle of pixels from one part of the display to another.
+ */
+void gfx_copyrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
+{
+	// trim
+	if (x >= surface->width)
+		return;
+	if (x2 >= surface->width)
+		return;
+	if (y >= surface->height)
+		return;
+	if (y2 >= surface->height)
+		return;
+	if (width == 0 || height == 0)
+		return;
+
+	// clip the width to src or dest
+	if (x + width > surface->width)
+		width = surface->width - x;
+	if (x2 + width > surface->width)
+		width = surface->width - x2;
+
+	// clip the height to src or dest
+	if (y + height > surface->height)
+		height = surface->height - y;
+	if (y2 + height > surface->height)
+		height = surface->height - y2;
+
+	surface->copyrect(surface, x, y, width, height, x2, y2);
+}
+
+/**
+ * @brief  Fill a rectangle on the screen with a constant color.
+ */
+void gfx_fillrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
+{
+	LTRACEF("surface %p, x %u y %u w %u h %u c %u\n", surface, x, y, width, height, color);
+	// trim
+	if (unlikely(x >= surface->width))
+		return;
+	if (y >= surface->height)
+		return;
+	if (width == 0 || height == 0)
+		return;
+
+	// clip the width
+	if (x + width > surface->width)
+		width = surface->width - x;
+
+	// clip the height
+	if (y + height > surface->height)
+		height = surface->height - y;
+
+	surface->fillrect(surface, x, y, width, height, color);
+}
+
+/**
+ * @brief  Write a single pixel to the screen.
+ */
+void gfx_putpixel(gfx_surface *surface, uint x, uint y, uint color)
+{
+	if (unlikely(x >= surface->width))
+		return;
+	if (y >= surface->height)
+		return;
+
+	surface->putpixel(surface, x, y, color);
+}
+
+static void putpixel16(gfx_surface *surface, uint x, uint y, uint color)
+{
+	uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride];
+
+	// colors come in in ARGB 8888 form, flatten them
+	*dest = ARGB8888_to_RGB565(color);
+}
+
+static void putpixel32(gfx_surface *surface, uint x, uint y, uint color)
+{
+	uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride];
+
+	*dest = color;
+}
+
+static void copyrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
+{
+	// copy
+	const uint16_t *src = &((const uint16_t *)surface->ptr)[x + y * surface->stride];
+	uint16_t *dest = &((uint16_t *)surface->ptr)[x2 + y2 * surface->stride];
+	uint stride_diff = surface->stride - width;
+
+	if (dest < src) {
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				*dest = *src;
+				dest++;
+				src++;
+			}
+			dest += stride_diff;
+			src += stride_diff;
+		}
+	} else {
+		// copy backwards
+		src += height * surface->stride + width;
+		dest += height * surface->stride + width;
+
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				*dest = *src;
+				dest--;
+				src--;
+			}
+			dest -= stride_diff;
+			src -= stride_diff;
+		}
+	}
+}
+
+static void fillrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
+{
+	uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride];
+	uint stride_diff = surface->stride - width;
+
+	uint16_t color16 = ARGB8888_to_RGB565(color);
+	
+	uint i, j;
+	for (i=0; i < height; i++) {
+		for (j=0; j < width; j++) {
+			*dest = color16;
+			dest++;
+		}
+		dest += stride_diff;
+	}
+}
+
+static void copyrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
+{
+	// copy
+	const uint32_t *src = &((const uint32_t *)surface->ptr)[x + y * surface->stride];
+	uint32_t *dest = &((uint32_t *)surface->ptr)[x2 + y2 * surface->stride];
+	uint stride_diff = surface->stride - width;
+
+	if (dest < src) {
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				*dest = *src;
+				dest++;
+				src++;
+			}
+			dest += stride_diff;
+			src += stride_diff;
+		}
+	} else {
+		// copy backwards
+		src += height * surface->stride + width;
+		dest += height * surface->stride + width;
+
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				*dest = *src;
+				dest--;
+				src--;
+			}
+			dest -= stride_diff;
+			src -= stride_diff;
+		}
+	}
+}
+
+static void fillrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
+{
+	uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride];
+	uint stride_diff = surface->stride - width;
+	
+	uint i, j;
+	for (i=0; i < height; i++) {
+		for (j=0; j < width; j++) {
+			*dest = color;
+			dest++;
+		}
+		dest += stride_diff;
+	}
+}
+
+uint32_t alpha32_add_ignore_destalpha(uint32_t dest, uint32_t src)
+{
+	uint32_t cdest[3];
+	uint32_t csrc[3];
+
+	uint32_t srca;
+	uint32_t srcainv;
+
+	srca = (src >> 24) & 0xff;
+	if (srca == 0) {
+		return dest;
+	} else if (srca == 255) {
+		return src;
+	}
+	srca++;
+	srcainv = (255 - srca);
+
+	cdest[0] = (dest >> 16) & 0xff;
+	cdest[1] = (dest >> 8) & 0xff;
+	cdest[2] = (dest >> 0) & 0xff;
+
+	csrc[0] = (src >> 16) & 0xff;
+	csrc[1] = (src >> 8) & 0xff;
+	csrc[2] = (src >> 0) & 0xff;
+
+//	if (srca > 0)
+//		printf("s %d %d %d d %d %d %d a %d ai %d\n", csrc[0], csrc[1], csrc[2], cdest[0], cdest[1], cdest[2], srca, srcainv);
+
+	uint32_t cres[3];
+
+	cres[0] = ((csrc[0] * srca) / 256) + ((cdest[0] * srcainv) / 256);
+	cres[1] = ((csrc[1] * srca) / 256) + ((cdest[1] * srcainv) / 256);
+	cres[2] = ((csrc[2] * srca) / 256) + ((cdest[2] * srcainv) / 256);
+
+	return (srca << 24) | (cres[0] << 16) | (cres[1] << 8) | (cres[2]);
+}
+
+/**
+ * @brief  Copy pixels from source to dest.
+ *
+ * Currently does not support alpha channel.
+ */
+void gfx_surface_blend(struct gfx_surface *target, struct gfx_surface *source, uint destx, uint desty)
+{
+	DEBUG_ASSERT(target->format == source->format);
+
+	LTRACEF("target %p, source %p, destx %u, desty %u\n", target, source, destx, desty);
+
+	if (destx >= target->width)
+		return;
+	if (desty >= target->height)
+		return;
+
+	uint width = source->width;
+	if (destx + width > target->width)
+		width = target->width - destx;
+
+	uint height = source->height;
+	if (desty + height > target->height)
+		height = target->height - desty;
+
+	// XXX total hack to deal with various blends
+	if (source->format == GFX_FORMAT_RGB_565 && target->format == GFX_FORMAT_RGB_565) {
+		// 16 bit to 16 bit
+		const uint16_t *src = (const uint16_t *)source->ptr;
+		uint16_t *dest = &((uint16_t *)target->ptr)[destx + desty * target->stride];
+		uint dest_stride_diff = target->stride - width;
+		uint source_stride_diff = source->stride - width;
+
+		LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
+
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				*dest = *src;
+				dest++;
+				src++;
+			}
+			dest += dest_stride_diff;
+			src += source_stride_diff;
+		}
+	} else if (source->format == GFX_FORMAT_ARGB_8888 && target->format == GFX_FORMAT_ARGB_8888) {
+		// both are 32 bit modes, both alpha
+		const uint32_t *src = (const uint32_t *)source->ptr;
+		uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride];
+		uint dest_stride_diff = target->stride - width;
+		uint source_stride_diff = source->stride - width;
+
+		LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
+
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				// XXX ignores destination alpha
+				*dest = alpha32_add_ignore_destalpha(*dest, *src);
+				dest++;
+				src++;
+			}
+			dest += dest_stride_diff;
+			src += source_stride_diff;
+		}
+	} else if (source->format == GFX_FORMAT_RGB_x888 && target->format == GFX_FORMAT_RGB_x888) {
+		// both are 32 bit modes, no alpha
+		const uint32_t *src = (const uint32_t *)source->ptr;
+		uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride];
+		uint dest_stride_diff = target->stride - width;
+		uint source_stride_diff = source->stride - width;
+
+		LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
+
+		uint i, j;
+		for (i=0; i < height; i++) {
+			for (j=0; j < width; j++) {
+				*dest = *src;
+				dest++;
+				src++;
+			}
+			dest += dest_stride_diff;
+			src += source_stride_diff;
+		}
+	} else {
+		panic("gfx_surface_blend: unimplemented colorspace combination (source %d target %d)\n", source->format, target->format);
+	}
+}
+
+/**
+ * @brief  Ensure all graphics rendering is sent to display
+ */
+void gfx_flush(gfx_surface *surface)
+{
+	arch_clean_cache_range((addr_t)surface->ptr, surface->len);
+
+	if (surface->flush)
+		surface->flush(0, surface->height-1);
+}
+
+/**
+ * @brief  Ensure that a sub-region of the display is up to date.
+ */
+void gfx_flush_rows(struct gfx_surface *surface, uint start, uint end)
+{
+	if (start > end) {
+		uint temp = start;
+		start = end;
+		end = temp;
+	}
+
+	if (start >= surface->height)
+		return;
+	if (end >= surface->height)
+		end = surface->height - 1;
+
+	arch_clean_cache_range((addr_t)surface->ptr + start * surface->stride * surface->pixelsize, (end - start + 1) * surface->stride * surface->pixelsize);
+
+	if (surface->flush)
+		surface->flush(start, end);
+
+}
+
+
+/**
+ * @brief  Create a new graphics surface object
+ */
+gfx_surface *gfx_create_surface(void *ptr, uint width, uint height, uint stride, gfx_format format)
+{
+	DEBUG_ASSERT(width > 0);
+	DEBUG_ASSERT(height > 0);
+	DEBUG_ASSERT(stride >= width);
+	DEBUG_ASSERT(format < GFX_FORMAT_MAX);
+
+	gfx_surface *surface = malloc(sizeof(gfx_surface));
+
+	surface->free_on_destroy = false;
+	surface->format = format;
+	surface->width = width;
+	surface->height = height;
+	surface->stride = stride;
+	surface->alpha = MAX_ALPHA;
+
+	// set up some function pointers
+	switch (format) {
+		case GFX_FORMAT_RGB_565:
+			surface->copyrect = &copyrect16;
+			surface->fillrect = &fillrect16;
+			surface->putpixel = &putpixel16;
+			surface->pixelsize = 2;
+			surface->len = surface->height * surface->stride * surface->pixelsize;
+			break;
+		case GFX_FORMAT_RGB_x888:
+		case GFX_FORMAT_ARGB_8888:
+			surface->copyrect = &copyrect32;
+			surface->fillrect = &fillrect32;
+			surface->putpixel = &putpixel32;
+			surface->pixelsize = 4;
+			surface->len = surface->height * surface->stride * surface->pixelsize;
+			break;
+		default:
+			dprintf(INFO, "invalid graphics format\n");
+			DEBUG_ASSERT(0);
+			free(surface);
+			return NULL;
+	}
+
+	if (ptr == NULL) {
+		// allocate a buffer
+		ptr = malloc(surface->len);
+		DEBUG_ASSERT(ptr);
+		surface->free_on_destroy = true;
+	}
+	surface->ptr = ptr;
+
+	return surface;
+}
+
+/**
+ * @brief  Create a new graphics surface object from a display
+ */
+gfx_surface *gfx_create_surface_from_display(struct display_info *info)
+{
+	gfx_surface* surface;
+	surface = gfx_create_surface(info->framebuffer, info->width, info->height, info->stride, info->format);
+
+	surface->flush = info->flush;
+
+	return surface;
+}
+
+/**
+ * @brief  Destroy a graphics surface and free all resources allocated to it.
+ *
+ * @param  surface  Surface to destroy.  This pointer is no longer valid after
+ * 		this call.
+ */
+void gfx_surface_destroy(struct gfx_surface *surface)
+{
+	if (surface->free_on_destroy)
+		free(surface->ptr);
+	free(surface);
+}
+
+/**
+ * @brief  Write a test pattern to the default display.
+ */
+void gfx_draw_pattern(void)
+{
+	struct display_info info;
+	display_get_info(&info);
+
+	gfx_surface *surface = gfx_create_surface_from_display(&info);
+
+	uint x, y;
+	for (y = 0; y < surface->height; y++) {
+		for (x = 0; x < surface->width; x++) {
+			uint scaledx;
+			uint scaledy;
+
+			scaledx = x * 256 / surface->width;
+			scaledy = y * 256 / surface->height;
+
+			gfx_putpixel(surface, x, y, (scaledx * scaledy) << 16 | (scaledx >> 1) << 8 | scaledy >> 1);
+		}
+	}
+
+	if (surface->flush)
+		surface->flush(0, surface->height-1);
+
+	gfx_surface_destroy(surface);
+}
+
+/**
+ * @brief  Fill default display with white
+ */
+void gfx_draw_pattern_white(void)
+{
+	struct display_info info;
+	display_get_info(&info);
+
+	gfx_surface *surface = gfx_create_surface_from_display(&info);
+
+	uint x, y;
+	for (y = 0; y < surface->height; y++) {
+		for (x = 0; x < surface->width; x++) {
+			gfx_putpixel(surface, x, y, 0xFFFFFF);
+		}
+	}
+
+	if (surface->flush)
+		surface->flush(0, surface->height-1);
+
+	gfx_surface_destroy(surface);
+}
+
+#if defined(WITH_LIB_CONSOLE)
+
+#if DEBUGLEVEL > 1
+#include <lib/console.h>
+
+static int cmd_gfx(int argc, const cmd_args *argv);
+
+STATIC_COMMAND_START
+STATIC_COMMAND("gfx", "gfx commands", &cmd_gfx)
+STATIC_COMMAND_END(gfx);
+
+static int gfx_draw_rgb_bars(gfx_surface *surface)
+{
+	uint x, y;
+
+	int step32 = surface->height*100 / 32;
+	int step64 = surface->height*100 / 64;
+	int color;
+
+	for (y = 0; y < surface->height; y++) {
+		//R
+		for (x = 0; x < surface->width/3; x++) {
+			color = y*100 / step32;
+			gfx_putpixel(surface, x, y, color << 16);
+		}
+		//G
+		for (;x < 2*(surface->width/3); x++) {
+			color = y*100 / step64;
+			gfx_putpixel(surface, x, y, color << 8);
+		}
+		//B
+		for (;x < surface->width; x++) {
+			color = y*100 / step32;
+			gfx_putpixel(surface, x, y, color);
+		}
+	}
+
+	return 0;
+}
+
+
+static int cmd_gfx(int argc, const cmd_args *argv)
+{
+	if (argc < 2) {
+		printf("not enough arguments:\n");
+usage:
+		printf("%s rgb_bars		: Fill frame buffer with rgb bars\n", argv[0].str);
+		printf("%s fill r g b	: Fill frame buffer with RGB565 value and force update\n", argv[0].str);
+
+		return -1;
+	}
+
+	struct display_info info;
+	display_get_info(&info);
+
+	gfx_surface *surface = gfx_create_surface_from_display(&info);
+
+	if (!strcmp(argv[1].str, "rgb_bars"))
+	{
+		gfx_draw_rgb_bars(surface);
+	}
+	else if (!strcmp(argv[1].str, "fill"))
+	{
+		uint x, y;
+
+		for (y = 0; y < surface->height; y++)
+		{
+			for (x = 0; x < surface->width; x++)
+			{
+				/* write pixel to frame buffer */
+				gfx_putpixel(surface, x, y, (argv[2].i << 16) | (argv[3].i << 8) | argv[4].i);
+			}
+		}
+	}
+
+	 if (surface->flush)
+	 		surface->flush(0, surface->height-1);
+
+	 gfx_surface_destroy(surface);
+
+	 return 0;
+}
+
+#endif
+#endif
diff --git a/lib/gfx/rules.mk b/lib/gfx/rules.mk
new file mode 100644
index 0000000..9527360
--- /dev/null
+++ b/lib/gfx/rules.mk
@@ -0,0 +1,4 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+OBJS += \
+	$(LOCAL_DIR)/gfx.o
diff --git a/lib/gfxconsole/gfxconsole.c b/lib/gfxconsole/gfxconsole.c
new file mode 100644
index 0000000..8f1fb13
--- /dev/null
+++ b/lib/gfxconsole/gfxconsole.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ * @brief  Manage graphics console
+ *
+ * This file contains functions to provide stdout to the graphics console.
+ *
+ * @ingroup graphics
+ */
+
+#include <debug.h>
+#include <lib/gfx.h>
+#include <lib/gfxconsole.h>
+#include <lib/font.h>
+#include <dev/display.h>
+
+/** @addtogroup graphics
+ * @{
+ */
+
+/**
+ * @brief  Represent state of graphics console
+ */
+static struct {
+	gfx_surface *surface;
+	uint rows, columns;
+	uint extray; // extra pixels left over if the rows doesn't fit precisely
+
+	uint x, y;
+
+	uint32_t front_color;
+	uint32_t back_color;
+} gfxconsole;
+
+static void gfxconsole_putc(char c)
+{
+	static enum { NORMAL, ESCAPE } state = NORMAL;
+	static uint32_t p_num = 0;
+
+	switch (state) {
+		case NORMAL:
+		{
+			if(c == '\n' || c == '\r') {
+				gfxconsole.x = 0;
+				gfxconsole.y++;
+			} else if (c == 0x1b) {
+				p_num = 0;
+				state = ESCAPE;
+			} else {
+				font_draw_char(gfxconsole.surface, c, gfxconsole.x * FONT_X, gfxconsole.y * FONT_Y, gfxconsole.front_color);
+				gfxconsole.x++;
+			}
+			break;
+		}
+
+		case ESCAPE:
+		{
+			if (c >= '0' && c <= '9') {
+				p_num = (p_num * 10) + (c - '0');
+			} else if (c == 'D') {
+				if (p_num <= gfxconsole.x)
+					gfxconsole.x -= p_num;
+				state = NORMAL;
+			} else if (c == '[') {
+				// eat this character
+			} else {
+				font_draw_char(gfxconsole.surface, c, gfxconsole.x * FONT_X, gfxconsole.y * FONT_Y, gfxconsole.front_color);
+				gfxconsole.x++;
+				state = NORMAL;
+			}
+			break;
+		}
+	}
+
+	if(gfxconsole.x >= gfxconsole.columns) {
+		gfxconsole.x = 0;
+		gfxconsole.y++;
+	}
+	if(gfxconsole.y >= gfxconsole.rows) {
+		// scroll up
+		gfx_copyrect(gfxconsole.surface, 0, FONT_Y, gfxconsole.surface->width, gfxconsole.surface->height - FONT_Y - gfxconsole.extray, 0, 0);
+		gfxconsole.y--;
+		gfx_fillrect(gfxconsole.surface, 0, gfxconsole.surface->height - FONT_Y - gfxconsole.extray, gfxconsole.surface->width, FONT_Y, gfxconsole.back_color);
+		gfx_flush(gfxconsole.surface);
+	}
+}
+
+/**
+ * @brief  Initialize graphics console on given drawing surface.
+ *
+ * The graphics console subsystem is initialized, and registered as
+ * an output device for debug output.
+ */
+void gfxconsole_start(gfx_surface *surface)
+{
+	DEBUG_ASSERT(gfxconsole.surface == NULL);
+
+	// set up the surface
+	gfxconsole.surface = surface;
+
+	// calculate how many rows/columns we have
+	gfxconsole.rows = surface->height / FONT_Y;
+	gfxconsole.columns = surface->width / FONT_X;
+	gfxconsole.extray = surface->height - (gfxconsole.rows * FONT_Y);
+
+	dprintf(SPEW, "gfxconsole: rows %d, columns %d, extray %d\n", gfxconsole.rows, gfxconsole.columns, gfxconsole.extray);
+
+	// start in the upper left
+	gfxconsole.x = 0;
+	gfxconsole.y = 0;
+
+	// colors are white and black for now
+	gfxconsole.front_color = 0xffffffff;
+	gfxconsole.back_color = 0;
+
+	// register for debug callbacks
+	register_debug_output(&gfxconsole_putc);
+}
+
+/**
+ * @brief  Initialize graphics console on default display
+ */
+void gfxconsole_start_on_display(void)
+{
+	static bool started = false;
+
+	if (started)
+		return;
+
+	/* pop up the console */
+	struct display_info info;
+	display_get_info(&info);
+	gfx_surface *s = gfx_create_surface_from_display(&info);
+	gfxconsole_start(s);
+	started = true;
+}
+
diff --git a/lib/gfxconsole/rules.mk b/lib/gfxconsole/rules.mk
new file mode 100644
index 0000000..a61382b
--- /dev/null
+++ b/lib/gfxconsole/rules.mk
@@ -0,0 +1,7 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES += lib/gfx \
+			lib/font
+
+OBJS += \
+	$(LOCAL_DIR)/gfxconsole.o
diff --git a/lib/heap/heap.c b/lib/heap/heap.c
index c67bc25..385d13a 100644
--- a/lib/heap/heap.c
+++ b/lib/heap/heap.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
+ * Copyright (c) 2009 Corey Tabaka
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -30,6 +31,12 @@
 
 #define LOCAL_TRACE 0
 
+#define DEBUG_HEAP 0
+#define ALLOC_FILL 0x99
+#define FREE_FILL 0x77
+#define PADDING_FILL 0x55
+#define PADDING_SIZE 64
+
 #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
 
 #define HEAP_MAGIC 'HEAP'
@@ -45,10 +52,10 @@
 extern int _end;
 
 // end of memory
-extern int _end_of_ram;
+extern int _heap_end;
 
 #define HEAP_START ((unsigned long)&_end)
-#define HEAP_LEN ((size_t)&_end_of_ram - (size_t)&_end)
+#define HEAP_LEN ((size_t)_heap_end - (size_t)&_end)
 #endif
 
 struct free_heap_chunk {
@@ -70,6 +77,10 @@
 	unsigned int magic;
 	void *ptr;
 	size_t size;
+#if DEBUG_HEAP
+	void *padding_start;
+	size_t padding_size;
+#endif
 };
 
 static void dump_free_chunk(struct free_heap_chunk *chunk)
@@ -203,6 +214,10 @@
 {
 	DEBUG_ASSERT((len % sizeof(void *)) == 0); // size must be aligned on pointer boundary
 
+#if DEBUG_HEAP
+	memset(ptr, FREE_FILL, len);
+#endif
+
 	struct free_heap_chunk *chunk = (struct free_heap_chunk *)ptr;
 	chunk->len = len;
 
@@ -212,6 +227,9 @@
 void *heap_alloc(size_t size, unsigned int alignment)
 {
 	void *ptr;
+#if DEBUG_HEAP
+	size_t original_size = size;
+#endif
 	
 	LTRACEF("size %zd, align %d\n", size, alignment);
 
@@ -221,7 +239,10 @@
 
 	// we always put a size field + base pointer + magic in front of the allocation
 	size += sizeof(struct alloc_struct_begin);
-	
+#if DEBUG_HEAP
+	size += PADDING_SIZE;
+#endif
+
 	// make sure we allocate at least the size of a struct free_heap_chunk so that
 	// when we free it, we can create a struct free_heap_chunk struct and stick it
 	// in the spot
@@ -275,6 +296,10 @@
 			DEBUG_ASSERT(chunk->len >= size);
 			size = chunk->len;
 
+#if DEBUG_HEAP
+			memset(ptr, ALLOC_FILL, size);
+#endif
+
 			ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
 
 			// align the output if requested
@@ -287,6 +312,13 @@
 			as->magic = HEAP_MAGIC;
 			as->ptr = (void *)chunk;
 			as->size = size;
+#if DEBUG_HEAP
+			as->padding_start = ((uint8_t *)ptr + original_size);
+			as->padding_size = (((addr_t)chunk + size) - ((addr_t)ptr + original_size));
+//			printf("padding start %p, size %u, chunk %p, size %u\n", as->padding_start, as->padding_size, chunk, size);
+
+			memset(as->padding_start, PADDING_FILL, as->padding_size);
+#endif
 
 			break;
 		}
@@ -314,6 +346,21 @@
 	
 	DEBUG_ASSERT(as->magic == HEAP_MAGIC);
 
+#if DEBUG_HEAP
+	{
+		uint i;
+		uint8_t *pad = (uint8_t *)as->padding_start;
+
+		for (i = 0; i < as->padding_size; i++) {
+			if (pad[i] != PADDING_FILL) {
+				printf("free at %p scribbled outside the lines:\n", ptr);
+				hexdump(pad, as->padding_size);
+				panic("die\n");
+			}
+		}
+	}
+#endif
+
 	LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
 
 	// looks good, create a free chunk and add it to the pool
@@ -355,7 +402,7 @@
 static int cmd_heap(int argc, const cmd_args *argv);
 
 STATIC_COMMAND_START
-	{ "heap", "heap debug commands", &cmd_heap },
+STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
 STATIC_COMMAND_END(heap);
 
 static int cmd_heap(int argc, const cmd_args *argv)
diff --git a/lib/libc/printf.c b/lib/libc/printf.c
index b7bf2bb..8a3aeb6 100644
--- a/lib/libc/printf.c
+++ b/lib/libc/printf.c
@@ -39,7 +39,7 @@
 
 int getc(char *c)
 {
-	return dgetc(c);
+	return dgetc(c, true);
 }
 
 int printf(const char *fmt, ...)
diff --git a/lib/libc/string/arch/x86/memcpy.S b/lib/libc/string/arch/x86/memcpy.S
new file mode 100644
index 0000000..a2a2a44
--- /dev/null
+++ b/lib/libc/string/arch/x86/memcpy.S
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <asm.h>
+
+/* TODO: */
+
+.text
+.align 2
+
+/* void bcopy(const void *src, void *dest, size_t n); */
+FUNCTION(bcopy)
+	ret
+
+/* void *memcpy(void *dest, const void *src, size_t n); */
+FUNCTION(memmove)
+FUNCTION(memcpy)
+	ret
+
diff --git a/lib/libc/string/arch/x86/memset.S b/lib/libc/string/arch/x86/memset.S
new file mode 100644
index 0000000..b4bccc9
--- /dev/null
+++ b/lib/libc/string/arch/x86/memset.S
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <asm.h>
+
+/* TODO: */
+
+.text
+.align 2
+
+/* void bzero(void *s, size_t n); */
+FUNCTION(bzero)
+	ret
+
+/* void *memset(void *s, int c, size_t n); */
+FUNCTION(memset)
+	ret
+
diff --git a/lib/libc/string/arch/x86/rules.mk b/lib/libc/string/arch/x86/rules.mk
new file mode 100644
index 0000000..b3a5aa9
--- /dev/null
+++ b/lib/libc/string/arch/x86/rules.mk
@@ -0,0 +1,11 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+ASM_STRING_OPS := #bcopy bzero memcpy memmove memset
+
+OBJS += \
+	#$(LOCAL_DIR)/memcpy.o \
+	#$(LOCAL_DIR)/memset.o
+
+# filter out the C implementation
+C_STRING_OPS := $(filter-out $(ASM_STRING_OPS),$(C_STRING_OPS))
+
diff --git a/lib/partition/partition.c b/lib/partition/partition.c
new file mode 100644
index 0000000..5565fe5
--- /dev/null
+++ b/lib/partition/partition.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009 Travis Geiselbrecht
+ *
+ * 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 <debug.h>
+#include <printf.h>
+#include <string.h>
+#include <compiler.h>
+#include <stdlib.h>
+#include <arch.h>
+#include <lib/bio.h>
+#include <lib/partition.h>
+
+struct chs {
+	uint8_t c;
+	uint8_t h;
+	uint8_t s;
+} __PACKED;
+
+struct mbr_part {
+	uint8_t status;
+	struct chs start;
+	uint8_t type;
+	struct chs end;
+	uint32_t lba_start;
+	uint32_t lba_length;
+} __PACKED;
+
+static status_t validate_mbr_partition(bdev_t *dev, const struct mbr_part *part)
+{
+	/* check for invalid types */
+	if (part->type == 0)
+		return -1;
+	/* check for invalid status */
+	if (part->status != 0x80 && part->status != 0x00)
+		return -1;
+
+	/* make sure the range fits within the device */
+	if (part->lba_start >= dev->block_count)
+		return -1;
+	if ((part->lba_start + part->lba_length) > dev->block_count)
+		return -1;
+
+	/* that's about all we can do, MBR has no other good way to see if it's valid */
+
+	return 0;
+}
+
+int partition_publish(const char *device, off_t offset)
+{
+	int err = 0;
+	int count = 0;
+
+	// clear any partitions that may have already existed
+	partition_unpublish(device);
+
+	bdev_t *dev = bio_open(device);
+	if (!dev) {
+		printf("partition_publish: unable to open device\n");
+		return -1;
+	}
+
+	// get a dma aligned and padded block to read info
+	STACKBUF_DMA_ALIGN(buf, dev->block_size);
+	
+	/* sniff for MBR partition types */
+	do {
+		int i;
+
+		err = bio_read(dev, buf, offset, 512);
+		if (err < 0)
+			goto err;
+
+		/* look for the aa55 tag */
+		if (buf[510] != 0x55 || buf[511] != 0xaa)
+			break;
+
+		/* see if a partition table makes sense here */
+		struct mbr_part part[4];
+		memcpy(part, buf + 446, sizeof(part));
+
+#if DEBUGLEVEL >= INFO
+		dprintf(INFO, "mbr partition table dump:\n");
+		for (i=0; i < 4; i++) {
+			dprintf(INFO, "\t%i: status 0x%hhx, type 0x%hhx, start 0x%x, len 0x%x\n", i, part[i].status, part[i].type, part[i].lba_start, part[i].lba_length);
+		}
+#endif
+
+		/* validate each of the partition entries */
+		for (i=0; i < 4; i++) {
+			if (validate_mbr_partition(dev, &part[i]) >= 0) {
+				// publish it
+				char subdevice[128];
+
+				sprintf(subdevice, "%sp%d", device, i); 
+
+				err = bio_publish_subdevice(device, subdevice, part[i].lba_start, part[i].lba_length);
+				if (err < 0) {
+					dprintf(INFO, "error publishing subdevice '%s'\n", subdevice);
+					continue;
+				}
+				count++;
+			}
+		}
+	} while(0);
+
+	bio_close(dev);
+
+err:
+	return (err < 0) ? err : count;
+}
+
+int partition_unpublish(const char *device)
+{
+	int i;
+	int count;
+	bdev_t *dev;
+	char devname[512];	
+
+	count = 0;
+	for (i=0; i < 16; i++) {
+		sprintf(devname, "%sp%d", device, i);
+
+		dev = bio_open(devname);
+		if (!dev)
+			continue;
+
+		bio_unregister_device(dev);
+		bio_close(dev);
+		count++;
+	}
+
+	return count;
+}
+
diff --git a/lib/partition/rules.mk b/lib/partition/rules.mk
new file mode 100644
index 0000000..f80eb44
--- /dev/null
+++ b/lib/partition/rules.mk
@@ -0,0 +1,6 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES += lib/bio
+
+OBJS += \
+	$(LOCAL_DIR)/partition.o
diff --git a/lib/text/rules.mk b/lib/text/rules.mk
new file mode 100644
index 0000000..9555d00
--- /dev/null
+++ b/lib/text/rules.mk
@@ -0,0 +1,6 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULES += lib/font
+
+OBJS += \
+	$(LOCAL_DIR)/text.o
diff --git a/lib/text/text.c b/lib/text/text.c
new file mode 100644
index 0000000..4b5f4ca
--- /dev/null
+++ b/lib/text/text.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
+
+/**
+ * @addtogroup graphics
+ * @{
+ */
+
+/**
+ * @file
+ * @brief  Console text display
+ *
+ * This module displays text on the console.  The text is retained so that
+ * it can be redisplayed if the display is cleared.
+ *
+ * Output is to the default graphics display
+ */
+
+#include <debug.h>
+#include <list.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dev/display.h>
+#include <lib/gfx.h>
+#include <lib/font.h>
+#include <lib/text.h>
+
+#define TEXT_COLOR 0xffffffff
+
+static struct list_node text_list = LIST_INITIAL_VALUE(text_list);
+
+struct text_line {
+	struct list_node node;
+	const char *str;
+	int x, y;
+};
+
+/**
+ * @brief  Add a string to the console text
+ */
+void text_draw(int x, int y, const char *string)
+{
+	struct text_line *line = malloc(sizeof(struct text_line));
+
+	line->str = strdup(string);
+	line->x = x;
+	line->y = y;
+
+	list_add_head(&text_list, &line->node);
+
+	text_update();
+}
+
+/**
+ * @brief  Refresh the display
+ */
+void text_update(void)
+{
+	struct display_info info;
+	display_get_info(&info);
+
+	/* get the display's surface */
+	gfx_surface *surface = gfx_create_surface_from_display(&info);
+
+	struct text_line *line;
+	list_for_every_entry(&text_list, line, struct text_line, node) {
+		const char *c;
+		int x = line->x;
+		for (c = line->str; *c; c++) {
+			font_draw_char(surface, *c, x, line->y, TEXT_COLOR);
+			x += FONT_X;
+		}
+	}
+
+	gfx_flush(surface);
+
+	gfx_surface_destroy(surface);
+}
+
diff --git a/lib/tga/rules.mk b/lib/tga/rules.mk
new file mode 100644
index 0000000..8538f47
--- /dev/null
+++ b/lib/tga/rules.mk
@@ -0,0 +1,4 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+OBJS += \
+	$(LOCAL_DIR)/tga.o
diff --git a/lib/tga/tga.c b/lib/tga/tga.c
new file mode 100644
index 0000000..7b38ded
--- /dev/null
+++ b/lib/tga/tga.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2008-2010 Travis Geiselbrecht
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ * @brief  Parse tga format files
+ *
+ * @ingroup graphics
+ */
+
+#include <debug.h>
+#include <compiler.h>
+#include <lib/tga.h>
+
+#define LOCAL_TRACE 0
+
+struct tga_header {
+	uint8_t  idlength;
+	uint8_t  colormaptype;
+	uint8_t  datatypecode;
+	uint16_t colormaporigin;
+	uint16_t colormaplength;
+	uint8_t  colormapdepth;
+	uint16_t x_origin;
+	uint16_t y_origin;
+	uint16_t width;
+	uint16_t height;
+	uint8_t  bitsperpixel;
+	uint8_t  imagedescriptor;
+} __PACKED;
+
+static void print_tga_info(const struct tga_header *header)
+{
+	LTRACEF("idlength %hhd\n", header->idlength);	
+	LTRACEF("colormaptype %hhd\n", header->colormaptype);	
+	LTRACEF("datatypecode %hhd\n", header->datatypecode);	
+	LTRACEF("colormaporigin %hd\n", header->colormaporigin);	
+	LTRACEF("colormaplength %hd\n", header->colormaplength);	
+	LTRACEF("colormapdepth %hhd\n", header->colormapdepth);	
+	LTRACEF("x_origin %hd\n", header->x_origin);	
+	LTRACEF("y_origin %hd\n", header->y_origin);	
+	LTRACEF("width %hd\n", header->width);	
+	LTRACEF("height %hd\n", header->height);	
+	LTRACEF("bitsperpixel %hhd\n", header->bitsperpixel);	
+	LTRACEF("imagedescriptor %hhd\n", header->imagedescriptor);	
+
+}
+
+static void decode_2byte(gfx_surface *surface, uint x, uint y, const void *input)
+{
+	const uint8_t *in = (const uint8_t *)input;
+
+//	printf("in 0x%hhx 0x%hhx\n", in[0], in[1]);
+	uint r,g,b;
+
+	b = (in[0] & 0x1f) << 3;
+	g = (((in[0] >> 5) & 0x7) | ((in[1] & 0x3) << 3)) << 3;
+	r = ((in[1] >> 2) & 0x1f) << 3;
+
+	gfx_putpixel(surface, x, y, 0xff000000 | r << 16 | g << 8 | b);
+}
+
+static void decode_3byte(gfx_surface *surface, uint x, uint y, const void *input)
+{
+	const uint8_t *in = (const uint8_t *)input;
+
+//	printf("in 0x%hhx 0x%hhx\n", in[0], in[1]);
+
+	gfx_putpixel(surface, x, y, 0xff000000 | in[2] << 16 | in[1] << 8 | in[0]);
+}
+
+static void decode_4byte(gfx_surface *surface, uint x, uint y, const void *input)
+{
+	const uint8_t *in = (const uint8_t *)input;
+
+//	printf("in 0x%hhx 0x%hhx 0x%hhx 0x%hhx\n", in[0], in[1], in[2], in[3]);
+
+	if (in[3] == 0)
+		gfx_putpixel(surface, x, y, 0);
+	else
+		gfx_putpixel(surface, x, y, in[3] << 24 | in[2] << 16 | in[1] << 8 | in[0]);
+}
+
+/**
+ * @brief  Decode a tga image
+ *
+ * @param  ptr  Pointer to tga data in memory
+ * @param  len  Length of tga data
+ * @param  format  Desired format of returned graphics surface
+ *
+ * @return Graphics surface or NULL on error.
+ *
+ * @ingroup graphics
+ */
+gfx_surface *tga_decode(const void *ptr, size_t len, gfx_format format)
+{
+	const struct tga_header *header = (const struct tga_header *)ptr;
+
+	LTRACEF("ptr %p, len %zu\n", ptr, len);
+
+#if LOCAL_TRACE > 0
+	print_tga_info(header);
+#endif
+
+	/* do some sanity checks */
+	if (header->datatypecode != 2 && header->datatypecode != 10) {
+		dprintf(INFO, "tga_decode: unknown data type %d\n", header->datatypecode);
+		return NULL;
+	}
+	if (header->bitsperpixel != 16 && header->bitsperpixel != 24 && header->bitsperpixel != 32) {
+		dprintf(INFO, "tga_decode: unsupported bits per pixel %d\n", header->bitsperpixel);
+		return NULL;
+	}
+	if (header->colormaptype != 0) {
+		dprintf(INFO, "tga_decode: has colormap, can't handle\n");
+		return NULL;
+	}
+
+	const void *imagestart = ((const uint8_t *)ptr + sizeof(struct tga_header) + header->idlength);
+
+	/* create a surface to hold the decoded bits */
+	gfx_surface *surface = gfx_create_surface(NULL, header->width, header->height, header->width, format);
+	DEBUG_ASSERT(surface);
+
+	/* copy the bits out */
+	void (*decodefunc)(gfx_surface *, uint x, uint y, const void *) = NULL;
+
+	uint step = 1;
+	if (header->bitsperpixel == 16) {
+		step = 2;
+		decodefunc = decode_2byte;
+	} else if (header->bitsperpixel == 24) {
+		step = 3;
+		decodefunc = decode_3byte;
+	} else if (header->bitsperpixel == 32) {
+		step = 4;
+		decodefunc = decode_4byte;
+	}
+
+	if (header->datatypecode == 2) {
+		/* no RLE */
+		uint pos = 0;
+		uint x, y;
+		uint surfacey;
+
+		for (y = 0; y < header->height; y++) {
+
+			if ((header->imagedescriptor & (1 << 5)) == 0)
+				surfacey = (surface->height - 1) - y;
+			else
+				surfacey = y;
+
+			for (x = 0; x < header->width; x++) {
+				decodefunc(surface, x, surfacey, (const uint8_t *)imagestart + pos);
+				pos += step;
+			}
+		}
+	} else if (header->datatypecode == 10) {
+		/* RLE compression */
+		uint pos = 0;
+		uint count = 0;
+		uint x, y;
+
+		x = 0;
+		if ((header->imagedescriptor & (1 << 5)) == 0)
+			y = header->height - 1;
+		else
+			y = 0;
+
+		while (count < (uint)header->height * (uint)header->width) {
+			uint runpos;
+
+			uint8_t run = *((const uint8_t *)imagestart + pos);
+			bool repeat_run = (run & 0x80);
+			uint runlen = (run & 0x7f) + 1;
+
+//			printf("pos 0x%x count %u run 0x%hhx runtype %d runlen %u\n", pos, count, run, run & 0x80, runlen);
+
+			/* consume the run byte */
+			pos++;
+
+			/* start of a run */
+			for (runpos = 0; runpos < runlen; runpos++) {
+				decodefunc(surface, x, y, (const uint8_t *)imagestart + pos);
+				count++;
+
+				x++;
+				if (x == surface->width) {
+					if ((header->imagedescriptor & (1 << 5)) == 0)
+						y--;
+					else
+						y++;
+					x = 0;
+				}
+
+				/* if a run of raw pixels, consume an input pixel */
+				if (!repeat_run)
+					pos += step;
+			}
+			/* if this was a run of repeated pixels, consume the one input pixel we repeated */
+			if (repeat_run)
+				pos += step;
+
+		}
+//		printf("done with RLE: x %d, y %d, pos %d, count %d\n", x, y, pos, count);
+	}
+	
+	return surface;
+}
+
diff --git a/makefile b/makefile
index f93bfd9..da8567d 100644
--- a/makefile
+++ b/makefile
@@ -1,3 +1,8 @@
+ifeq ($(MAKECMDGOALS),spotless)
+spotless:
+	rm -rf build-*
+else
+
 -include local.mk
 include make/macros.mk
 
@@ -155,9 +160,6 @@
 clean: $(EXTRA_CLEANDEPS)
 	rm -f $(ALLOBJS) $(DEPS) $(GENERATED) $(OUTBIN) $(OUTELF) $(OUTELF).lst
 
-spotless:
-	rm -rf build-*
-
 install: all
 	scp $(OUTBIN) 192.168.0.4:/tftproot
 
@@ -194,3 +196,5 @@
 
 .PHONY: configheader
 endif
+
+endif # make spotless
diff --git a/platform/armemu/blkdev.c b/platform/armemu/blkdev.c
new file mode 100644
index 0000000..b5fbc43
--- /dev/null
+++ b/platform/armemu/blkdev.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010 Travis Geiselbrecht
+ *
+ * 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 <err.h>
+#include <debug.h>
+#include <platform.h>
+#include "platform_p.h"
+#include <platform/armemu.h>
+#include <lib/bio.h>
+#include <reg.h>
+
+static bdev_t dev;
+
+static uint64_t get_blkdev_len(void)
+{
+	return *REG64(BDEV_LEN);
+}
+
+ssize_t read_block(struct bdev *dev, void *buf, bnum_t block, uint count)
+{
+	/* assume args have been validated by layer above */
+	*REG32(BDEV_CMD_ADDR) = (uint32_t)buf;
+	*REG64(BDEV_CMD_OFF) = (uint64_t)((uint64_t)block * dev->block_size);
+	*REG32(BDEV_CMD_LEN) = count * dev->block_size;
+
+	*REG32(BDEV_CMD) = BDEV_CMD_READ;
+
+	uint32_t err = *REG32(BDEV_CMD) & BDEV_CMD_ERRMASK;
+	if (err == BDEV_CMD_ERR_NONE)
+		return count * dev->block_size;
+	else
+		return ERR_IO;
+}
+
+ssize_t write_block(struct bdev *dev, const void *buf, bnum_t block, uint count)
+{
+	/* assume args have been validated by layer above */
+	*REG32(BDEV_CMD_ADDR) = (uint32_t)buf;
+	*REG64(BDEV_CMD_OFF) = (uint64_t)((uint64_t)block * dev->block_size);
+	*REG32(BDEV_CMD_LEN) = count * dev->block_size;
+
+	*REG32(BDEV_CMD) = BDEV_CMD_WRITE;
+
+	uint32_t err = *REG32(BDEV_CMD) & BDEV_CMD_ERRMASK;
+	if (err == BDEV_CMD_ERR_NONE)
+		return count * dev->block_size;
+	else
+		return ERR_IO;
+}
+
+void platform_init_blkdev(void)
+{
+	if ((*REG32(SYSINFO_FEATURES) & SYSINFO_FEATURE_BLOCKDEV) == 0)
+		return; // no block device
+
+	TRACEF("device len %lld\n", get_blkdev_len());
+
+	if (get_blkdev_len() == 0)
+		return;
+
+	bio_initialize_bdev(&dev, "block0", 512, get_blkdev_len() / 512);
+
+	// fill in hooks
+	dev.read_block = &read_block;
+	dev.write_block = &write_block;
+
+	bio_register_device(&dev);
+}
+
diff --git a/platform/armemu/debug.c b/platform/armemu/debug.c
index 09aabd5..9eea536 100644
--- a/platform/armemu/debug.c
+++ b/platform/armemu/debug.c
@@ -32,15 +32,21 @@
 	*REG8(DEBUG_STDOUT) = c;
 }
 
-int dgetc(char *c)
+int dgetc(char *c, bool wait)
 {
-	int8_t result = (int8_t)*REG8(DEBUG_STDIN);
+	for (;;) {
+		int8_t result = (int8_t)*REG8(DEBUG_STDIN);
 
-	if (result == -1)
-		return -1;
+		if (result == -1) {
+			if (wait)
+				continue;
+			else
+				return -1;
+		}
 
-	*c = (char)result;
-	return 0;
+		*c = (char)result;
+		return 0;
+	}
 }
 
 void debug_dump_regs(void)
diff --git a/platform/armemu/display.c b/platform/armemu/display.c
new file mode 100644
index 0000000..5b33965
--- /dev/null
+++ b/platform/armemu/display.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010 Travis Geiselbrecht
+ *
+ * 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 <err.h>
+#include <debug.h>
+#include <platform.h>
+#include "platform_p.h"
+#include <platform/armemu.h>
+#include <dev/display.h>
+#include <lib/gfx.h>
+#include <reg.h>
+
+static int display_w, display_h;
+static void *display_fb;
+
+inline static int has_display(void)
+{
+	return *REG32(SYSINFO_FEATURES) & SYSINFO_FEATURE_DISPLAY;
+}
+
+void platform_init_display(void)
+{
+	if (!has_display())
+		return;
+
+	display_fb = (void *)DISPLAY_FRAMEBUFFER;
+	display_w = *REG32(DISPLAY_WIDTH);
+	display_h = *REG32(DISPLAY_HEIGHT);
+
+	gfx_draw_pattern();
+}
+
+void display_get_info(struct display_info *info)
+{
+	if (!has_display())
+		return;
+
+	info->framebuffer = display_fb;
+	info->format = GFX_FORMAT_RGB_x888;
+	info->width = display_w;
+	info->height = display_h;
+	info->stride = display_w;
+	info->flush = NULL;
+}
+
diff --git a/platform/armemu/include/platform/armemu/memmap.h b/platform/armemu/include/platform/armemu/memmap.h
index 9df4e0b..b13050f 100644
--- a/platform/armemu/include/platform/armemu/memmap.h
+++ b/platform/armemu/include/platform/armemu/memmap.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2006 Travis Geiselbrecht
+ * Copyright (c) 2005-2010 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -45,6 +45,7 @@
 #define SYSINFO_FEATURE_DISPLAY 0x00000001
 #define SYSINFO_FEATURE_CONSOLE 0x00000002
 #define SYSINFO_FEATURE_NETWORK 0x00000004
+#define SYSINFO_FEATURE_BLOCKDEV 0x00000008
 
     /* a write to this register latches the current emulator system time, so the next two regs can be read atomically */
 #define SYSINFO_TIME_LATCH (SYSINFO_REGS_BASE + 4)
@@ -58,7 +59,10 @@
 #define DISPLAY_FRAMEBUFFER DISPLAY_BASE
 #define DISPLAY_REGS_BASE (DISPLAY_BASE + DISPLAY_SIZE)
 #define DISPLAY_REGS_SIZE MEMBANK_SIZE
-	/* no display regs for now */
+
+#define DISPLAY_WIDTH     (DISPLAY_REGS_BASE + 0) // pixels width/height read/only
+#define DISPLAY_HEIGHT    (DISPLAY_REGS_BASE + 4)
+#define DISPLAY_BPP       (DISPLAY_REGS_BASE + 8) // bits per pixel (16/32)
 
 /* console (keyboard controller */
 #define CONSOLE_REGS_BASE (DISPLAY_REGS_BASE + DISPLAY_REGS_SIZE)
@@ -145,4 +149,27 @@
 #define NET_IN_BUF_LEN (NET_REGS_BASE + 16)	/* length of the currently selected in buffer, via tail register */
 #define NET_IN_BUF	(NET_REGS_BASE + NET_BUF_LEN*2)
 
+/* block device interface */
+#define BDEV_REGS_BASE (NET_REGS_BASE + NET_REGS_SIZE)
+#define BDEV_REGS_SIZE MEMBANK_SIZE
+
+#define BDEV_CMD	(BDEV_REGS_BASE + 0)	/* command */
+#define BDEV_CMD_ADDR	(BDEV_REGS_BASE + 4)	/* address of next transfer, 32bit */
+#define BDEV_CMD_OFF	(BDEV_REGS_BASE + 8)	/* offset of next transfer, 64bit */
+#define BDEV_CMD_LEN	(BDEV_REGS_BASE + 16)	/* length of next transfer, 32bit */
+
+#define BDEV_LEN	(BDEV_REGS_BASE + 20)	/* length of block device, 64bit */
+
+/* BDEV_CMD bits */
+#define BDEV_CMD_MASK	(0x3)
+#define BDEV_CMD_NOP	(0)
+#define BDEV_CMD_READ	(1)
+#define BDEV_CMD_WRITE	(2)
+#define BDEV_CMD_ERASE	(3)
+#define BDEV_CMD_ERRSHIFT	16
+#define BDEV_CMD_ERRMASK	(0xffff << BDEV_CMD_ERRSHIFT)
+#define BDEV_CMD_ERR_NONE (0 << BDEV_CMD_ERRSHIFT)
+#define BDEV_CMD_ERR_GENERAL (1 << BDEV_CMD_ERRSHIFT)
+#define BDEV_CMD_ERR_BAD_OFFSET (2 << BDEV_CMD_ERRSHIFT)
+
 #endif
diff --git a/platform/armemu/platform.c b/platform/armemu/platform.c
index 1425674..0076b57 100644
--- a/platform/armemu/platform.c
+++ b/platform/armemu/platform.c
@@ -40,5 +40,7 @@
 
 void platform_init(void)
 {
+	platform_init_blkdev();
+	platform_init_display();
 }
 
diff --git a/platform/armemu/platform_p.h b/platform/armemu/platform_p.h
index 872ea2b..28f7210 100644
--- a/platform/armemu/platform_p.h
+++ b/platform/armemu/platform_p.h
@@ -25,6 +25,8 @@
 
 void platform_init_interrupts(void);
 void platform_init_timer(void);
+void platform_init_blkdev(void);
+void platform_init_display(void);
 
 #endif
 
diff --git a/platform/armemu/rules.mk b/platform/armemu/rules.mk
index 13543f4..9910960 100644
--- a/platform/armemu/rules.mk
+++ b/platform/armemu/rules.mk
@@ -15,11 +15,20 @@
 	$(LOCAL_DIR)/interrupts.o \
 	$(LOCAL_DIR)/platform.o \
 	$(LOCAL_DIR)/timer.o \
+	$(LOCAL_DIR)/blkdev.o \
+	$(LOCAL_DIR)/display.o \
 
 
 #	$(LOCAL_DIR)/console.o \
 	$(LOCAL_DIR)/net.o \
 
+DEFINES += \
+	WITH_DEV_DISPLAY=1
+
+MODULES += \
+	lib/gfx
+
+
 MEMBASE := 0x0
 MEMSIZE := 0x400000	# 4MB
 
diff --git a/platform/armemu/timer.c b/platform/armemu/timer.c
index 4d209a4..4cc2b50 100644
--- a/platform/armemu/timer.c
+++ b/platform/armemu/timer.c
@@ -48,13 +48,23 @@
 	return NO_ERROR;
 }
 
+bigtime_t current_time_hires(void)
+{
+	bigtime_t time;
+	*REG(SYSINFO_TIME_LATCH) = 1;
+	time = *REG(SYSINFO_TIME_SECS) * 1000000ULL;
+	time += *REG(SYSINFO_TIME_USECS);
+
+	return time;
+}
+
 time_t current_time(void)
 {
 	time_t time;
 	*REG(SYSINFO_TIME_LATCH) = 1;
 	time = *REG(SYSINFO_TIME_SECS) * 1000;
 	time += *REG(SYSINFO_TIME_USECS) / 1000;
-			
+
 	return time;
 }
 
diff --git a/platform/at91sam7/debug.c b/platform/at91sam7/debug.c
index ad8f262..5520a21 100644
--- a/platform/at91sam7/debug.c
+++ b/platform/at91sam7/debug.c
@@ -64,6 +64,12 @@
     }
 }
 
+int dgetc(char *c, bool wait)
+{
+	return -1;
+}
+
+
 void _dputc(char c)
 {
 	ser_putc(c);
@@ -75,7 +81,3 @@
     for(;;);
 }
 
-uint32_t debug_cycle_count()
-{
-	PANIC_UNIMPLEMENTED;
-}
diff --git a/platform/at91sam7/timer.c b/platform/at91sam7/timer.c
index be65e9c..8e2c6e3 100644
--- a/platform/at91sam7/timer.c
+++ b/platform/at91sam7/timer.c
@@ -48,6 +48,11 @@
 	return ticks;
 }
 
+bigtime_t current_time_hires(void)
+{
+	return ticks * 1000ULL;
+}
+
 static enum handler_return pit_irq_handler(void *arg)
 {
     AT91PIT *pit = AT91PIT_ADDR;
diff --git a/platform/integrator/debug.c b/platform/integrator/debug.c
index 0a2e058..f8f456c 100644
--- a/platform/integrator/debug.c
+++ b/platform/integrator/debug.c
@@ -98,7 +98,7 @@
 	uart_putc(0, c);
 }
 
-int dgetc(char *c)
+int dgetc(char *c, bool wait)
 {
 	int result = uart_getc(0, false);
 
@@ -140,7 +140,3 @@
 	PANIC_UNIMPLEMENTED;
 }
 
-uint32_t debug_cycle_count()
-{
-	PANIC_UNIMPLEMENTED;
-}
diff --git a/platform/msm7x30/acpuclock.c b/platform/msm7x30/acpuclock.c
index c6aae9c..af16c88 100644
--- a/platform/msm7x30/acpuclock.c
+++ b/platform/msm7x30/acpuclock.c
@@ -320,7 +320,7 @@
 	}
 }
 
-void ce_enable_clock(void)
+void ce_clock_init(void)
 {
 	unsigned int val=0;
 
diff --git a/platform/msm7x30/platform.c b/platform/msm7x30/platform.c
index 79c6a1d..5916866 100644
--- a/platform/msm7x30/platform.c
+++ b/platform/msm7x30/platform.c
@@ -88,7 +88,6 @@
 	dprintf(INFO, "platform_init()\n");
 	acpu_clock_init();
 	adm_enable_clock();
-	ce_enable_clock();
 }
 
 void mdp4_display_intf_sel(int output, int intf)
diff --git a/platform/msm8960/acpuclock.c b/platform/msm8960/acpuclock.c
index 404bee2..1deed72 100644
--- a/platform/msm8960/acpuclock.c
+++ b/platform/msm8960/acpuclock.c
@@ -257,3 +257,13 @@
 	reg |= MMC_BOOT_MCI_CLK_IN_FEEDBACK;
 	writel( reg, MMC_BOOT_MCI_CLK );
 }
+
+/* Configure crypto engine clock */
+void ce_clock_init(void)
+{
+	/* Enable HCLK for CE1 */
+	writel((1<<4), CE1_HCLK_CTL);
+	/* Enable core clk for CE1 */
+	writel((1<<4), CE1_CORE_CLK_CTL);
+	return;
+}
diff --git a/platform/msm8960/include/platform/iomap.h b/platform/msm8960/include/platform/iomap.h
index e72efd6..04f15e0 100644
--- a/platform/msm8960/include/platform/iomap.h
+++ b/platform/msm8960/include/platform/iomap.h
@@ -105,6 +105,8 @@
 #define GSBIn_UART_APPS_NS(n)   (CLK_CTL_BASE + 0x29D4 + (32 * ((n) - 1)))
 #define MSM_BOOT_PLL8_STATUS    (CLK_CTL_BASE + 0x3158)
 #define MSM_BOOT_PLL_ENABLE_SC0 (CLK_CTL_BASE + 0x34C0)
+#define CE1_HCLK_CTL            (CLK_CTL_BASE + 0x2720)
+#define CE1_CORE_CLK_CTL        (CLK_CTL_BASE + 0x2724)
 
 #define MSM_MMSS_CLK_CTL_BASE 0x04000000
 
@@ -137,4 +139,6 @@
 //TODO: Where does this go?
 #define MMSS_SFPB_GPREG                       (0x05700058)
 
+#define CE1_CRYPTO4_BASE                      (0x18500000)
+#define MSM_CRYPTO_BASE                       CE1_CRYPTO4_BASE
 #endif
diff --git a/platform/msm8x60/platform.c b/platform/msm8x60/platform.c
index cc17781..1f08c2b 100644
--- a/platform/msm8x60/platform.c
+++ b/platform/msm8x60/platform.c
@@ -117,7 +117,6 @@
 void platform_init(void)
 {
     dprintf(INFO, "platform_init()\n");
-    ce_clock_init();
 }
 
 void display_init(void)
diff --git a/platform/msm_shared/crypto4_eng.c b/platform/msm_shared/crypto4_eng.c
new file mode 100644
index 0000000..59562c4
--- /dev/null
+++ b/platform/msm_shared/crypto4_eng.c
@@ -0,0 +1,355 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <endian.h>
+#include <debug.h>
+#include <reg.h>
+#include <bits.h>
+#include <platform/iomap.h>
+#include <crypto4_eng.h>
+#include <crypto_hash.h>
+
+extern void dsb(void);
+
+/*
+ * Function to reset the crypto engine.
+ */
+
+void crypto_eng_reset(void)
+{
+	return;
+}
+
+/*
+ * Function to initialize the crypto engine for a new session. It enables the
+ * auto shutdown feature of CRYPTO and mask various interrupts since we use
+ * polling. We are not using DMOV now.
+ */
+
+void crypto_eng_init(void)
+{
+	unsigned int val;
+	val = (AUTO_SHUTDOWN_EN | MASK_ERR_INTR | MASK_DIN_INTR |
+			MASK_DOUT_INTR | HIGH_SPD_IN_EN_N | HIGH_SPD_OUT_EN_N);
+
+	val |= MASK_OP_DONE_INTR;
+
+	wr_ce(val,CRYPTO_CONFIG);
+}
+
+/*
+ * Function to set various SHAx registers in CRYPTO based on algorithm type.
+ */
+
+void crypto_set_sha_ctx(void *ctx_ptr, unsigned int bytes_to_write,
+		crypto_auth_alg_type auth_alg, bool first, bool last)
+{
+	crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx*)ctx_ptr;
+	crypto_SHA256_ctx *sha256_ctx = (crypto_SHA256_ctx*)ctx_ptr;
+	unsigned int i=0;
+	unsigned int iv_len=0;
+	unsigned int *auth_iv;
+	unsigned int seg_cfg_val;
+
+	seg_cfg_val = SEG_CFG_AUTH_ALG_SHA;
+
+	if(auth_alg == CRYPTO_AUTH_ALG_SHA1)
+	{
+		seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA1;
+
+		if(last)
+		{
+			seg_cfg_val |= SEG_CFG_LAST;
+		}
+
+		iv_len = SHA1_INIT_VECTOR_SIZE;
+		auth_iv = sha1_ctx->auth_iv;
+	}
+	else if(auth_alg == CRYPTO_AUTH_ALG_SHA256)
+	{
+		seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA256;
+
+		if(last)
+		{
+			seg_cfg_val |= SEG_CFG_LAST;
+		}
+
+		iv_len = SHA256_INIT_VECTOR_SIZE;
+		auth_iv = sha256_ctx->auth_iv;
+	}
+	else
+	{
+		dprintf(CRITICAL, "crypto_set_sha_ctx invalid auth algorithm\n");
+		return;
+	}
+
+	for(i=0; i<iv_len; i++)
+	{
+		wr_ce(*(auth_iv+i),CRYPTO_AUTH_IVn(i));
+	}
+	wr_ce(seg_cfg_val,CRYPTO_AUTH_SEG_CFG);
+
+	/* Typecast with crypto_SHA1_ctx because offset of auth_bytecnt in both
+	   crypto_SHA1_ctx and crypto_SHA256_ctx are same */
+
+	wr_ce(((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[0],CRYPTO_AUTH_BYTECNTn(0));
+	wr_ce(((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[1],CRYPTO_AUTH_BYTECNTn(1));
+
+	wr_ce(bytes_to_write,CRYPTO_AUTH_SEG_SIZE);
+
+	wr_ce(bytes_to_write,CRYPTO_SEG_SIZE);
+
+	/*
+	 * Ensure previous instructions (any writes to config registers)
+	 * are completed.
+	 *
+	 * TODO: Revisit dsb.
+	 */
+	dsb();
+
+	wr_ce(GOPROC_GO,CRYPTO_GOPROC);
+
+	return;
+}
+
+/*
+ * Function to send data to CRYPTO. This is non-DMOV implementation and uses
+ * polling to send the requested amount of data.
+ */
+
+void crypto_send_data(void *ctx_ptr, unsigned char *data_ptr,
+		unsigned int buff_size, unsigned int bytes_to_write,
+		unsigned int *ret_status)
+{
+	crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx*)ctx_ptr;
+	unsigned int bytes_left=0;
+	unsigned int i=0;
+	unsigned int ce_status=0;
+	unsigned int ce_err_bmsk=0;
+	unsigned int is_not_aligned=FALSE;
+	unsigned char data[4];
+	unsigned char *buff_ptr=data_ptr;
+
+	/* Check if the buff_ptr is aligned */
+	if(!(IS_ALIGNED(buff_ptr)))
+	{
+		is_not_aligned = TRUE;
+	}
+
+	/* Fill the saved_buff with data from buff_ptr. First we have to write
+	   all the data from the saved_buff and then we will write data from
+	   buff_ptr. We will update bytes_left and buff_ptr in the while loop
+	   once are done writing all the data from saved_buff. */
+
+	if(sha1_ctx->saved_buff_indx != 0)
+	{
+		memcpy(sha1_ctx->saved_buff + sha1_ctx->saved_buff_indx, buff_ptr,
+				(((buff_size + sha1_ctx->saved_buff_indx) <= CRYPTO_SHA_BLOCK_SIZE)
+				 ? buff_size : (CRYPTO_SHA_BLOCK_SIZE - sha1_ctx->saved_buff_indx)));
+
+		if(bytes_to_write >= CRYPTO_SHA_BLOCK_SIZE)
+		{
+			bytes_left = CRYPTO_SHA_BLOCK_SIZE;
+		}
+		else
+		{
+			bytes_left = bytes_to_write;
+		}
+	}
+	else
+	{
+		bytes_left = bytes_to_write;
+	}
+
+	/* Error bitmask to check crypto engine status */
+	ce_err_bmsk = (SW_ERR | DIN_RDY | DIN_SIZE_AVAIL);
+
+	while(bytes_left >= 4)
+	{
+		ce_status = rd_ce(CRYPTO_STATUS);
+		ce_status &= ce_err_bmsk;
+
+		if(ce_status & SW_ERR)
+		{
+			/* If there is SW_ERR, reset the engine */
+			crypto_eng_reset();
+			*ret_status = CRYPTO_ERR_FAIL;
+			dprintf(CRITICAL, "crypto_send_data sw error\n");
+			return;
+		}
+
+		/* We can write data now - 4 bytes at a time in network byte order */
+		if((ce_status & DIN_RDY) && ((ce_status & DIN_SIZE_AVAIL) >= 4))
+		{
+			if(sha1_ctx->saved_buff_indx != 0)
+			{
+				/* Write from saved_buff */
+				wr_ce(htonl(*((unsigned int *)(sha1_ctx->saved_buff)+i)),CRYPTO_DATA_IN);
+			}
+			else
+			{
+				if(!is_not_aligned)
+				{
+					/* Write from buff_ptr aligned */
+					wr_ce(htonl(*((unsigned int *)buff_ptr+i)),CRYPTO_DATA_IN);
+				}
+				else
+				{
+					/* If buff_ptr is not aligned write byte by byte */
+					data[0] = *(buff_ptr+i);
+					data[1] = *(buff_ptr+i+1);
+					data[2] = *(buff_ptr+i+2);
+					data[3] = *(buff_ptr+i+3);
+					/* i will incremented by 1 in outside block */
+					i+=3;
+					wr_ce(htonl(*(unsigned int *)data),CRYPTO_DATA_IN);
+					memset(data,0,4);
+				}
+			}
+			i++;
+			bytes_left -=4;
+
+			/* Check if we have written from saved_buff. Adjust buff_ptr and
+			   bytes_left accordingly */
+			if((sha1_ctx->saved_buff_indx != 0) && (bytes_left == 0) &&
+					(bytes_to_write > CRYPTO_SHA_BLOCK_SIZE))
+			{
+				bytes_left = (bytes_to_write - CRYPTO_SHA_BLOCK_SIZE);
+				buff_ptr = (unsigned char *)((unsigned char *)data_ptr +
+						CRYPTO_SHA_BLOCK_SIZE - sha1_ctx->saved_buff_indx);
+				i = 0;
+				sha1_ctx->saved_buff_indx = 0;
+				if(!(IS_ALIGNED(buff_ptr)))
+				{
+					is_not_aligned = TRUE;
+				}
+			}
+		}
+	}
+
+	/* We might have bytes_left < 4. Write them now if available */
+	if(bytes_left)
+	{
+		memset(data,0,sizeof(unsigned int));
+
+		if(sha1_ctx->saved_buff_indx)
+			buff_ptr = (sha1_ctx->saved_buff + bytes_to_write - 1);
+		else
+			buff_ptr = (((unsigned char *)data_ptr) + buff_size - 1);
+
+		for(i=0;i<bytes_left;i++)
+		{
+			data[3-i] = *(buff_ptr-bytes_left+i+1);
+		}
+
+		ce_status = rd_ce(CRYPTO_STATUS);
+		ce_status &= ce_err_bmsk;
+
+		if(ce_status & SW_ERR)
+		{
+			crypto_eng_reset();
+			*ret_status = CRYPTO_ERR_FAIL;
+			dprintf(CRITICAL, "crypto_send_data sw error 2\n");
+			return;
+		}
+		if((ce_status & DIN_RDY) && ((ce_status & DIN_SIZE_AVAIL) >= 4))
+		{
+			wr_ce(*(unsigned int *)data,CRYPTO_DATA_IN);
+		}
+	}
+	*ret_status = CRYPTO_ERR_NONE;
+	return;
+}
+
+/*
+ * Function to get digest from CRYPTO. We poll for AUTH_DONE from CRYPTO.
+ */
+
+void crypto_get_digest(unsigned char *digest_ptr, unsigned int *ret_status,
+		crypto_auth_alg_type auth_alg, bool last)
+{
+	unsigned int ce_status=0;
+	unsigned int ce_err_bmsk=0;
+	unsigned int i=0;
+	unsigned int digest_len=0;
+
+	ce_err_bmsk = (OPERATION_DONE | SW_ERR);
+
+	do
+	{
+		ce_status = rd_ce(CRYPTO_STATUS);
+		ce_status &= ce_err_bmsk;
+	}while (ce_status == 0);
+
+	if(ce_status & SW_ERR)
+	{
+		crypto_eng_reset();
+		*ret_status = CRYPTO_ERR_FAIL;
+		dprintf(CRITICAL, "crypto_get_digest sw error\n");
+		return;
+	}
+
+	/* Digest length depends on auth_alg */
+
+	if(auth_alg == CRYPTO_AUTH_ALG_SHA1)
+	{
+		digest_len = SHA1_INIT_VECTOR_SIZE;
+	}
+	else if (auth_alg == CRYPTO_AUTH_ALG_SHA256)
+	{
+		digest_len = SHA256_INIT_VECTOR_SIZE;
+	}
+
+	/* Retrieve digest from CRYPTO */
+
+	for(i=0; i < digest_len;i++)
+	{
+		unsigned int auth_iv = rd_ce(CRYPTO_AUTH_IVn(i));
+
+		if(last)
+		{
+			*((unsigned int *)digest_ptr + i) = htonl(auth_iv);
+		}
+		else
+		{
+			*((unsigned int *)digest_ptr + i) = auth_iv;
+		}
+	}
+	*ret_status = CRYPTO_ERR_NONE;
+	return;
+}
+
+/* Function to restore auth_bytecnt registers for ctx_ptr */
+
+void crypto_get_ctx(void *ctx_ptr)
+{
+	((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[0] = rd_ce(CRYPTO_AUTH_BYTECNTn(0));
+	((crypto_SHA1_ctx*)ctx_ptr)->auth_bytecnt[1] = rd_ce(CRYPTO_AUTH_BYTECNTn(1));
+	return;
+}
diff --git a/platform/msm_shared/crypto_hash.c b/platform/msm_shared/crypto_hash.c
index 21c15ec..bf869f5 100644
--- a/platform/msm_shared/crypto_hash.c
+++ b/platform/msm_shared/crypto_hash.c
@@ -30,12 +30,13 @@
 #include <debug.h>
 #include <sys/types.h>
 #include "crypto_hash.h"
-#include "crypto_eng.h"
 
 static crypto_SHA256_ctx g_sha256_ctx;
 static crypto_SHA1_ctx g_sha1_ctx;
 static unsigned char crypto_init_done = FALSE;
 
+extern void ce_clock_init(void);
+
 /*
  * Top level function which calculates SHAx digest with given data and size.
  * Digest varies based on the authentication algorithm.
@@ -71,6 +72,7 @@
 {
     if(crypto_init_done != TRUE)
     {
+         ce_clock_init();
          crypto_eng_reset();
          crypto_init_done = TRUE;
     }
diff --git a/platform/msm_shared/debug.c b/platform/msm_shared/debug.c
index 935d886..9adc4e4 100644
--- a/platform/msm_shared/debug.c
+++ b/platform/msm_shared/debug.c
@@ -55,7 +55,7 @@
 #endif
 }
 
-int dgetc(char *c)
+int dgetc(char *c, bool wait)
 {
 	int n;
 #if WITH_DEBUG_DCC
diff --git a/platform/msm_shared/include/crypto4_eng.h b/platform/msm_shared/include/crypto4_eng.h
new file mode 100644
index 0000000..8046a67
--- /dev/null
+++ b/platform/msm_shared/include/crypto4_eng.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __CRYPTO4_ENG_H__
+#define __CRYPYO4_ENG_H__
+
+#define CRYPTO_ENG_REG(offset)		(MSM_CRYPTO_BASE + offset)
+
+#define wr_ce(val,reg)				writel(val,CRYPTO_ENG_REG(reg))
+#define rd_ce(reg)					readl(CRYPTO_ENG_REG(reg))
+
+#define IS_ALIGNED(ptr)				(!(((unsigned int)ptr) & 0x03))
+
+/* CRYPTO4 registers */
+#define CRYPTO_DATA_IN				0x0008
+#define CRYPTO_DATA_OUT				0x0010
+#define CRYPTO_STATUS				0x0100
+#define CRYPTO_CONFIG				0x0500
+#define CRYPTO_DEBUG				0x0508
+#define CRYPTO_ENCR_SEG_CFG			0x0300
+#define CRYPTO_SEG_SIZE				0x0200
+#define CRYPTO_GOPROC				0x0204
+#define CRYPTO_ENGINES_AVAIL		0x0104
+#define CRYPTO_AUTH_SEG_CFG			0x0400
+#define CRYPTO_AUTH_SEG_SIZE		0x0404
+#define CRYPTO_AUTH_SEG_START		0x0408
+
+#define CRYPTO_AUTH_BYTECNTn(n)		(0x04A0 + 4*(n))
+#define CRYPTO_AUTH_IVn(n)			(0x0450 + 4*(n))
+
+
+/* Register bit definitions */
+#define SW_ERR						BIT(0)
+#define OPERATION_DONE				BIT(1)
+#define DIN_RDY						BIT(2)
+#define DIN_SIZE_AVAIL				0x00380000
+
+/* CRYPTO_CONFIG register bit definitions */
+#define AUTO_SHUTDOWN_EN			BIT(2)
+#define MASK_ERR_INTR				BIT(3)
+#define MASK_OP_DONE_INTR			BIT(4)
+#define MASK_DIN_INTR				BIT(5)
+#define MASK_DOUT_INTR				BIT(6)
+#define HIGH_SPD_IN_EN_N			BIT(13)
+#define HIGH_SPD_OUT_EN_N			BIT(14)
+
+/* CRYPTO_AUTH_SEG_CFG register bit definitions */
+#define SEG_CFG_AUTH_ALG_SHA		(1<<0)
+#define SEG_CFG_AUTH_SIZE_SHA1		(0<<9)
+#define SEG_CFG_AUTH_SIZE_SHA256	(1<<9)
+#define SEG_CFG_LAST				(1<<14)
+
+#define GOPROC_GO					1
+
+#endif
diff --git a/platform/msm_shared/include/crypto_hash.h b/platform/msm_shared/include/crypto_hash.h
index 4f3c992..2f9af3c 100644
--- a/platform/msm_shared/include/crypto_hash.h
+++ b/platform/msm_shared/include/crypto_hash.h
@@ -42,6 +42,9 @@
 #define CRYPTO_SHA_BLOCK_SIZE		64
 #define CRYPTO_MAX_AUTH_BLOCK_SIZE	0xFA00
 
+#define CRYPTO_ERR_NONE				0x01
+#define CRYPTO_ERR_FAIL				0x02
+
 typedef enum {
     CRYPTO_SHA_ERR_NONE,
     CRYPTO_SHA_ERR_BUSY,
diff --git a/platform/msm_shared/include/mmc.h b/platform/msm_shared/include/mmc.h
index 8b9bdc6..bce4d0e 100644
--- a/platform/msm_shared/include/mmc.h
+++ b/platform/msm_shared/include/mmc.h
@@ -548,37 +548,9 @@
 #define BINARY_IN_TABLE_SIZE      (16 * 512)

 #define MAX_FILE_ENTRIES          20

 

-#define MMC_EBR_TYPE			0x05

-#define MMC_MODEM_TYPE	 		0x06

-#define MMC_MODEM_TYPE2			0x0C

-#define MMC_SBL1_TYPE 			0x4D

-#define MMC_SBL2_TYPE 			0x51

-#define MMC_SBL3_TYPE 			0x45

-#define MMC_RPM_TYPE 			0x47

-#define MMC_TZ_TYPE 			0x46

-#define MMC_MODEM_ST1_TYPE 		0x4A

-#define MMC_MODEM_ST2_TYPE 		0x4B

-#define MMC_EFS2_TYPE 			0x4E

-

-#define MMC_ABOOT_TYPE 			0x4C

-#define MMC_BOOT_TYPE 			0x48

-#define MMC_SYSTEM_TYPE 		0x82

-#define MMC_USERDATA_TYPE 		0x83

-#define MMC_RECOVERY_TYPE 		0x60

-#define MMC_MISC_TYPE 			0x63

-

-#define MMC_PROTECTED_TYPE		0xEE

-

 #define MMC_RCA 2

 

-struct mbr_entry

-{

-    unsigned dstatus;

-    unsigned dtype ;

-    unsigned dfirstsec;

-    unsigned dsize;

-    unsigned char name[64];

-};

+extern unsigned gpt_partitions_exist;

 

 /* Can be used to unpack array of upto 32 bits data */

 #define UNPACK_BITS(array, start, len, size_of)                               \

@@ -625,8 +597,6 @@
                                       unsigned int* out );

 unsigned int mmc_write (unsigned long long data_addr,

 			unsigned int data_len, unsigned int* in);

-unsigned long long mmc_ptn_offset (unsigned char * name);

-unsigned long long mmc_ptn_size (unsigned char * name);

 

 unsigned int mmc_read (unsigned long long data_addr, unsigned int* out,

                        unsigned int data_len);

@@ -639,8 +609,8 @@
                                      unsigned int *in );

 

 unsigned int mmc_write_partition (unsigned size, unsigned char *partition);

-

-void mmc_dump_partition_info();

+unsigned int mmc_write_mbr_in_blocks(unsigned size, unsigned char *mbrImage);

+unsigned int mmc_write_mbr(unsigned size, unsigned char *mbrImage);

 

 #endif

 

diff --git a/platform/msm_shared/include/partition_parser.h b/platform/msm_shared/include/partition_parser.h
index 56855e2..fa90044 100644
--- a/platform/msm_shared/include/partition_parser.h
+++ b/platform/msm_shared/include/partition_parser.h
@@ -26,15 +26,19 @@
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* Lookup Type */
-//TODO: Remove when merging MBR code to mmc_parser
-#define PTN_OFFSET 0x01
-#define PTN_SIZE   0x02
+#define INVALID_PTN               -1
+
+#define PARTITION_TYPE_MBR         0
+#define PARTITION_TYPE_GPT         1
+#define PARTITION_TYPE_GPT_BACKUP  2
 
 /* GPT Signature should be 0x5452415020494645 */
 #define GPT_SIGNATURE_1 0x54524150
 #define GPT_SIGNATURE_2 0x20494645
 
+#define MMC_MBR_SIGNATURE_BYTE_0  0x55
+#define MMC_MBR_SIGNATURE_BYTE_1  0xAA
+
 /* GPT Offsets */
 #define PROTECTIVE_MBR_SIZE       512
 #define PARTITION_TABLE_SIZE      512
@@ -57,7 +61,44 @@
 #define MAX_GPT_NAME_SIZE          72
 #define PARTITION_TYPE_GUID_SIZE   16
 #define UNIQUE_PARTITION_GUID_SIZE 16
-#define NUM_GPT_PARTITIONS         32
+#define NUM_PARTITIONS             32
+
+/* Some useful define used to access the MBR/EBR table */
+#define BLOCK_SIZE                0x200
+#define TABLE_ENTRY_0             0x1BE
+#define TABLE_ENTRY_1             0x1CE
+#define TABLE_ENTRY_2             0x1DE
+#define TABLE_ENTRY_3             0x1EE
+#define TABLE_SIGNATURE           0x1FE
+#define TABLE_ENTRY_SIZE          0x010
+
+#define OFFSET_STATUS             0x00
+#define OFFSET_TYPE               0x04
+#define OFFSET_FIRST_SEC          0x08
+#define OFFSET_SIZE               0x0C
+#define COPYBUFF_SIZE             (1024 * 16)
+#define BINARY_IN_TABLE_SIZE      (16 * 512)
+#define MAX_FILE_ENTRIES          20
+
+#define MBR_EBR_TYPE              0x05
+#define MBR_MODEM_TYPE            0x06
+#define MBR_MODEM_TYPE2           0x0C
+#define MBR_SBL1_TYPE             0x4D
+#define MBR_SBL2_TYPE             0x51
+#define MBR_SBL3_TYPE             0x45
+#define MBR_RPM_TYPE              0x47
+#define MBR_TZ_TYPE               0x46
+#define MBR_MODEM_ST1_TYPE        0x4A
+#define MBR_MODEM_ST2_TYPE        0x4B
+#define MBR_EFS2_TYPE             0x4E
+
+#define MBR_ABOOT_TYPE            0x4C
+#define MBR_BOOT_TYPE             0x48
+#define MBR_SYSTEM_TYPE           0x82
+#define MBR_USERDATA_TYPE         0x83
+#define MBR_RECOVERY_TYPE         0x60
+#define MBR_MISC_TYPE             0x63
+#define MBR_PROTECTED_TYPE        0xEE
 
 #define GET_LLWORD_FROM_BYTE(x)    ((unsigned long long)*(x) | \
         ((unsigned long long)*(x+1) << 8) | \
@@ -68,16 +109,33 @@
         ((unsigned long long)*(x+6) << 48) | \
         ((unsigned long long)*(x+7) << 56))
 
-struct gpt_entry
+/* Unified mbr and gpt entry types */
+struct partition_entry
 {
-  unsigned char partition_type_guid[PARTITION_TYPE_GUID_SIZE];
-  unsigned char unique_partition_guid[UNIQUE_PARTITION_GUID_SIZE];
-  unsigned long long first_lba;
-  unsigned long long last_lba;
-  unsigned long long attribute_flag;
-  unsigned char partition_name[MAX_GPT_NAME_SIZE];
+    unsigned char type_guid[PARTITION_TYPE_GUID_SIZE];
+    unsigned dtype;
+    unsigned char unique_partition_guid[UNIQUE_PARTITION_GUID_SIZE];
+    unsigned long long first_lba;
+    unsigned long long last_lba;
+    unsigned long long size;
+    unsigned long long attribute_flag;
+    unsigned char name[MAX_GPT_NAME_SIZE];
 };
 
-unsigned int mmc_boot_read_gpt(struct mmc_boot_host * mmc_host,
-                               struct mmc_boot_card * mmc_card);
-unsigned long long gpt_lookup(unsigned char * name, unsigned type);
+static void mbr_fill_name (struct partition_entry *partition_ent, unsigned int type);
+unsigned int mmc_boot_read_gpt(  struct mmc_boot_host * mmc_host,
+                                 struct mmc_boot_card * mmc_card);
+unsigned int mmc_boot_read_mbr(  struct mmc_boot_host * mmc_host,
+                                 struct mmc_boot_card * mmc_card);
+unsigned partition_get_index (const char * name);
+unsigned long long partition_get_size (int index);
+unsigned long long partition_get_offset (int index);
+unsigned int partition_verify_mbr_signature(unsigned size, unsigned char* buffer);
+unsigned int mbr_partition_get_type(unsigned size, unsigned char* partition,
+                                    unsigned int *partition_type);
+unsigned int partition_get_type(unsigned size, unsigned char* partition,
+                                unsigned int *partition_type);
+unsigned int partition_read_table( struct mmc_boot_host * mmc_host,
+                                   struct mmc_boot_card * mmc_card);
+/* For Debugging */
+void partition_dump(void);
diff --git a/platform/msm_shared/mmc.c b/platform/msm_shared/mmc.c
index 608a3c5..fdd8a9e 100644
--- a/platform/msm_shared/mmc.c
+++ b/platform/msm_shared/mmc.c
@@ -31,7 +31,7 @@
 #include <debug.h>

 #include <reg.h>

 #include "mmc.h"

-#include "partition_parser.h"

+#include <partition_parser.h>

 #include <platform/iomap.h>

 #include <platform/timer.h>
 

@@ -45,13 +45,6 @@
 

 #define MMC_BOOT_DATA_READ     0

 #define MMC_BOOT_DATA_WRITE    1

-
-#define MMC_MBR_SIGNATURE_BYTE_0			0x55
-#define MMC_MBR_SIGNATURE_BYTE_1			0xAA
-
-#define PARTITION_TYPE_MBR			0
-#define PARTITION_TYPE_GPT			1
-#define PARTITION_TYPE_GPT_BACKUP	2
 

 

 static unsigned int mmc_boot_fifo_data_transfer(unsigned int* data_ptr,

@@ -80,10 +73,6 @@
 static const unsigned int xfer_rate_value[] =

 { 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80 };

 

-char *ext3_partitions[] = {"system", "userdata", "persist", "cache", "tombstones"};

-char *vfat_partitions[] = {"modem", "mdm", "NONE"};

-unsigned int ext3_count = 0;

-unsigned int vfat_count = 0;

 

 unsigned char mmc_slot = 0;

 unsigned int mmc_boot_mci_base = 0;

@@ -97,11 +86,7 @@
 

 struct mmc_boot_host mmc_host;

 struct mmc_boot_card mmc_card;

-struct mbr_entry mbr[MAX_PARTITIONS];

-unsigned mmc_partition_count = 0;

-static unsigned gpt_partitions_exist = 0;

 

-static void mbr_fill_name (struct mbr_entry *mbr_ent, unsigned int type);

 static unsigned int mmc_wp(unsigned int addr, unsigned int size,

                            unsigned char set_clear_wp);

 static unsigned int mmc_boot_send_ext_cmd (struct mmc_boot_card* card,

@@ -1496,7 +1481,7 @@
 

     return MMC_BOOT_E_SUCCESS;

 }

-

+
 

 /*

  * Adjust the interface speed to optimal speed

@@ -2187,195 +2172,6 @@
 }

 

 
-
-unsigned int mmc_verify_mbr_signature(unsigned size, unsigned char* buffer)
-{
-    /* Avoid checking past end of buffer */
-    if ((TABLE_SIGNATURE + 1) > size)
-    {
-        return MMC_BOOT_E_FAILURE;
-    }
-    /* Check to see if signature exists */
-    if ((buffer[TABLE_SIGNATURE] != MMC_MBR_SIGNATURE_BYTE_0) || \
-        (buffer[TABLE_SIGNATURE + 1] != MMC_MBR_SIGNATURE_BYTE_1))
-    {
-        dprintf(CRITICAL,  "MBR signature does not match. \n" );
-        return MMC_BOOT_E_FAILURE;
-    }
-    return MMC_BOOT_E_SUCCESS;
-}
-
-
-
-void print_mbr_partition_info(struct mbr_entry* mbrEntry)
-{
-    char buffer[128];
-
-    (void) snprintf(buffer, 128,
-        "{name:%s, status:0x%X, type:0x%X, start:0x%X, size:0x%X}\n",
-        mbrEntry->name,
-        mbrEntry->dstatus,
-        mbrEntry->dtype,
-        mbrEntry->dfirstsec,
-        mbrEntry->dsize
-    );
-    dprintf(INFO, buffer);
-}
-
-
-
-/* Print the contents of the partition table */
-/* NOTE:  exporting this function so that aboot can use it in fastboot
-   after display is initialized. */
-void mmc_dump_partition_info()
-{
-    if (gpt_partitions_exist)
-    {
-        /* TODO */
-    }
-    else
-    {
-        unsigned int i;
-        for (i = 0; i < mmc_partition_count; i++)
-        {
-            print_mbr_partition_info(&mbr[i]);
-        }
-    }
-}
-
-
-
-
-/*

- * Read MBR from MMC card and fill partition table.

- */

-static unsigned int mmc_boot_read_mbr(void)

-{

-    unsigned char buffer[MMC_BOOT_RD_BLOCK_LEN];

-    unsigned int dtype;

-    unsigned int dfirstsec;

-    unsigned int EBR_first_sec;

-    unsigned int EBR_current_sec;

-    int ret = MMC_BOOT_E_SUCCESS;

-    int idx, i;

-
-    dprintf(INFO, "Reading mbr\n");
-
-    /* Read the MBR block which is 512 bytes */

-    ret = mmc_boot_read_from_card( &mmc_host, &mmc_card, 0, \

-                                   MMC_BOOT_RD_BLOCK_LEN,   \

-                                   (unsigned int *)buffer);

-    if (ret)
-    {
-        dprintf(CRITICAL, "Could not read partition from mmc");
-        return ret;

-    }

-

-    ret = mmc_verify_mbr_signature(MMC_BOOT_RD_BLOCK_LEN, buffer);
-    if (ret)
-    {
-       return ret;
-    }
-
-    /* Process each of the four partitions in the MBR by reading the table
-       information into our mbr table. */
-    mmc_partition_count = 0;
-    idx = TABLE_ENTRY_0;

-    for (i = 0; i < 4; i++)
-    {
-        dtype  = buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];

-        /* Type 0xEE indicates end of MBR and GPT partitions exist */

-        if (dtype == MMC_PROTECTED_TYPE)
-        {

-            gpt_partitions_exist = 1;

-            return ret;

-        }

-        mbr[mmc_partition_count].dtype = dtype;

-        mbr[mmc_partition_count].dstatus = \

-                    buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];

-        mbr[mmc_partition_count].dfirstsec = \

-                    GET_LWORD_FROM_BYTE(&buffer[idx + \

-                                        i * TABLE_ENTRY_SIZE + \

-                                        OFFSET_FIRST_SEC]);

-        mbr[mmc_partition_count].dsize  = \

-                    GET_LWORD_FROM_BYTE(&buffer[idx + \

-                                        i * TABLE_ENTRY_SIZE + \

-                                        OFFSET_SIZE]);

-        dfirstsec = mbr[mmc_partition_count].dfirstsec;

-        mbr_fill_name(&mbr[mmc_partition_count],  \

-                      mbr[mmc_partition_count].dtype);

-        mmc_partition_count++;

-        if (mmc_partition_count == MAX_PARTITIONS)

-            return ret;

-    }

-

-    /* See if the last partition is EBR, if not, parsing is done */

-    if (dtype != MMC_EBR_TYPE)
-    {
-        return ret;

-    }

-

-    EBR_first_sec = dfirstsec;

-    EBR_current_sec = dfirstsec;

-

-    dprintf(INFO, "Reading first EBR block from 0x%X\n", EBR_first_sec);
-    ret = mmc_boot_read_from_card( &mmc_host, &mmc_card,  \

-                                   (EBR_first_sec * 512), \

-                                   MMC_BOOT_RD_BLOCK_LEN, \

-                                   (unsigned int *)buffer);

-    if (ret)

-    {

-        return ret;

-    }

-    /* Loop to parse the EBR */

-    for (i = 0;; i++)

-    {

-        ret = mmc_verify_mbr_signature(MMC_BOOT_RD_BLOCK_LEN, buffer);
-        if (ret)
-        {
-           ret = MMC_BOOT_E_SUCCESS;
-           break;
-        }
-        mbr[mmc_partition_count].dstatus = \

-                    buffer[TABLE_ENTRY_0 + OFFSET_STATUS];

-        mbr[mmc_partition_count].dtype   = \

-                    buffer[TABLE_ENTRY_0 + OFFSET_TYPE];

-        mbr[mmc_partition_count].dfirstsec = \

-                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \

-                                        OFFSET_FIRST_SEC])    + \

-                                        EBR_current_sec;

-        mbr[mmc_partition_count].dsize = \

-                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \

-                                        OFFSET_SIZE]);

-        mbr_fill_name(&(mbr[mmc_partition_count]), \

-                      mbr[mmc_partition_count].dtype);

-        mmc_partition_count++;

-        if (mmc_partition_count == MAX_PARTITIONS)

-            return ret;

-

-        dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);

-        if(dfirstsec == 0)

-        {

-            /* Getting to the end of the EBR tables */

-            break;

-        }

-        /* More EBR to follow - read in the next EBR sector */

-        dprintf(INFO, "Reading EBR block from 0x%X\n", EBR_first_sec + dfirstsec);
-        ret = mmc_boot_read_from_card( &mmc_host, &mmc_card, \

-                                       ((EBR_first_sec + dfirstsec) * 512), \

-                                       MMC_BOOT_RD_BLOCK_LEN, \

-                                       (unsigned int *)buffer);

-        if (ret)

-        {

-            return ret;

-        }

-        EBR_current_sec = EBR_first_sec + dfirstsec;

-    }

-    return ret;

-}

-

-
-
 void mmc_display_ext_csd(void)

 {

     dprintf(SPEW,  "part_config: %x\n", ext_csd_buf[179] );

@@ -2430,25 +2226,8 @@
     mmc_display_csd();

     mmc_display_ext_csd();

 

-    /* Read MBR of the card */

-    mmc_ret = mmc_boot_read_mbr();

-    if( mmc_ret != MMC_BOOT_E_SUCCESS )

-    {

-        dprintf(CRITICAL,  "MMC Boot: MBR read failed!\n" );

-        return MMC_BOOT_E_FAILURE;

-    }

-

-    /* Read GPT of the card if exist */

-    if(gpt_partitions_exist){

-        mmc_ret = mmc_boot_read_gpt(&mmc_host, &mmc_card);

-        if( mmc_ret != MMC_BOOT_E_SUCCESS )

-        {

-            dprintf(CRITICAL,  "GPT Boot: GPT read failed!\n" );

-            return MMC_BOOT_E_FAILURE;

-        }

-    }

-

-    return MMC_BOOT_E_SUCCESS;

+    mmc_ret = partition_read_table(&mmc_host, &mmc_card);
+    return mmc_ret;

 }

 

 /*

@@ -2516,13 +2295,13 @@
     for (i = 0; i < 4; i++)
     {
         dtype = mbrImage[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
-        if (MMC_EBR_TYPE == dtype)
+        if (MBR_EBR_TYPE == dtype)
         {
             dprintf(SPEW, "EBR found.\n");
             break;
         }
     }
-    if (MMC_EBR_TYPE != dtype)
+    if (MBR_EBR_TYPE != dtype)
     {
         dprintf(SPEW, "No EBR in this image\n");
         goto end;
@@ -2558,16 +2337,8 @@
 {
     unsigned int ret;
 
-    /* Don't allow overwrite of gpt with a mbr partition */
-    if (gpt_partitions_exist)
-    {
-        ret = MMC_BOOT_E_INVAL;
-        dprintf(CRITICAL, "Cannot overwrite GPT partition with MBR image.\n");
-        goto end;
-    }
-
     /* Verify that passed in block is a valid MBR */
-    ret = mmc_verify_mbr_signature(size, mbrImage);
+    ret = partition_verify_mbr_signature(size, mbrImage);
     if (ret)
     {
         goto end;
@@ -2581,80 +2352,17 @@
         goto end;
     }
     /* Re-read the MBR partition into mbr table */
-    ret = mmc_boot_read_mbr();
+    ret = mmc_boot_read_mbr( &mmc_host, &mmc_card );
     if (ret)
     {
         dprintf(CRITICAL, "Failed to re-read mbr partition.\n");
         goto end;
     }
-    mmc_dump_partition_info();
+    partition_dump();
 end:
     return ret;
 }
 
-
-
-unsigned int get_mbr_partition_type(unsigned size, unsigned char* partition,
-                                    unsigned int *partition_type)
-{
-    unsigned int type_offset = TABLE_ENTRY_0 + OFFSET_TYPE;
-
-    if (size < type_offset)
-    {
-        goto end;
-    }
-
-    *partition_type = partition[type_offset];
-end:
-    return MMC_BOOT_E_SUCCESS;
-}
-
-
-
-unsigned int get_partition_type(unsigned size, unsigned char* partition,
-                                unsigned int *partition_type)
-{
-    unsigned int ret = MMC_BOOT_E_SUCCESS;
-
-    /*
-        If the block contains the MBR signature, then it's likely either
-        MBR or MBR with protective type (GPT).  If the MBR signature is
-        not there, then it could be the GPT backup.
-    */
-
-    /* First check the MBR signature */
-    ret = mmc_verify_mbr_signature(size, partition);
-    if (ret == MMC_BOOT_E_SUCCESS)
-    {
-        unsigned int mbr_partition_type = PARTITION_TYPE_MBR;
-
-        /* MBR signature verified.  This could be MBR, MBR + EBR, or GPT */
-        ret = get_mbr_partition_type(size, partition, &mbr_partition_type);
-        if (ret != MMC_BOOT_E_SUCCESS)
-        {
-            dprintf(CRITICAL, "Cannot get TYPE of partition");
-        }
-        else if (MMC_PROTECTED_TYPE == mbr_partition_type)
-        {
-            *partition_type = PARTITION_TYPE_GPT;
-        }
-        else
-        {
-            *partition_type = PARTITION_TYPE_MBR;
-        }
-    }
-    else
-    {
-        /* This could be the GPT backup.  Make that assumption for now.
-           Anybody who treats the block as GPT backup should check the
-           signature. */
-        *partition_type = PARTITION_TYPE_GPT_BACKUP;
-    }
-    return ret;
-}
-
-
-
 unsigned int mmc_write_partition(unsigned size, unsigned char* partition)
 {
     unsigned int ret = MMC_BOOT_E_INVAL;
@@ -2665,7 +2373,7 @@
         dprintf(CRITICAL, "NULL partition\n");
         goto end;
     }
-    ret = get_partition_type(size, partition, &partition_type);
+    ret = partition_get_type(size, partition, &partition_type);
     if (ret != MMC_BOOT_E_SUCCESS)
     {
         goto end;
@@ -2708,101 +2416,6 @@
 }

 

 /*

- * Fill name for android partition found.

- */

-static void mbr_fill_name (struct mbr_entry *mbr_ent, unsigned int type)

-{

-    switch(type)

-    {

-        memset(mbr_ent->name, 0, 64);

-        case MMC_MODEM_TYPE:

-        case MMC_MODEM_TYPE2:

-            /* if already assigned last name available then return */

-            if(!strcmp((const char *)vfat_partitions[vfat_count], "NONE"))

-                return;

-            strcpy((char *)mbr_ent->name,(const char *)vfat_partitions[vfat_count]);

-            vfat_count++;

-            break;

-        case MMC_SBL1_TYPE:

-            memcpy(mbr_ent->name,"sbl1",4);

-            break;

-        case MMC_SBL2_TYPE:

-            memcpy(mbr_ent->name,"sbl2",4);

-            break;

-        case MMC_SBL3_TYPE:

-            memcpy(mbr_ent->name,"sbl3",4);

-            break;

-        case MMC_RPM_TYPE:

-            memcpy(mbr_ent->name,"rpm",3);

-            break;

-        case MMC_TZ_TYPE:

-            memcpy(mbr_ent->name,"tz",2);

-            break;

-        case MMC_ABOOT_TYPE:

-            memcpy(mbr_ent->name,"aboot",5);

-            break;

-        case MMC_BOOT_TYPE:

-            memcpy(mbr_ent->name,"boot",4);

-            break;

-        case MMC_MODEM_ST1_TYPE:

-            memcpy(mbr_ent->name,"modem_st1",9);

-            break;

-        case MMC_MODEM_ST2_TYPE:

-            memcpy(mbr_ent->name,"modem_st2",9);

-            break;

-        case MMC_EFS2_TYPE:

-            memcpy(mbr_ent->name,"efs2",4);

-            break;

-        case MMC_USERDATA_TYPE:

-            if (ext3_count == sizeof(ext3_partitions) / sizeof(char*))

-                return;

-            strcpy((char *)mbr_ent->name,(const char *)ext3_partitions[ext3_count]);

-            ext3_count++;

-            break;

-        case MMC_RECOVERY_TYPE:

-            memcpy(mbr_ent->name,"recovery",8);

-            break;

-        case MMC_MISC_TYPE:

-            memcpy(mbr_ent->name,"misc",4);

-            break;

-    };

-}

-

-/*

- * Returns offset of given partition

- */

-uint64_t mmc_ptn_offset (unsigned char * name)

-{

-    unsigned n;

-    for(n = 0; n < mmc_partition_count; n++) {

-        if(!strcmp((const char *)mbr[n].name, (const char *)name)) {

-            return ((uint64_t)mbr[n].dfirstsec * MMC_BOOT_RD_BLOCK_LEN);

-        }

-    }

-    if (gpt_partitions_exist)

-      return gpt_lookup(name, PTN_OFFSET);

-    else

-      return 0;

-}

-

-/*

- * Returns size of given partition

- */

-uint64_t mmc_ptn_size (unsigned char * name)

-{

-    unsigned n;

-    for(n = 0; n < mmc_partition_count; n++) {

-        if(!strcmp((const char *)mbr[n].name, (const char *)name)) {

-            return ((uint64_t)mbr[n].dsize * MMC_BOOT_RD_BLOCK_LEN);

-        }

-    }

-    if (gpt_partitions_exist)

-      return gpt_lookup(name, PTN_SIZE);

-    else

-      return 0;

-}

-

-/*

  * Function to read registers from MMC or SD card

  */

 static unsigned int mmc_boot_read_reg(struct mmc_boot_card *card,

diff --git a/platform/msm_shared/partition_parser.c b/platform/msm_shared/partition_parser.c
index 1f68e4b..23d4778 100644
--- a/platform/msm_shared/partition_parser.c
+++ b/platform/msm_shared/partition_parser.c
@@ -31,32 +31,194 @@
 #include "mmc.h"
 #include "partition_parser.h"
 
-static struct gpt_entry gpt[NUM_GPT_PARTITIONS];
-static uint32_t gpt_partition_count = 0;
+char *ext3_partitions[] = {"system", "userdata", "persist", "cache", "tombstones"};
+char *vfat_partitions[] = {"modem", "mdm", "NONE"};
+unsigned int ext3_count = 0;
+unsigned int vfat_count = 0;
+
+struct partition_entry partition_entries[NUM_PARTITIONS];
+unsigned gpt_partitions_exist = 0;
+unsigned partition_count = 0;
+
+//TODO: Remove the dependency of mmc in these functions
+unsigned int partition_read_table( struct mmc_boot_host * mmc_host,
+                                   struct mmc_boot_card * mmc_card)
+{
+    unsigned int ret;
+
+    /* Read MBR of the card */
+    ret = mmc_boot_read_mbr( mmc_host, mmc_card );
+    if( ret != MMC_BOOT_E_SUCCESS )
+    {
+        dprintf(CRITICAL,  "MMC Boot: MBR read failed!\n" );
+        return MMC_BOOT_E_FAILURE;
+    }
+
+    /* Read GPT of the card if exist */
+    if(gpt_partitions_exist){
+        ret = mmc_boot_read_gpt(mmc_host, mmc_card);
+        if( ret != MMC_BOOT_E_SUCCESS )
+        {
+            dprintf(CRITICAL,  "MMC Boot: GPT read failed!\n" );
+            return MMC_BOOT_E_FAILURE;
+        }
+    }
+    return MMC_BOOT_E_SUCCESS;
+}
+
+/*
+ * Read MBR from MMC card and fill partition table.
+ */
+unsigned int mmc_boot_read_mbr( struct mmc_boot_host * mmc_host,
+                                struct mmc_boot_card * mmc_card)
+{
+    unsigned char buffer[MMC_BOOT_RD_BLOCK_LEN];
+    unsigned int dtype;
+    unsigned int dfirstsec;
+    unsigned int EBR_first_sec;
+    unsigned int EBR_current_sec;
+    int ret = MMC_BOOT_E_SUCCESS;
+    int idx, i;
+
+    /* Print out the MBR first */
+    ret = mmc_boot_read_from_card( mmc_host, mmc_card, 0, \
+                                   MMC_BOOT_RD_BLOCK_LEN,   \
+                                   (unsigned int *)buffer);
+    if (ret)
+    {
+        dprintf(CRITICAL, "Could not read partition from mmc");
+        return ret;
+    }
+
+    /* Check to see if signature exists */
+    ret = partition_verify_mbr_signature(MMC_BOOT_RD_BLOCK_LEN, buffer);
+    if (ret)
+    {
+       return ret;
+    }
+
+    /*
+     * Process each of the four partitions in the MBR by reading the table
+     * information into our mbr table.
+     */
+    partition_count = 0;
+    idx = TABLE_ENTRY_0;
+    for (i = 0; i < 4; i++)
+    {
+        /* Type 0xEE indicates end of MBR and GPT partitions exist */
+        dtype  = buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
+        if (dtype == MBR_PROTECTED_TYPE){
+            gpt_partitions_exist = 1;
+            return ret;
+        }
+        partition_entries[partition_count].dtype = dtype;
+        partition_entries[partition_count].attribute_flag = \
+                    buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
+        partition_entries[partition_count].first_lba = \
+                    GET_LWORD_FROM_BYTE(&buffer[idx + \
+                                        i * TABLE_ENTRY_SIZE + \
+                                        OFFSET_FIRST_SEC]);
+        partition_entries[partition_count].size  = \
+                    GET_LWORD_FROM_BYTE(&buffer[idx + \
+                                        i * TABLE_ENTRY_SIZE + \
+                                        OFFSET_SIZE]);
+        dfirstsec = partition_entries[partition_count].first_lba;
+        mbr_fill_name(&partition_entries[partition_count],  \
+                      partition_entries[partition_count].dtype);
+        partition_count++;
+        if (partition_count == NUM_PARTITIONS)
+            return ret;
+    }
+
+    /* See if the last partition is EBR, if not, parsing is done */
+    if (dtype != MBR_EBR_TYPE)
+    {
+        return ret;
+    }
+
+    EBR_first_sec = dfirstsec;
+    EBR_current_sec = dfirstsec;
+
+    ret = mmc_boot_read_from_card( mmc_host, mmc_card,  \
+                                   (EBR_first_sec * 512), \
+                                   MMC_BOOT_RD_BLOCK_LEN, \
+                                   (unsigned int *)buffer);
+    if (ret)
+    {
+        return ret;
+    }
+    /* Loop to parse the EBR */
+    for (i = 0;; i++)
+    {
+        ret = partition_verify_mbr_signature(MMC_BOOT_RD_BLOCK_LEN, buffer);
+        if (ret)
+        {
+           ret = MMC_BOOT_E_SUCCESS;
+           break;
+        }
+        partition_entries[partition_count].attribute_flag = \
+                    buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
+        partition_entries[partition_count].dtype   = \
+                    buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
+        partition_entries[partition_count].first_lba = \
+                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
+                                        OFFSET_FIRST_SEC])    + \
+                                        EBR_current_sec;
+        partition_entries[partition_count].size = \
+                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
+                                        OFFSET_SIZE]);
+        mbr_fill_name(&(partition_entries[partition_count]), \
+                      partition_entries[partition_count].dtype);
+        partition_count++;
+        if (partition_count == NUM_PARTITIONS)
+            return ret;
+
+        dfirstsec =
+            GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
+        if(dfirstsec == 0)
+        {
+            /* Getting to the end of the EBR tables */
+            break;
+        }
+        /* More EBR to follow - read in the next EBR sector */
+        dprintf(SPEW, "Reading EBR block from 0x%X\n", EBR_first_sec
+                                                          + dfirstsec);
+        ret = mmc_boot_read_from_card( mmc_host, mmc_card, \
+                                       ((EBR_first_sec + dfirstsec) * 512), \
+                                       MMC_BOOT_RD_BLOCK_LEN, \
+                                       (unsigned int *)buffer);
+        if (ret)
+        {
+            return ret;
+        }
+        EBR_current_sec = EBR_first_sec + dfirstsec;
+    }
+    return ret;
+}
 
 /*
  * Read GPT from MMC and fill partition table
  */
-uint32_t mmc_boot_read_gpt(struct mmc_boot_host * mmc_host,
-                               struct mmc_boot_card * mmc_card){
+unsigned int mmc_boot_read_gpt( struct mmc_boot_host * mmc_host,
+                               struct mmc_boot_card * mmc_card)
+{
 
-    int32_t ret = MMC_BOOT_E_SUCCESS;
-    uint32_t header_size = 0;
-    //uint32_t header_crc = 0;
-    uint64_t first_usable_lba = 0;
-    uint64_t last_usable_lba = 0;
-    uint32_t partition_count = 0;
-    uint32_t partition_entry_size = 0;
-    //uint32_t partition_array_crc;
-    uint8_t data[MMC_BOOT_RD_BLOCK_LEN];
-    uint32_t i = 0; /* Counter for each 512 block */
-    uint32_t j = 0; /* Counter for each 128 entry in the 512 block */
+    int ret = MMC_BOOT_E_SUCCESS;
+    unsigned int header_size;
+    unsigned long long first_usable_lba;
+    unsigned int max_partition_count;
+    unsigned int partition_entry_size;
+    unsigned char data[MMC_BOOT_RD_BLOCK_LEN];
+    unsigned int i = 0; /* Counter for each 512 block */
+    unsigned int j = 0; /* Counter for each 128 entry in the 512 block */
+    unsigned int n = 0; /* Counter for UTF-16 -> 8 conversion */
+    unsigned char UTF16_name[MAX_GPT_NAME_SIZE];
 
     /* Print out the GPT first */
-    ret = mmc_boot_read_from_card( mmc_host, mmc_card,
-                                   PROTECTIVE_MBR_SIZE,
-                                   MMC_BOOT_RD_BLOCK_LEN,
-                                   (uint32_t *)data);
+    ret = mmc_boot_read_from_card( mmc_host, mmc_card, \
+                                   PROTECTIVE_MBR_SIZE, \
+                                   MMC_BOOT_RD_BLOCK_LEN, \
+                                   (unsigned int *)data);
 
     /* Check GPT Signature */
     if( ((uint32_t *)data)[0] != GPT_SIGNATURE_2 ||
@@ -67,75 +229,260 @@
     }
 
     header_size = GET_LWORD_FROM_BYTE(&data[HEADER_SIZE_OFFSET]);
-    //header_crc = GET_LWORD_FROM_BYTE(&data[HEADER_CRC_OFFSET]);
     first_usable_lba = GET_LLWORD_FROM_BYTE(&data[FIRST_USABLE_LBA_OFFSET]);
-    last_usable_lba = GET_LLWORD_FROM_BYTE(&data[LAST_USABLE_LBA_OFFSET]);
-    partition_count = GET_LWORD_FROM_BYTE(&data[PARTITION_COUNT_OFFSET]);
+    max_partition_count = GET_LWORD_FROM_BYTE(&data[PARTITION_COUNT_OFFSET]);
     partition_entry_size = GET_LWORD_FROM_BYTE(&data[PENTRY_SIZE_OFFSET]);
 
     /* Read GPT Entries */
-    for(i = 0; i < (partition_count/4); i++)
+    for(i = 0; i < (max_partition_count/4); i++)
     {
         ret = mmc_boot_read_from_card( mmc_host, mmc_card,
-                                       PROTECTIVE_MBR_SIZE + PARTITION_TABLE_SIZE +
+                                       PROTECTIVE_MBR_SIZE +
+                                       PARTITION_TABLE_SIZE +
                                        (i * MMC_BOOT_RD_BLOCK_LEN),
                                        MMC_BOOT_RD_BLOCK_LEN,
                                        (uint32_t *)data);
 
         if (ret)
         {
-            dprintf(CRITICAL, "GPT: mmc read card failed reading partition entries.\n" );
+            dprintf(CRITICAL,
+                    "GPT: mmc read card failed reading partition entries.\n" );
             return ret;
         }
 
         for(j=0; j < 4; j++)
         {
-            memcpy(&(gpt[j+(i*4)].partition_type_guid),
-                    &data[(j * partition_entry_size)], PARTITION_TYPE_GUID_SIZE);
-
-            if (gpt[j+(i*4)].partition_type_guid[0] == 0x00 &&
-                gpt[j+(i*4)].partition_type_guid[1] == 0x00)
+            memcpy(&(partition_entries[partition_count].type_guid),
+                    &data[(j * partition_entry_size)],
+                    PARTITION_TYPE_GUID_SIZE);
+            if (partition_entries[partition_count].type_guid[0] == 0x00 &&
+                partition_entries[partition_count].type_guid[1] == 0x00)
             {
-                i = partition_count;
+                i = max_partition_count;
                 break;
             }
-            gpt_partition_count++;
+            memcpy(&(partition_entries[partition_count].unique_partition_guid),
+                    &data[(j * partition_entry_size) +
+                            UNIQUE_GUID_OFFSET], UNIQUE_PARTITION_GUID_SIZE);
+            partition_entries[partition_count].first_lba =
+                GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) +
+                                             FIRST_LBA_OFFSET]);
+            partition_entries[partition_count].last_lba =
+                GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) +
+                                             LAST_LBA_OFFSET]);
+            partition_entries[partition_count].size =
+                partition_entries[partition_count].last_lba -
+                partition_entries[partition_count].first_lba;
+            partition_entries[partition_count].attribute_flag =
+                GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) +
+                                             ATTRIBUTE_FLAG_OFFSET]);
 
-            memcpy(&(gpt[j+(i*4)].unique_partition_guid),
-                    &data[(j * partition_entry_size) + UNIQUE_GUID_OFFSET], UNIQUE_PARTITION_GUID_SIZE);
-            gpt[j+(i*4)].first_lba = GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) +
-                                                                     FIRST_LBA_OFFSET]);
-            gpt[j+(i*4)].last_lba = GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) +
-                                                                     LAST_LBA_OFFSET]);
-            gpt[j+(i*4)].attribute_flag = GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) +
-                                                                     ATTRIBUTE_FLAG_OFFSET]);
-            memcpy(&(gpt[j+(i*4)].partition_name),
-                    &data[(j * partition_entry_size) + PARTITION_NAME_OFFSET], MAX_GPT_NAME_SIZE);
+            memset(&UTF16_name, 0x00, MAX_GPT_NAME_SIZE);
+            memcpy(UTF16_name, &data[(j * partition_entry_size) +
+                                             PARTITION_NAME_OFFSET],
+                                             MAX_GPT_NAME_SIZE);
+            /*
+             * Currently partition names in *.xml are UTF-8 and lowercase
+             * Only supporting english for now so removing 2nd byte of UTF-16
+             */
+            for(n = 0; n < MAX_GPT_NAME_SIZE/2; n++){
+                partition_entries[partition_count].name[n] = UTF16_name[n*2];
+            }
+            partition_count++;
         }
     }
+
     return ret;
 }
 
-uint64_t gpt_lookup(uint8_t * name, unsigned type){
-    uint32_t input_string_length = strlen(name);
-    uint8_t UTF16_name[MAX_GPT_NAME_SIZE];
+/*
+ * Fill name for android partition found.
+ */
+static void mbr_fill_name (struct partition_entry *partition_ent,
+                            unsigned int type)
+{
+    switch(type)
+    {
+        memset(partition_ent->name, 0, MAX_GPT_NAME_SIZE);
+        case MBR_MODEM_TYPE:
+        case MBR_MODEM_TYPE2:
+            /* if already assigned last name available then return */
+            if(!strcmp((const char *)vfat_partitions[vfat_count], "NONE"))
+                return;
+            strcpy((char *)partition_ent->name,
+                    (const char *)vfat_partitions[vfat_count]);
+            vfat_count++;
+            break;
+        case MBR_SBL1_TYPE:
+            memcpy(partition_ent->name,"sbl1",4);
+            break;
+        case MBR_SBL2_TYPE:
+            memcpy(partition_ent->name,"sbl2",4);
+            break;
+        case MBR_SBL3_TYPE:
+            memcpy(partition_ent->name,"sbl3",4);
+            break;
+        case MBR_RPM_TYPE:
+            memcpy(partition_ent->name,"rpm",3);
+            break;
+        case MBR_TZ_TYPE:
+            memcpy(partition_ent->name,"tz",2);
+            break;
+        case MBR_ABOOT_TYPE:
+            memcpy(partition_ent->name,"aboot",5);
+            break;
+        case MBR_BOOT_TYPE:
+            memcpy(partition_ent->name,"boot",4);
+            break;
+        case MBR_MODEM_ST1_TYPE:
+            memcpy(partition_ent->name,"modem_st1",9);
+            break;
+        case MBR_MODEM_ST2_TYPE:
+            memcpy(partition_ent->name,"modem_st2",9);
+            break;
+        case MBR_EFS2_TYPE:
+            memcpy(partition_ent->name,"efs2",4);
+            break;
+        case MBR_USERDATA_TYPE:
+            if (ext3_count == sizeof(ext3_partitions) / sizeof(char*))
+                return;
+            strcpy((char *)partition_ent->name,
+                    (const char *)ext3_partitions[ext3_count]);
+            ext3_count++;
+            break;
+        case MBR_RECOVERY_TYPE:
+            memcpy(partition_ent->name,"recovery",8);
+            break;
+        case MBR_MISC_TYPE:
+            memcpy(partition_ent->name,"misc",4);
+            break;
+    };
+}
+
+/*
+ * Find index of parition in array of partition entries
+ */
+unsigned partition_get_index (const char * name)
+{
+    unsigned int input_string_length = strlen(name);
     unsigned n;
 
-    memset(&UTF16_name, 0x00, MAX_GPT_NAME_SIZE);
-    /* Currently partition names in partition.xml are UTF-8 and lowercase */
-    for(n = 0; n < input_string_length && n < MAX_GPT_NAME_SIZE/2; n++){
-        UTF16_name[n*2] = name[n];
-        UTF16_name[n*2+1] = 0x00;
-    }
-    for(n = 0; n < gpt_partition_count; n++){
-        if(!memcmp(&UTF16_name, &gpt[n].partition_name, MAX_GPT_NAME_SIZE)){
-            if(type == PTN_SIZE)
-                return ((uint64_t)(gpt[n].last_lba - gpt[n].first_lba) * MMC_BOOT_RD_BLOCK_LEN);
-            else if(type == PTN_OFFSET)
-                return ((uint64_t)gpt[n].first_lba * MMC_BOOT_RD_BLOCK_LEN);
-            else
-                return 0;
+    for(n = 0; n < partition_count; n++){
+        if(!memcmp(name, &partition_entries[n].name, input_string_length) &&
+        input_string_length == strlen((const char *)&partition_entries[n].name))
+        {
+            return n;
         }
     }
-    return 0;
+    return INVALID_PTN;
+}
+
+/* Get size of the partition */
+unsigned long long partition_get_size (int index)
+{
+    if (index == INVALID_PTN)
+        return 0;
+    else{
+        return partition_entries[index].size * MMC_BOOT_RD_BLOCK_LEN;
+    }
+}
+
+/* Get offset of the partition */
+unsigned long long partition_get_offset (int index)
+{
+    if (index == INVALID_PTN)
+        return 0;
+    else{
+        return partition_entries[index].first_lba * MMC_BOOT_RD_BLOCK_LEN;
+    }
+}
+
+/* Debug: Print all parsed partitions */
+void partition_dump()
+{
+    unsigned i = 0;
+    for (i=0; i< partition_count; i++){
+        dprintf(SPEW,
+                "ptn[%d]:Name[%s] Size[%llu] Type[%u] First[%llu] Last[%llu]\n",
+                i, partition_entries[i].name, partition_entries[i].size,
+                partition_entries[i].dtype, partition_entries[i].first_lba,
+                partition_entries[i].last_lba);
+    }
+}
+
+unsigned int partition_verify_mbr_signature(unsigned size,
+                                            unsigned char* buffer)
+{
+    /* Avoid checking past end of buffer */
+    if ((TABLE_SIGNATURE + 1) > size)
+    {
+        return MMC_BOOT_E_FAILURE;
+    }
+    /* Check to see if signature exists */
+    if ((buffer[TABLE_SIGNATURE] != MMC_MBR_SIGNATURE_BYTE_0) || \
+        (buffer[TABLE_SIGNATURE + 1] != MMC_MBR_SIGNATURE_BYTE_1))
+    {
+        dprintf(CRITICAL,  "MBR signature does not match. \n" );
+        return MMC_BOOT_E_FAILURE;
+    }
+    return MMC_BOOT_E_SUCCESS;
+}
+
+unsigned int mbr_partition_get_type(unsigned size, unsigned char* partition,
+                                    unsigned int *partition_type)
+{
+    unsigned int type_offset = TABLE_ENTRY_0 + OFFSET_TYPE;
+
+    if (size < type_offset)
+    {
+        goto end;
+    }
+
+    *partition_type = partition[type_offset];
+end:
+    return MMC_BOOT_E_SUCCESS;
+}
+
+unsigned int partition_get_type(unsigned size, unsigned char* partition,
+                                unsigned int *partition_type)
+{
+    unsigned int ret = MMC_BOOT_E_SUCCESS;
+
+    /*
+     * If the block contains the MBR signature, then it's likely either
+     * MBR or MBR with protective type (GPT).  If the MBR signature is
+     * not there, then it could be the GPT backup.
+     */
+
+    /* First check the MBR signature */
+    ret = partition_verify_mbr_signature(size, partition);
+    if (ret == MMC_BOOT_E_SUCCESS)
+    {
+        unsigned int mbr_partition_type = PARTITION_TYPE_MBR;
+
+        /* MBR signature verified.  This could be MBR, MBR + EBR, or GPT */
+        ret = mbr_partition_get_type(size, partition, &mbr_partition_type);
+        if (ret != MMC_BOOT_E_SUCCESS)
+        {
+            dprintf(CRITICAL, "Cannot get TYPE of partition");
+        }
+        else if (MBR_PROTECTED_TYPE == mbr_partition_type)
+        {
+            *partition_type = PARTITION_TYPE_GPT;
+        }
+        else
+        {
+            *partition_type = PARTITION_TYPE_MBR;
+        }
+    }
+    else
+    {
+        /*
+         * This could be the GPT backup.  Make that assumption for now.
+         * Anybody who treats the block as GPT backup should check the
+         * signature.
+         */
+        *partition_type = PARTITION_TYPE_GPT_BACKUP;
+    }
+    return ret;
 }
diff --git a/platform/msm_shared/rules.mk b/platform/msm_shared/rules.mk
index a170109..abe15d7 100644
--- a/platform/msm_shared/rules.mk
+++ b/platform/msm_shared/rules.mk
@@ -41,7 +41,9 @@
 			$(LOCAL_DIR)/i2c_qup.o \
 			$(LOCAL_DIR)/uart_dm.o \
 			$(LOCAL_DIR)/qgic.o \
-			$(LOCAL_DIR)/mdp4.o
+			$(LOCAL_DIR)/mdp4.o \
+			$(LOCAL_DIR)/crypto4_eng.o \
+			$(LOCAL_DIR)/crypto_hash.o
 endif
 
 ifeq ($(PLATFORM),msm7x27a)
diff --git a/platform/msm_shared/smem.h b/platform/msm_shared/smem.h
index d3469d6..6d1a616 100644
--- a/platform/msm_shared/smem.h
+++ b/platform/msm_shared/smem.h
@@ -190,6 +190,7 @@
     HW_PLATFORM_FLUID   = 3,
     HW_PLATFORM_SVLTE   = 4,
     HW_PLATFORM_QT      = 6,
+    HW_PLATFORM_LIQUID  = 9,
     HW_PLATFORM_DRAGON  = 10,
     HW_PLATFORM_32BITS  = 0x7FFFFFFF
 };
diff --git a/platform/omap3/debug.c b/platform/omap3/debug.c
index ac6300d..87aac8d 100644
--- a/platform/omap3/debug.c
+++ b/platform/omap3/debug.c
@@ -37,7 +37,7 @@
 	uart_putc(DEBUG_UART, c);
 }
 
-int dgetc(char *c)
+int dgetc(char *c, bool wait)
 {
 	int _c;
 
@@ -79,8 +79,3 @@
 	PANIC_UNIMPLEMENTED;
 }
 
-uint32_t debug_cycle_count(void)
-{
-//	PANIC_UNIMPLEMENTED;
-	return 0;
-}
diff --git a/platform/omap5912/debug.c b/platform/omap5912/debug.c
index d190fa0..3afb39c 100644
--- a/platform/omap5912/debug.c
+++ b/platform/omap5912/debug.c
@@ -25,9 +25,14 @@
 #include <debug.h>
 #include <printf.h>
 #include <kernel/thread.h>
+#include <kernel/timer.h>
 #include <platform/debug.h>
 #include <arch/ops.h>
 #include <platform/omap5912.h>
+#include <lib/cbuf.h>
+
+static cbuf_t debug_buf;
+static timer_t debug_timer;
 
 static void write_uart_reg(int uart, int reg, unsigned char data)
 {
@@ -94,15 +99,24 @@
 	uart_putc(0, c);
 }
 
-int dgetc(char *c)
+static enum handler_return debug_timer_callback(timer_t *t, time_t now, void *arg)
 {
-	int _c;
+	signed char c;
+	c = uart_getc(0, false);
+	if (c > 0) {
+		cbuf_write(&debug_buf, &c, 1, false);
+		return INT_RESCHEDULE;
+	} else {
+		return INT_NO_RESCHEDULE;
+	}
+}
 
-	if ((_c = uart_getc(0, false)) < 0)
-		return -1;
+int dgetc(char *c, bool wait)
+{
+	ssize_t len;
 
-	*c = _c;
-	return 0;
+	len = cbuf_read(&debug_buf, c, 1, wait);
+	return len;
 }
 
 void debug_dump_regs(void)
@@ -136,8 +150,8 @@
 	PANIC_UNIMPLEMENTED;
 }
 
-uint32_t debug_cycle_count(void)
+void platform_init_debug(void)
 {
-//	PANIC_UNIMPLEMENTED;
-	return 0;
+	cbuf_initialize(&debug_buf, 512);
+	timer_set_periodic(&debug_timer, 10, &debug_timer_callback, NULL);
 }
diff --git a/platform/omap5912/platform.c b/platform/omap5912/platform.c
index 1dd703d..a4dfbbc 100644
--- a/platform/omap5912/platform.c
+++ b/platform/omap5912/platform.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -48,5 +48,6 @@
 
 void platform_init(void)
 {
+	platform_init_debug();
 }
 
diff --git a/platform/omap5912/platform_p.h b/platform/omap5912/platform_p.h
index 872ea2b..c3c3349 100644
--- a/platform/omap5912/platform_p.h
+++ b/platform/omap5912/platform_p.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Travis Geiselbrecht
+ * Copyright (c) 2008-2009 Travis Geiselbrecht
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files
@@ -25,6 +25,7 @@
 
 void platform_init_interrupts(void);
 void platform_init_timer(void);
+void platform_init_debug(void);
 
 #endif
 
diff --git a/platform/omap5912/rules.mk b/platform/omap5912/rules.mk
index e1bddc1..2ab8cbe 100644
--- a/platform/omap5912/rules.mk
+++ b/platform/omap5912/rules.mk
@@ -4,6 +4,9 @@
 ARM_CPU := arm926ej-s
 CPU := generic
 
+MODULES += \
+	lib/cbuf
+
 INCLUDES += \
 	-I$(LOCAL_DIR)/include
 
diff --git a/platform/pc/console.c b/platform/pc/console.c
new file mode 100644
index 0000000..3537539
--- /dev/null
+++ b/platform/pc/console.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <arch/x86.h>
+#include <platform/pc.h>
+#include <platform/console.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/* CGA values */
+#define CURSOR_START		0x0A
+#define CURSOR_END			0x0B
+#define VIDEO_ADDRESS_MSB	0x0C
+#define VIDEO_ADDRESS_LSB	0x0D
+#define CURSOR_POS_MSB		0x0E
+#define CURSOR_POS_LSB		0x0F
+
+/* curr settings */
+static unsigned char curr_x;
+static unsigned char curr_y;
+static unsigned char curr_start;
+static unsigned char curr_end;
+static unsigned char curr_attr;
+
+/* video page buffer */
+#define VPAGE_SIZE		2048
+#define PAGE_MAX		8
+
+static int active_page = 0;
+static int visual_page = 0;
+
+static int curs_x[PAGE_MAX];
+static int curs_y[PAGE_MAX];
+
+static struct {
+	int x1, y1, x2, y2;
+} view_window = {
+	0, 0, 79, 24
+};
+
+void platform_init_console(void)
+{
+	curr_save();
+	window(0, 0, 79, 24);
+	clear();
+	place(0, 0);
+}
+
+void set_visual_page(int page)
+{
+    unsigned short page_offset = page*VPAGE_SIZE;
+    visual_page = page;
+    
+    outp(CGA_INDEX_REG, VIDEO_ADDRESS_LSB);
+    outp(CGA_DATA_REG, page_offset & 0xFF);
+    outp(CGA_INDEX_REG, VIDEO_ADDRESS_MSB);
+    outp(CGA_DATA_REG, (page_offset >> 8) & 0xFF);
+}
+
+void set_active_page(int page)
+{
+    curs_x[active_page] = curr_x;
+    curs_y[active_page] = curr_y;
+    curr_x = curs_x[page];
+    curr_y = curs_y[page];
+    active_page = page;
+}
+
+int get_visual_page(void)
+{
+    return visual_page;
+}
+
+int get_active_page(void)
+{
+    return active_page;
+}
+
+void place(int x,int y)
+{
+    unsigned short cursor_word = x + y*80 + active_page*VPAGE_SIZE;
+    
+    /*
+     * program CGA using index reg, then data reg
+     */
+    outp(CGA_INDEX_REG, CURSOR_POS_LSB);
+    outp(CGA_DATA_REG, cursor_word & 0xFF);
+    outp(CGA_INDEX_REG, CURSOR_POS_MSB);
+    outp(CGA_DATA_REG, (cursor_word >> 8) & 0xFF);
+    
+    curr_x = x;
+    curr_y = y;
+}
+
+void cursor(int start,int end)
+{
+    outp(CGA_INDEX_REG, CURSOR_START);
+    outp(CGA_DATA_REG, start);
+    outp(CGA_INDEX_REG, CURSOR_END);
+    outp(CGA_DATA_REG, end);
+}
+
+void curr_save(void)
+{
+    /* grab some info from the bios data area (these should be defined in memmap.h */
+    curr_attr = *((unsigned char *)0xB8000 + 159);
+    curr_x = *((unsigned char *)0x00450);
+    curr_y = *((unsigned char *)0x00451);
+    curr_end = *((unsigned char *)0x00460);
+    curr_start = *((unsigned char *)0x00461);
+    active_page = visual_page = 0;
+}
+
+void curr_restore(void)
+{
+    *((unsigned char *)0x00450) = curr_x;
+    *((unsigned char *)0x00451) = curr_y;
+    
+    place(curr_x, curr_y);
+    cursor(curr_start, curr_end);
+}
+
+void window(int x1, int y1, int x2, int y2) {
+	view_window.x1 = x1;
+	view_window.y1 = y1;
+	view_window.x2 = x2;
+	view_window.y2 = y2;
+	
+	//place(x1, y1);
+}
+
+void _clear(char c,char attr,int x1,int y1,int x2,int y2)
+{
+	register int i,j;
+	unsigned short w = attr;
+	
+	w <<= 8; w |= c;
+	for (i = x1; i <= x2; i++) {
+		for (j = y1; j <= y2; j++) { 
+			*((unsigned short *)(0xB8000 + 2*i+160*j + 2*active_page*VPAGE_SIZE)) = w;
+	    }
+	}
+	
+	place(x1,y1);
+	curr_y = y1;
+	curr_x = x1;
+}
+
+void clear()
+{
+    _clear(' ', curr_attr, view_window.x1, view_window.y1, view_window.x2,
+    	view_window.y2);
+}
+
+void _scroll(char attr, int x1, int y1, int x2, int y2)
+{
+    register int x,y;
+    unsigned short xattr = attr << 8,w;
+    unsigned char *v = (unsigned char *)(0xB8000 + active_page*(2*VPAGE_SIZE));
+      
+    for (y = y1+1; y <= y2; y++) {
+		for (x = x1; x <= x2; x++) {
+			w = *((unsigned short *) (v + 2*(y*80+x)));
+			*((unsigned short *)(v + 2*((y-1)*80+x))) = w;
+		}
+	}
+	
+    for (x = x1; x <= x2; x++) {
+   		*((unsigned short *)(v + 2*((y-1)*80+x))) = xattr;
+   	}
+}
+
+void scroll(void)
+{
+    _scroll(curr_attr, view_window.x1, view_window.y1, view_window.x2,
+    	view_window.y2);
+}
+
+void cputc(char c)
+{
+    static unsigned short scan_x, x, y;
+    unsigned char *v = (unsigned char *)(0xB8000 + active_page*(2*VPAGE_SIZE));
+    x = curr_x;
+    y = curr_y;
+    
+    switch (c) {
+	case '\t':
+		x += 8;		
+		if (x >= view_window.x2+1) {
+			x = view_window.x1;
+			if (y == view_window.y2) {
+				scroll();
+			} else {
+				y++;
+			}
+		} else {
+			scan_x = 0;
+			
+			while ((scan_x+8) < x) {
+				scan_x += 8;
+			}
+			
+			x = scan_x;
+		}
+		break;
+		
+	case '\n':
+		x = view_window.x1;
+		if (y == view_window.y2) {
+			scroll();
+		} else {
+			y++;
+		}
+		break;
+		
+	case '\b':
+		x--;
+		*(v + 2*(x + y*80)) = ' ';
+		break;
+		
+	default:
+		*(v + 2*(x + y*80)) = c;
+		x++;
+    	
+    	if (x >= view_window.x2+1) {
+			x = view_window.x1;
+			if (y == view_window.y2) {
+				scroll();
+			} else {
+				y++;
+			}
+		}
+    }
+    
+    place(x, y);
+}
+
+void cputs(char *s)
+{
+    char c;
+    while (*s != '\0') {
+		c = *s++;
+		cputc(c);
+    }
+}
+
+void puts_xy(int x,int y,char attr,char *s)
+{
+    unsigned char *v = (unsigned char *)(0xB8000 + (80*y+x)*2 + active_page*(2*VPAGE_SIZE));
+    while (*s != 0) {
+		*v = *s; s++; v++;
+		*v = attr; v++;
+    }
+}
+
+void putc_xy(int x, int y, char attr, char c)
+{
+    unsigned char *v = (unsigned char *)(0xB8000 + (80*y+x)*2 + active_page*(2*VPAGE_SIZE));
+    *v = c; v++;
+    *v = attr;
+}
+
+int printf_xy(int x, int y, char attr, char *fmt, ...)
+{
+	char cbuf[200];
+	va_list parms;
+	int result;
+	
+	va_start(parms, fmt);
+	result = vsprintf(cbuf, fmt, parms);
+	va_end(parms);
+	
+	puts_xy(x, y, attr, cbuf);
+	
+	return result;
+}
diff --git a/platform/pc/debug.c b/platform/pc/debug.c
new file mode 100644
index 0000000..b1d70ee
--- /dev/null
+++ b/platform/pc/debug.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <stdarg.h>
+#include <reg.h>
+#include <printf.h>
+#include <kernel/thread.h>
+#include <arch/x86.h>
+#include <platform/pc/memmap.h>
+#include <platform/console.h>
+#include <platform/keyboard.h>
+#include <platform/debug.h>
+
+void _dputc(char c)
+{
+	cputc(c);
+}
+
+int dgetc(char *c, bool wait)
+{
+	int ret =  platform_read_key(c);
+	//if (ret < 0)
+	//	arch_idle();
+	
+	return ret;
+}
+
+void debug_dump_regs(void)
+{
+}
+
+void platform_halt(void)
+{
+	for(;;) {
+		x86_cli();
+		x86_hlt();
+	}
+}
+
+void debug_dump_memory_bytes(void *mem, int len)
+{
+}
+
+void debug_dump_memory_halfwords(void *mem, int len)
+{
+}
+
+void debug_dump_memory_words(void *mem, int len)
+{
+}
+
+void debug_set_trace_level(int trace_type, int level)
+{
+}
+
diff --git a/platform/pc/include/platform/console.h b/platform/pc/include/platform/console.h
new file mode 100644
index 0000000..3a1b802
--- /dev/null
+++ b/platform/pc/include/platform/console.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __PLATFORM_CONSOLE_H
+#define __PLATFORM_CONSOLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void platform_init_console(void);
+
+void set_visual_page(int page);
+void set_active_page(int page);
+
+int get_visual_page(void);
+int get_active_page(void);
+
+void place(int x,int y);
+void cursor(int start, int end);
+
+void _clear(char c, char attr, int x1, int y1, int x2, int y2);
+void clear(void);
+
+void _scroll(char attr, int x1, int y1, int x2, int y2);
+void scroll(void);
+
+void curr_save(void);
+void curr_restore(void);
+
+void cputc(char c);
+void cputs(char *s);
+
+void window(int x1, int y1, int x2, int y2);
+
+void putc_xy(int x, int y, char attr, char c);
+void puts_xy(int x, int y, char attr, char *s);
+
+int printf_xy(int x, int y, char attr, char *fmt, ...) __PRINTFLIKE(4, 5);
+
+#define CURSOR_BLOCK()	cursor(0, 15);
+#define CURSOR_OFF()	cursor(16, 16);
+#define CURSOR_STD()	cursor(14, 15);
+
+/* text colors */
+#define BLACK			0
+#define BLUE			1
+#define GREEN			2
+#define CYAN			3
+#define RED				4
+#define MAGENTA			5
+#define BROWN			6
+#define LIGHTGRAY		7
+#define DARKGRAY		8
+#define LIGHTBLUE		9
+#define LIGHTGREEN		10
+#define LIGHTCYAN		11
+#define LIGHTRED		12
+#define LIGHTMAGENTA	13
+#define YELLOW 			14
+#define WHITE			15
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/platform/pc/include/platform/keyboard.h b/platform/pc/include/platform/keyboard.h
new file mode 100644
index 0000000..5464995
--- /dev/null
+++ b/platform/pc/include/platform/keyboard.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __PLATFORM_KEYBOARD_H
+#define __PLATFORM_KEYBOARD_H
+
+void platform_init_keyboard(void);
+
+int platform_read_key(char *c);
+
+#endif
diff --git a/platform/pc/include/platform/multiboot.h b/platform/pc/include/platform/multiboot.h
new file mode 100644
index 0000000..f5d3310
--- /dev/null
+++ b/platform/pc/include/platform/multiboot.h
@@ -0,0 +1,137 @@
+/*

+ * Copyright (c) 2009 Corey Tabaka

+ *

+ * 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.

+ */

+#ifndef __PLATFORM_MULTIBOOT_H

+#define __PLATFORM_MULTIBOOT_H

+

+#include <sys/types.h>

+     

+/* magic number for multiboot header */

+#define MULTIBOOT_HEADER_MAGIC		0x1BADB002

+

+/* flags for multiboot header */

+#ifdef __ELF__

+#define MULTIBOOT_HEADER_FLAGS		0x00000003

+#else

+#define MULTIBOOT_HEADER_FLAGS		0x00010003

+#endif

+

+/* magic number passed by multiboot-compliant boot loaders */

+#define MULTIBOOT_BOOTLOADER_MAGIC	0x2BADB002

+

+#ifndef ASSEMBLY

+

+/* multiboot header */

+typedef struct multiboot_header

+{

+	uint32_t magic;

+	uint32_t flags;

+	uint32_t checksum;

+	uint32_t header_addr;

+	uint32_t load_addr;

+	uint32_t load_end_addr;

+	uint32_t bss_end_addr;

+	uint32_t entry_addr;

+} multiboot_header_t;

+

+/* symbol table for a.out */

+typedef struct aout_symbol_table

+{

+	uint32_t tabsize;

+	uint32_t strsize;

+	uint32_t addr;

+	uint32_t reserved;

+} aout_symbol_table_t;

+

+/* section header table for ELF */

+typedef struct elf_section_header_table

+{

+	uint32_t num;

+	uint32_t size;

+	uint32_t addr;

+	uint32_t shndx;

+} elf_section_header_table_t;

+

+/* multiboot info */

+typedef struct multiboot_info

+{

+	uint32_t flags;

+	uint32_t mem_lower;

+	uint32_t mem_upper;

+	uint32_t boot_device;

+	uint32_t cmdline;

+	uint32_t mods_count;

+	uint32_t mods_addr;

+	union

+	{

+		aout_symbol_table_t aout_sym;

+		elf_section_header_table_t elf_sec;

+	} u;

+	uint32_t mmap_length;

+	uint32_t mmap_addr;

+} multiboot_info_t;

+

+enum {

+	MB_INFO_MEM_SIZE	= 0x001,

+	MB_INFO_BOOT_DEV	= 0x002,

+	MB_INFO_CMD_LINE	= 0x004,

+	MB_INFO_MODS		= 0x008,

+	MB_INFO_SYMS		= 0x010,

+	MB_INFO_MMAP		= 0x020,

+	MB_INFO_DRIVES		= 0x040,

+	MB_INFO_CONFIG		= 0x080,

+	MB_INFO_BOOT_LOADER	= 0x100,

+	MB_INFO_APM_TABLE	= 0x200,

+	MB_INFO_VBE			= 0x400,

+};

+

+/* module structure */

+typedef struct module

+{

+	uint32_t mod_start;

+	uint32_t mod_end;

+	uint32_t string;

+	uint32_t reserved;

+} module_t;

+

+/* memory map - be careful that the offset 0 is base_addr_low without size */

+typedef struct memory_map

+{

+	uint32_t size;

+	uint32_t base_addr_low;

+	uint32_t base_addr_high;

+	uint32_t length_low;

+	uint32_t length_high;

+	uint32_t type;

+} memory_map_t;

+

+/* memory map entry types */

+enum {

+	MB_MMAP_TYPE_AVAILABLE		= 0x01,

+	MB_MMAP_TYPE_RESERVED		= 0x02,

+	MB_MMAP_TYPE_ACPI_RECLAIM	= 0x03,

+	MB_MMAP_TYPE_ACPI_NVS		= 0x04,

+};

+

+#endif

+

+#endif

diff --git a/platform/pc/include/platform/pc.h b/platform/pc/include/platform/pc.h
new file mode 100644
index 0000000..a8a7251
--- /dev/null
+++ b/platform/pc/include/platform/pc.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __PLATFORM_PC_H
+#define __PLATFORM_PC_H
+
+#include <platform/pc/memmap.h>
+#include <platform/pc/iomap.h>
+
+/* NOTE: keep arch/x86/crt0.S in sync with these definitions */
+
+/* interrupts */
+#define INT_VECTORS 0x31
+
+/* defined interrupts */
+#define INT_BASE			0x20
+#define INT_PIT				0x20
+#define INT_KEYBOARD		0x21
+#define INT_PIC2			0x22
+
+#define INT_BASE2			0x28
+#define INT_CMOSRTC			0x28
+#define INT_PS2MOUSE		0x2c
+#define INT_IDE0			0x2e
+#define INT_IDE1			0x2f
+
+/* exceptions */
+#define INT_DIVIDE_0		0x00
+#define INT_DEBUG_EX		0x01
+#define INT_INVALID_OP		0x06
+#define INT_DEV_NA_EX		0x07
+
+/* faults */
+#define INT_STACK_FAULT		0x0c
+#define INT_GP_FAULT		0x0d
+#define INT_PAGE_FAULT		0x0e
+
+/* APIC vectors */
+#define INT_APIC_TIMER		0x22
+
+#define INT_SYSCALL			0x30
+
+/* PIC remap bases */
+#define PIC1_BASE 0x20
+#define PIC2_BASE 0x28
+
+#endif
+
diff --git a/platform/pc/include/platform/pc/iomap.h b/platform/pc/include/platform/pc/iomap.h
new file mode 100644
index 0000000..fe75964
--- /dev/null
+++ b/platform/pc/include/platform/pc/iomap.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __IOMAP_H
+#define __IOMAP_H
+
+/* i8253/i8254 programmable interval timer registers */
+#define I8253_CONTROL_REG	0x43
+#define I8253_DATA_REG		0x40
+
+/* i8042 keyboard controller registers */
+#define I8042_COMMAND_REG	0x64
+#define I8042_STATUS_REG	0x64
+#define I8042_DATA_REG		0x60
+
+/* CGA registers */
+#define CGA_INDEX_REG		0x3D4
+#define CGA_DATA_REG		0x3D5
+
+#endif
diff --git a/platform/pc/include/platform/pc/memmap.h b/platform/pc/include/platform/pc/memmap.h
new file mode 100644
index 0000000..1243aad
--- /dev/null
+++ b/platform/pc/include/platform/pc/memmap.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __MEMMAP_H
+#define __MEMMAP_H
+
+/* some helpful macros */
+#define REG(x) ((volatile unsigned int *)(x))
+#define REG_H(x) ((volatile unsigned short *)(x))
+#define REG_B(x) ((volatile unsigned char *)(x))
+
+/* TODO: put fixed memory address definitions here */
+
+#endif
diff --git a/platform/pc/interrupts.c b/platform/pc/interrupts.c
new file mode 100644
index 0000000..c507c19
--- /dev/null
+++ b/platform/pc/interrupts.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <debug.h>
+#include <err.h>
+#include <reg.h>
+#include <kernel/thread.h>
+#include <platform/interrupts.h>
+#include <arch/ops.h>
+#include <arch/x86.h>
+#include "platform_p.h"
+#include <platform/pc.h>
+
+void x86_gpf_handler(struct x86_iframe *frame);
+void x86_invop_handler(struct x86_iframe *frame);
+void x86_unhandled_exception(struct x86_iframe *frame);
+
+#define PIC1 0x20
+#define PIC2 0xA0
+
+#define ICW1 0x11
+#define ICW4 0x01
+
+struct int_handler_struct {
+	int_handler handler;
+	void *arg;
+};
+
+static struct int_handler_struct int_handler_table[INT_VECTORS];
+
+/*
+ * Cached IRQ mask (enabled/disabled)
+ */
+static uint8_t irqMask[2];
+
+/*
+ * init the PICs and remap them
+ */
+static void map(uint32_t pic1, uint32_t pic2)
+{
+	/* send ICW1 */
+	outp(PIC1, ICW1);
+	outp(PIC2, ICW1);
+
+	/* send ICW2 */
+	outp(PIC1 + 1, pic1);	/* remap */
+	outp(PIC2 + 1, pic2);	/*  pics */
+
+	/* send ICW3 */
+	outp(PIC1 + 1, 4);	/* IRQ2 -> connection to slave */
+	outp(PIC2 + 1, 2);
+
+	/* send ICW4 */
+	outp(PIC1 + 1, 5);
+	outp(PIC2 + 1, 1);
+
+	/* disable all IRQs */
+	outp(PIC1 + 1, 0xff);
+	outp(PIC2 + 1, 0xff);
+	
+	irqMask[0] = 0xff;
+	irqMask[1] = 0xff;
+}
+
+static void enable(unsigned int vector, bool enable)
+{
+	if (vector >= PIC1_BASE && vector < PIC1_BASE + 8) {
+		vector -= PIC1_BASE;
+		
+		uint8_t bit = 1 << vector;
+		
+		if (enable && (irqMask[0] & bit)) {
+			irqMask[0] = inp(PIC1 + 1);
+			irqMask[0] &= ~bit;
+			outp(PIC1 + 1, irqMask[0]);
+			irqMask[0] = inp(PIC1 + 1);
+		} else if (!enable && !(irqMask[0] & bit)) {
+			irqMask[0] = inp(PIC1 + 1);
+			irqMask[0] |= bit;
+			outp(PIC1 + 1, irqMask[0]);
+			irqMask[0] = inp(PIC1 + 1);
+		}
+	} else if (vector >= PIC2_BASE && vector < PIC2_BASE + 8) {
+		vector -= PIC2_BASE;
+		
+		uint8_t bit = 1 << vector;
+		
+		if (enable && (irqMask[1] & bit)) {
+			irqMask[1] = inp(PIC2 + 1);
+			irqMask[1] &= ~bit;
+			outp(PIC2 + 1, irqMask[1]);
+			irqMask[1] = inp(PIC2 + 1);
+		} else if (!enable && !(irqMask[1] & bit)) {
+			irqMask[1] = inp(PIC2 + 1);
+			irqMask[1] |= bit;
+			outp(PIC2 + 1, irqMask[1]);
+			irqMask[1] = inp(PIC2 + 1);
+		}
+		
+		bit = 1 << (INT_PIC2 - PIC1_BASE);
+		
+		if (irqMask[1] != 0xff && (irqMask[0] & bit)) {
+			irqMask[0] = inp(PIC1 + 1);
+			irqMask[0] &= ~bit;
+			outp(PIC1 + 1, irqMask[0]);
+			irqMask[0] = inp(PIC1 + 1);
+		} else if (irqMask[1] == 0 && !(irqMask[0] & bit)) {
+			irqMask[0] = inp(PIC1 + 1);
+			irqMask[0] |= bit;
+			outp(PIC1 + 1, irqMask[0]);
+			irqMask[0] = inp(PIC1 + 1);
+		}
+	} else {
+		//dprintf(DEBUG, "Invalid PIC interrupt: %02x\n", vector);
+	}
+}
+
+void issueEOI(unsigned int vector)
+{
+	if (vector >= PIC1_BASE && vector <= PIC1_BASE + 7) {
+		outp(PIC1, 0x20);
+	} else if (vector >= PIC2_BASE && vector <= PIC2_BASE + 7) {
+		outp(PIC2, 0x20);
+		outp(PIC1, 0x20);	// must issue both for the second PIC
+	}
+}
+
+void platform_init_interrupts(void)
+{
+	// rebase the PIC out of the way of processor exceptions
+	map(PIC1_BASE, PIC2_BASE);
+}
+
+status_t mask_interrupt(unsigned int vector)
+{
+	if (vector >= INT_VECTORS)
+		return ERR_INVALID_ARGS;
+
+//	dprintf(DEBUG, "%s: vector %d\n", __PRETTY_FUNCTION__, vector);
+
+	enter_critical_section();
+
+	enable(vector, false);
+
+	exit_critical_section();
+
+	return NO_ERROR;
+}
+
+
+void platform_mask_irqs(void)
+{
+	irqMask[0] = inp(PIC1 + 1);
+	irqMask[1] = inp(PIC2 + 1);
+	
+	outp(PIC1 + 1, 0xff);
+	outp(PIC2 + 1, 0xff);
+	
+	irqMask[0] = inp(PIC1 + 1);
+	irqMask[1] = inp(PIC2 + 1);
+}
+
+status_t unmask_interrupt(unsigned int vector)
+{
+	if (vector >= INT_VECTORS)
+		return ERR_INVALID_ARGS;
+
+//	dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);
+
+	enter_critical_section();
+
+	enable(vector, true);
+
+	exit_critical_section();
+
+	return NO_ERROR;
+}
+
+enum handler_return platform_irq(struct x86_iframe *frame)
+{
+	// get the current vector
+	unsigned int vector = frame->vector;
+
+#if THREAD_STATS
+	thread_stats.interrupts++;
+#endif
+	
+	// deliver the interrupt	
+	enum handler_return ret = INT_NO_RESCHEDULE;
+	
+	switch (vector) {
+		case INT_GP_FAULT:
+			x86_gpf_handler(frame);
+			break;
+		
+		case INT_INVALID_OP:
+			x86_invop_handler(frame);
+			break;
+		
+		case INT_DIVIDE_0:
+		case INT_DEBUG_EX:
+		case INT_DEV_NA_EX:
+		case INT_PAGE_FAULT:
+		case INT_STACK_FAULT:
+		case 3:
+			x86_unhandled_exception(frame);
+			break;
+		
+		default:
+			if (int_handler_table[vector].handler)
+				ret = int_handler_table[vector].handler(int_handler_table[vector].arg);
+	}
+
+	// ack the interrupt
+	issueEOI(vector);
+
+	return ret;
+}
+
+void register_int_handler(unsigned int vector, int_handler handler, void *arg)
+{
+	if (vector >= INT_VECTORS)
+		panic("register_int_handler: vector out of range %d\n", vector);
+
+	enter_critical_section();
+
+	int_handler_table[vector].arg = arg;
+	int_handler_table[vector].handler = handler;
+
+	exit_critical_section();
+}
+
+
diff --git a/platform/pc/keyboard.c b/platform/pc/keyboard.c
new file mode 100644
index 0000000..27e3d3c
--- /dev/null
+++ b/platform/pc/keyboard.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <err.h>
+#include <reg.h>
+#include <debug.h>
+#include <kernel/thread.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/console.h>
+#include <platform/timer.h>
+#include <platform/pc.h>
+#include "platform_p.h"
+#include <arch/x86.h>
+#include <lib/cbuf.h>
+
+static inline int i8042_read_data(void)
+{
+	return inp(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return inp(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	outp(I8042_DATA_REG, val);
+}
+
+static inline void i8042_write_command(int val)
+{
+	outp(I8042_COMMAND_REG, val);
+}
+
+/*
+ * timeout in milliseconds
+ */
+#define I8042_CTL_TIMEOUT	500
+
+/*
+ * status register bits
+ */
+#define I8042_STR_PARITY	0x80
+#define I8042_STR_TIMEOUT	0x40
+#define I8042_STR_AUXDATA	0x20
+#define I8042_STR_KEYLOCK	0x10
+#define I8042_STR_CMDDAT	0x08
+#define I8042_STR_MUXERR	0x04
+#define I8042_STR_IBF		0x02
+#define	I8042_STR_OBF		0x01
+
+/*
+ * control register bits
+ */
+#define I8042_CTR_KBDINT	0x01
+#define I8042_CTR_AUXINT	0x02
+#define I8042_CTR_IGNKEYLK	0x08
+#define I8042_CTR_KBDDIS	0x10
+#define I8042_CTR_AUXDIS	0x20
+#define I8042_CTR_XLATE		0x40
+
+/*
+ * commands
+ */
+#define I8042_CMD_CTL_RCTR	0x0120
+#define I8042_CMD_CTL_WCTR	0x1060
+#define I8042_CMD_CTL_TEST	0x01aa
+
+#define I8042_CMD_KBD_DIS	0x00ad
+#define I8042_CMD_KBD_EN	0x00ae
+#define I8042_CMD_KBD_TEST	0x01ab
+#define I8042_CMD_KBD_MODE	0x01f0
+
+/*
+ * used for flushing buffers. the i8042 internal buffer shoudn't exceed this.
+ */
+#define I8042_BUFFER_LENGTH	32
+
+static inline void delay(time_t delay) {
+	bigtime_t start = current_time();
+	
+	while (start + delay > current_time());
+}
+
+/* scancodes we want to do something with that don't translate via table */
+#define SCANCODE_LSHIFT 0x2a
+#define SCANCODE_RSHIFT 0x36
+
+/* scancode translation tables */
+static const int KeyCodeSingleLower[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+  -1,  -1, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', // 0
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\n',  -1, 'a', 's', // 1
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',  -1,'\\', 'z', 'x', 'c', 'v', // 2
+ 'b', 'n', 'm', ',', '.', '/',  -1, '*',  -1, ' ',  -1,  -1,  -1,  -1,  -1,  -1, // 3
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+static const int KeyCodeMultiLower[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 0
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 1
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 2
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 3
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+static const int KeyCodeSingleUpper[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+  -1,  -1, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', // 0
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\n',  -1, 'A', 'S', // 1
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',  -1, '|', 'Z', 'X', 'C', 'V', // 2
+ 'B', 'N', 'M', '<', '>', '?',  -1, '*',  -1, ' ',  -1,  -1,  -1,  -1,  -1,  -1, // 3
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+static const int KeyCodeMultiUpper[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 0
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 1
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 2
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 3
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+/*
+ * state key flags
+ */
+static bool key_lshift;
+static bool key_rshift;
+
+static cbuf_t key_buf;
+
+static void i8042_process_scode(uint8_t scode, unsigned int flags)
+{
+	static int lastCode = 0;
+	int keyCode;
+	uint8_t keyUpBit;
+	
+	bool multi = lastCode == 0xe0;
+	
+	// save the key up event bit
+	keyUpBit = scode & 0x80;
+	scode &= 0x7f;
+	
+	if (scode == SCANCODE_LSHIFT) {
+		key_lshift = !keyUpBit;
+	}
+	
+	if (scode == SCANCODE_RSHIFT) {
+		key_rshift = !keyUpBit;
+	}
+	
+	if (key_lshift || key_rshift) {
+		keyCode = multi ? KeyCodeMultiUpper[scode] : KeyCodeSingleUpper[scode];
+	} else {
+		keyCode = multi ? KeyCodeMultiLower[scode] : KeyCodeSingleLower[scode];
+	}
+	
+	/*printf_xy(71, 3, BLUE, "%02x%02x %c %c%c", multi ? lastCode : 0, scode,
+		keyCode != -1 ? (char) keyCode : ' ', key_lshift ? 'L' : ' ',
+		key_rshift ? 'R' : ' ');*/
+	
+	if (keyCode != -1 && !keyUpBit) {
+		char c = (char) keyCode;
+		cbuf_write(&key_buf, &c, 1, false);
+	}
+	
+	// update the last received code
+	lastCode = scode;
+}
+
+static int i8042_wait_read(void)
+{
+	int i = 0;
+	while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
+		delay(1);
+		i++;
+	}
+	return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_wait_write(void)
+{
+	int i = 0;
+	while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
+		delay(1);
+		i++;
+	}
+	return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_flush(void)
+{
+	unsigned char data;
+	int i = 0;
+
+	//enter_critical_section();
+
+	while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) {
+		delay(1);
+		data = i8042_read_data();
+	}
+
+	//exit_critical_section();
+
+	return i;
+}
+
+static int i8042_command(uint8_t *param, int command)
+{
+	int retval = 0, i = 0;
+
+	//enter_critical_section();
+
+	retval = i8042_wait_write();
+	if (!retval) {
+		i8042_write_command(command & 0xff);
+	}
+
+	if (!retval) {
+		for (i = 0; i < ((command >> 12) & 0xf); i++) {
+			if ((retval = i8042_wait_write())) {
+				break;
+			}
+			
+			i8042_write_data(param[i]);
+		}
+	}
+
+	if (!retval) {
+		for (i = 0; i < ((command & 0xf0) >> 8); i++) {
+			if ((retval = i8042_wait_read())) {
+				break;
+			}
+			
+			if (i8042_read_status() & I8042_STR_AUXDATA) {
+				param[i] = ~i8042_read_data();
+			} else {
+				param[i] = i8042_read_data();
+			}
+		}
+	}
+
+	//exit_critical_section();
+
+	return retval;
+}
+
+static enum handler_return i8042_interrupt(void *arg)
+{
+	uint8_t str, data = 0;
+
+	//enter_critical_section();
+	str = i8042_read_status();
+	if (str & I8042_STR_OBF) {
+		data = i8042_read_data();
+	}
+	//exit_critical_section();
+
+	if (str & I8042_STR_OBF) {
+		i8042_process_scode(data,
+			((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) |
+			((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0));
+	}
+	
+	return INT_NO_RESCHEDULE;
+}
+
+int platform_read_key(char *c)
+{
+	ssize_t len;
+
+	len = cbuf_read(&key_buf, c, 1, true);
+	return len;
+}
+
+void platform_init_keyboard(void)
+{
+	uint8_t ctr;
+	
+	cbuf_initialize(&key_buf, 32);
+
+	i8042_flush();
+	
+	if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
+		dprintf(DEBUG, "Failed to read CTR while initializing i8042\n");
+		return;
+	}
+	
+	// turn on translation
+	ctr |= I8042_CTR_XLATE;
+	
+	// enable keyboard and keyboard irq
+	ctr &= ~I8042_CTR_KBDDIS;
+	ctr |= I8042_CTR_KBDINT;
+	
+	if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
+		dprintf(DEBUG, "Failed to write CTR while initializing i8042\n");
+		return;
+	}
+	
+	register_int_handler(INT_KEYBOARD, &i8042_interrupt, NULL);
+	unmask_interrupt(INT_KEYBOARD);
+	
+	i8042_interrupt(NULL);
+}
diff --git a/platform/pc/pci.c b/platform/pc/pci.c
new file mode 100644
index 0000000..c1c255f
--- /dev/null
+++ b/platform/pc/pci.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <debug.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kernel/thread.h>
+#include <arch/x86/descriptor.h>
+#include <dev/pci.h>
+
+static int last_bus = 0;
+
+typedef struct {
+	uint16_t size;
+	void *offset;
+	uint16_t selector;
+} __PACKED irq_routing_options_t;
+
+static int pci_type1_detect(void);
+static int pci_bios_detect(void);
+
+int pci_get_last_bus(void)
+{
+	return last_bus;
+}
+
+/*
+ * pointers to installed PCI routines
+ */
+int (*g_pci_find_pci_device)(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
+int (*g_pci_find_pci_class_code)(pci_location_t *state, uint32_t class_code, uint16_t index);
+ 
+int (*g_pci_read_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t *value);
+int (*g_pci_read_config_half)(const pci_location_t *state, uint32_t reg, uint16_t *value);
+int (*g_pci_read_config_word)(const pci_location_t *state, uint32_t reg, uint32_t *value);
+
+int (*g_pci_write_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t value);
+int (*g_pci_write_config_half)(const pci_location_t *state, uint32_t reg, uint16_t value);
+int (*g_pci_write_config_word)(const pci_location_t *state, uint32_t reg, uint32_t value);
+	
+int (*g_pci_get_irq_routing_options)(irq_routing_options_t *options, uint16_t *pci_irqs);
+int (*g_pci_set_irq_hw_int)(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
+
+
+int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
+{
+	enter_critical_section();
+	
+	int res = g_pci_find_pci_device(state, device_id, vendor_id, index);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
+{
+	enter_critical_section();
+	
+	int res = g_pci_find_pci_class_code(state, class_code, index);
+	
+	exit_critical_section();
+	
+	return res;
+}
+ 
+int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_read_config_byte(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_read_config_half(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_read_config_word(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_write_config_byte(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_write_config_half(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_write_config_word(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+	
+int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs)
+{
+	enter_critical_section();
+	
+	irq_routing_options_t options;
+	options.size = sizeof(irq_routing_entry) * *count;
+	options.selector = DATA_SELECTOR;
+	options.offset = entries;
+	
+	int res = g_pci_get_irq_routing_options(&options, pci_irqs);
+	
+	*count = options.size / sizeof(irq_routing_entry);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
+{
+	enter_critical_section();
+	
+	int res = g_pci_set_irq_hw_int(state, int_pin, irq);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+void pci_init(void)
+{
+	if (!pci_bios_detect()) {
+		dprintf(INFO, "pci bios functions installed\n");
+		dprintf(INFO, "last pci bus is %d\n", last_bus);
+	}
+}
+
+#define PCIBIOS_PRESENT					0xB101
+#define PCIBIOS_FIND_PCI_DEVICE			0xB102
+#define PCIBIOS_FIND_PCI_CLASS_CODE		0xB103
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE	0xB106
+#define PCIBIOS_READ_CONFIG_BYTE		0xB108
+#define PCIBIOS_READ_CONFIG_WORD		0xB109
+#define PCIBIOS_READ_CONFIG_DWORD		0xB10A
+#define PCIBIOS_WRITE_CONFIG_BYTE		0xB10B
+#define PCIBIOS_WRITE_CONFIG_WORD		0xB10C
+#define PCIBIOS_WRITE_CONFIG_DWORD		0xB10D
+#define PCIBIOS_GET_IRQ_ROUTING_OPTIONS	0xB10E
+#define PCIBIOS_PCI_SET_IRQ_HW_INT		0xB10F
+
+#define PCIBIOS_SUCCESSFUL				0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED		0x81
+#define PCIBIOS_BAD_VENDOR_ID			0x83
+#define PCIBIOS_DEVICE_NOT_FOUND		0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER		0x87
+#define PCIBIOS_SET_FAILED				0x88
+#define PCIBIOS_BUFFER_TOO_SMALL		0x89
+
+/*
+ * far call structure used by BIOS32 routines
+ */
+static struct {
+	uint32_t offset;
+	uint16_t selector;
+} __PACKED bios32_entry;
+
+/*
+ * BIOS32 entry header
+ */
+typedef struct {
+	uint8_t magic[4];	// "_32_"
+	void * entry;		// entry point
+	uint8_t revision;
+	uint8_t length;
+	uint8_t checksum;
+	uint8_t reserved[5];
+} __PACKED pci_bios_info;
+
+/*
+ * scan for pci bios
+ */
+static const char * pci_bios_magic = "_32_";
+static pci_bios_info *find_pci_bios_info(void) {
+	uint32_t *head = (uint32_t *) 0x000e0000;
+	int8_t sum, *b;
+	uint i;
+	
+	while (head < (uint32_t *) 0x000ffff0) {
+		if (*head == *(uint32_t *) pci_bios_magic) {
+			// perform the checksum
+			sum = 0;
+			b = (int8_t *) head;
+			for (i=0; i < sizeof(pci_bios_info); i++) {
+				sum += b[i];
+			}
+			
+			if (sum == 0) {
+				return (pci_bios_info *) head;
+			}
+		}
+		
+		head += 4;
+	}
+	
+	return NULL;
+}
+
+/*
+ * local BIOS32 PCI routines
+ */
+static int bios_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
+{
+	uint32_t bx, ret;
+
+	__asm__(
+		"lcall *(%%edi)		\n\t"
+		"jc 1f				\n\t"
+		"xor %%ah,%%ah		\n"
+		"1:"
+		: "=b"(bx),
+		  "=a"(ret)
+		: "1"(PCIBIOS_FIND_PCI_DEVICE),
+		  "c"(device_id),
+		  "d"(vendor_id),
+		  "S"(index),
+		  "D"(&bios32_entry));
+	
+	state->bus = bx >> 8;
+	state->dev_fn = bx & 0xFF;
+	
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
+{
+	uint32_t bx, ret;
+
+	__asm__(
+		"lcall *(%%edi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=b"(bx),
+		  "=a"(ret)
+		: "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
+		  "c"(class_code),
+		  "S"(index),
+		  "D"(&bios32_entry));
+	
+	state->bus = bx >> 8;
+	state->dev_fn = bx & 0xFF;
+	
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+
+static int bios_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=c"(*value),
+		  "=a"(ret)
+		: "1"(PCIBIOS_READ_CONFIG_BYTE),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=c"(*value),
+		  "=a"(ret)
+		: "1"(PCIBIOS_READ_CONFIG_WORD),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=c"(*value),
+		  "=a"(ret)
+		: "1"(PCIBIOS_READ_CONFIG_DWORD),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	 __asm__(
+	 	"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_WRITE_CONFIG_BYTE),
+		  "c"(value),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	 __asm__(
+	 	"lcall *(%%esi)	\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_WRITE_CONFIG_WORD),
+		  "c"(value),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	 __asm__(
+	 	"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_WRITE_CONFIG_DWORD),
+		  "c"(value),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_get_irq_routing_options(irq_routing_options_t *route_buffer, uint16_t *pciIrqs)
+{
+	uint32_t ret;
+	
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=b"(*pciIrqs),
+		  "=a"(ret)
+		: "1"(PCIBIOS_GET_IRQ_ROUTING_OPTIONS),
+		  "b"(0),
+		  "D"(route_buffer),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xff;
+}
+
+static int bios_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
+{
+	uint32_t bx, cx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	cx = irq;
+	cx <<= 8;
+	cx |= int_pin;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_PCI_SET_IRQ_HW_INT),
+		  "b"(bx),
+		  "c"(cx),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static const char *pci_signature = "PCI ";
+static int pci_bios_detect(void) {
+	pci_bios_info * pci = find_pci_bios_info();
+	
+	if (pci != NULL) {
+		/*printf("Found PCI structure at %08x\n", (uint32_t) pci);
+			
+		printf("\nPCI header info:\n");
+		printf("%c%c%c%c\n", pci->magic[0], pci->magic[1], pci->magic[2],
+			pci->magic[3]);
+		printf("%08x\n", (uint32_t) pci->entry);
+		printf("%d\n", pci->length * 16);
+		printf("%d\n", pci->checksum);*/
+		
+		uint32_t adr, temp, len;
+		uint8_t err;
+		
+		bios32_entry.offset = (uint32_t) pci->entry;
+		bios32_entry.selector = CODE_SELECTOR;
+		
+		__asm__(
+			"lcall *(%%edi)"
+			: "=a"(err),	/* AL out=status */
+			  "=b"(adr),	/* EBX out=code segment base adr */
+			  "=c"(len),	/* ECX out=code segment size */
+			  "=d"(temp)	/* EDX out=entry pt offset in code */
+			: "0"(0x49435024),/* EAX in=service="$PCI" */
+			  "1"(0),	/* EBX in=0=get service entry pt */
+			  "D"(&bios32_entry)
+		);
+		
+		if (err == 0x80) {
+			dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
+			return -1;
+		}
+		
+		if (err != 0) {
+			dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
+			return -1;
+		}
+		
+		bios32_entry.offset = adr + temp;
+		
+		// now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
+		uint16_t present, version, busses;
+		uint32_t signature;
+		__asm__(
+			"lcall *(%%edi)		\n\t"
+			"jc 1f				\n\t"
+			"xor %%ah,%%ah		\n"
+			"1:"
+			: "=a"(present),
+			  "=b"(version),
+			  "=c"(busses),
+			  "=d"(signature)
+			: "0"(PCIBIOS_PRESENT),
+			  "D"(&bios32_entry)
+		);
+		
+		if (present & 0xff00) {
+			dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%02x\n", present >> 8);
+			return -1;
+		}
+		
+		if (signature != *(uint32_t *)pci_signature) {
+			dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%08x\n", signature);
+			return -1;
+		}
+		
+		//dprintf(DEBUG, "busses=%04x\n", busses);
+		last_bus = busses & 0xff;
+		
+		g_pci_find_pci_device = bios_find_pci_device;
+		g_pci_find_pci_class_code = bios_find_pci_class_code;
+		
+		g_pci_read_config_word = bios_read_config_word;
+		g_pci_read_config_half = bios_read_config_half;
+		g_pci_read_config_byte = bios_read_config_byte;
+	
+		g_pci_write_config_word = bios_write_config_word;
+		g_pci_write_config_half = bios_write_config_half;
+		g_pci_write_config_byte = bios_write_config_byte;
+		
+		g_pci_get_irq_routing_options = bios_get_irq_routing_options;
+		g_pci_set_irq_hw_int = bios_set_irq_hw_int;
+		
+		return 0;
+	}
+	
+	return -1;
+}
diff --git a/platform/pc/platform.c b/platform/pc/platform.c
new file mode 100644
index 0000000..ca5a946
--- /dev/null
+++ b/platform/pc/platform.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <err.h>
+#include <debug.h>
+#include <arch/x86/mmu.h>
+#include <platform.h>
+#include "platform_p.h"
+#include <platform/pc.h>
+#include <platform/multiboot.h>
+#include <platform/console.h>
+#include <platform/keyboard.h>
+#include <dev/pci.h>
+
+extern multiboot_info_t *_multiboot_info;
+extern unsigned int _heap_end;
+
+void platform_init_mmu_mappings(void)
+{
+	/* do some memory map initialization */
+}
+
+
+void platform_init_multiboot_info(void)
+{
+	unsigned int i;
+	
+	if (_multiboot_info) {
+		if (_multiboot_info->flags & MB_INFO_MEM_SIZE) {
+			_heap_end = _multiboot_info->mem_upper * 1024;
+		}
+		
+		if (_multiboot_info->flags & MB_INFO_MMAP) {
+			memory_map_t *mmap = (memory_map_t *) (_multiboot_info->mmap_addr - 4);
+			
+			dprintf(DEBUG, "mmap length: %u\n", _multiboot_info->mmap_length);
+			
+			for (i=0; i < _multiboot_info->mmap_length / sizeof(memory_map_t); i++) {
+				dprintf(DEBUG, "base=%08x, length=%08x, type=%02x\n",
+					mmap[i].base_addr_low, mmap[i].length_low, mmap[i].type);
+				
+				if (mmap[i].type == MB_MMAP_TYPE_AVAILABLE && mmap[i].base_addr_low >= _heap_end) {
+					_heap_end = mmap[i].base_addr_low + mmap[i].length_low;
+				} else if (mmap[i].type != MB_MMAP_TYPE_AVAILABLE && mmap[i].base_addr_low >= _heap_end) {
+					/* 
+					 * break on first memory hole above default heap end for now.
+					 * later we can add facilities for adding free chunks to the
+					 * heap for each segregated memory region.
+					 */
+					 break;
+				}
+			}
+		}
+	}
+}
+
+void platform_early_init(void)
+{
+	/* update the heap end so we can take advantage of more ram */
+	platform_init_multiboot_info();
+	
+	/* get the text console working */
+	platform_init_console();
+	
+	/* initialize the interrupt controller */
+	platform_init_interrupts();
+
+	/* initialize the timer */
+	platform_init_timer();
+}
+
+void platform_init(void)
+{
+	platform_init_keyboard();
+	
+	pci_init();
+}
+
diff --git a/platform/pc/platform_p.h b/platform/pc/platform_p.h
new file mode 100644
index 0000000..b8c2d95
--- /dev/null
+++ b/platform/pc/platform_p.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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.
+ */
+#ifndef __PLATFORM_P_H
+#define __PLATFORM_P_H
+
+void platform_init_interrupts(void);
+void platform_init_timer(void);
+
+#endif
+
diff --git a/platform/pc/rules.mk b/platform/pc/rules.mk
new file mode 100644
index 0000000..e10fb1b
--- /dev/null
+++ b/platform/pc/rules.mk
@@ -0,0 +1,23 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+ARCH := x86
+CPU := generic
+
+MODULES += \
+	lib/cbuf
+
+INCLUDES += \
+	-I$(LOCAL_DIR)/include
+
+OBJS += \
+	$(LOCAL_DIR)/interrupts.o \
+	$(LOCAL_DIR)/platform.o \
+	$(LOCAL_DIR)/timer.o \
+	$(LOCAL_DIR)/debug.o \
+	$(LOCAL_DIR)/console.o \
+	$(LOCAL_DIR)/keyboard.o \
+	$(LOCAL_DIR)/pci.o
+
+LINKER_SCRIPT += \
+	$(BUILDDIR)/kernel.ld
+
diff --git a/platform/pc/timer.c b/platform/pc/timer.c
new file mode 100644
index 0000000..2211511
--- /dev/null
+++ b/platform/pc/timer.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * 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 <err.h>
+#include <reg.h>
+#include <debug.h>
+#include <kernel/thread.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/console.h>
+#include <platform/timer.h>
+#include <platform/pc.h>
+#include "platform_p.h"
+#include <arch/x86.h>
+
+static platform_timer_callback t_callback;
+static void *callback_arg;
+
+static uint64_t next_trigger_time;
+static uint64_t next_trigger_delta;
+
+static uint64_t timer_delta_time;
+static uint64_t timer_current_time;
+
+static uint16_t divisor;
+
+#define INTERNAL_FREQ 1193182ULL
+#define INTERNAL_FREQ_3X 3579546ULL
+
+status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, time_t interval)
+{
+	enter_critical_section();
+
+	t_callback = callback;
+	callback_arg = arg;
+	
+	next_trigger_delta = (uint64_t) interval << 32;
+	next_trigger_time = timer_current_time + next_trigger_delta;
+
+	exit_critical_section();
+
+	return NO_ERROR;
+}
+
+time_t current_time(void)
+{
+	time_t time;
+	
+	enter_critical_section();
+	time = (time_t) (timer_current_time >> 32);
+	exit_critical_section();
+	
+	return time;
+}
+
+bigtime_t current_time_hires(void)
+{
+	bigtime_t time;
+	
+	enter_critical_section();
+	time = (bigtime_t) ((timer_current_time >> 22) * 1000) >> 10;
+	exit_critical_section();
+	
+	return time;
+}
+static enum handler_return os_timer_tick(void *arg)
+{
+	uint64_t delta;
+	
+	timer_current_time += timer_delta_time;
+	
+	time_t time = current_time();
+	//bigtime_t btime = current_time_hires();
+	//printf_xy(71, 0, WHITE, "%08u", (uint32_t) time);
+	//printf_xy(63, 1, WHITE, "%016llu", (uint64_t) btime);
+	
+	if (t_callback && timer_current_time >= next_trigger_time) {
+		delta = timer_current_time - next_trigger_time;
+		next_trigger_time = timer_current_time + next_trigger_delta - delta;
+		
+		return t_callback(callback_arg, time);
+	} else {
+		return INT_NO_RESCHEDULE;
+	}
+}
+
+static void set_pit_frequency(uint32_t frequency)
+{
+	uint32_t count, remainder;
+	
+	/* figure out the correct divisor for the desired frequency */
+	if (frequency <= 18) {
+		count = 0xffff;
+	} else if (frequency >= INTERNAL_FREQ) {
+		count = 1;
+	} else {
+		count = INTERNAL_FREQ_3X / frequency;
+		remainder = INTERNAL_FREQ_3X % frequency;
+		
+		if (remainder >= INTERNAL_FREQ_3X / 2) {
+			count += 1;
+		}
+		
+		count /= 3;
+		remainder = count % 3;
+		
+		if (remainder >= 1) {
+			count += 1;
+		}
+	}
+		
+	divisor = count & 0xffff;
+	
+	/*
+	 * funky math that i don't feel like explaining. essentially 32.32 fixed
+	 * point representation of the configured timer delta.
+	 */
+	timer_delta_time = (3685982306ULL * count) >> 10;
+	
+	//dprintf(DEBUG, "set_pit_frequency: dt=%016llx\n", timer_delta_time);
+	//dprintf(DEBUG, "set_pit_frequency: divisor=%04x\n", divisor);
+	
+	/*
+	 * setup the Programmable Interval Timer
+	 * timer 0, mode 2, binary counter, LSB followed by MSB
+	 */
+	outp(I8253_CONTROL_REG, 0x34);
+	outp(I8253_DATA_REG, divisor & 0xff); // LSB
+	outp(I8253_DATA_REG, divisor >> 8); // MSB
+}
+
+void platform_init_timer(void)
+{
+	timer_current_time = 0;
+	
+	set_pit_frequency(1000); // ~1ms granularity
+
+	register_int_handler(INT_PIT, &os_timer_tick, NULL);
+	unmask_interrupt(INT_PIT);
+}
+
+void platform_halt_timers(void)
+{
+	mask_interrupt(INT_PIT);
+}
+
diff --git a/project/armemu-test.mk b/project/armemu-test.mk
index 151beed..9b222e6 100644
--- a/project/armemu-test.mk
+++ b/project/armemu-test.mk
@@ -4,6 +4,15 @@
 
 TARGET := armemu
 MODULES += \
+	lib/bio \
+	lib/partition \
+	lib/bcache \
+	lib/fs \
+	lib/fs/ext2 \
+	lib/gfx \
+	lib/gfxconsole \
+	lib/text \
+	lib/tga \
 	app/tests \
 	app/shell
 
diff --git a/project/pc-x86-test.mk b/project/pc-x86-test.mk
new file mode 100644
index 0000000..d0b1937
--- /dev/null
+++ b/project/pc-x86-test.mk
@@ -0,0 +1,17 @@
+# top level project rules for the pc-x86-test project
+#
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+TARGET := pc-x86
+MODULES += \
+	app/tests \
+	app/shell \
+	app/pcitests
+
+# extra rules to copy the pc-x86.conf file to the build dir
+#$(BUILDDIR)/pc-x86.conf: $(LOCAL_DIR)/pc-x86.conf
+#	@echo copy $< to $@
+#	$(NOECHO)cp $< $@
+
+#EXTRA_BUILDDEPS += $(BUILDDIR)/pc-x86.conf
+#GENERATED += $(BUILDDIR)/pc-x86.conf
diff --git a/scripts/buildall b/scripts/buildall
index 5601d7c..906ee0c 100755
--- a/scripts/buildall
+++ b/scripts/buildall
@@ -1,7 +1,14 @@
 #!/bin/sh
 
 PROJECTS="armemu-test sam7ex256-test osk5912-test qemu-arm-test beagle-test surf-test"
+FAILED=""
 
 for p in $PROJECTS; do
-	PROJECT=$p make -j2
+	PROJECT=$p make -j2 || FAILED="$FAILED $p"
 done
+
+if [ "$FAILED" != "" ]; then
+	echo
+	echo some projects have failed to build:
+	echo $FAILED
+fi
diff --git a/scripts/do-armemu-test b/scripts/do-armemu-test
index 1d910f6..04c9e50 100755
--- a/scripts/do-armemu-test
+++ b/scripts/do-armemu-test
@@ -2,6 +2,10 @@
 
 export PROJECT=armemu-test
 
-make -C ../armemu && 
-make && 
+if [ ! -f blk.bin ]; then
+	dd if=/dev/zero of=blk.bin bs=1024k count=16
+fi
+
+make -j8 -C ../armemu &&
+make -j8 &&
 (cd build-$PROJECT; ../../armemu/build-generic/armemu)
diff --git a/scripts/do-sam7ex256-test b/scripts/do-sam7ex256-test
index 4505ad6..d549831 100755
--- a/scripts/do-sam7ex256-test
+++ b/scripts/do-sam7ex256-test
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-export DEBUG=true
+export DEBUG=2
 export PROJECT=sam7ex256-test
 
 make
diff --git a/target/armemu/armemu.conf b/target/armemu/armemu.conf
new file mode 100644
index 0000000..bcd1bff
--- /dev/null
+++ b/target/armemu/armemu.conf
@@ -0,0 +1,23 @@
+[cpu]
+core = arm926ejs
+
+# the rom file is loaded at address 0x0
+[rom]
+file = lk.bin
+
+[system]
+display = yes
+console = yes
+network = no
+block = yes
+
+[network]
+device = /dev/tap0
+
+[block]
+file = ../blk.bin
+
+[display]
+width = 800
+height = 600
+depth = 32
diff --git a/target/armemu/rules.mk b/target/armemu/rules.mk
index d79834a..4c7bdd1 100644
--- a/target/armemu/rules.mk
+++ b/target/armemu/rules.mk
@@ -4,3 +4,8 @@
 
 PLATFORM := armemu
 
+$(BUILDDIR)/armemu.conf: $(LOCAL_DIR)/armemu.conf
+	cp $< $@
+
+EXTRA_BUILDDEPS += $(BUILDDIR)/armemu.conf
+GENERATED += $(BUILDDIR)/armemu.conf
diff --git a/target/msm8960/init.c b/target/msm8960/init.c
index e8dba0a..48482e1 100644
--- a/target/msm8960/init.c
+++ b/target/msm8960/init.c
@@ -49,6 +49,7 @@
 #define LINUX_MACHTYPE_8960_MTP     3397
 #define LINUX_MACHTYPE_8960_FLUID   3398
 #define LINUX_MACHTYPE_8960_APQ     3399
+#define LINUX_MACHTYPE_8960_LIQUID  3535
 
 
 extern void dmb(void);
@@ -151,6 +152,9 @@
 		case HW_PLATFORM_FLUID:
 			mach_id = LINUX_MACHTYPE_8960_FLUID;
 			break;
+		case HW_PLATFORM_LIQUID:
+			mach_id = LINUX_MACHTYPE_8960_LIQUID;
+			break;
 		default:
 			mach_id = LINUX_MACHTYPE_8960_CDP;
 	};
diff --git a/target/pc-x86/rules.mk b/target/pc-x86/rules.mk
new file mode 100644
index 0000000..ed7bbfb
--- /dev/null
+++ b/target/pc-x86/rules.mk
@@ -0,0 +1,5 @@
+# mostly null target configuration for pc-x86
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+PLATFORM := pc
+