Merge "msm8960: Add Conversion for LiQUID from HW-ID to machine-type"
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/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 f567574..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
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
index 99ca572..bb6a17b 100644
--- a/arch/x86/arch.c
+++ b/arch/x86/arch.c
@@ -60,3 +60,11 @@
 {
 }
 
+uint32_t arch_cycle_count(void)
+{
+	uint32_t timestamp;
+	rdtscl(timestamp);
+	
+	return timestamp;
+}
+
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/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/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/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/kernel/debug.c b/kernel/debug.c
index 7b7a648..ec4b987 100644
--- a/kernel/debug.c
+++ b/kernel/debug.c
@@ -21,6 +21,16 @@
  * 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>
diff --git a/kernel/event.c b/kernel/event.c
index 97eb955..bdb6ec2 100644
--- a/kernel/event.c
+++ b/kernel/event.c
@@ -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 1f7015d..07d2b19 100644
--- a/kernel/mutex.c
+++ b/kernel/mutex.c
@@ -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();
@@ -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 860e712..58dff8d 100644
--- a/kernel/thread.c
+++ b/kernel/thread.c
@@ -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>
@@ -100,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;
@@ -143,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
@@ -186,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
@@ -216,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)
 {
@@ -317,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
@@ -339,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
@@ -364,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
@@ -407,7 +500,16 @@
 	return INT_RESCHEDULE;
 }
 
-/* Put thread to sleep; delay specified in ms */
+/**
+ * @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;
@@ -426,6 +528,11 @@
 	exit_critical_section();
 }
 
+/**
+ * @brief  Initialize threading system
+ *
+ * This function is called once, from kmain()
+ */
 void thread_init_early(void)
 {
 	int i;
@@ -449,6 +556,11 @@
 	current_thread = t;
 }
 
+/**
+ * @brief Complete thread initialization
+ *
+ * This function is called once at boot time
+ */
 void thread_init(void)
 {
 #if PLATFORM_HAS_DYNAMIC_TIMER
@@ -456,11 +568,19 @@
 #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)
@@ -470,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");
@@ -478,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);
@@ -493,6 +623,9 @@
 	dprintf(INFO, "\n");
 }
 
+/**
+ * @brief  Dump debugging info about all threads
+ */
 void dump_all_threads(void)
 {
 	thread_t *t;
@@ -504,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;
@@ -526,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;
@@ -561,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;
@@ -598,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;
@@ -641,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
@@ -651,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 c041d49..dd5cf9f 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -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>
@@ -31,6 +45,9 @@
 
 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;
@@ -93,6 +110,20 @@
 	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)
@@ -100,6 +131,20 @@
 	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)
@@ -107,6 +152,9 @@
 	timer_set(timer, period, period, callback, arg);
 }
 
+/**
+ * @brief  Cancel a pending timer
+ */
 void timer_cancel(timer_t *timer)
 {
 	DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
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/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/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 e576208..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,18 +99,24 @@
 	uart_putc(0, c);
 }
 
+static enum handler_return debug_timer_callback(timer_t *t, time_t now, void *arg)
+{
+	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;
+	}
+}
+
 int dgetc(char *c, bool wait)
 {
-	int _c;
+	ssize_t len;
 
-	while ((_c = uart_getc(0, false)) < 0) {
-		if (!wait) {
-			return -1;
-		}
-	}
-
-	*c = _c;
-	return 0;
+	len = cbuf_read(&debug_buf, c, 1, wait);
+	return len;
 }
 
 void debug_dump_regs(void)
@@ -139,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/debug.c b/platform/pc/debug.c
index 06ac263..b1d70ee 100644
--- a/platform/pc/debug.c
+++ b/platform/pc/debug.c
@@ -35,11 +35,11 @@
 	cputc(c);
 }
 
-int dgetc(char *c)
+int dgetc(char *c, bool wait)
 {
 	int ret =  platform_read_key(c);
-	if (ret < 0)
-		arch_idle();
+	//if (ret < 0)
+	//	arch_idle();
 	
 	return ret;
 }
@@ -72,10 +72,3 @@
 {
 }
 
-uint32_t debug_cycle_count()
-{
-	uint32_t timestamp;
-	rdtscl(timestamp);
-	
-	return timestamp;
-}
diff --git a/platform/pc/keyboard.c b/platform/pc/keyboard.c
index 8b4d623..27e3d3c 100644
--- a/platform/pc/keyboard.c
+++ b/platform/pc/keyboard.c
@@ -32,6 +32,7 @@
 #include <platform/pc.h>
 #include "platform_p.h"
 #include <arch/x86.h>
+#include <lib/cbuf.h>
 
 static inline int i8042_read_data(void)
 {
@@ -162,50 +163,7 @@
 static bool key_lshift;
 static bool key_rshift;
 
-/*
- * key buffer
- */
-#define KEY_BUFFER_LEN 10
-
-static uint8_t key_buffer[KEY_BUFFER_LEN];
-static int key_buffer_in_ptr, key_buffer_out_ptr;
-
-static int inq(uint8_t data) {
-	int temp;
-
-	temp = key_buffer_in_ptr + 1;
-	if (temp >= KEY_BUFFER_LEN) {
-		temp = 0;
-	}
-	
-	// if in_ptr reaches out_ptr, the queue is full
-	if (temp == key_buffer_out_ptr) {
-		return -1;
-	}
-	
-	key_buffer[key_buffer_in_ptr] = data;
-	key_buffer_in_ptr = temp;
-	
-	return 0;
-}
-
-static int deq(void) {
-	int rv;
-	
-	// if out_ptr reaches in_ptr, the queue is empty
-	if (key_buffer_out_ptr == key_buffer_in_ptr) {
-		return -1;
-	}
-	
-	rv = key_buffer[key_buffer_out_ptr];
-	key_buffer_out_ptr++;
-	
-	if (key_buffer_out_ptr >= KEY_BUFFER_LEN) {
-		key_buffer_out_ptr = 0;
-	}
-	
-	return rv;
-}
+static cbuf_t key_buf;
 
 static void i8042_process_scode(uint8_t scode, unsigned int flags)
 {
@@ -238,7 +196,8 @@
 		key_rshift ? 'R' : ' ');*/
 	
 	if (keyCode != -1 && !keyUpBit) {
-		inq(keyCode);
+		char c = (char) keyCode;
+		cbuf_write(&key_buf, &c, 1, false);
 	}
 	
 	// update the last received code
@@ -344,26 +303,18 @@
 
 int platform_read_key(char *c)
 {
-	int data;
-	
-	enter_critical_section();
-	data = deq();
-	
-	if (data != -1) {
-		*c = (char) data;
-	}
-	exit_critical_section();
-	
-	return data == -1 ? -1 : 0;
+	ssize_t len;
+
+	len = cbuf_read(&key_buf, c, 1, true);
+	return len;
 }
 
 void platform_init_keyboard(void)
 {
 	uint8_t ctr;
 	
-	// clear in case of reinit
-	key_buffer_in_ptr = key_buffer_out_ptr = 0;
-	
+	cbuf_initialize(&key_buf, 32);
+
 	i8042_flush();
 	
 	if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
diff --git a/platform/pc/rules.mk b/platform/pc/rules.mk
index e083a80..e10fb1b 100644
--- a/platform/pc/rules.mk
+++ b/platform/pc/rules.mk
@@ -3,6 +3,9 @@
 ARCH := x86
 CPU := generic
 
+MODULES += \
+	lib/cbuf
+
 INCLUDES += \
 	-I$(LOCAL_DIR)/include
 
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/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