[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/platform/pc/console.c b/src/bsp/lk/platform/pc/console.c
new file mode 100644
index 0000000..d4ccabd
--- /dev/null
+++ b/src/bsp/lk/platform/pc/console.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <arch/x86.h>
+#include <platform/pc.h>
+#include <platform/console.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/* CGA values */
+#define CURSOR_START        0x0A
+#define CURSOR_END          0x0B
+#define VIDEO_ADDRESS_MSB   0x0C
+#define VIDEO_ADDRESS_LSB   0x0D
+#define CURSOR_POS_MSB      0x0E
+#define CURSOR_POS_LSB      0x0F
+
+/* curr settings */
+static unsigned char curr_x;
+static unsigned char curr_y;
+static unsigned char curr_start;
+static unsigned char curr_end;
+static unsigned char curr_attr;
+
+/* video page buffer */
+#define VPAGE_SIZE      2048
+#define PAGE_MAX        8
+
+static int active_page = 0;
+static int visual_page = 0;
+
+static int curs_x[PAGE_MAX];
+static int curs_y[PAGE_MAX];
+
+static struct {
+    int x1, y1, x2, y2;
+} view_window = {
+    0, 0, 79, 24
+};
+
+void platform_init_console(void)
+{
+    curr_save();
+    window(0, 0, 79, 24);
+    clear();
+    place(0, 0);
+}
+
+void set_visual_page(int page)
+{
+    unsigned short page_offset = page*VPAGE_SIZE;
+    visual_page = page;
+
+    outp(CGA_INDEX_REG, VIDEO_ADDRESS_LSB);
+    outp(CGA_DATA_REG, page_offset & 0xFF);
+    outp(CGA_INDEX_REG, VIDEO_ADDRESS_MSB);
+    outp(CGA_DATA_REG, (page_offset >> 8) & 0xFF);
+}
+
+void set_active_page(int page)
+{
+    curs_x[active_page] = curr_x;
+    curs_y[active_page] = curr_y;
+    curr_x = curs_x[page];
+    curr_y = curs_y[page];
+    active_page = page;
+}
+
+int get_visual_page(void)
+{
+    return visual_page;
+}
+
+int get_active_page(void)
+{
+    return active_page;
+}
+
+void place(int x,int y)
+{
+    unsigned short cursor_word = x + y*80 + active_page*VPAGE_SIZE;
+
+    /*
+     * program CGA using index reg, then data reg
+     */
+    outp(CGA_INDEX_REG, CURSOR_POS_LSB);
+    outp(CGA_DATA_REG, cursor_word & 0xFF);
+    outp(CGA_INDEX_REG, CURSOR_POS_MSB);
+    outp(CGA_DATA_REG, (cursor_word >> 8) & 0xFF);
+
+    curr_x = x;
+    curr_y = y;
+}
+
+void cursor(int start,int end)
+{
+    outp(CGA_INDEX_REG, CURSOR_START);
+    outp(CGA_DATA_REG, start);
+    outp(CGA_INDEX_REG, CURSOR_END);
+    outp(CGA_DATA_REG, end);
+}
+
+void curr_save(void)
+{
+    /* grab some info from the bios data area (these should be defined in memmap.h */
+    curr_attr = *((unsigned char *)0xB8000 + 159);
+    curr_x = *((unsigned char *)0x00450);
+    curr_y = *((unsigned char *)0x00451);
+    curr_end = *((unsigned char *)0x00460);
+    curr_start = *((unsigned char *)0x00461);
+    active_page = visual_page = 0;
+}
+
+void curr_restore(void)
+{
+    *((unsigned char *)0x00450) = curr_x;
+    *((unsigned char *)0x00451) = curr_y;
+
+    place(curr_x, curr_y);
+    cursor(curr_start, curr_end);
+}
+
+void window(int x1, int y1, int x2, int y2)
+{
+    view_window.x1 = x1;
+    view_window.y1 = y1;
+    view_window.x2 = x2;
+    view_window.y2 = y2;
+
+    //place(x1, y1);
+}
+
+void _clear(char c,char attr,int x1,int y1,int x2,int y2)
+{
+    register int i,j;
+    unsigned short w = attr;
+
+    w <<= 8;
+    w |= c;
+    for (i = x1; i <= x2; i++) {
+        for (j = y1; j <= y2; j++) {
+            *((unsigned short *)(uintptr_t)(0xB8000 + 2*i+160*j + 2 * active_page * VPAGE_SIZE)) = w;
+        }
+    }
+
+    place(x1,y1);
+    curr_y = y1;
+    curr_x = x1;
+}
+
+void clear()
+{
+    _clear(' ', curr_attr, view_window.x1, view_window.y1, view_window.x2,
+           view_window.y2);
+}
+
+void _scroll(char attr, int x1, int y1, int x2, int y2)
+{
+    register int x,y;
+    unsigned short xattr = attr << 8,w;
+    unsigned char *v = (unsigned char *)(uintptr_t)(0xB8000 + active_page*(2*VPAGE_SIZE));
+
+    for (y = y1+1; y <= y2; y++) {
+        for (x = x1; x <= x2; x++) {
+            w = *((unsigned short *) (v + 2*(y*80+x)));
+            *((unsigned short *)(v + 2*((y-1)*80+x))) = w;
+        }
+    }
+
+    for (x = x1; x <= x2; x++) {
+        *((unsigned short *)(v + 2*((y-1)*80+x))) = xattr;
+    }
+}
+
+void scroll(void)
+{
+    _scroll(curr_attr, view_window.x1, view_window.y1, view_window.x2,
+            view_window.y2);
+}
+
+void cputc(char c)
+{
+    static unsigned short scan_x, x, y;
+    unsigned char *v = (unsigned char *)(uintptr_t)(0xB8000 + active_page*(2*VPAGE_SIZE));
+    x = curr_x;
+    y = curr_y;
+
+    switch (c) {
+        case '\t':
+            x += 8;
+            if (x >= view_window.x2+1) {
+                x = view_window.x1;
+                if (y == view_window.y2) {
+                    scroll();
+                } else {
+                    y++;
+                }
+            } else {
+                scan_x = 0;
+
+                while ((scan_x+8) < x) {
+                    scan_x += 8;
+                }
+
+                x = scan_x;
+            }
+            break;
+
+        case '\r':
+            x = view_window.x1;
+            break;
+
+        case '\n':
+            if (y == view_window.y2) {
+                scroll();
+            } else {
+                y++;
+            }
+            break;
+
+        case '\b':
+            x--;
+            *(v + 2*(x + y*80)) = ' ';
+            break;
+
+        default:
+            *(v + 2*(x + y*80)) = c;
+            x++;
+
+            if (x >= view_window.x2+1) {
+                x = view_window.x1;
+                if (y == view_window.y2) {
+                    scroll();
+                } else {
+                    y++;
+                }
+            }
+    }
+
+    place(x, y);
+}
+
+void cputs(char *s)
+{
+    char c;
+    while (*s != '\0') {
+        c = *s++;
+        cputc(c);
+    }
+}
+
+void puts_xy(int x,int y,char attr,char *s)
+{
+    unsigned char *v = (unsigned char *)(uintptr_t)(0xB8000 + (80*y+x)*2 + active_page*(2*VPAGE_SIZE));
+    while (*s != 0) {
+        *v = *s;
+        s++;
+        v++;
+        *v = attr;
+        v++;
+    }
+}
+
+void putc_xy(int x, int y, char attr, char c)
+{
+    unsigned char *v = (unsigned char *)(uintptr_t)(0xB8000 + (80*y+x)*2 + active_page*(2*VPAGE_SIZE));
+    *v = c;
+    v++;
+    *v = attr;
+}
+
+int printf_xy(int x, int y, char attr, char *fmt, ...)
+{
+    char cbuf[200];
+    va_list parms;
+    int result;
+
+    va_start(parms, fmt);
+    result = vsprintf(cbuf, fmt, parms);
+    va_end(parms);
+
+    puts_xy(x, y, attr, cbuf);
+
+    return result;
+}
diff --git a/src/bsp/lk/platform/pc/debug.c b/src/bsp/lk/platform/pc/debug.c
new file mode 100644
index 0000000..e7d0e05
--- /dev/null
+++ b/src/bsp/lk/platform/pc/debug.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdarg.h>
+#include <reg.h>
+#include <stdio.h>
+#include <kernel/thread.h>
+#include <arch/x86.h>
+#include <lib/cbuf.h>
+#include <platform/interrupts.h>
+#include <platform/pc/memmap.h>
+#include <platform/console.h>
+#include <platform/keyboard.h>
+#include <platform/debug.h>
+
+static int uart_baud_rate = 115200;
+static int uart_io_port = 0x3f8;
+
+static cbuf_t uart_rx_buf;
+
+static enum handler_return uart_irq_handler(void *arg)
+{
+    unsigned char c;
+    bool resched = false;
+
+    while (inp(uart_io_port + 5) & (1<<0)) {
+        c = inp(uart_io_port + 0);
+        cbuf_write_char(&uart_rx_buf, c, false);
+        resched = true;
+    }
+
+    return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
+}
+
+void platform_init_uart(void)
+{
+    /* configure the uart */
+    int divisor = 115200 / uart_baud_rate;
+
+    /* get basic config done so that tx functions */
+    outp(uart_io_port + 3, 0x80); // set up to load divisor latch
+    outp(uart_io_port + 0, divisor & 0xff); // lsb
+    outp(uart_io_port + 1, divisor >> 8); // msb
+    outp(uart_io_port + 3, 3); // 8N1
+    outp(uart_io_port + 2, 0x07); // enable FIFO, clear, 14-byte threshold
+}
+
+void uart_init(void)
+{
+    /* finish uart init to get rx going */
+    cbuf_initialize(&uart_rx_buf, 16);
+
+    register_int_handler(0x24, uart_irq_handler, NULL);
+    unmask_interrupt(0x24);
+
+    outp(uart_io_port + 1, 0x1); // enable receive data available interrupt
+}
+
+void uart_putc(char c)
+{
+    while ((inp(uart_io_port + 5) & (1<<6)) == 0)
+        ;
+    outp(uart_io_port + 0, c);
+}
+
+int uart_getc(char *c, bool wait)
+{
+    return cbuf_read_char(&uart_rx_buf, c, wait);
+}
+
+void platform_dputc(char c)
+{
+    if (c == '\n')
+        platform_dputc('\r');
+#if WITH_CGA_CONSOLE
+    cputc(c);
+#else
+    uart_putc(c);
+#endif
+}
+
+int platform_dgetc(char *c, bool wait)
+{
+#if WITH_CGA_CONSOLE
+    int ret =  platform_read_key(c);
+    //if (ret < 0)
+    //  arch_idle();
+#else
+    int ret = uart_getc(c, wait);
+#endif
+
+    return ret;
+}
+
+void platform_halt(void)
+{
+    for (;;) {
+        x86_cli();
+        x86_hlt();
+    }
+}
+
diff --git a/src/bsp/lk/platform/pc/ide.c b/src/bsp/lk/platform/pc/ide.c
new file mode 100644
index 0000000..48ee309
--- /dev/null
+++ b/src/bsp/lk/platform/pc/ide.c
@@ -0,0 +1,908 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <reg.h>
+#include <debug.h>
+#include <trace.h>
+#include <assert.h>
+#include <err.h>
+#include <malloc.h>
+#include <arch/x86.h>
+#include <sys/types.h>
+#include <platform/interrupts.h>
+#include <platform/ide.h>
+#include <platform/pc.h>
+#include <platform.h>
+#include <dev/pci.h>
+#include <dev/driver.h>
+#include <dev/class/block.h>
+#include <kernel/event.h>
+
+#define LOCAL_TRACE 1
+
+// status register bits
+#define IDE_CTRL_BSY   0x80
+#define IDE_DRV_RDY    0x40
+#define IDE_DRV_WRTFLT 0x20
+#define IDE_DRV_SKCOMP 0x10
+#define IDE_DRV_DRQ    0x08
+#define IDE_DRV_CORDAT 0x04
+#define IDE_DRV_IDX    0x02
+#define IDE_DRV_ERR    0x01
+
+// ATA commands
+#define ATA_NOP            0x00
+#define ATA_ATAPIRESET     0x08
+#define ATA_RECALIBRATE    0x10
+#define ATA_READMULT_RET   0x20
+#define ATA_READMULT       0x21
+#define ATA_READECC_RET    0x22
+#define ATA_READECC        0x23
+#define ATA_WRITEMULT_RET  0x30
+#define ATA_WRITEMULT      0x31
+#define ATA_WRITEECC_RET   0x32
+#define ATA_WRITEECC       0x33
+#define ATA_VERIFYMULT_RET 0x40
+#define ATA_VERIFYMULT     0x41
+#define ATA_FORMATTRACK    0x50
+#define ATA_SEEK           0x70
+#define ATA_DIAG           0x90
+#define ATA_INITPARAMS     0x91
+#define ATA_ATAPIPACKET    0xA0
+#define ATA_ATAPIIDENTIFY  0xA1
+#define ATA_ATAPISERVICE   0xA2
+#define ATA_READ_DMA        0xC8
+#define ATA_READ_DMA_EXT    0x25
+#define ATA_WRITE_DMA       0xCA
+#define ATA_WRITE_DMA_EXT   0x35
+#define ATA_GETDEVINFO     0xEC
+#define ATA_ATAPISETFEAT   0xEF
+
+// error codes
+#define IDE_NOERROR         0
+#define IDE_ADDRESSMARK     1
+#define IDE_CYLINDER0       2
+#define IDE_INVALIDCOMMAND  3
+#define IDE_MEDIAREQ        4
+#define IDE_SECTNOTFOUND    5
+#define IDE_MEDIACHANGED    6
+#define IDE_BADDATA         7
+#define IDE_BADSECTOR       8
+#define IDE_TIMEOUT         9
+#define IDE_DMAERROR        10
+
+enum {
+    IDE_REG_DATA            = 0,
+    IDE_REG_ERROR           = 1,
+    IDE_REG_PRECOMP         = 1,
+    IDE_REG_SECTOR_COUNT    = 2,
+    IDE_REG_SECTOR_NUM      = 3,
+    IDE_REG_CYLINDER_LOW    = 4,
+    IDE_REG_CYLINDER_HIGH   = 5,
+    IDE_REG_DRIVE_HEAD      = 6,
+    IDE_REG_STATUS          = 7,
+    IDE_REG_COMMAND         = 7,
+    IDE_REG_ALT_STATUS      = 8,
+    IDE_REG_DEVICE_CONTROL  = 8,
+
+    IDE_REG_NUM,
+};
+
+enum {
+    TYPE_NONE,
+    TYPE_UNKNOWN,
+    TYPE_FLOPPY,
+    TYPE_IDECDROM,
+    TYPE_SCSICDROM,
+    TYPE_IDEDISK,
+    TYPE_SCSIDISK
+};
+
+static const char *ide_type_str[] = {
+    "None",
+    "Unknown",
+    "Floppy",
+    "IDE CDROM",
+    "SCSI CDROM",
+    "IDE Disk",
+    "SCSI Disk",
+};
+
+static const char *ide_error_str[] = {
+    "Unknown error",
+    "Address mark not found",
+    "Cylinder 0 not found",
+    "Command aborted - invalid command",
+    "Media change requested",
+    "ID or target sector not found",
+    "Media changed",
+    "Uncorrectable data error",
+    "Bad sector detected",
+    "Command timed out",
+    "DMA error"
+};
+
+struct ide_driver_state {
+    int irq;
+    const uint16_t *regs;
+
+    event_t completion;
+
+    int type[2];
+    struct {
+        int sectors;
+        int sector_size;
+    } drive[2];
+};
+
+static const uint16_t ide_device_regs[][IDE_REG_NUM] = {
+    { 0x01F0, 0x01F1, 0x01F2, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x03F6 },
+    { 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, 0x0376 },
+};
+
+static const int ide_device_irqs[] = {
+    INT_IDE0,
+    INT_IDE1,
+};
+
+static status_t ide_init(struct device *dev);
+
+static enum handler_return ide_irq_handler(void *arg);
+
+static status_t ide_init(struct device *dev);
+static ssize_t ide_get_block_size(struct device *dev);
+static ssize_t ide_get_block_count(struct device *dev);
+static ssize_t ide_write(struct device *dev, off_t offset, const void *buf, size_t count);
+static ssize_t ide_read(struct device *dev, off_t offset, void *buf, size_t count);
+
+static struct block_ops the_ops = {
+    .std = {
+        .init = ide_init,
+    },
+    .get_block_size = ide_get_block_size,
+    .get_block_count = ide_get_block_count,
+    .write = ide_write,
+    .read = ide_read,
+};
+
+DRIVER_EXPORT(ide, &the_ops.std);
+
+static uint8_t ide_read_reg8(struct device *dev, int index);
+static uint16_t ide_read_reg16(struct device *dev, int index);
+static uint32_t ide_read_reg32(struct device *dev, int index);
+
+static void ide_write_reg8(struct device *dev, int index, uint8_t value);
+static void ide_write_reg16(struct device *dev, int index, uint16_t value);
+static void ide_write_reg32(struct device *dev, int index, uint32_t value);
+
+static void ide_read_reg8_array(struct device *dev, int index, void *buf, size_t count);
+static void ide_read_reg16_array(struct device *dev, int index, void *buf, size_t count);
+static void ide_read_reg32_array(struct device *dev, int index, void *buf, size_t count);
+
+static void ide_write_reg8_array(struct device *dev, int index, const void *buf, size_t count);
+static void ide_write_reg16_array(struct device *dev, int index, const void *buf, size_t count);
+static void ide_write_reg32_array(struct device *dev, int index, const void *buf, size_t count);
+
+static void ide_device_select(struct device *dev, int index);
+static void ide_device_reset(struct device *dev);
+static void ide_delay_400ns(struct device *dev);
+static int ide_poll_status(struct device *dev, uint8_t on_mask, uint8_t off_mask);
+static int ide_eval_error(struct device *dev);
+static void ide_detect_drives(struct device *dev);
+static int ide_wait_for_completion(struct device *dev);
+static int ide_detect_ata(struct device *dev, int index);
+static void ide_lba_setup(struct device *dev, uint32_t addr, int index);
+
+static status_t ide_init(struct device *dev)
+{
+    pci_location_t loc;
+    pci_config_t pci_config;
+    status_t res = NO_ERROR;
+    uint32_t i;
+    int err;
+
+    if (!dev)
+        return ERR_INVALID_ARGS;
+
+    if (!dev->config)
+        return ERR_NOT_CONFIGURED;
+
+    __UNUSED const struct platform_ide_config *config = dev->config;
+
+    err = pci_find_pci_class_code(&loc, 0x010180, 0);
+    if (err != _PCI_SUCCESSFUL) {
+        LTRACEF("Failed to find IDE device\n");
+        res = ERR_NOT_FOUND;
+    }
+
+    LTRACEF("Found IDE device at %02x:%02x\n", loc.bus, loc.dev_fn);
+
+    for (i=0; i < sizeof(pci_config) / sizeof(uint32_t); i++) {
+        uint32_t reg = sizeof(uint32_t) * i;
+
+        err = pci_read_config_word(&loc, reg, ((uint32_t *) &pci_config) + i);
+        if (err != _PCI_SUCCESSFUL) {
+            LTRACEF("Failed to read config reg %d: 0x%02x\n", reg, err);
+            res = ERR_NOT_CONFIGURED;
+            goto done;
+        }
+    }
+
+    for (i=0; i < 6; i++) {
+        LTRACEF("BAR[%d]: 0x%08x\n", i, pci_config.base_addresses[i]);
+    }
+
+    struct ide_driver_state *state = malloc(sizeof(struct ide_driver_state));
+    if (!state) {
+        res = ERR_NO_MEMORY;
+        goto done;
+    }
+    dev->state = state;
+
+    /* TODO: select io regs and irq based on device index */
+    state->irq = ide_device_irqs[0];
+    state->regs = ide_device_regs[0];
+    state->type[0] = state->type[1] = TYPE_NONE;
+
+    event_init(&state->completion, false, EVENT_FLAG_AUTOUNSIGNAL);
+
+    register_int_handler(state->irq, ide_irq_handler, dev);
+    unmask_interrupt(state->irq);
+
+    /* enable interrupts */
+    ide_write_reg8(dev, IDE_REG_DEVICE_CONTROL, 0);
+
+    /* detect drives */
+    ide_detect_drives(dev);
+
+done:
+    return res;
+}
+
+static enum handler_return ide_irq_handler(void *arg)
+{
+    struct device *dev = arg;
+    struct ide_driver_state *state = dev->state;
+    uint8_t val;
+
+    val = ide_read_reg8(dev, IDE_REG_STATUS);
+
+    if ((val & IDE_DRV_ERR) == 0) {
+        event_signal(&state->completion, false);
+
+        return INT_RESCHEDULE;
+    } else {
+        return INT_NO_RESCHEDULE;
+    }
+}
+
+static ssize_t ide_get_block_size(struct device *dev)
+{
+    DEBUG_ASSERT(dev);
+    DEBUG_ASSERT(dev->state);
+
+    struct ide_driver_state *state = dev->state;
+    return state->drive[0].sector_size;
+}
+
+static ssize_t ide_get_block_count(struct device *dev)
+{
+    DEBUG_ASSERT(dev);
+    DEBUG_ASSERT(dev->state);
+
+    struct ide_driver_state *state = dev->state;
+    return state->drive[0].sectors;
+}
+
+static ssize_t ide_write(struct device *dev, off_t offset, const void *buf, size_t count)
+{
+    DEBUG_ASSERT(dev);
+    DEBUG_ASSERT(dev->state);
+
+    __UNUSED struct ide_driver_state *state = dev->state;
+
+    size_t sectors, do_sectors, i;
+    const uint16_t *ubuf = buf;
+    int index = 0; // hard code drive for now
+    ssize_t ret = 0;
+    int err;
+
+    ide_device_select(dev, index);
+    ide_delay_400ns(dev);
+
+    err = ide_poll_status(dev, 0, IDE_CTRL_BSY | IDE_DRV_DRQ);
+    if (err) {
+        LTRACEF("Error while waiting for controller: %s\n", ide_error_str[err]);
+        ret = ERR_GENERIC;
+        goto done;
+    }
+
+    sectors = count;
+
+    while (sectors > 0) {
+        do_sectors = sectors;
+
+        if (do_sectors > 256)
+            do_sectors = 256;
+
+        err = ide_poll_status(dev, 0, IDE_CTRL_BSY);
+        if (err) {
+            LTRACEF("Error while waiting for controller: %s\n", ide_error_str[err]);
+            ret = ERR_GENERIC;
+            goto done;
+        }
+
+        ide_lba_setup(dev, offset, index);
+
+        if (do_sectors == 256)
+            ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0);
+        else
+            ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, do_sectors);
+
+        err = ide_poll_status(dev, IDE_DRV_RDY, 0);
+        if (err) {
+            LTRACEF("Error while waiting for controller: %s\n", ide_error_str[err]);
+            ret = ERR_GENERIC;
+            goto done;
+        }
+
+        ide_write_reg8(dev, IDE_REG_COMMAND, ATA_WRITEMULT_RET);
+        ide_delay_400ns(dev);
+
+        for (i=0; i < do_sectors; i++) {
+            err = ide_poll_status(dev, IDE_DRV_DRQ, 0);
+            if (err) {
+                LTRACEF("Error while waiting for drive: %s\n", ide_error_str[err]);
+                ret = ERR_GENERIC;
+                goto done;
+            }
+
+            ide_write_reg16_array(dev, IDE_REG_DATA, ubuf, 256);
+
+            ubuf += 256;
+        }
+
+        err = ide_wait_for_completion(dev);
+        if (err) {
+            LTRACEF("Error waiting for completion: %s\n", ide_error_str[err]);
+            ret = ERR_TIMED_OUT;
+            goto done;
+        }
+
+        sectors -= do_sectors;
+        offset += do_sectors;
+    }
+
+    ret = count;
+
+done:
+    return ret;
+}
+
+static ssize_t ide_read(struct device *dev, off_t offset, void *buf, size_t count)
+{
+    DEBUG_ASSERT(dev);
+    DEBUG_ASSERT(dev->state);
+
+    __UNUSED struct ide_driver_state *state = dev->state;
+
+    size_t sectors, do_sectors, i;
+    uint16_t *ubuf = buf;
+    int index = 0; // hard code drive for now
+    ssize_t ret = 0;
+    int err;
+
+    ide_device_select(dev, index);
+    ide_delay_400ns(dev);
+
+    err = ide_poll_status(dev, 0, IDE_CTRL_BSY | IDE_DRV_DRQ);
+    if (err) {
+        LTRACEF("Error while waiting for controller: %s\n", ide_error_str[err]);
+        ret = ERR_GENERIC;
+        goto done;
+    }
+
+    sectors = count;
+
+    while (sectors > 0) {
+        do_sectors = sectors;
+
+        if (do_sectors > 256)
+            do_sectors = 256;
+
+        err = ide_poll_status(dev, 0, IDE_CTRL_BSY);
+        if (err) {
+            LTRACEF("Error while waiting for controller: %s\n", ide_error_str[err]);
+            ret = ERR_GENERIC;
+            goto done;
+        }
+
+        ide_lba_setup(dev, offset, index);
+
+        if (do_sectors == 256)
+            ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0);
+        else
+            ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, do_sectors);
+
+        err = ide_poll_status(dev, IDE_DRV_RDY, 0);
+        if (err) {
+            LTRACEF("Error while waiting for controller: %s\n", ide_error_str[err]);
+            ret = ERR_GENERIC;
+            goto done;
+        }
+
+        ide_write_reg8(dev, IDE_REG_COMMAND, ATA_READMULT_RET);
+        ide_delay_400ns(dev);
+
+        for (i=0; i < do_sectors; i++) {
+            err = ide_poll_status(dev, IDE_DRV_DRQ, 0);
+            if (err) {
+                LTRACEF("Error while waiting for drive: %s\n", ide_error_str[err]);
+                ret = ERR_GENERIC;
+                goto done;
+            }
+
+            ide_read_reg16_array(dev, IDE_REG_DATA, ubuf, 256);
+
+            ubuf += 256;
+        }
+
+        err = ide_wait_for_completion(dev);
+        if (err) {
+            LTRACEF("Error waiting for completion: %s\n", ide_error_str[err]);
+            ret = ERR_TIMED_OUT;
+            goto done;
+        }
+
+        sectors -= do_sectors;
+        offset += do_sectors;
+    }
+
+    ret = count;
+
+done:
+    return ret;
+}
+
+static uint8_t ide_read_reg8(struct device *dev, int index)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    return inp(state->regs[index]);
+}
+
+static uint16_t ide_read_reg16(struct device *dev, int index)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    return inpw(state->regs[index]);
+}
+
+static uint32_t ide_read_reg32(struct device *dev, int index)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    return inpd(state->regs[index]);
+}
+
+static void ide_read_reg8_array(struct device *dev, int index, void *buf, size_t count)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    inprep(state->regs[index], (uint8_t *) buf, count);
+}
+
+static void ide_read_reg16_array(struct device *dev, int index, void *buf, size_t count)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    inpwrep(state->regs[index], (uint16_t *) buf, count);
+}
+
+static void ide_read_reg32_array(struct device *dev, int index, void *buf, size_t count)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    inpdrep(state->regs[index], (uint32_t *) buf, count);
+}
+
+static void ide_write_reg8_array(struct device *dev, int index, const void *buf, size_t count)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    outprep(state->regs[index], (uint8_t *) buf, count);
+}
+
+static void ide_write_reg16_array(struct device *dev, int index, const void *buf, size_t count)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    outpwrep(state->regs[index], (uint16_t *) buf, count);
+}
+
+static void ide_write_reg32_array(struct device *dev, int index, const void *buf, size_t count)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    outpdrep(state->regs[index], (uint32_t *) buf, count);
+}
+
+static void ide_write_reg8(struct device *dev, int index, uint8_t value)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    outp(state->regs[index], value);
+}
+
+static void ide_write_reg16(struct device *dev, int index, uint16_t value)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    outpw(state->regs[index], value);
+}
+
+static void ide_write_reg32(struct device *dev, int index, uint32_t value)
+{
+    DEBUG_ASSERT(index >= 0 && index < IDE_REG_NUM);
+
+    struct ide_driver_state *state = dev->state;
+
+    outpd(state->regs[index], value);
+}
+
+static void ide_device_select(struct device *dev, int index)
+{
+    ide_write_reg8(dev, IDE_REG_DRIVE_HEAD, (index & 1) << 4);
+}
+
+static void ide_delay_400ns(struct device *dev)
+{
+    ide_read_reg8(dev, IDE_REG_ALT_STATUS);
+    ide_read_reg8(dev, IDE_REG_ALT_STATUS);
+    ide_read_reg8(dev, IDE_REG_ALT_STATUS);
+    ide_read_reg8(dev, IDE_REG_ALT_STATUS);
+}
+
+static void ide_device_reset(struct device *dev)
+{
+    struct ide_driver_state *state = dev->state;
+
+    lk_time_t start;
+    uint8_t sect_cnt, sect_num;
+    int err;
+
+    ide_device_select(dev, 0);
+    ide_delay_400ns(dev);
+
+    // set bit 2 for at least 4.8us
+    ide_write_reg8(dev, IDE_REG_DEVICE_CONTROL, 1<<2);
+
+    // delay 5us
+    spin(5);
+
+    ide_write_reg8(dev, IDE_REG_DEVICE_CONTROL, 0x00);
+
+    err = ide_poll_status(dev, 0, IDE_CTRL_BSY);
+    if (err) {
+        LTRACEF("Failed while waiting for controller to be ready: %s\n", ide_error_str[err]);
+        return;
+    }
+
+    // make sure the slave is ready if present
+    if (state->type[1] != TYPE_NONE) {
+        ide_device_select(dev, 1);
+        ide_delay_400ns(dev);
+
+        start = current_time();
+
+        do {
+            sect_cnt = ide_read_reg8(dev, IDE_REG_SECTOR_COUNT);
+            sect_num = ide_read_reg8(dev, IDE_REG_SECTOR_NUM);
+
+            if (sect_cnt == 1 && sect_num == 1) {
+                err = ide_poll_status(dev, 0, IDE_CTRL_BSY);
+                if (err) {
+                    LTRACEF("Failed while waiting for slave ready: %s\n", ide_error_str[err]);
+                    return;
+                }
+
+                break;
+            }
+        } while (TIME_LTE(current_time(), start + 20000));
+
+        err = ide_read_reg8(dev, IDE_REG_ALT_STATUS);
+        if (err & IDE_DRV_ERR) {
+            err = ide_eval_error(dev);
+            LTRACEF("Failed while resetting controller: %s\n", ide_error_str[err]);
+            return;
+        }
+    }
+}
+
+static int ide_eval_error(struct device *dev)
+{
+    int err = 0;
+    uint8_t data = 0;
+
+    data = ide_read_reg8(dev, IDE_REG_ERROR);
+
+    if (data & 0x01) {
+        err = IDE_ADDRESSMARK;
+    } else if (data & 0x02) {
+        err = IDE_CYLINDER0;
+    } else if (data & 0x04) {
+        err = IDE_INVALIDCOMMAND;
+    } else if (data & 0x08) {
+        err = IDE_MEDIAREQ;
+    } else if (data & 0x10) {
+        err = IDE_SECTNOTFOUND;
+    } else if (data & 0x20) {
+        err = IDE_MEDIACHANGED;
+    } else if (data & 0x40) {
+        err = IDE_BADDATA;
+    } else if (data & 0x80) {
+        err = IDE_BADSECTOR;
+    } else {
+        err = IDE_NOERROR;
+    }
+
+    return err;
+}
+
+static int ide_poll_status(struct device *dev, uint8_t on_mask, uint8_t off_mask)
+{
+    int err;
+    uint8_t value;
+    lk_time_t start = current_time();
+
+    do {
+        value = ide_read_reg8(dev, IDE_REG_ALT_STATUS);
+
+        if (value & IDE_DRV_ERR) {
+            err = ide_eval_error(dev);
+            LTRACEF("Error while polling status: %s\n", ide_error_str[err]);
+            return err;
+        }
+
+        if ((value & on_mask) == on_mask && (value & off_mask) == 0)
+            return IDE_NOERROR;
+    } while (TIME_LTE(current_time(), start + 20000));
+
+    return IDE_TIMEOUT;
+}
+
+static void ide_detect_drives(struct device *dev)
+{
+    struct ide_driver_state *state = dev->state;
+    uint8_t sc = 0, sn = 0, st = 0, cl = 0, ch = 0;
+
+    ide_device_select(dev, 0);
+    ide_delay_400ns(dev);
+    ide_delay_400ns(dev);
+
+    ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0x55);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, 0xaa);
+    ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0xaa);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, 0x55);
+    ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0x55);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, 0xaa);
+
+    sc = ide_read_reg8(dev, IDE_REG_SECTOR_COUNT);
+    sn = ide_read_reg8(dev, IDE_REG_SECTOR_NUM);
+
+    if (sc == 0x55 && sn == 0xaa) {
+        state->type[0] = TYPE_UNKNOWN;
+    }
+
+    // check for device 1
+    ide_device_select(dev, 1);
+    ide_delay_400ns(dev);
+
+    ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0x55);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, 0xaa);
+    ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0xaa);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, 0x55);
+    ide_write_reg8(dev, IDE_REG_SECTOR_COUNT, 0x55);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, 0xaa);
+
+    sc = ide_read_reg8(dev, IDE_REG_SECTOR_COUNT);
+    sn = ide_read_reg8(dev, IDE_REG_SECTOR_NUM);
+
+    if (sc == 0x55 && sn == 0xaa) {
+        state->type[1] = TYPE_UNKNOWN;
+    }
+
+    // now the drives present should be known
+    // soft reset now
+    ide_device_select(dev, 0);
+    ide_delay_400ns(dev);
+    ide_device_reset(dev);
+
+    ide_device_select(dev, 0);
+    ide_delay_400ns(dev);
+
+    sc = ide_read_reg8(dev, IDE_REG_SECTOR_COUNT);
+    sn = ide_read_reg8(dev, IDE_REG_SECTOR_NUM);
+    if (sc == 0x01 && sn == 0x01) {
+        state->type[0] = TYPE_UNKNOWN;
+
+        st = ide_read_reg8(dev, IDE_REG_STATUS);
+        cl = ide_read_reg8(dev, IDE_REG_CYLINDER_LOW);
+        ch = ide_read_reg8(dev, IDE_REG_CYLINDER_HIGH);
+
+        // PATAPI or SATAPI respectively
+        if ((cl == 0x14 && ch == 0xeb) || (cl == 0x69 && ch == 0x96)) {
+            state->type[0] = TYPE_IDECDROM;
+        } else if (st != 0 && ((cl == 0x00 && ch == 0x00) || (cl == 0x3c && ch == 0xc3))) {
+            state->type[0] = TYPE_IDEDISK;
+        }
+    }
+
+    ide_device_select(dev, 1);
+    ide_delay_400ns(dev);
+
+    sc = ide_read_reg8(dev, IDE_REG_SECTOR_COUNT);
+    sn = ide_read_reg8(dev, IDE_REG_SECTOR_NUM);
+    if (sc == 0x01 && sn == 0x01) {
+        state->type[1] = TYPE_UNKNOWN;
+
+        st = ide_read_reg8(dev, IDE_REG_STATUS);
+        cl = ide_read_reg8(dev, IDE_REG_CYLINDER_LOW);
+        ch = ide_read_reg8(dev, IDE_REG_CYLINDER_HIGH);
+
+        // PATAPI or SATAPI respectively
+        if ((cl == 0x14 && ch == 0xeb) || (cl == 0x69 && ch == 0x96)) {
+            state->type[1] = TYPE_IDECDROM;
+        } else if (st != 0 && ((cl == 0x00 && ch == 0x00) || (cl == 0x3c && ch == 0xc3))) {
+            state->type[1] = TYPE_IDEDISK;
+        }
+    }
+
+    LTRACEF("Detected drive 0: %s\n", ide_type_str[state->type[0]]);
+    LTRACEF("Detected drive 1: %s\n", ide_type_str[state->type[1]]);
+
+    switch (state->type[0]) {
+        case TYPE_IDEDISK:
+            ide_detect_ata(dev, 0);
+            break;
+
+        default:
+            break;
+    }
+
+    switch (state->type[1]) {
+        case TYPE_IDEDISK:
+            ide_detect_ata(dev, 1);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static int ide_wait_for_completion(struct device *dev)
+{
+    struct ide_driver_state *state = dev->state;
+    status_t err;
+
+    err = event_wait_timeout(&state->completion, 20000);
+    if (err)
+        return IDE_TIMEOUT;
+
+    return IDE_NOERROR;
+}
+
+static status_t ide_detect_ata(struct device *dev, int index)
+{
+    struct ide_driver_state *state = dev->state;
+    status_t res = NO_ERROR;
+    uint8_t *info = NULL;
+    int err;
+
+    ide_device_select(dev, index);
+    ide_delay_400ns(dev);
+
+    err = ide_poll_status(dev, 0, IDE_CTRL_BSY | IDE_DRV_DRQ);
+    if (err) {
+        LTRACEF("Error while detecting drive %d: %s\n", index, ide_error_str[err]);
+        res = ERR_TIMED_OUT;
+        goto error;
+    }
+
+    ide_device_select(dev, index);
+    ide_delay_400ns(dev);
+
+    err = ide_poll_status(dev, 0, IDE_CTRL_BSY | IDE_DRV_DRQ);
+    if (err) {
+        LTRACEF("Error while detecting drive %d: %s\n", index, ide_error_str[err]);
+        res = ERR_TIMED_OUT;
+        goto error;
+    }
+
+    // try to wait for the selected drive to be ready, but don't quit if not
+    // since CD-ROMs don't seem to respond to this when they're masters
+    ide_poll_status(dev, IDE_DRV_RDY, 0);
+
+    // send the "identify device" command
+    ide_write_reg8(dev, IDE_REG_COMMAND, ATA_GETDEVINFO);
+    ide_delay_400ns(dev);
+
+    err = ide_wait_for_completion(dev);
+    if (err) {
+        LTRACEF("Error while waiting for command: %s\n", ide_error_str[err]);
+        res = ERR_TIMED_OUT;
+        goto error;
+    }
+
+    info = malloc(512);
+    if (!info) {
+        res = ERR_NO_MEMORY;
+        goto error;
+    }
+
+    LTRACEF("Found ATA hard disk on channel %d!\n", index);
+
+    ide_read_reg16_array(dev, IDE_REG_DATA, info, 256);
+
+    state->drive[index].sectors = *((uint32_t *) (info + 120));
+    state->drive[index].sector_size = 512;
+
+    LTRACEF("Disk supports %u sectors for a total of %u bytes\n", state->drive[index].sectors,
+            state->drive[index].sectors * 512);
+
+error:
+    free(info);
+    return res;
+}
+
+static void ide_lba_setup(struct device *dev, uint32_t addr, int drive)
+{
+    ide_write_reg8(dev, IDE_REG_DRIVE_HEAD, 0xe0 | ((drive & 0x00000001) << 4) | ((addr >> 24) & 0xf));
+    ide_write_reg8(dev, IDE_REG_CYLINDER_LOW, (addr >> 8) & 0xff);
+    ide_write_reg8(dev, IDE_REG_CYLINDER_HIGH, (addr >> 16) & 0xff);
+    ide_write_reg8(dev, IDE_REG_SECTOR_NUM, addr & 0xff);
+    ide_write_reg8(dev, IDE_REG_PRECOMP, 0xff);
+}
+
diff --git a/src/bsp/lk/platform/pc/include/pcnet.h b/src/bsp/lk/platform/pc/include/pcnet.h
new file mode 100644
index 0000000..40f08e3
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/pcnet.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_PC_PCNET_H
+#define __PLATFORM_PC_PCNET_H
+
+#include <compiler.h>
+#include <stdint.h>
+
+#define REG_APROM 0x00
+#define REG_RDP   0x10
+#define REG_RAP   0x14
+#define REG_RESET 0x18
+#define REG_BDP   0x1c
+
+#define CSR0_INIT 0x0001
+#define CSR0_STRT 0x0002
+#define CSR0_STOP 0x0004
+#define CSR0_TDMD 0x0008
+#define CSR0_TXON 0x0010
+#define CSR0_RXON 0x0020
+#define CSR0_IENA 0x0040
+#define CSR0_INTR 0x0080
+#define CSR0_IDON 0x0100
+#define CSR0_TINT 0x0200
+#define CSR0_RINT 0x0400
+#define CSR0_MERR 0x0800
+#define CSR0_MISS 0x1000
+#define CSR0_CERR 0x2000
+#define CSR0_BABL 0x4000
+#define CSR0_ERR  0x8000
+
+#define CSR4_DMAPLUS 0x4000
+
+#define DESC_SIZE (4*sizeof(uint32_t))
+
+struct init_block_32 {
+    uint16_t mode;
+
+    uint16_t reserved_0 : 4;
+    uint16_t rlen       : 4;
+    uint16_t reserved_1 : 4;
+    uint16_t tlen       : 4;
+
+    uint8_t padr[6];
+
+    uint16_t reserved_2;
+
+    uint64_t ladr;
+    uint32_t rdra;
+    uint32_t tdra;
+} __PACKED;
+
+struct td_style3 {
+    uint32_t trc         : 4;
+    uint32_t reserved_1  : 8;
+    uint32_t tdr         : 14;
+    uint32_t rtry        : 1;
+    uint32_t lcar        : 1;
+    uint32_t lcol        : 1;
+    uint32_t exdef       : 1;
+    uint32_t uflo        : 1;
+    uint32_t buff        : 1;
+
+    uint32_t bcnt        : 12;
+    uint32_t ones        : 4;
+    uint32_t reserved_0  : 7;
+    uint32_t bpe         : 1;
+    uint32_t enp         : 1;
+    uint32_t stp         : 1;
+    uint32_t def         : 1;
+    uint32_t one         : 1;
+    uint32_t more_ltinit : 1;
+    uint32_t add_no_fcs  : 1;
+    uint32_t err         : 1;
+    uint32_t own         : 1;
+
+    uint32_t tbadr;
+
+    uint32_t reserved_2;
+} __PACKED;
+
+struct rd_style3 {
+    uint16_t mcnt        : 12;
+    uint16_t zeros       : 4;
+
+    uint8_t rpc;
+    uint8_t rcc;
+
+    uint32_t bcnt        : 12;
+    uint32_t ones        : 4;
+    uint32_t reserved_0  : 4;
+    uint32_t bam         : 1;
+    uint32_t lafm        : 1;
+    uint32_t pam         : 1;
+    uint32_t bpe         : 1;
+    uint32_t enp         : 1;
+    uint32_t stp         : 1;
+    uint32_t buff        : 1;
+    uint32_t crc         : 1;
+    uint32_t oflo        : 1;
+    uint32_t fram        : 1;
+    uint32_t err         : 1;
+    uint32_t own         : 1;
+
+    uint32_t rbadr;
+
+    uint32_t reserved_1;
+} __PACKED;
+
+#endif
+
diff --git a/src/bsp/lk/platform/pc/include/platform/console.h b/src/bsp/lk/platform/pc/include/platform/console.h
new file mode 100644
index 0000000..c7870a5
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/console.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_CONSOLE_H
+#define __PLATFORM_CONSOLE_H
+
+#include <compiler.h>
+
+__BEGIN_CDECLS
+
+void platform_init_console(void);
+
+void set_visual_page(int page);
+void set_active_page(int page);
+
+int get_visual_page(void);
+int get_active_page(void);
+
+void place(int x,int y);
+void cursor(int start, int end);
+
+void _clear(char c, char attr, int x1, int y1, int x2, int y2);
+void clear(void);
+
+void _scroll(char attr, int x1, int y1, int x2, int y2);
+void scroll(void);
+
+void curr_save(void);
+void curr_restore(void);
+
+void cputc(char c);
+void cputs(char *s);
+
+void window(int x1, int y1, int x2, int y2);
+
+void putc_xy(int x, int y, char attr, char c);
+void puts_xy(int x, int y, char attr, char *s);
+
+int printf_xy(int x, int y, char attr, char *fmt, ...) __PRINTFLIKE(4, 5);
+
+#define CURSOR_BLOCK()  cursor(0, 15);
+#define CURSOR_OFF()    cursor(16, 16);
+#define CURSOR_STD()    cursor(14, 15);
+
+/* text colors */
+#define BLACK           0
+#define BLUE            1
+#define GREEN           2
+#define CYAN            3
+#define RED             4
+#define MAGENTA         5
+#define BROWN           6
+#define LIGHTGRAY       7
+#define DARKGRAY        8
+#define LIGHTBLUE       9
+#define LIGHTGREEN      10
+#define LIGHTCYAN       11
+#define LIGHTRED        12
+#define LIGHTMAGENTA    13
+#define YELLOW          14
+#define WHITE           15
+
+__END_CDECLS
+
+#endif
+
diff --git a/src/bsp/lk/platform/pc/include/platform/ide.h b/src/bsp/lk/platform/pc/include/platform/ide.h
new file mode 100644
index 0000000..4dd8e25
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/ide.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __PLATFORM_IDE_H
+#define __PLATFORM_IDE_H
+
+struct platform_ide_config {
+};
+
+#endif
+
+
diff --git a/src/bsp/lk/platform/pc/include/platform/keyboard.h b/src/bsp/lk/platform/pc/include/platform/keyboard.h
new file mode 100644
index 0000000..5464995
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/keyboard.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_KEYBOARD_H
+#define __PLATFORM_KEYBOARD_H
+
+void platform_init_keyboard(void);
+
+int platform_read_key(char *c);
+
+#endif
diff --git a/src/bsp/lk/platform/pc/include/platform/multiboot.h b/src/bsp/lk/platform/pc/include/platform/multiboot.h
new file mode 100644
index 0000000..8b61faa
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/multiboot.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_MULTIBOOT_H
+#define __PLATFORM_MULTIBOOT_H
+
+#include <sys/types.h>
+
+/* magic number for multiboot header */
+#define MULTIBOOT_HEADER_MAGIC      0x1BADB002
+
+/* flags for multiboot header */
+#ifdef __ELF__
+#define MULTIBOOT_HEADER_FLAGS      0x00000003
+#else
+#define MULTIBOOT_HEADER_FLAGS      0x00010003
+#endif
+
+/* magic number passed by multiboot-compliant boot loaders */
+#define MULTIBOOT_BOOTLOADER_MAGIC  0x2BADB002
+
+#ifndef ASSEMBLY
+
+/* multiboot header */
+typedef struct multiboot_header {
+    uint32_t magic;
+    uint32_t flags;
+    uint32_t checksum;
+    uint32_t header_addr;
+    uint32_t load_addr;
+    uint32_t load_end_addr;
+    uint32_t bss_end_addr;
+    uint32_t entry_addr;
+} multiboot_header_t;
+
+/* symbol table for a.out */
+typedef struct aout_symbol_table {
+    uint32_t tabsize;
+    uint32_t strsize;
+    uint32_t addr;
+    uint32_t reserved;
+} aout_symbol_table_t;
+
+/* section header table for ELF */
+typedef struct elf_section_header_table {
+    uint32_t num;
+    uint32_t size;
+    uint32_t addr;
+    uint32_t shndx;
+} elf_section_header_table_t;
+
+/* multiboot info */
+typedef struct multiboot_info {
+    uint32_t flags;
+    uint32_t mem_lower;
+    uint32_t mem_upper;
+    uint32_t boot_device;
+    uint32_t cmdline;
+    uint32_t mods_count;
+    uint32_t mods_addr;
+    union {
+        aout_symbol_table_t aout_sym;
+        elf_section_header_table_t elf_sec;
+    } u;
+    uint32_t mmap_length;
+    uint32_t mmap_addr;
+} multiboot_info_t;
+
+enum {
+    MB_INFO_MEM_SIZE    = 0x001,
+    MB_INFO_BOOT_DEV    = 0x002,
+    MB_INFO_CMD_LINE    = 0x004,
+    MB_INFO_MODS        = 0x008,
+    MB_INFO_SYMS        = 0x010,
+    MB_INFO_MMAP        = 0x020,
+    MB_INFO_DRIVES      = 0x040,
+    MB_INFO_CONFIG      = 0x080,
+    MB_INFO_BOOT_LOADER = 0x100,
+    MB_INFO_APM_TABLE   = 0x200,
+    MB_INFO_VBE         = 0x400,
+};
+
+/* module structure */
+typedef struct module {
+    uint32_t mod_start;
+    uint32_t mod_end;
+    uint32_t string;
+    uint32_t reserved;
+} module_t;
+
+/* memory map - be careful that the offset 0 is base_addr_low without size */
+typedef struct memory_map {
+    uint32_t size;
+    uint32_t base_addr_low;
+    uint32_t base_addr_high;
+    uint32_t length_low;
+    uint32_t length_high;
+    uint32_t type;
+} memory_map_t;
+
+/* memory map entry types */
+enum {
+    MB_MMAP_TYPE_AVAILABLE      = 0x01,
+    MB_MMAP_TYPE_RESERVED       = 0x02,
+    MB_MMAP_TYPE_ACPI_RECLAIM   = 0x03,
+    MB_MMAP_TYPE_ACPI_NVS       = 0x04,
+};
+
+#endif
+
+#endif
diff --git a/src/bsp/lk/platform/pc/include/platform/pc.h b/src/bsp/lk/platform/pc/include/platform/pc.h
new file mode 100644
index 0000000..9f8b812
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/pc.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_PC_H
+#define __PLATFORM_PC_H
+
+#include <platform/pc/memmap.h>
+#include <platform/pc/iomap.h>
+
+/* NOTE: keep arch/x86/crt0.S in sync with these definitions */
+
+/* interrupts */
+#define INT_VECTORS 0x31
+
+/* defined interrupts */
+#define INT_BASE            0x20
+#define INT_PIT             0x20
+#define INT_KEYBOARD        0x21
+#define INT_PIC2            0x22
+
+#define INT_BASE2           0x28
+#define INT_CMOSRTC         0x28
+#define INT_PS2MOUSE        0x2c
+#define INT_IDE0            0x2e
+#define INT_IDE1            0x2f
+
+/* exceptions */
+#define INT_DIVIDE_0        0x00
+#define INT_DEBUG_EX        0x01
+#define INT_INVALID_OP      0x06
+#define INT_DEV_NA_EX       0x07
+#define INT_MF              0x10
+#define INT_XM              0x13
+
+/* faults */
+#define INT_STACK_FAULT     0x0c
+#define INT_GP_FAULT        0x0d
+#define INT_PAGE_FAULT      0x0e
+
+/* APIC vectors */
+#define INT_APIC_TIMER      0x22
+
+#define INT_SYSCALL         0x30
+
+/* PIC remap bases */
+#define PIC1_BASE 0x20
+#define PIC2_BASE 0x28
+
+#endif
+
diff --git a/src/bsp/lk/platform/pc/include/platform/pc/iomap.h b/src/bsp/lk/platform/pc/include/platform/pc/iomap.h
new file mode 100644
index 0000000..26c808b
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/pc/iomap.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __IOMAP_H
+#define __IOMAP_H
+
+/* i8253/i8254 programmable interval timer registers */
+#define I8253_CONTROL_REG   0x43
+#define I8253_DATA_REG      0x40
+
+/* i8042 keyboard controller registers */
+#define I8042_COMMAND_REG   0x64
+#define I8042_STATUS_REG    0x64
+#define I8042_DATA_REG      0x60
+
+/* CGA registers */
+#define CGA_INDEX_REG       0x3D4
+#define CGA_DATA_REG        0x3D5
+
+#endif
diff --git a/src/bsp/lk/platform/pc/include/platform/pc/memmap.h b/src/bsp/lk/platform/pc/include/platform/pc/memmap.h
new file mode 100644
index 0000000..e782116
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/pc/memmap.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __MEMMAP_H
+#define __MEMMAP_H
+
+/* some helpful macros */
+#define REG(x) ((volatile unsigned int *)(x))
+#define REG_H(x) ((volatile unsigned short *)(x))
+#define REG_B(x) ((volatile unsigned char *)(x))
+
+/* TODO: put fixed memory address definitions here */
+
+#endif
diff --git a/src/bsp/lk/platform/pc/include/platform/pcnet.h b/src/bsp/lk/platform/pc/include/platform/pcnet.h
new file mode 100644
index 0000000..cb04234
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/pcnet.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __PLATFORM_PCNET_H
+#define __PLATFORM_PCNET_H
+
+#include <stdint.h>
+
+struct platform_pcnet_config {
+    uint16_t vendor_id;
+    uint16_t device_id;
+    int index;
+};
+
+#endif
+
+
+
diff --git a/src/bsp/lk/platform/pc/include/platform/uart.h b/src/bsp/lk/platform/pc/include/platform/uart.h
new file mode 100644
index 0000000..60fd661
--- /dev/null
+++ b/src/bsp/lk/platform/pc/include/platform/uart.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __PLATFORM_UART_H
+#define __PLATFORM_UART_H
+
+struct platform_uart_config {
+    int baud_rate;
+    int io_port;
+    int irq;
+    int rx_buf_len;
+    int tx_buf_len;
+};
+
+#endif
+
diff --git a/src/bsp/lk/platform/pc/interrupts.c b/src/bsp/lk/platform/pc/interrupts.c
new file mode 100755
index 0000000..dc2869b
--- /dev/null
+++ b/src/bsp/lk/platform/pc/interrupts.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <sys/types.h>
+#include <debug.h>
+#include <err.h>
+#include <reg.h>
+#include <kernel/thread.h>
+#include <platform/interrupts.h>
+#include <arch/ops.h>
+#include <arch/x86.h>
+#include <arch/fpu.h>
+#include <kernel/spinlock.h>
+#include "platform_p.h"
+#include <platform/pc.h>
+
+static spin_lock_t lock;
+
+void x86_gpf_handler(struct x86_iframe *frame);
+void x86_invop_handler(struct x86_iframe *frame);
+void x86_unhandled_exception(struct x86_iframe *frame);
+#ifdef ARCH_X86_64
+void x86_pfe_handler(struct x86_iframe *frame);
+#endif
+
+#define PIC1 0x20
+#define PIC2 0xA0
+
+#define ICW1 0x11
+#define ICW4 0x01
+
+struct int_handler_struct {
+    int_handler handler;
+    void *arg;
+};
+
+static struct int_handler_struct int_handler_table[INT_VECTORS];
+
+/*
+ * Cached IRQ mask (enabled/disabled)
+ */
+static uint8_t irqMask[2];
+
+/*
+ * init the PICs and remap them
+ */
+static void map(uint32_t pic1, uint32_t pic2)
+{
+    /* send ICW1 */
+    outp(PIC1, ICW1);
+    outp(PIC2, ICW1);
+
+    /* send ICW2 */
+    outp(PIC1 + 1, pic1);   /* remap */
+    outp(PIC2 + 1, pic2);   /*  pics */
+
+    /* send ICW3 */
+    outp(PIC1 + 1, 4);  /* IRQ2 -> connection to slave */
+    outp(PIC2 + 1, 2);
+
+    /* send ICW4 */
+    outp(PIC1 + 1, 5);
+    outp(PIC2 + 1, 1);
+
+    /* disable all IRQs */
+    outp(PIC1 + 1, 0xff);
+    outp(PIC2 + 1, 0xff);
+
+    irqMask[0] = 0xff;
+    irqMask[1] = 0xff;
+}
+
+static void enable(unsigned int vector, bool enable)
+{
+    if (vector >= PIC1_BASE && vector < PIC1_BASE + 8) {
+        vector -= PIC1_BASE;
+
+        uint8_t bit = 1 << vector;
+
+        if (enable && (irqMask[0] & bit)) {
+            irqMask[0] = inp(PIC1 + 1);
+            irqMask[0] &= ~bit;
+            outp(PIC1 + 1, irqMask[0]);
+            irqMask[0] = inp(PIC1 + 1);
+        } else if (!enable && !(irqMask[0] & bit)) {
+            irqMask[0] = inp(PIC1 + 1);
+            irqMask[0] |= bit;
+            outp(PIC1 + 1, irqMask[0]);
+            irqMask[0] = inp(PIC1 + 1);
+        }
+    } else if (vector >= PIC2_BASE && vector < PIC2_BASE + 8) {
+        vector -= PIC2_BASE;
+
+        uint8_t bit = 1 << vector;
+
+        if (enable && (irqMask[1] & bit)) {
+            irqMask[1] = inp(PIC2 + 1);
+            irqMask[1] &= ~bit;
+            outp(PIC2 + 1, irqMask[1]);
+            irqMask[1] = inp(PIC2 + 1);
+        } else if (!enable && !(irqMask[1] & bit)) {
+            irqMask[1] = inp(PIC2 + 1);
+            irqMask[1] |= bit;
+            outp(PIC2 + 1, irqMask[1]);
+            irqMask[1] = inp(PIC2 + 1);
+        }
+
+        bit = 1 << (INT_PIC2 - PIC1_BASE);
+
+        if (irqMask[1] != 0xff && (irqMask[0] & bit)) {
+            irqMask[0] = inp(PIC1 + 1);
+            irqMask[0] &= ~bit;
+            outp(PIC1 + 1, irqMask[0]);
+            irqMask[0] = inp(PIC1 + 1);
+        } else if (irqMask[1] == 0 && !(irqMask[0] & bit)) {
+            irqMask[0] = inp(PIC1 + 1);
+            irqMask[0] |= bit;
+            outp(PIC1 + 1, irqMask[0]);
+            irqMask[0] = inp(PIC1 + 1);
+        }
+    } else {
+        //dprintf(DEBUG, "Invalid PIC interrupt: %02x\n", vector);
+    }
+}
+
+void issueEOI(unsigned int vector)
+{
+    if (vector >= PIC1_BASE && vector <= PIC1_BASE + 7) {
+        outp(PIC1, 0x20);
+    } else if (vector >= PIC2_BASE && vector <= PIC2_BASE + 7) {
+        outp(PIC2, 0x20);
+        outp(PIC1, 0x20);   // must issue both for the second PIC
+    }
+}
+
+void platform_init_interrupts(void)
+{
+    // rebase the PIC out of the way of processor exceptions
+    map(PIC1_BASE, PIC2_BASE);
+}
+
+status_t mask_interrupt(unsigned int vector)
+{
+    if (vector >= INT_VECTORS)
+        return ERR_INVALID_ARGS;
+
+//  dprintf(DEBUG, "%s: vector %d\n", __PRETTY_FUNCTION__, vector);
+
+    spin_lock_saved_state_t state;
+    spin_lock_irqsave(&lock, state);
+
+    enable(vector, false);
+
+    spin_unlock_irqrestore(&lock, state);
+
+    return NO_ERROR;
+}
+
+
+void platform_mask_irqs(void)
+{
+    irqMask[0] = inp(PIC1 + 1);
+    irqMask[1] = inp(PIC2 + 1);
+
+    outp(PIC1 + 1, 0xff);
+    outp(PIC2 + 1, 0xff);
+
+    irqMask[0] = inp(PIC1 + 1);
+    irqMask[1] = inp(PIC2 + 1);
+}
+
+status_t unmask_interrupt(unsigned int vector)
+{
+    if (vector >= INT_VECTORS)
+        return ERR_INVALID_ARGS;
+
+//  dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);
+
+    spin_lock_saved_state_t state;
+    spin_lock_irqsave(&lock, state);
+
+    enable(vector, true);
+
+    spin_unlock_irqrestore(&lock, state);
+
+    return NO_ERROR;
+}
+
+enum handler_return platform_irq(struct x86_iframe *frame)
+{
+    // get the current vector
+    unsigned int vector = frame->vector;
+
+    THREAD_STATS_INC(interrupts);
+
+    // deliver the interrupt
+    enum handler_return ret = INT_NO_RESCHEDULE;
+
+    switch (vector) {
+        case INT_GP_FAULT:
+            x86_gpf_handler(frame);
+            break;
+
+        case INT_INVALID_OP:
+            x86_invop_handler(frame);
+            break;
+        case INT_PAGE_FAULT:
+#ifdef ARCH_X86_64
+            x86_pfe_handler(frame);
+#endif
+            break;
+
+        case INT_DEV_NA_EX:
+#if X86_WITH_FPU
+            fpu_dev_na_handler();
+            break;
+#endif
+
+        case INT_MF:
+        case INT_XM:
+        case INT_DIVIDE_0:
+        case INT_DEBUG_EX:
+        case INT_STACK_FAULT:
+        case 3:
+            x86_unhandled_exception(frame);
+            break;
+
+        default:
+            if (int_handler_table[vector].handler)
+                ret = int_handler_table[vector].handler(int_handler_table[vector].arg);
+    }
+
+    // ack the interrupt
+    issueEOI(vector);
+
+    return ret;
+}
+
+void register_int_handler(unsigned int vector, int_handler handler, void *arg)
+{
+    if (vector >= INT_VECTORS)
+        panic("register_int_handler: vector out of range %d\n", vector);
+
+    spin_lock_saved_state_t state;
+    spin_lock_irqsave(&lock, state);
+
+    int_handler_table[vector].arg = arg;
+    int_handler_table[vector].handler = handler;
+
+    spin_unlock_irqrestore(&lock, state);
+}
+
+/* vim: set noexpandtab: */
+
diff --git a/src/bsp/lk/platform/pc/keyboard.c b/src/bsp/lk/platform/pc/keyboard.c
new file mode 100644
index 0000000..3478cf2
--- /dev/null
+++ b/src/bsp/lk/platform/pc/keyboard.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <sys/types.h>
+#include <err.h>
+#include <reg.h>
+#include <debug.h>
+#include <kernel/thread.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/console.h>
+#include <platform/timer.h>
+#include <platform/pc.h>
+#include "platform_p.h"
+#include <arch/x86.h>
+#include <lib/cbuf.h>
+
+static inline int i8042_read_data(void)
+{
+    return inp(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+    return inp(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+    outp(I8042_DATA_REG, val);
+}
+
+static inline void i8042_write_command(int val)
+{
+    outp(I8042_COMMAND_REG, val);
+}
+
+/*
+ * timeout in milliseconds
+ */
+#define I8042_CTL_TIMEOUT   500
+
+/*
+ * status register bits
+ */
+#define I8042_STR_PARITY    0x80
+#define I8042_STR_TIMEOUT   0x40
+#define I8042_STR_AUXDATA   0x20
+#define I8042_STR_KEYLOCK   0x10
+#define I8042_STR_CMDDAT    0x08
+#define I8042_STR_MUXERR    0x04
+#define I8042_STR_IBF       0x02
+#define I8042_STR_OBF       0x01
+
+/*
+ * control register bits
+ */
+#define I8042_CTR_KBDINT    0x01
+#define I8042_CTR_AUXINT    0x02
+#define I8042_CTR_IGNKEYLK  0x08
+#define I8042_CTR_KBDDIS    0x10
+#define I8042_CTR_AUXDIS    0x20
+#define I8042_CTR_XLATE     0x40
+
+/*
+ * commands
+ */
+#define I8042_CMD_CTL_RCTR  0x0120
+#define I8042_CMD_CTL_WCTR  0x1060
+#define I8042_CMD_CTL_TEST  0x01aa
+
+#define I8042_CMD_KBD_DIS   0x00ad
+#define I8042_CMD_KBD_EN    0x00ae
+#define I8042_CMD_KBD_TEST  0x01ab
+#define I8042_CMD_KBD_MODE  0x01f0
+
+/*
+ * used for flushing buffers. the i8042 internal buffer shoudn't exceed this.
+ */
+#define I8042_BUFFER_LENGTH 32
+
+static inline void delay(lk_time_t delay)
+{
+    lk_time_t start = current_time();
+
+    while (start + delay > current_time());
+}
+
+/* scancodes we want to do something with that don't translate via table */
+#define SCANCODE_LSHIFT 0x2a
+#define SCANCODE_RSHIFT 0x36
+
+/* scancode translation tables */
+static const int KeyCodeSingleLower[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+    -1,  -1, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', // 0
+    'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\n',  -1, 'a', 's', // 1
+    'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',  -1,'\\', 'z', 'x', 'c', 'v', // 2
+    'b', 'n', 'm', ',', '.', '/',  -1, '*',  -1, ' ',  -1,  -1,  -1,  -1,  -1,  -1, // 3
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+static const int KeyCodeMultiLower[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 0
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 1
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 2
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 3
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+static const int KeyCodeSingleUpper[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+    -1,  -1, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', // 0
+    'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\n',  -1, 'A', 'S', // 1
+    'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',  -1, '|', 'Z', 'X', 'C', 'V', // 2
+    'B', 'N', 'M', '<', '>', '?',  -1, '*',  -1, ' ',  -1,  -1,  -1,  -1,  -1,  -1, // 3
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+static const int KeyCodeMultiUpper[] = {
+// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 0
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 1
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 2
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 3
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 4
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 5
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 6
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, // 7
+};
+
+/*
+ * state key flags
+ */
+static bool key_lshift;
+static bool key_rshift;
+
+static cbuf_t key_buf;
+
+static void i8042_process_scode(uint8_t scode, unsigned int flags)
+{
+    static int lastCode = 0;
+    int keyCode;
+    uint8_t keyUpBit;
+
+    bool multi = lastCode == 0xe0;
+
+    // save the key up event bit
+    keyUpBit = scode & 0x80;
+    scode &= 0x7f;
+
+    if (scode == SCANCODE_LSHIFT) {
+        key_lshift = !keyUpBit;
+    }
+
+    if (scode == SCANCODE_RSHIFT) {
+        key_rshift = !keyUpBit;
+    }
+
+    if (key_lshift || key_rshift) {
+        keyCode = multi ? KeyCodeMultiUpper[scode] : KeyCodeSingleUpper[scode];
+    } else {
+        keyCode = multi ? KeyCodeMultiLower[scode] : KeyCodeSingleLower[scode];
+    }
+
+    /*printf_xy(71, 3, BLUE, "%02x%02x %c %c%c", multi ? lastCode : 0, scode,
+        keyCode != -1 ? (char) keyCode : ' ', key_lshift ? 'L' : ' ',
+        key_rshift ? 'R' : ' ');*/
+
+    if (keyCode != -1 && !keyUpBit) {
+        char c = (char) keyCode;
+        cbuf_write_char(&key_buf, c, false);
+    }
+
+    // update the last received code
+    lastCode = scode;
+}
+
+static int i8042_wait_read(void)
+{
+    int i = 0;
+    while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
+        delay(1);
+        i++;
+    }
+    return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_wait_write(void)
+{
+    int i = 0;
+    while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
+        delay(1);
+        i++;
+    }
+    return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_flush(void)
+{
+    unsigned char data __UNUSED;
+    int i = 0;
+
+    //enter_critical_section();
+
+    while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) {
+        delay(1);
+        data = i8042_read_data();
+    }
+
+    //exit_critical_section();
+
+    return i;
+}
+
+static int i8042_command(uint8_t *param, int command)
+{
+    int retval = 0, i = 0;
+
+    //enter_critical_section();
+
+    retval = i8042_wait_write();
+    if (!retval) {
+        i8042_write_command(command & 0xff);
+    }
+
+    if (!retval) {
+        for (i = 0; i < ((command >> 12) & 0xf); i++) {
+            if ((retval = i8042_wait_write())) {
+                break;
+            }
+
+            i8042_write_data(param[i]);
+        }
+    }
+
+    if (!retval) {
+        for (i = 0; i < ((command & 0xf0) >> 8); i++) {
+            if ((retval = i8042_wait_read())) {
+                break;
+            }
+
+            if (i8042_read_status() & I8042_STR_AUXDATA) {
+                param[i] = ~i8042_read_data();
+            } else {
+                param[i] = i8042_read_data();
+            }
+        }
+    }
+
+    //exit_critical_section();
+
+    return retval;
+}
+
+static enum handler_return i8042_interrupt(void *arg)
+{
+    uint8_t str, data = 0;
+
+    //enter_critical_section();
+    str = i8042_read_status();
+    if (str & I8042_STR_OBF) {
+        data = i8042_read_data();
+    }
+    //exit_critical_section();
+
+    if (str & I8042_STR_OBF) {
+        i8042_process_scode(data,
+                            ((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) |
+                            ((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0));
+    }
+
+    return INT_NO_RESCHEDULE;
+}
+
+int platform_read_key(char *c)
+{
+    ssize_t len;
+
+    len = cbuf_read_char(&key_buf, c, true);
+    return len;
+}
+
+void platform_init_keyboard(void)
+{
+    uint8_t ctr;
+
+    cbuf_initialize(&key_buf, 32);
+
+    i8042_flush();
+
+    if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
+        dprintf(SPEW, "Failed to read CTR while initializing i8042\n");
+        return;
+    }
+
+    // turn on translation
+    ctr |= I8042_CTR_XLATE;
+
+    // enable keyboard and keyboard irq
+    ctr &= ~I8042_CTR_KBDDIS;
+    ctr |= I8042_CTR_KBDINT;
+
+    if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
+        dprintf(SPEW, "Failed to write CTR while initializing i8042\n");
+        return;
+    }
+
+    register_int_handler(INT_KEYBOARD, &i8042_interrupt, NULL);
+    unmask_interrupt(INT_KEYBOARD);
+
+    i8042_interrupt(NULL);
+}
diff --git a/src/bsp/lk/platform/pc/pci.c b/src/bsp/lk/platform/pc/pci.c
new file mode 100644
index 0000000..87aac3b
--- /dev/null
+++ b/src/bsp/lk/platform/pc/pci.c
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <debug.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kernel/thread.h>
+#include <kernel/spinlock.h>
+#include <arch/x86/descriptor.h>
+#include <dev/pci.h>
+
+static int last_bus = 0;
+static spin_lock_t lock;
+
+typedef struct {
+    uint16_t size;
+    void *offset;
+    uint16_t selector;
+} __PACKED irq_routing_options_t;
+
+static int pci_type1_detect(void);
+static int pci_bios_detect(void);
+
+int pci_get_last_bus(void)
+{
+    return last_bus;
+}
+
+/*
+ * pointers to installed PCI routines
+ */
+int (*g_pci_find_pci_device)(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
+int (*g_pci_find_pci_class_code)(pci_location_t *state, uint32_t class_code, uint16_t index);
+
+int (*g_pci_read_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t *value);
+int (*g_pci_read_config_half)(const pci_location_t *state, uint32_t reg, uint16_t *value);
+int (*g_pci_read_config_word)(const pci_location_t *state, uint32_t reg, uint32_t *value);
+
+int (*g_pci_write_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t value);
+int (*g_pci_write_config_half)(const pci_location_t *state, uint32_t reg, uint16_t value);
+int (*g_pci_write_config_word)(const pci_location_t *state, uint32_t reg, uint32_t value);
+
+int (*g_pci_get_irq_routing_options)(irq_routing_options_t *options, uint16_t *pci_irqs);
+int (*g_pci_set_irq_hw_int)(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
+
+
+int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_find_pci_device(state, device_id, vendor_id, index);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_find_pci_class_code(state, class_code, index);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_read_config_byte(state, reg, value);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_read_config_half(state, reg, value);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_read_config_word(state, reg, value);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_write_config_byte(state, reg, value);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_write_config_half(state, reg, value);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_write_config_word(state, reg, value);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+
+int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs)
+{
+    irq_routing_options_t options;
+    options.size = sizeof(irq_routing_entry) **count;
+    options.selector = DATA_SELECTOR;
+    options.offset = entries;
+
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_get_irq_routing_options(&options, pci_irqs);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    *count = options.size / sizeof(irq_routing_entry);
+
+    return res;
+}
+
+int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
+{
+    spin_lock_saved_state_t irqstate;
+    spin_lock_irqsave(&lock, irqstate);
+
+    int res = g_pci_set_irq_hw_int(state, int_pin, irq);
+
+    spin_unlock_irqrestore(&lock, irqstate);
+
+    return res;
+}
+
+void pci_init(void)
+{
+    if (!pci_bios_detect()) {
+        dprintf(INFO, "pci bios functions installed\n");
+        dprintf(INFO, "last pci bus is %d\n", last_bus);
+    }
+}
+
+#define PCIBIOS_PRESENT                 0xB101
+#define PCIBIOS_FIND_PCI_DEVICE         0xB102
+#define PCIBIOS_FIND_PCI_CLASS_CODE     0xB103
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE  0xB106
+#define PCIBIOS_READ_CONFIG_BYTE        0xB108
+#define PCIBIOS_READ_CONFIG_WORD        0xB109
+#define PCIBIOS_READ_CONFIG_DWORD       0xB10A
+#define PCIBIOS_WRITE_CONFIG_BYTE       0xB10B
+#define PCIBIOS_WRITE_CONFIG_WORD       0xB10C
+#define PCIBIOS_WRITE_CONFIG_DWORD      0xB10D
+#define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0xB10E
+#define PCIBIOS_PCI_SET_IRQ_HW_INT      0xB10F
+
+#define PCIBIOS_SUCCESSFUL              0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED      0x81
+#define PCIBIOS_BAD_VENDOR_ID           0x83
+#define PCIBIOS_DEVICE_NOT_FOUND        0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER     0x87
+#define PCIBIOS_SET_FAILED              0x88
+#define PCIBIOS_BUFFER_TOO_SMALL        0x89
+
+/*
+ * far call structure used by BIOS32 routines
+ */
+static struct {
+    uint32_t offset;
+    uint16_t selector;
+} __PACKED bios32_entry;
+
+/*
+ * BIOS32 entry header
+ */
+typedef struct {
+    uint8_t magic[4];   // "_32_"
+    void * entry;       // entry point
+    uint8_t revision;
+    uint8_t length;
+    uint8_t checksum;
+    uint8_t reserved[5];
+} __PACKED pci_bios_info;
+
+/*
+ * scan for pci bios
+ */
+static const char * pci_bios_magic = "_32_";
+static pci_bios_info *find_pci_bios_info(void)
+{
+    uint32_t *head = (uint32_t *) 0x000e0000;
+    int8_t sum, *b;
+    uint i;
+
+    while (head < (uint32_t *) 0x000ffff0) {
+        if (*head == *(uint32_t *) pci_bios_magic) {
+            // perform the checksum
+            sum = 0;
+            b = (int8_t *) head;
+            for (i=0; i < sizeof(pci_bios_info); i++) {
+                sum += b[i];
+            }
+
+            if (sum == 0) {
+                return (pci_bios_info *) head;
+            }
+        }
+
+        head += 4;
+    }
+
+    return NULL;
+}
+
+/*
+ * local BIOS32 PCI routines
+ */
+static int bios_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
+{
+    uint32_t bx, ret;
+
+    __asm__(
+        "lcall *(%%edi)		\n\t"
+        "jc 1f				\n\t"
+        "xor %%ah,%%ah		\n"
+        "1:"
+        : "=b"(bx),
+        "=a"(ret)
+        : "1"(PCIBIOS_FIND_PCI_DEVICE),
+        "c"(device_id),
+        "d"(vendor_id),
+        "S"(index),
+        "D"(&bios32_entry));
+
+    state->bus = bx >> 8;
+    state->dev_fn = bx & 0xFF;
+
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
+{
+    uint32_t bx, ret;
+
+    __asm__(
+        "lcall *(%%edi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=b"(bx),
+        "=a"(ret)
+        : "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
+        "c"(class_code),
+        "S"(index),
+        "D"(&bios32_entry));
+
+    state->bus = bx >> 8;
+    state->dev_fn = bx & 0xFF;
+
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+
+static int bios_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
+{
+    uint32_t bx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=c"(*value),
+        "=a"(ret)
+        : "1"(PCIBIOS_READ_CONFIG_BYTE),
+        "b"(bx),
+        "D"(reg),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
+{
+    uint32_t bx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=c"(*value),
+        "=a"(ret)
+        : "1"(PCIBIOS_READ_CONFIG_WORD),
+        "b"(bx),
+        "D"(reg),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
+{
+    uint32_t bx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=c"(*value),
+        "=a"(ret)
+        : "1"(PCIBIOS_READ_CONFIG_DWORD),
+        "b"(bx),
+        "D"(reg),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
+{
+    uint32_t bx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=a"(ret)
+        : "0"(PCIBIOS_WRITE_CONFIG_BYTE),
+        "c"(value),
+        "b"(bx),
+        "D"(reg),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
+{
+    uint32_t bx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    __asm__(
+        "lcall *(%%esi)	\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=a"(ret)
+        : "0"(PCIBIOS_WRITE_CONFIG_WORD),
+        "c"(value),
+        "b"(bx),
+        "D"(reg),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
+{
+    uint32_t bx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=a"(ret)
+        : "0"(PCIBIOS_WRITE_CONFIG_DWORD),
+        "c"(value),
+        "b"(bx),
+        "D"(reg),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static int bios_get_irq_routing_options(irq_routing_options_t *route_buffer, uint16_t *pciIrqs)
+{
+    uint32_t ret;
+
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=b"(*pciIrqs),
+        "=a"(ret)
+        : "1"(PCIBIOS_GET_IRQ_ROUTING_OPTIONS),
+        "b"(0),
+        "D"(route_buffer),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xff;
+}
+
+static int bios_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
+{
+    uint32_t bx, cx, ret;
+
+    bx = state->bus;
+    bx <<= 8;
+    bx |= state->dev_fn;
+    cx = irq;
+    cx <<= 8;
+    cx |= int_pin;
+    __asm__(
+        "lcall *(%%esi)			\n\t"
+        "jc 1f					\n\t"
+        "xor %%ah,%%ah			\n"
+        "1:"
+        : "=a"(ret)
+        : "0"(PCIBIOS_PCI_SET_IRQ_HW_INT),
+        "b"(bx),
+        "c"(cx),
+        "S"(&bios32_entry));
+    ret >>= 8;
+    return ret & 0xFF;
+}
+
+static const char *pci_signature = "PCI ";
+static int pci_bios_detect(void)
+{
+    pci_bios_info * pci = find_pci_bios_info();
+
+    if (pci != NULL) {
+        /*printf("Found PCI structure at %08x\n", (uint32_t) pci);
+
+        printf("\nPCI header info:\n");
+        printf("%c%c%c%c\n", pci->magic[0], pci->magic[1], pci->magic[2],
+            pci->magic[3]);
+        printf("%08x\n", (uint32_t) pci->entry);
+        printf("%d\n", pci->length * 16);
+        printf("%d\n", pci->checksum);*/
+
+        uint32_t adr, temp, len;
+        uint8_t err;
+
+        bios32_entry.offset = (uint32_t) pci->entry;
+        bios32_entry.selector = CODE_SELECTOR;
+
+        __asm__(
+            "lcall *(%%edi)"
+            : "=a"(err),    /* AL out=status */
+            "=b"(adr),    /* EBX out=code segment base adr */
+            "=c"(len),    /* ECX out=code segment size */
+            "=d"(temp)    /* EDX out=entry pt offset in code */
+            : "0"(0x49435024),/* EAX in=service="$PCI" */
+            "1"(0),   /* EBX in=0=get service entry pt */
+            "D"(&bios32_entry)
+        );
+
+        if (err == 0x80) {
+            dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
+            return -1;
+        }
+
+        if (err != 0) {
+            dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
+            return -1;
+        }
+
+        bios32_entry.offset = adr + temp;
+
+        // now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
+        uint16_t present, version, busses;
+        uint32_t signature;
+        __asm__(
+            "lcall *(%%edi)		\n\t"
+            "jc 1f				\n\t"
+            "xor %%ah,%%ah		\n"
+            "1:"
+            : "=a"(present),
+            "=b"(version),
+            "=c"(busses),
+            "=d"(signature)
+            : "0"(PCIBIOS_PRESENT),
+            "D"(&bios32_entry)
+        );
+
+        if (present & 0xff00) {
+            dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%02x\n", present >> 8);
+            return -1;
+        }
+
+        if (signature != *(uint32_t *)pci_signature) {
+            dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%08x\n", signature);
+            return -1;
+        }
+
+        //dprintf(DEBUG, "busses=%04x\n", busses);
+        last_bus = busses & 0xff;
+
+        g_pci_find_pci_device = bios_find_pci_device;
+        g_pci_find_pci_class_code = bios_find_pci_class_code;
+
+        g_pci_read_config_word = bios_read_config_word;
+        g_pci_read_config_half = bios_read_config_half;
+        g_pci_read_config_byte = bios_read_config_byte;
+
+        g_pci_write_config_word = bios_write_config_word;
+        g_pci_write_config_half = bios_write_config_half;
+        g_pci_write_config_byte = bios_write_config_byte;
+
+        g_pci_get_irq_routing_options = bios_get_irq_routing_options;
+        g_pci_set_irq_hw_int = bios_set_irq_hw_int;
+
+        return 0;
+    }
+
+    return -1;
+}
diff --git a/src/bsp/lk/platform/pc/platform.c b/src/bsp/lk/platform/pc/platform.c
new file mode 100644
index 0000000..d97190c
--- /dev/null
+++ b/src/bsp/lk/platform/pc/platform.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * 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 <trace.h>
+#include <arch/x86/mmu.h>
+#include <platform.h>
+#include "platform_p.h"
+#include <platform/pc.h>
+#include <platform/multiboot.h>
+#include <platform/console.h>
+#include <platform/keyboard.h>
+#include <dev/pci.h>
+#include <dev/uart.h>
+#include <arch/x86.h>
+#include <arch/mmu.h>
+#include <malloc.h>
+#include <string.h>
+#include <assert.h>
+#include <kernel/vm.h>
+
+extern multiboot_info_t *_multiboot_info;
+
+#define DEFAULT_MEMEND (16*1024*1024)
+
+#ifdef WITH_KERNEL_VM
+extern int _end;
+static uintptr_t _heap_end = (uintptr_t)DEFAULT_MEMEND;
+#else
+extern uintptr_t _heap_end;
+#endif
+extern uint64_t __code_start;
+extern uint64_t __code_end;
+extern uint64_t __rodata_start;
+extern uint64_t __rodata_end;
+extern uint64_t __data_start;
+extern uint64_t __data_end;
+extern uint64_t __bss_start;
+extern uint64_t __bss_end;
+extern void pci_init(void);
+extern void arch_mmu_init(void);
+
+/* Address width including virtual/physical address*/
+uint8_t g_vaddr_width = 0;
+uint8_t g_paddr_width = 0;
+
+/* Kernel global CR3 */
+map_addr_t g_CR3 = 0;
+
+void platform_init_mmu_mappings(void)
+{
+    struct map_range range;
+    arch_flags_t access;
+    map_addr_t *init_table, phy_init_table;
+    uint32_t   addr_width;
+
+    /* getting the address width from CPUID instr */
+    /* Bits 07-00: Physical Address width info */
+    /* Bits 15-08: Linear Address width info */
+    addr_width    = x86_get_address_width();
+    g_paddr_width = (uint8_t)(addr_width & 0xFF);
+    g_vaddr_width = (uint8_t)((addr_width >> 8) & 0xFF);
+
+    /* Creating the First page in the page table hirerachy */
+    /* Can be pml4, pdpt or pdt based on x86_64, x86 PAE mode & x86 non-PAE mode respectively */
+    init_table = memalign(PAGE_SIZE, PAGE_SIZE);
+    ASSERT(init_table);
+    memset(init_table, 0, PAGE_SIZE);
+
+    phy_init_table = (map_addr_t)X86_VIRT_TO_PHYS(init_table);
+
+    /* kernel code section mapping */
+    access = ARCH_MMU_FLAG_PERM_RO;
+    range.start_vaddr = range.start_paddr = (map_addr_t) &__code_start;
+    range.size = ((map_addr_t)&__code_end) - ((map_addr_t)&__code_start);
+    x86_mmu_map_range(phy_init_table, &range, access);
+
+    /* kernel data section mapping */
+    access = 0;
+#if defined(ARCH_X86_64) || defined(PAE_MODE_ENABLED)
+    access |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
+#endif
+    range.start_vaddr = range.start_paddr = (map_addr_t) &__data_start;
+    range.size = ((map_addr_t)&__data_end) - ((map_addr_t)&__data_start);
+    x86_mmu_map_range(phy_init_table, &range, access);
+
+    /* kernel rodata section mapping */
+    access = ARCH_MMU_FLAG_PERM_RO;
+#if defined(ARCH_X86_64) || defined(PAE_MODE_ENABLED)
+    access |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
+#endif
+    range.start_vaddr = range.start_paddr = (map_addr_t) &__rodata_start;
+    range.size = ((map_addr_t)&__rodata_end) - ((map_addr_t)&__rodata_start);
+    x86_mmu_map_range(phy_init_table, &range, access);
+
+    /* kernel bss section and kernel heap mappings */
+    access = 0;
+#ifdef ARCH_X86_64
+    access |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
+#endif
+    range.start_vaddr = range.start_paddr = (map_addr_t) &__bss_start;
+    range.size = ((map_addr_t)_heap_end) - ((map_addr_t)&__bss_start);
+    x86_mmu_map_range(phy_init_table, &range, access);
+
+    /* Mapping for BIOS, devices */
+    access = 0;
+    range.start_vaddr = range.start_paddr = (map_addr_t) 0;
+    range.size = ((map_addr_t)&__code_start);
+    x86_mmu_map_range(phy_init_table, &range, access);
+
+    /* Moving to the new CR3 */
+    g_CR3 = (map_addr_t)phy_init_table;
+    x86_set_cr3((map_addr_t)phy_init_table);
+}
+
+#if WITH_KERNEL_VM
+struct mmu_initial_mapping mmu_initial_mappings[] = {
+    /* all of detected memory */
+    {
+        .phys = MEMBASE,
+        .virt = MEMBASE,
+        .size = DEFAULT_MEMEND - MEMBASE,
+        .flags = 0,
+        .name = "memory"
+    },
+
+    /* null entry to terminate the list */
+    { 0 }
+};
+
+/* set up the size of the identity map of physical ram to virtual at the base
+ * of the kernel to match what we detected in platform_init_multiboot_info()
+ */
+void initial_mapping_init(void)
+{
+    /* tweak the amount of physical memory map we have mapped
+     * in the mmu_initial_mappings table, which is used by the vm
+     * for reverse lookups of memory in the kernel area */
+    mmu_initial_mappings[0].size = _heap_end - mmu_initial_mappings[0].virt;
+}
+
+static pmm_arena_t mem_arena = {
+    .name = "memory",
+    .base = MEMBASE, /* start 2MB into memory */
+    .size = DEFAULT_MEMEND, /* default amount of memory in case we don't have multiboot */
+    .priority = 1,
+    .flags = PMM_ARENA_FLAG_KMAP
+};
+
+/* set up the size of the physical memory map based on the end of memory we detected in
+ * platform_init_multiboot_info()
+ */
+void mem_arena_init(void)
+{
+    uintptr_t mem_base = ((uintptr_t)MEMBASE);
+    uintptr_t mem_size = (uintptr_t)_heap_end - (uintptr_t)mem_base;
+
+    mem_arena.base = PAGE_ALIGN(mem_base);
+    mem_arena.size = PAGE_ALIGN(mem_size);
+}
+#endif
+
+void platform_init_multiboot_info(void)
+{
+    if (_multiboot_info) {
+        if (_multiboot_info->flags & MB_INFO_MEM_SIZE) {
+            _heap_end = _multiboot_info->mem_upper * 1024;
+        }
+
+        if (_multiboot_info->flags & MB_INFO_MMAP) {
+            memory_map_t *mmap = (memory_map_t *)(uintptr_t)(_multiboot_info->mmap_addr - 4);
+
+            for (uint i = 0; i < _multiboot_info->mmap_length / sizeof(memory_map_t); i++) {
+
+                if (mmap[i].type == MB_MMAP_TYPE_AVAILABLE && mmap[i].base_addr_low >= _heap_end) {
+                    _heap_end = mmap[i].base_addr_low + mmap[i].length_low;
+                } else if (mmap[i].type != MB_MMAP_TYPE_AVAILABLE && mmap[i].base_addr_low >= _heap_end) {
+                    /*
+                     * break on first memory hole above default heap end for now.
+                     * later we can add facilities for adding free chunks to the
+                     * heap for each segregated memory region.
+                     */
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void platform_early_init(void)
+{
+
+    platform_init_uart();
+
+    /* get the text console working */
+    platform_init_console();
+
+    /* initialize the interrupt controller */
+    platform_init_interrupts();
+
+    /* initialize the timer */
+    platform_init_timer();
+
+    /* look at multiboot to determine our memory size */
+    platform_init_multiboot_info();
+
+#ifdef WITH_KERNEL_VM
+    initial_mapping_init();
+    mem_arena_init();
+    pmm_add_arena(&mem_arena);
+#endif
+}
+
+void platform_init(void)
+{
+    uart_init();
+
+    platform_init_keyboard();
+#if defined(ARCH_X86)
+    pci_init();
+#endif
+
+    /* MMU init for x86 Archs done after the heap is setup */
+    // XXX move this into arch/
+    arch_mmu_init();
+    platform_init_mmu_mappings();
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/bsp/lk/platform/pc/platform_p.h b/src/bsp/lk/platform/pc/platform_p.h
new file mode 100644
index 0000000..07bbbb6
--- /dev/null
+++ b/src/bsp/lk/platform/pc/platform_p.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_P_H
+#define __PLATFORM_P_H
+
+void platform_init_interrupts(void);
+void platform_init_timer(void);
+void platform_init_uart(void);
+
+#endif
+
diff --git a/src/bsp/lk/platform/pc/rules.mk b/src/bsp/lk/platform/pc/rules.mk
new file mode 100644
index 0000000..24f9e5d
--- /dev/null
+++ b/src/bsp/lk/platform/pc/rules.mk
@@ -0,0 +1,40 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+CPU := generic
+
+MODULE_DEPS += \
+	lib/cbuf \
+
+ifeq ($(ARCH), x86)
+MODULE_SRCS += \
+	$(LOCAL_DIR)/interrupts.c \
+	$(LOCAL_DIR)/platform.c \
+	$(LOCAL_DIR)/timer.c \
+	$(LOCAL_DIR)/debug.c \
+	$(LOCAL_DIR)/console.c \
+	$(LOCAL_DIR)/keyboard.c \
+	$(LOCAL_DIR)/pci.c \
+	$(LOCAL_DIR)/ide.c \
+	$(LOCAL_DIR)/uart.c \
+
+else
+MODULE_SRCS += \
+        $(LOCAL_DIR)/interrupts.c \
+        $(LOCAL_DIR)/platform.c \
+        $(LOCAL_DIR)/timer.c \
+        $(LOCAL_DIR)/debug.c \
+        $(LOCAL_DIR)/console.c \
+        $(LOCAL_DIR)/keyboard.c \
+        $(LOCAL_DIR)/uart.c \
+
+endif
+
+LK_HEAP_IMPLEMENTATION ?= dlmalloc
+
+LINKER_SCRIPT += \
+	$(BUILDDIR)/kernel.ld
+
+include make/module.mk
+
diff --git a/src/bsp/lk/platform/pc/timer.c b/src/bsp/lk/platform/pc/timer.c
new file mode 100644
index 0000000..06f329b
--- /dev/null
+++ b/src/bsp/lk/platform/pc/timer.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <sys/types.h>
+#include <err.h>
+#include <reg.h>
+#include <debug.h>
+#include <kernel/thread.h>
+#include <kernel/spinlock.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/console.h>
+#include <platform/timer.h>
+#include <platform/pc.h>
+#include "platform_p.h"
+#include <arch/x86.h>
+
+static platform_timer_callback t_callback;
+static void *callback_arg;
+static spin_lock_t lock;
+
+static uint64_t next_trigger_time;
+static uint64_t next_trigger_delta;
+static uint64_t ticks_per_ms;
+
+static uint64_t timer_delta_time;
+static volatile uint64_t timer_current_time;
+
+static uint16_t divisor;
+
+#define INTERNAL_FREQ 1193182ULL
+#define INTERNAL_FREQ_3X 3579546ULL
+
+/* Maximum amount of time that can be program on the timer to schedule the next
+ *  interrupt, in miliseconds */
+#define MAX_TIMER_INTERVAL 55
+
+
+
+status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
+{
+    t_callback = callback;
+    callback_arg = arg;
+
+    next_trigger_delta = (uint64_t) interval << 32;
+    next_trigger_time = timer_current_time + next_trigger_delta;
+
+    return NO_ERROR;
+}
+
+lk_time_t current_time(void)
+{
+    lk_time_t time;
+
+    // XXX slight race
+    time = (lk_time_t) (timer_current_time >> 32);
+
+    return time;
+}
+
+lk_bigtime_t current_time_hires(void)
+{
+    lk_bigtime_t time;
+
+    // XXX slight race
+    time = (lk_bigtime_t) ((timer_current_time >> 22) * 1000) >> 10;
+
+    return time;
+}
+static enum handler_return os_timer_tick(void *arg)
+{
+    uint64_t delta;
+
+    timer_current_time += timer_delta_time;
+
+    lk_time_t time = current_time();
+    //lk_bigtime_t btime = current_time_hires();
+    //printf_xy(71, 0, WHITE, "%08u", (uint32_t) time);
+    //printf_xy(63, 1, WHITE, "%016llu", (uint64_t) btime);
+
+    if (t_callback && timer_current_time >= next_trigger_time) {
+        delta = timer_current_time - next_trigger_time;
+        next_trigger_time = timer_current_time + next_trigger_delta - delta;
+
+        return t_callback(callback_arg, time);
+    } else {
+        return INT_NO_RESCHEDULE;
+    }
+}
+
+static void set_pit_frequency(uint32_t frequency)
+{
+    uint32_t count, remainder;
+
+    /* figure out the correct divisor for the desired frequency */
+    if (frequency <= 18) {
+        count = 0xffff;
+    } else if (frequency >= INTERNAL_FREQ) {
+        count = 1;
+    } else {
+        count = INTERNAL_FREQ_3X / frequency;
+        remainder = INTERNAL_FREQ_3X % frequency;
+
+        if (remainder >= INTERNAL_FREQ_3X / 2) {
+            count += 1;
+        }
+
+        count /= 3;
+        remainder = count % 3;
+
+        if (remainder >= 1) {
+            count += 1;
+        }
+    }
+
+    divisor = count & 0xffff;
+
+    /*
+     * funky math that i don't feel like explaining. essentially 32.32 fixed
+     * point representation of the configured timer delta.
+     */
+    timer_delta_time = (3685982306ULL * count) >> 10;
+
+    //dprintf(DEBUG, "set_pit_frequency: dt=%016llx\n", timer_delta_time);
+    //dprintf(DEBUG, "set_pit_frequency: divisor=%04x\n", divisor);
+
+    /*
+     * setup the Programmable Interval Timer
+     * timer 0, mode 2, binary counter, LSB followed by MSB
+     */
+    outp(I8253_CONTROL_REG, 0x34);
+    outp(I8253_DATA_REG, divisor & 0xff); // LSB
+    outp(I8253_DATA_REG, divisor >> 8); // MSB
+}
+
+void platform_init_timer(void)
+{
+
+    timer_current_time = 0;
+    ticks_per_ms = INTERNAL_FREQ/1000;
+    set_pit_frequency(1000); // ~1ms granularity
+    register_int_handler(INT_PIT, &os_timer_tick, NULL);
+    unmask_interrupt(INT_PIT);
+}
+
+void platform_halt_timers(void)
+{
+    mask_interrupt(INT_PIT);
+}
+
+
+
+status_t platform_set_oneshot_timer(platform_timer_callback callback,
+                                    void *arg, lk_time_t interval)
+{
+
+    uint32_t count;
+
+    spin_lock_saved_state_t state;
+    spin_lock_irqsave(&lock, state);
+
+    t_callback = callback;
+    callback_arg = arg;
+
+
+    if (interval > MAX_TIMER_INTERVAL)
+        interval = MAX_TIMER_INTERVAL;
+    if (interval < 1) interval = 1;
+
+    count = ticks_per_ms * interval;
+
+    divisor = count & 0xffff;
+    timer_delta_time = (3685982306ULL * count) >> 10;
+    /* Program PIT in teh software strobe configuration, to send one pulse
+     * after the count reach 0 */
+    outp(I8253_CONTROL_REG, 0x38);
+    outp(I8253_DATA_REG, divisor & 0xff); // LSB
+    outp(I8253_DATA_REG, divisor >> 8); // MSB
+
+
+    unmask_interrupt(INT_PIT);
+    spin_unlock_irqrestore(&lock, state);
+
+    return NO_ERROR;
+}
+
+void platform_stop_timer(void)
+{
+    /* Enable interrupt mode that will stop the decreasing counter of the PIT */
+    outp(I8253_CONTROL_REG, 0x30);
+    return;
+}
+
+/* vim: set noexpandtab */
diff --git a/src/bsp/lk/platform/pc/uart.c b/src/bsp/lk/platform/pc/uart.c
new file mode 100644
index 0000000..be94bf6
--- /dev/null
+++ b/src/bsp/lk/platform/pc/uart.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2012 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dev/driver.h>
+#include <dev/class/uart.h>
+#include <debug.h>
+#include <assert.h>
+#include <malloc.h>
+#include <err.h>
+#include <lib/cbuf.h>
+#include <platform/uart.h>
+#include <arch/x86.h>
+#include <kernel/thread.h>
+#include <platform/interrupts.h>
+
+struct device_class uart_device_class = {
+    .name = "uart",
+};
+
+struct uart_driver_state {
+    struct cbuf rx_buf;
+    struct cbuf tx_buf;
+};
+
+static status_t uart_init(struct device *dev);
+
+static enum handler_return uart_irq_handler(void *arg);
+static int uart_write_thread(void *arg);
+
+static ssize_t uart_read(struct device *dev, void *buf, size_t len);
+static ssize_t uart_write(struct device *dev, const void *buf, size_t len);
+
+static struct uart_ops the_ops = {
+    .std = {
+        .device_class = &uart_device_class,
+        .init = uart_init,
+    },
+    .read = uart_read,
+    .write = uart_write,
+};
+
+DRIVER_EXPORT(uart, &the_ops.std);
+
+static status_t uart_init(struct device *dev)
+{
+    status_t res = NO_ERROR;
+
+    if (!dev)
+        return ERR_INVALID_ARGS;
+
+    if (!dev->config)
+        return ERR_NOT_CONFIGURED;
+
+    const struct platform_uart_config *config = dev->config;
+
+    struct uart_driver_state *state = malloc(sizeof(struct uart_driver_state));
+    if (!state) {
+        res = ERR_NO_MEMORY;
+        goto done;
+    }
+
+    dev->state = state;
+
+    /* set up the driver state */
+    cbuf_initialize(&state->rx_buf, config->rx_buf_len);
+    cbuf_initialize(&state->tx_buf, config->tx_buf_len);
+
+    /* configure the uart */
+    int divisor = 115200 / config->baud_rate;
+
+    outp(config->io_port + 3, 0x80); // set up to load divisor latch
+    outp(config->io_port + 0, divisor & 0xff); // lsb
+    outp(config->io_port + 1, divisor >> 8); // msb
+    outp(config->io_port + 3, 3); // 8N1
+    outp(config->io_port + 2, 0x07); // enable FIFO, clear, 14-byte threshold
+
+    register_int_handler(config->irq, uart_irq_handler, dev);
+    unmask_interrupt(config->irq);
+
+    //outp(config->io_port + 1, 0x3); // enable rx data available and tx holding empty interrupts
+    outp(config->io_port + 1, 0x1); // enable rx data available interrupts
+
+    thread_resume(thread_create("[uart writer]", uart_write_thread, dev, DEFAULT_PRIORITY,
+                                DEFAULT_STACK_SIZE));
+
+done:
+    return res;
+}
+
+static enum handler_return uart_irq_handler(void *arg)
+{
+    bool resched = false;
+    struct device *dev = arg;
+
+    DEBUG_ASSERT(dev);
+    DEBUG_ASSERT(dev->config);
+    DEBUG_ASSERT(dev->state);
+
+    const struct platform_uart_config *config = dev->config;
+    struct uart_driver_state *state = dev->state;
+
+    while (inp(config->io_port + 5) & (1<<0)) {
+        char c = inp(config->io_port + 0);
+        cbuf_write(&state->rx_buf, &c, 1, false);
+        resched = true;
+    }
+
+    return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
+}
+
+static int uart_write_thread(void *arg)
+{
+    struct device *dev = arg;
+
+    DEBUG_ASSERT(dev);
+    DEBUG_ASSERT(dev->config);
+    DEBUG_ASSERT(dev->state);
+
+    const struct platform_uart_config *config = dev->config;
+    struct uart_driver_state *state = dev->state;
+
+    return 0;
+
+    while (true) {
+        char c = cbuf_read(&state->tx_buf, &c, 1, true);
+
+        while ((inp(config->io_port + 5) & (1<<6)) == 0)
+            ;
+
+        outp(config->io_port + 0, c);
+    }
+
+    return 0;
+}
+
+static ssize_t uart_read(struct device *dev, void *buf, size_t len)
+{
+    if (!dev || !buf)
+        return ERR_INVALID_ARGS;
+
+    DEBUG_ASSERT(dev->state);
+    struct uart_driver_state *state = dev->state;
+
+    return cbuf_read(&state->rx_buf, buf, len, true);
+}
+
+static ssize_t uart_write(struct device *dev, const void *buf, size_t len)
+{
+    if (!dev || !buf)
+        return ERR_INVALID_ARGS;
+
+    DEBUG_ASSERT(dev->state);
+    struct uart_driver_state *state = dev->state;
+
+    return cbuf_write(&state->tx_buf, buf, len, true);
+}
+