Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Cache information interface and implementation for AMD and Intel on Linux
[palacios.git] / palacios / src / devices / serial.c
index 5186b55..f1ae035 100644 (file)
@@ -1,8 +1,53 @@
-/* (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> */
-/* (c) 2008, The V3VEE Project <http://www.v3vee.org> */
+/*
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National
+ * Science Foundation and the Department of Energy.
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico.  You can find out more at
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2010, Rumou Duan <duanrumou@gmail.com>
+ * Copyright (c) 2010, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: Rumou Duan <duanrumou@gmail.com>
+ *         Lei Xia <lxia@northwestern.edu>
+ &         Peter Dinda <pdinda@northwestern.edu>
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
 
-#include <devices/serial.h>
 #include <palacios/vmm.h>
+#include <palacios/vmm_dev_mgr.h>
+#include <palacios/vmm_types.h>
+
+#include <palacios/vmm_ringbuffer.h>
+#include <palacios/vmm_lock.h>
+#include <palacios/vmm_intr.h>
+#include <palacios/vm_guest.h>
+
+#include <devices/serial.h>
+
+
+#ifndef V3_CONFIG_DEBUG_SERIAL
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
+
+/*
+
+  This is an implementation of a 16450 and 16550A-compatible UART (fifo-capable), based on
+  the TI 16550D spec (http://www.ti.com/lit/ds/symlink/pc16550d.pdf) and the Altera
+  16450 spec (ftp://ftp.altera.com/pub/lit_req/document/ds/ds16450.pdf)
+
+*/
+
+// This is not yet tested - enable at your own risk!
+#define BE_16550A  0  
+
 
 
 #define COM1_DATA_PORT           0x3f8
 #define COM4_SCRATCH_PORT        0x2ef
 
 
-
-struct irq_enable_reg {
-  uint_t erbfi   : 1;  // Enable Receiver Buffer full interrupt
-  uint_t etbei   : 1;  // Enable Transmit buffer empty interrupt
-  uint_t elsi    : 1;  // Enable Line Status Interrupt
-  uint_t edssi   : 1;  // Enable Delta Status signals interrupt
-  uint_t rsvd    : 4;   // MBZ
-};
-
-
-
 // Interrupt IDs (in priority order, highest is first)
-#define STATUS_IRQ_LSR_OE_SET   0x3
-#define STATUS_IRQ_LSR_PE_SET   0x3
-#define STATUS_IRQ_LSR_FE_SET   0x3
-#define STATUS_IRQ_LSR_BI_SET   0x3
+#define RX_IRQ_STATUS           0x3
 #define RX_IRQ_DR               0x2
 #define RX_IRQ_TRIGGER_LEVEL    0x2
 #define FIFO_IRQ                0x6
 #define TX_IRQ_THRE             0x1
-#define MODEL_IRQ_DELTA_SET     0x0
+#define MODEM_IRQ_DELTA_SET     0x0
+
+//COMs IRQ ID
+#define COM1_IRQ  0x4
+#define COM2_IRQ  0x3
+#define COM3_IRQ  0x4
+#define COM4_IRQ  0x3
+
+#define RX_BUFFER 0x1
+#define TX_BUFFER 0x2
+
+//initial value for registers
+
+// Per TI spec: http://www.ti.com/lit/ds/symlink/pc16550d.pdf
+// all interrupt enables off
+#define  IER_INIT_VAL 0x0 
+// no pending interrupts (active low flag)
+#define  IIR_INIT_VAL 0x1 
+// fifos disabled (comes up in 16450 mode)
+#define  FCR_INIT_VAL 0x0
+// 5 bits, 1 stop bit, no parity, no break, divisor latch off
+#define  LCR_INIT_VAL 0x0
+// not DTR, not RTS, not OUT1, not OUT2, not loopback
+#define  MCR_INIT_VAL 0x0
+// tx registers empty, no errors, no data available
+#define  LSR_INIT_VAL 0x60
+// not cts, not dsr, not ring, etc.
+#define  MSR_INIT_VAL 0x0
+// baud rate 115200 (divisor is 1)
+#define  DLL_INIT_VAL 0x1
+#define  DLM_INIT_VAL 0x0
+
+//receiver buffer register
+struct rbr_register {
+    uint8_t data;
+};
 
-struct irq_id_reg {
-  uint_t pending : 1; // Interrupt pending (0=interrupt pending)
-  uint_t iid     : 3; // Interrupt Identification
-  uint_t rsvd    : 2; // MBZ
-  uint_t fifo_en : 2; // FIFO enable
+// transmitter holding register
+struct thr_register {
+    uint8_t data;
 };
 
-struct fifo_ctrl_reg {
-  uint_t enable  : 1; // enable fifo
-  uint_t rfres   : 1; // RX FIFO reset
-  uint_t xfres   : 1; // TX FIFO reset
-  uint_t dma_sel : 1; // DMA mode select
-  uint_t rsvd    : 2; // MBZ
-  uint_t rx_trigger: 2; // RX FIFO trigger level select
+//interrupt enable register
+struct ier_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t erbfi   : 1;  // Enable Receiver Buffer full interrupt
+           uint8_t etbei   : 1;  // Enable Transmit buffer empty interrupt
+           uint8_t elsi    : 1;  // Enable Line Status Interrupt
+           uint8_t edssi   : 1;  // Enable Delta Status signals interrupt
+           uint8_t rsvd    : 4;   // MBZ
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+//interrupt identification register
+struct iir_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t pending : 1; // Interrupt pending (0=interrupt pending)
+           uint8_t iid     : 3; // Interrupt Identification
+           uint8_t rsvd    : 2; // MBZ
+           uint8_t fifo_en : 2; // FIFO enable
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+//FIFO control register
+struct fcr_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t enable  : 1; // enable fifo
+           uint8_t rfres   : 1; // RX FIFO reset
+           uint8_t xfres   : 1; // TX FIFO reset
+           uint8_t dma_sel : 1; // DMA mode select
+           uint8_t rsvd    : 2; // MBZ
+           uint8_t rx_trigger: 2; // RX FIFO trigger level select
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+//line control register
+struct lcr_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t word_len       : 2;  // word length select
+           uint8_t stop_bits      : 1;  // Stop Bit select
+           uint8_t parity_enable  : 1;  // Enable parity
+           uint8_t even_sel       : 1;  // Even Parity Select
+           uint8_t stick_parity   : 1;  // Stick Parity Select (e.g., force mark or space)
+           uint8_t sbr            : 1;  // Set Break
+           uint8_t dlab           : 1;  // Divisor latch access bit
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+//modem control register
+struct mcr_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t dtr      : 1;
+           uint8_t rts      : 1;
+           uint8_t out1     : 1;
+           uint8_t out2     : 1;
+           uint8_t loop     : 1;  // loopback mode
+           uint8_t rsvd     : 3;  // MBZ
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+//line status register
+struct lsr_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t dr      : 1;   // data ready
+           uint8_t oe       : 1;  // Overrun error
+           uint8_t pe       : 1;  // Parity Error
+           uint8_t fe       : 1;  // Framing Error
+           uint8_t brk      : 1;  // broken line detected
+           uint8_t thre     : 1;  // Transmitter holding register empty
+           uint8_t temt     : 1;  // Transmitter Empty
+           uint8_t fifo_err : 1;  // at least one error is pending in the RX FIFO chain
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msr_register {
+    union {
+       uint8_t val;
+       struct {
+           uint8_t dcts     : 1;  // Delta Clear To Send
+           uint8_t ddsr     : 1;  // Delta Data Set Ready
+           uint8_t teri     : 1;  // Trailing Edge Ring Indicator
+           uint8_t ddcd     : 1;  // Delta Data Carrier Detect
+           uint8_t cts      : 1;  // Clear to Send
+           uint8_t dsr      : 1;  // Data Set Ready
+           uint8_t ri       : 1;  // Ring Indicator
+           uint8_t dcd      : 1;  // Data Carrier Detect
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+//scratch register
+struct scr_register {
+    uint8_t data;
 };
 
-struct line_ctrl_reg {
-  uint_t word_len       : 2;  // word length select
-  uint_t stop_bits      : 1;  // Stop Bit select
-  uint_t parity_enable  : 1;  // Enable parity 
-  uint_t even_sel       : 1;  // Even Parity Select
-  uint_t stick_parity   : 1;  // Stick Parity Select
-  uint_t sbr            : 1;  // Set Break 
-  uint_t dlab           : 1;  // Divisor latch access bit
+//divisor latch LSB
+struct dll_register {
+    uint8_t data;
 };
 
+//divisor latch MSB
+struct dlm_register {
+    uint8_t data;
+};
+#define SERIAL_BUF_LEN 16   
 
-struct modem_ctrl_reg { 
-  uint_t dtr      : 1;
-  uint_t rts      : 1;
-  uint_t out1     : 1;
-  uint_t out2     : 1;
-  uint_t loop     : 1;  // loopback mode
-  uint_t rsvd     : 3;  // MBZ
+struct serial_buffer {
+    int head; // most recent data
+    int tail; // oldest char
+    int full;
+    uint8_t buffer[SERIAL_BUF_LEN];
 };
 
+struct serial_port {
+    struct rbr_register     rbr;
+    struct thr_register     thr;
+    struct ier_register     ier;
+    struct iir_register     iir;
+    struct fcr_register     fcr;
+    struct lcr_register     lcr;
+    struct mcr_register     mcr;
+    struct lsr_register     lsr;
+    struct msr_register     msr;
+    struct scr_register     scr;
+    struct dll_register     dll;
+    struct dlm_register     dlm;
+    
+    
+    struct serial_buffer tx_buffer;
+    struct serial_buffer rx_buffer;
+    uint_t irq_number;
+
+    /*
+      Multiple interrupt conditions can be active simultaneously. 
+      The chip does a priority encoding to determine which one shows up in the iid
 
-struct line_status_reg {
-  uint_t rbf      : 1;  // Receiver Buffer Full
-  uint_t oe       : 1;  // Overrun error
-  uint_t pe       : 1;  // Parity Error
-  uint_t fe       : 1;  // Framing Error
-  uint_t brk      : 1;  // broken line detected
-  uint_t thre     : 1;  // Transmitter holding register empty
-  uint_t temt     : 1;  // Transmitter Empty
-  uint_t fifo_err : 1;  // at least one error is pending in the RX FIFO chain
-};
+      INT_RX_STAT      1      line status change   cleared by LSR read
+      INT_RX_DATA      2      rx data available    cleared by RBR read or when FIFO below trigger level
+      INT_RX_TIMEOUT   2      rx fifo lonely       cleared by RBR read
+      INT_TX_EMPTY     3      tx space available   cleared by IIR read (if in iid) or write to THR
+      INT_MD_STAT      4      modem status change  cleared by MSR read
 
+    */
+#define INT_RX_STAT     1     
+#define INT_RX_DATA     2
+#define INT_RX_TIMEOUT  4
+#define INT_TX_EMPTY    8
+#define INT_MD_STAT     16
+#define INT_MASK        0x1f
+// whether we currently have an IRQ raised
+#define IRQ_RAISED_MASK  128
+
+    uint8_t int_state;  // all currently signaled interrupts, not just the one being delivered
+
+    v3_lock_t  lock;
+
+    void * backend_data;
+    struct v3_dev_char_ops * ops;
 
-struct modem_status_reg {
-  uint_t dcts     : 1;  // Delta Clear To Send
-  uint_t ddsr     : 1;  // Delta Data Set Ready
-  uint_t teri     : 1;  // Trailing Edge Ring Indicator
-  uint_t ddcd     : 1;  // Delta Data Carrier Detect
-  uint_t cts      : 1;  // Clear to Send
-  uint_t dsr      : 1;  // Data Set Ready
-  uint_t ri       : 1;  // Ring Indicator
-  uint_t dcd      : 1;  // Data Carrier Detect
 };
 
 
-#define SERIAL_BUF_LEN 256
+struct serial_state {
+    struct serial_port coms[4];
 
-struct serial_buffer {
-  uint_t head; // most recent data
-  uint_t tail; // oldest char
-  char buffer[SERIAL_BUF_LEN];
 };
 
-int queue_data(struct serial_buffer * buf, char data) {
-  int next_loc = (buf->head + 1) % SERIAL_BUF_LEN;
 
-  if (next_loc == buf->tail) {
-    return -1;
-  }
 
-  buf->buffer[next_loc] = data;
-  buf->head = next_loc;
+static struct serial_port * get_com_from_port(struct serial_state * serial, uint16_t port) {
+    if ((port >= COM1_DATA_PORT) && (port <= COM1_SCRATCH_PORT)) {
+        PrintDebug(VM_NONE, VCORE_NONE, "UART: COM1\n");
+       return &(serial->coms[0]);
+    } else if ((port >= COM2_DATA_PORT) && (port <= COM2_SCRATCH_PORT)) {
+        PrintDebug(VM_NONE, VCORE_NONE, "UART: COM2\n");
+       return &(serial->coms[1]);
+    } else if ((port >= COM3_DATA_PORT) && (port <= COM3_SCRATCH_PORT)) {
+        PrintDebug(VM_NONE, VCORE_NONE, "UART: COM3\n");
+       return &(serial->coms[2]);
+    } else if ((port >= COM4_DATA_PORT) && (port <= COM4_SCRATCH_PORT)) {
+        PrintDebug(VM_NONE, VCORE_NONE, "UART: COM4\n");
+       return &(serial->coms[3]);
+    } else {
+       PrintError(VM_NONE, VCORE_NONE, "UART: Error: Could not find serial port associated with IO port %d\n", port);
+       return NULL;
+    }
+}
+
+static inline bool receive_buffer_trigger(int number, int trigger_number) {
+
+    switch (trigger_number) {
+       case 0:
+           return (number >= 1);
+       case 1:
+           return (number >= 4);
+       case 2:
+           return (number >= 8);
+       case 3:
+           return (number >= 14);
+    }
 
-  return 0;
+    return false;
 }
 
-int dequeue_data(struct serial_buffer * buf, char * data) {
-  int next_tail = (buf->tail + 1) % SERIAL_BUF_LEN;
 
-  if (buf->head == buf->tail) {
-    return -1;
-  }
+static int getNumber(struct serial_buffer * buf) {
+    int number = buf->head - buf->tail;
+  
+    if (buf->full == 1) {
+       return SERIAL_BUF_LEN;
+    } else if (number >= 0) {
+       return number;
+    } else {
+       return SERIAL_BUF_LEN + number;
+    }
+}
+
 
-  *data = buf->buffer[buf->tail];
-  buf->tail = next_tail;
+                                                            
 
+static inline int can_queue(struct serial_buffer * buf) 
+{
+  return !(buf->full);
+}
+
+static inline int can_dequeue(struct serial_buffer * buf) 
+{
+  return !( (buf->head == buf->tail) && (buf->full != 1) );
+
+}
+
+static inline int peek_queue(struct serial_buffer *buf, uint8_t *data)
+{
+  int next_tail = (buf->tail + 1) % SERIAL_BUF_LEN;
+  *data = buf->buffer[next_tail];
+  
   return 0;
 }
 
 
-struct serial_port {
-  char     ier;
-  char     iir;
-  char     fcr;
-  char     lcr;
-  char     mcr;
-  char     lsr;
-  char     msr;
-
-  struct serial_buffer tx_buffer;
-  struct serial_buffer rx_buffer;
-};
+//  0 = successfully queued byte
+// <0 = full
+// caller responsible for locking
+// caller responsible for updateIRQ
+static int queue_data(struct v3_vm_info * vm, struct serial_port * com,
+                     struct serial_buffer * buf, uint8_t data) {
+    int next_loc = (buf->head + 1) % SERIAL_BUF_LEN;    
 
+    PrintDebug(vm, VCORE_NONE,"UART: queue 0x%x ('%c') to %s buffer\n", data, data, 
+              buf == &(com->rx_buffer) ? "RX" : "TX");
 
-struct serial_state {
-  struct serial_port com1;
-  struct serial_port com2;
-  struct serial_port com3;
-  struct serial_port com4;
-};
+    if (!can_queue(buf)) {
+       PrintDebug(vm, VCORE_NONE, "UART: Buffer is full!\n");
 
+       // We will not consider this an overrun, instead,
+       // we push the problem back to the caller
 
-int write_data_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
-  char * val = (char *)src;
-  PrintDebug("Write to Data Port 0x%x (val=%x)\n", port, *(char*)src);
+       return -1;
 
-  if (length != 1) {
-    PrintDebug("Invalid length(%d) in write to 0x%x\n", length, port);
-    return -1;
-  }
+    } else {
+    
+       buf->buffer[next_loc] = data;
+       buf->head = next_loc;
+       
+       if (buf->head == buf->tail) {
+           buf->full = 1;
+       }
+       
+       if (buf == &(com->rx_buffer)) {
+           com->lsr.dr = 1; //as soon as new data arrives at receive buffer, set data ready bit in lsr.
+           
+           // Set INT_RX_DATA if fifo is now above trigger level or fifos are off
+           
+           if ( (com->fcr.enable && (receive_buffer_trigger( getNumber(&(com->rx_buffer)), com->fcr.rx_trigger)))   
+                || !com->fcr.enable) {
+               com->lsr.dr = 1;
+               com->int_state |= INT_RX_DATA;
+
+               PrintDebug(vm, VCORE_NONE, "UART: Set data ready and raised INT_RX_DATA\n");
+               
+           }
+           
+       }
+       
+       if ((buf == &(com->tx_buffer))) {
+           // at least one item in the TX queue now
+           
+           com->lsr.thre = 0; //reset thre and temt bits.
+           com->lsr.temt = 0;
+           
+           // TX EMPTY deasserted 
+           com->int_state &= ~INT_TX_EMPTY;
+           
+           PrintDebug(vm, VCORE_NONE, "UART: lowered INT_TX_EMPTY\n");
+       }
+
+       return 0;
 
-  switch (port) {
-  case COM1_DATA_PORT:
-    queue_data(&(state->com1.tx_buffer), *val);
-    break;
-  case COM2_DATA_PORT:
-    queue_data(&(state->com2.tx_buffer), *val);
-    break;
-  case COM3_DATA_PORT:
-    queue_data(&(state->com3.tx_buffer), *val);
-    break;
-  case COM4_DATA_PORT:
-    queue_data(&(state->com4.tx_buffer), *val);
-    break;
-  default:
-    return -1;
-  }
+    }
+    
+}
+//  0 = data returned
+// <0 = data not returned
+// caller responsible for locking
+// caller responsible for doing updateIRQ
+static int dequeue_data(struct v3_vm_info * vm, struct serial_port * com,
+                       struct serial_buffer * buf, uint8_t * data)
+{
+
+
+    PrintDebug(vm, VCORE_NONE,"UART: dequeue from %s buffer\n",
+              buf == &(com->rx_buffer) ? "RX" : "TX");
+
+    if (!can_dequeue(buf)) {
+       PrintDebug(vm, VCORE_NONE, "UART: queue is empty - no state change, returning '!'\n");
+       *data='!'; // just in case it uses what's there, blindly
+       // for both rx and tx queues, we signaled when we droped to zero
+       // so we don't resignal here, so no need to call updateIRQ
+       return -1;
+    } else {
+
+       int next_tail = (buf->tail + 1) % SERIAL_BUF_LEN;
+       
+       
+       if (buf->full == 1) {
+           buf->full = 0;
+       }
+       
+        
+       *data = buf->buffer[next_tail];
+       
+       buf->buffer[next_tail] = 0;
+       buf->tail = next_tail;
+       
+       PrintDebug(vm,VCORE_NONE,"UART: dequeue will return 0x%x ('%c')\n", *data, *data);
+
+       if (buf == &(com->rx_buffer)) { 
+           
+           // Reset INT_RX_DATA if RBR read with if fifo is now below trigger level or fifos are off
+           
+           if ( (com->fcr.enable && (!receive_buffer_trigger( getNumber(&(com->rx_buffer)), com->fcr.rx_trigger)))   
+                || !com->fcr.enable) {
+               PrintDebug(vm, VCORE_NONE, "UART: lowering INT_RX_DATA since we're below threshold\n");
+               com->int_state &= ~INT_RX_DATA;
+               
+           }
+
+           // Reset DR if we have dropped to zero bytes
+           if (getNumber(&(com->rx_buffer)) ==0) { 
+               PrintDebug(vm, VCORE_NONE, "UART: setting DR to zero since we have zero bytes\n");
+               com->lsr.dr = 0;
+               // should have lowered INT_RX_DATA earlier
+           }
+           
+           // reset timeout
+           com->int_state &= ~INT_RX_TIMEOUT;
+           
+       }
+       
+       if ((buf == &(com->tx_buffer)) && (getNumber(&(com->tx_buffer)) == 0)) {
+           com->lsr.thre = 1;
+           com->lsr.temt = 1;
+           
+           // Aassert TX empty when we truly drop to zero; 
+           com->int_state |= INT_TX_EMPTY;
+       }
+
+       return 0;
+    }
+}
+
+//
+// Caller is assumed to have acquired the lock
+//
+static int pump_transmit(struct v3_vm_info * vm, struct serial_port * com) 
+// Now we will pump transmit data to the backend, if any
+{
+  uint8_t buf;
   
 
-  return length;
+  while (can_dequeue(&(com->tx_buffer))) {
+    if (com->ops) { // do we have a back-end to toss it to?
+       int rc; // why is this not 64 bit?
+      // let's take a peek and see if we can send it
+      peek_queue(&(com->tx_buffer),&buf);
+      rc = com->ops->output(&buf, 1, com->backend_data);
+      if (rc<0) {
+       PrintError(vm, VCORE_NONE, "UART: backend write returned error\n");
+       // we need to give up at this point - 
+       break;
+      } else if (rc==0) { 
+       // no room to send it
+       PrintDebug(vm, VCORE_NONE, "UART: backend write would block\n");
+       // we do nothing but we don't want to iterate again
+       break; // out we go
+      } else {
+       // it was sent, now we need to remove it from the queue for real
+       // as well as update the device state
+       if (dequeue_data(vm,com,&(com->tx_buffer),&buf)) { 
+         PrintError(vm, VCORE_NONE, "UART: uh... dequeue_data failed after successful peek?!\n");
+         break;  // out we go
+       }
+       // we have already sent the byte to the backend, so we
+       // just discard it now and continue to the next one
+      }
+    } else { // there is no backend
+      if (dequeue_data(vm,com,&(com->tx_buffer),&buf)) { 
+       PrintError(vm, VCORE_NONE, "UART: uh... dequeue_data failed after successful can_dequeue?!\n");
+      }
+      // no backend, so just discard the data
+    }
+  }
+  return 0;
 }
 
+static int updateIRQ(struct v3_vm_info * vm, struct serial_port * com) {
+
+
+
+  PrintDebug(vm,VCORE_NONE, "UART: updateIRQ before pending check: iir.pending=%d iir.iid=%d int_state=0x%x\n", 
+            com->iir.pending, com->iir.iid, com->int_state);
+
+  // if we have have raised an irq, we need to lower it if it's been handled
+  // we should also presumably lower it if the guest decided to 
+  // disable the interrupt
+  if (!com->iir.pending && (com->int_state & IRQ_RAISED_MASK)) {  // active low pending, and we have raised
+      switch (com->iir.iid) {
+         case RX_IRQ_STATUS:
+             if (!(com->int_state & INT_RX_STAT) ||                     // now low or
+                 ((com->int_state & INT_RX_STAT) && !com->ier.elsi))  { // no longer enabled
+                 // no longer asserted
+                 v3_lower_irq(vm, com->irq_number);
+                 com->int_state &= ~IRQ_RAISED_MASK;
+                 com->iir.pending=1;
+                 com->iir.iid=0;
+                 PrintDebug(vm, VCORE_NONE, "UART: lowered irq on reset of INT_RX_STAT\n");
+             }
+             break;
+         case RX_IRQ_DR:    
+         // case RX_IRQ_TRIGGER_LEVEL: is the smae
+             if (!(com->int_state & INT_RX_DATA) || 
+                 ((com->int_state & INT_RX_STAT) && !com->ier.erbfi)) {
+                 // no longer asserted
+                 v3_lower_irq(vm, com->irq_number);
+                 com->int_state &= ~IRQ_RAISED_MASK;
+                 com->iir.pending=1;
+                 com->iir.iid=0;
+                 PrintDebug(vm, VCORE_NONE, "UART: lowered irq on reset of INT_RX_DATA\n");
+             }
+             break;
+         case FIFO_IRQ:
+             if (!(com->int_state & INT_RX_TIMEOUT) ||
+                 ((com->int_state & INT_RX_TIMEOUT) && !com->ier.erbfi)) {
+                 // no longer asserted
+                 v3_lower_irq(vm, com->irq_number);
+                 com->int_state &= ~IRQ_RAISED_MASK;
+                 com->iir.pending=1;
+                 com->iir.iid=0;
+                 PrintDebug(vm, VCORE_NONE, "UART: lowered irq on reset of INT_RX_TIMEOUT\n");
+             }
+             break;
+         case TX_IRQ_THRE:
+             if (!(com->int_state & INT_TX_EMPTY)  ||
+                 ((com->int_state & INT_TX_EMPTY) && !com->ier.etbei)) {
+                 // no longer asserted
+                 v3_lower_irq(vm, com->irq_number);
+                 com->int_state &= ~IRQ_RAISED_MASK;
+                 com->iir.pending=1;
+                 com->iir.iid=0;
+                 PrintDebug(vm, VCORE_NONE, "UART: lowered irq on reset of INT_TX_EMPTY\n");
+             }
+             break;
+         case MODEM_IRQ_DELTA_SET:
+             if (!(com->int_state & INT_MD_STAT) ||
+                 ((com->int_state & INT_MD_STAT) && !com->ier.edssi)) {
+                 // no longer asserted
+                 v3_lower_irq(vm, com->irq_number);
+                 com->int_state &= ~IRQ_RAISED_MASK;
+                 com->iir.pending=1;
+                 com->iir.iid=0;
+                 PrintDebug(vm, VCORE_NONE, "UART: lowered irq on reset of INT_MD_STAT\n");
+             }
+             break;
+      }
+  }
 
+  PrintDebug(vm,VCORE_NONE, "UART: updateIRQ before transmit-pump: iir.pending=%d iir.iid=%d int_state=0x%x\n", 
+            com->iir.pending, com->iir.iid, com->int_state);
+  
+  if (pump_transmit(vm,com)) {
+    PrintError(vm,VCORE_NONE, "UART: pump_transmit failed - eh?\n");
+  }
 
-int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
-  char * val = (char *)dst;
-  PrintDebug("Read from Data Port 0x%x\n", port);
-
-  if (length != 1) {
-    PrintDebug("Invalid length(%d) in write to 0x%x\n", length, port);
-    return -1;
+  // At this point, INT_TX_EMPTY might have also been raised
+  if (com->lsr.temt) { 
+      // even if pump_transmit wasn't able to do anything, we need to
+      // raise the interrupt - this is the case, for example, if the
+      // guest does an interrupt enable of the tx empty interrupt before
+      // writing anything
+      com->int_state |= INT_TX_EMPTY;
   }
 
-  switch (port) {
-  case COM1_DATA_PORT:
-    dequeue_data(&(state->com1.tx_buffer), val);
-    break;
-  case COM2_DATA_PORT:
-    dequeue_data(&(state->com2.tx_buffer), val);
-    break;
-  case COM3_DATA_PORT:
-    dequeue_data(&(state->com3.tx_buffer), val);
-    break;
-  case COM4_DATA_PORT:
-    dequeue_data(&(state->com4.tx_buffer), val);
-    break;
-  default:
-    return -1;
+  PrintDebug(vm,VCORE_NONE, "UART: updateIRQ before priority encode:  iir.pending=%d iir.iid=%d int_state=0x%x\n", 
+            com->iir.pending, com->iir.iid, com->int_state);
+
+
+  if (!(com->int_state & IRQ_RAISED_MASK)) { 
+      // We can inject a new interrupt since the last one is done
+      // Now we do the priority encode
+      if ((com->int_state & INT_RX_STAT) && com->ier.elsi ) { 
+         // highest priority
+         com->iir.pending=0;
+         com->iir.iid = RX_IRQ_STATUS;
+         v3_raise_irq(vm,com->irq_number);
+         com->int_state |= IRQ_RAISED_MASK;
+         PrintDebug(vm, VCORE_NONE, "UART: raised irq on set of INT_RX_STAT\n");
+      } else if ((com->int_state & INT_RX_DATA) && com->ier.erbfi) { 
+         // 2nd highest priority
+         com->iir.pending=0;
+         com->iir.iid = RX_IRQ_DR;
+         v3_raise_irq(vm,com->irq_number);
+         com->int_state |= IRQ_RAISED_MASK;
+         PrintDebug(vm, VCORE_NONE, "UART: raised irq on set of INT_RX_DATA\n");
+      } else if ((com->int_state & INT_RX_TIMEOUT) && com->ier.erbfi) { 
+         // Also 2nd highest priority
+         com->iir.pending=0;
+         com->iir.iid = FIFO_IRQ;
+         v3_raise_irq(vm,com->irq_number);
+         com->int_state |= IRQ_RAISED_MASK;
+         PrintDebug(vm, VCORE_NONE, "UART: raised irq on set of INT_RX_TIMEOUT\n");
+      }        else if ((com->int_state & INT_TX_EMPTY) && com->ier.etbei) { 
+         // 3rd highest priority
+         com->iir.pending=0;
+         com->iir.iid = TX_IRQ_THRE;
+         v3_raise_irq(vm,com->irq_number);
+         com->int_state |= IRQ_RAISED_MASK;
+         PrintDebug(vm, VCORE_NONE, "UART: raised irq on set of INT_TX_EMPTY\n");
+      } else if ((com->int_state & INT_MD_STAT) && com->ier.edssi) { 
+         // 4th highest priority
+         com->iir.pending=0;
+         com->iir.iid = MODEM_IRQ_DELTA_SET;
+         v3_raise_irq(vm,com->irq_number);
+         com->int_state |= IRQ_RAISED_MASK;
+         PrintDebug(vm, VCORE_NONE, "UART: raised irq on set of INT_MD_STAT\n");
+      } else {
+         // nothing to do
+      }
   }
   
+  return 0;
+}
 
-  return length;
+
+
+static int write_data_port(struct guest_info * core, uint16_t port, 
+                          void * src, uint_t length, void * priv_data) {
+    struct serial_state * state = priv_data;
+    uint8_t * val = (uint8_t *)src;
+    struct serial_port * com_port = NULL;
+    addr_t irq_status;
+
+    PrintDebug(core->vm_info, core, "UART: Write to Data Port 0x%x (val=%x, '%c')\n", port, *val, *val);
+    
+    if (length != 1) {
+       PrintError(core->vm_info, core, "UART: Invalid length(%d) in write to 0x%x\n", length, port);
+       return -1;
+    }
+
+    if ((port != COM1_DATA_PORT) && (port != COM2_DATA_PORT) && 
+       (port != COM3_DATA_PORT) && (port != COM4_DATA_PORT)) {
+       PrintError(core->vm_info, core, "UART: Serial write data port for illegal port Number (%d)\n", port);
+       return -1;
+    }
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError(core->vm_info, core, "UART: write of unknown port %d - lookup failed?!\n",port);
+       return -1;
+    }
+    
+    irq_status = v3_lock_irqsave(com_port->lock);
+    
+    // dlab is always checked first
+    if (com_port->lcr.dlab == 1) {
+       PrintDebug(core->vm_info, core, "UART:  Write to DLM, old DLM is 0x%x\n",com_port->dlm.data);
+       com_port->dll.data = *val;
+       PrintDebug(core->vm_info, core, "UART:  Write to DLM, new DLM is 0x%x\n",com_port->dlm.data);
+    }  else {
+       // queue data to send and update interrupts
+       PrintDebug(core->vm_info, core, "UART: queue transmission of 0x%x ('%c')\n",*val,*val);
+       if (queue_data(core->vm_info, com_port, &(com_port->tx_buffer), *val)) { 
+         PrintDebug(core->vm_info,core, "UART: no room for transmitted data - dropped\n");
+         // note that it must "succeed" since this is a device port
+       } else {
+         updateIRQ(core->vm_info, com_port);
+       }
+    }
+
+    v3_unlock_irqrestore(com_port->lock, irq_status);
+
+    return length;
 }
 
 
 
-int handle_ier_write(struct serial_port * com, struct irq_enable_reg * ier) {
-  
+static int read_data_port(struct guest_info * core, uint16_t port, 
+                         void * dst, uint_t length, void * priv_data) {
+    struct serial_state * state = priv_data;
+    uint8_t * val = (uint8_t *)dst;
+    struct serial_port * com_port = NULL;
+    addr_t irq_status;
 
-  return -1;
+    PrintDebug(core->vm_info, core, "UART: Read from Data Port 0x%x\n", port);
+    
+    if (length != 1) {
+       PrintError(core->vm_info, core, "UART: Invalid length(%d) in write to 0x%x\n", length, port);
+       return -1;
+    }
+    
+    if ((port != COM1_DATA_PORT) && (port != COM2_DATA_PORT) && 
+       (port != COM3_DATA_PORT) && (port != COM4_DATA_PORT)) {
+       PrintError(core->vm_info, core, "UART: Serial Read data port for illegal port Number (%d)\n", port);
+       return -1;
+    }
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError(core->vm_info, core, "UART: write of unknown port %d - lookup failed?!\n",port);
+       return -1;
+    }
+
+    irq_status = v3_lock_irqsave(com_port->lock);
+
+    if (com_port->lcr.dlab == 1) {
+       *val = com_port->dll.data;
+       PrintDebug(core->vm_info, core, "UART: read of DLL returning 0x%x\n",*val);
+    } else {
+      if (dequeue_data(core->vm_info, com_port, &(com_port->rx_buffer), val)) { 
+       PrintError(core->vm_info, core, "UART: no received data available - returning garbaage\n");
+       // note that it must "succeed" since this is device port read
+      } else {
+       updateIRQ(core->vm_info, com_port);
+      }
+      PrintDebug(core->vm_info, core, "UART: dequeued received data 0x%x ('%c')\n",*val,*val);
+    }    
+       
+    v3_unlock_irqrestore(com_port->lock, irq_status);
+
+    return length;
+}
+
+
+static void flush_buffer(struct serial_buffer *buf)
+{
+    buf->head = 0;
+    buf->tail = 0;
+    buf->full = 0;
+    memset(buf->buffer, 0, SERIAL_BUF_LEN);
 }
 
+//
+// caller is assumed to have acquired the lock
+// caller is responsible for updateIRQ
+static void handle_fcr_write(struct serial_port * com, uint8_t value) {
 
-int write_ctrl_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
-  char * val = (char *)src;
-  PrintDebug("Write to Control Port (val=%x)\n", *(char *)src);
+#if BE_16550A
 
-  if (length != 1) {
-    PrintDebug("Invalid Write length to control port\n", port, port);
-    return -1;
+    if ((!com->fcr.enable && (value & 0x1)) || (com->fcr.enable && !(value & 0x1))) { 
+       // switch of modes from 16450<->16550
+       // flush fifos, reset state
+       flush_buffer(&(com->rx_buffer));
+       flush_buffer(&(com->tx_buffer));
+       com->lsr.dr = 0;
+       com->int_state &= ~INT_RX_DATA; // no data in rx buffer - lower
+
+       com->lsr.thre = 1;
+       com->lsr.temt = 1;
+       com->int_state |= INT_TX_EMPTY; // tx buffer empty - raise
+    }
+
+    if (!(value & 0x1)) { // disabling
+       // enable->disable requires flush and state reset, handled above
+       // disable->disable doesn't require flush, I don't think 
+       com->fcr.enable = 0;
+       com->iir.fifo_en = 0;
+       // reset does not change rest of fcr register
+    } else { // enabling
+       com->fcr.val = value;
+       com->fcr.rsvd = 0; // we are not some weird chip
+       
+       //if rfres set, clear receive buffer.
+       if (com->fcr.rfres == 0x1) {
+           flush_buffer(&(com->rx_buffer));
+           com->fcr.rfres = 0; // bit is self-clearing
+           com->lsr.dr = 0;
+           com->int_state &= ~INT_RX_DATA; // no data in rx buffer - lower
+       }
+       
+       //if xfres set, clear transmit buffer.
+       if (com->fcr.xfres == 0x1) {
+           flush_buffer(&(com->tx_buffer));
+           com->fcr.xfres = 0; // bit is self-clearing
+           com->lsr.thre = 1;
+           com->lsr.temt = 1;
+           com->int_state |= INT_TX_EMPTY; // tx buffer empty -raise
+       }
+
+       com->iir.fifo_en = 0x3; // We are a 16550A
+       com->iir.fifo_en = 0x0; // We are a 16450A
+
+       // 00  => 8250/16450 (latter if have scratchpad)
+       // 10  => 16550
+       // 11  => 16550A
+    }   
+
+#else
+    // does nothing since a 16450 has no fcr
+#endif
+
+    // caller must update irq!
+}
+
+
+
+
+
+static int handle_multiple(int (*func)(struct guest_info *core,
+                                      uint16_t port, void *dst,
+                                      uint_t length, void *priv_data),
+                          struct guest_info *core, uint16_t port, 
+                          void *dst, uint_t length, void *priv_data) 
+{
+  uint16_t i;
+  int rc;
+
+  if (length == 1) { 
+    return func(core,port,dst,length,priv_data);
+  } else {
+    for (i=0;i<length;i++) {
+      rc = func(core,port+i,dst+i,1,priv_data);
+      if (rc!=1) { 
+       return i;
+      }
+    }
+    return length;
   }
+}
 
-  switch (port) {
-  case COM1_IRQ_ENABLE_PORT:
-    if (handle_ier_write(&(state->com1), (struct irq_enable_reg *)val) == -1) {
-      return -1;
+static int write_ctrl_port(struct guest_info * core, uint16_t port, void * src, 
+                          uint_t length, void * priv_data) {
+    struct serial_state * state = priv_data;
+    uint8_t val = *(uint8_t *)src;
+    struct serial_port * com_port = NULL;
+    addr_t irq_status;
+    int ret;
+
+    PrintDebug(core->vm_info, core, "UART: Write to Control Port (val=%x)\n", val);
+    
+    if (length != 1) {
+       PrintError(core->vm_info, core, "UART: Invalid Write length to control port%d\n", port);
+       return -1;
     }
-    break;
-  case COM2_IRQ_ENABLE_PORT:
-    if (handle_ier_write(&(state->com2), (struct irq_enable_reg *)val) == -1) {
-      return -1;
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError(core->vm_info, core, "UART: Could not find serial port corresponding to IO port %d\n", port);
+       return -1;
     }
-    break;
-  case COM3_IRQ_ENABLE_PORT:
-    if (handle_ier_write(&(state->com3), (struct irq_enable_reg *)val) == -1) {
-      return -1;
+
+    irq_status = v3_lock_irqsave(com_port->lock);
+  
+    ret = 1;
+
+    //always check dlab first
+    switch (port) {
+       case COM1_IRQ_ENABLE_PORT:
+       case COM2_IRQ_ENABLE_PORT:
+       case COM3_IRQ_ENABLE_PORT:
+       case COM4_IRQ_ENABLE_PORT: {
+           PrintDebug(core->vm_info, core, "UART: Write to IER/LATCH port: dlab is %x\n", com_port->lcr.dlab);
+
+           if (com_port->lcr.dlab == 1) {
+               PrintDebug(core->vm_info, core, "UART:  Write to DLM, old DLM is 0x%x\n",com_port->dlm.data);
+               com_port->dlm.data = val;
+               PrintDebug(core->vm_info, core, "UART:  Write to DLM, new DLM is 0x%x\n",com_port->dlm.data);
+           } else {
+               PrintDebug(core->vm_info, core, "UART: Write to IER, old IER is erbfi=%d etbei=%d elsi=%d edssi=%d\n",
+                          com_port->ier.erbfi,com_port->ier.etbei,com_port->ier.elsi,com_port->ier.edssi);
+               com_port->ier.val = val;
+               com_port->ier.rsvd = 0; // we are not some weird chip
+               PrintDebug(core->vm_info, core, "UART: Write to IER, new IER is erbfi=%d etbei=%d elsi=%d edssi=%d\n",
+                          com_port->ier.erbfi,com_port->ier.etbei,com_port->ier.elsi,com_port->ier.edssi);
+               // some signaled interrupt might now need to fire
+           }
+
+           updateIRQ(core->vm_info,com_port);
+
+           break;
+       }           
+       case COM1_FIFO_CTRL_PORT:
+       case COM2_FIFO_CTRL_PORT:
+       case COM3_FIFO_CTRL_PORT:
+       case COM4_FIFO_CTRL_PORT: {
+           PrintDebug(core->vm_info, core, "UART: Write to FCR, old FCR is enable=%d rfres=%d xfres=%d dma_sel=%d rx_trigger=%d\n",
+                      com_port->fcr.enable, com_port->fcr.rfres, com_port->fcr.xfres, com_port->fcr.dma_sel, com_port->fcr.rx_trigger);
+
+           handle_fcr_write(com_port, val); // cannot fail
+           
+           PrintDebug(core->vm_info, core, "UART: Write to FCR, new FCR is enable=%d rfres=%d xfres=%d dma_sel=%d rx_trigger=%d\n",
+                      com_port->fcr.enable, com_port->fcr.rfres, com_port->fcr.xfres, com_port->fcr.dma_sel, com_port->fcr.rx_trigger);
+
+           updateIRQ(core->vm_info,com_port);
+           
+           break;
+       }
+       case COM1_LINE_CTRL_PORT:
+       case COM2_LINE_CTRL_PORT:
+       case COM3_LINE_CTRL_PORT:
+       case COM4_LINE_CTRL_PORT: {
+           PrintDebug(core->vm_info, core, "UART: Write to LCR, old LCR is word_len=%d stop_bits=%d parity_enable=%d even_sel=%d stick_parity=%d sbr=%d dlab=%d\n",
+                      com_port->lcr.word_len,com_port->lcr.stop_bits,com_port->lcr.parity_enable,com_port->lcr.even_sel,com_port->lcr.stick_parity,com_port->lcr.sbr,com_port->lcr.dlab);
+
+           com_port->lcr.val = val;
+           // no reserved bits
+           
+           PrintDebug(core->vm_info, core, "UART: Write to LCR, new LCR is word_len=%d stop_bits=%d parity_enable=%d even_sel=%d stick_parity=%d sbr=%d dlab=%d\n",
+                      com_port->lcr.word_len,com_port->lcr.stop_bits,com_port->lcr.parity_enable,com_port->lcr.even_sel,com_port->lcr.stick_parity,com_port->lcr.sbr,com_port->lcr.dlab);
+
+           updateIRQ(core->vm_info, com_port);
+
+           break;
+       }
+       case COM1_MODEM_CTRL_PORT:
+       case COM2_MODEM_CTRL_PORT:
+       case COM3_MODEM_CTRL_PORT:
+       case COM4_MODEM_CTRL_PORT: {
+           PrintDebug(core->vm_info, core, "UART: Write to MCR, old MCR is dtr=%d rts=%d out1=%d out2=%d loop=%d\n",
+                      com_port->mcr.dtr,com_port->mcr.rts,com_port->mcr.out1,com_port->mcr.out2,com_port->mcr.loop);
+
+           com_port->mcr.val = val;
+           com_port->mcr.rsvd = 0; // we are not some weird chip
+
+           PrintDebug(core->vm_info, core, "UART: Write to MCR, new MCR is dtr=%d rts=%d out1=%d out2=%d loop=%d\n",
+                      com_port->mcr.dtr,com_port->mcr.rts,com_port->mcr.out1,com_port->mcr.out2,com_port->mcr.loop);
+           
+           updateIRQ(core->vm_info, com_port);
+
+           break;
+       }
+       case COM1_SCRATCH_PORT:
+       case COM2_SCRATCH_PORT:
+       case COM3_SCRATCH_PORT:
+       case COM4_SCRATCH_PORT: {
+           PrintDebug(core->vm_info, core, "UART: Write to SCRATCH, old value is 0x%x\n",com_port->scr.data);
+
+           com_port->scr.data = val;
+
+           PrintDebug(core->vm_info, core, "UART: Write to SCRATCH, new value is 0x%x\n",com_port->scr.data);
+           break;
+       }
+       default:
+           PrintError(core->vm_info, core, "UART: Write to unknown port %d, ERROR\n",port);
+           ret = -1;
     }
-    break;
-  case COM4_IRQ_ENABLE_PORT:
-    if (handle_ier_write(&(state->com4), (struct irq_enable_reg *)val) == -1) {
-      return -1;
+    
+    v3_unlock_irqrestore(com_port->lock,irq_status);
+
+    return ret;
+}
+
+
+
+
+static int read_ctrl_port(struct guest_info * core, uint16_t port, void * dst, 
+                         uint_t length, void * priv_data) {
+    struct serial_state * state = priv_data;
+    uint8_t * val = (uint8_t *)dst;
+    struct serial_port * com_port = NULL;
+    addr_t irq_status;
+    int ret;
+
+    PrintDebug(core->vm_info, core, "UART: Read from Control Port\n");
+    
+    if (length != 1) {
+       PrintError(core->vm_info, core, "UART: Invalid Read length to control port\n");
+       return -1;
     }
-    break;
 
-  case COM1_FIFO_CTRL_PORT:
-  case COM2_FIFO_CTRL_PORT:
-  case COM3_FIFO_CTRL_PORT:
-  case COM4_FIFO_CTRL_PORT:
+    com_port = get_com_from_port(state, port);
 
-  case COM1_LINE_CTRL_PORT:
-  case COM2_LINE_CTRL_PORT:
-  case COM3_LINE_CTRL_PORT:
-  case COM4_LINE_CTRL_PORT:
+    if (com_port == NULL) {
+       PrintError(core->vm_info, core, "UART: Could not find serial port corresponding to IO port %d\n", port);
+       return -1;
+    }
+    
+    irq_status = v3_lock_irqsave(com_port->lock);
+
+    ret = 1;
+
+    //always check dlab first
+    switch (port) {
+       case COM1_IRQ_ENABLE_PORT:
+       case COM2_IRQ_ENABLE_PORT:
+       case COM3_IRQ_ENABLE_PORT:
+       case COM4_IRQ_ENABLE_PORT: {
+           PrintDebug(core->vm_info, core, "UART: Read from IER/LATCH port: dlab is %x\n", com_port->lcr.dlab);
+
+           if (com_port->lcr.dlab == 1) {
+               PrintDebug(core->vm_info, core, "UART: Read of DLM, value 0x%x\n",com_port->dlm.data);
+               *val = com_port->dlm.data;
+           } else {
+               *val = com_port->ier.val;
+               PrintDebug(core->vm_info, core, "UART: Read of IER is val=0x%x - erbfi=%d etbei=%d elsi=%d edssi=%d\n",
+                          com_port->ier.val, com_port->ier.erbfi,com_port->ier.etbei,com_port->ier.elsi,com_port->ier.edssi);
+           }
+           break;
+       }
+
+       case COM1_IIR_PORT:
+       case COM2_IIR_PORT:
+       case COM3_IIR_PORT:
+       case COM4_IIR_PORT:
+           PrintDebug(core->vm_info, core, "UART: read from IIR is val=0x%x - pending=%d iid=%d fifo_en=%d\n",
+                      com_port->iir.val,com_port->iir.pending,com_port->iir.iid,com_port->iir.fifo_en);
+           *val = com_port->iir.val;
+
+           if ((com_port->int_state & IRQ_RAISED_MASK) && 
+               !com_port->iir.pending && com_port->iir.iid==TX_IRQ_THRE) { 
+               // we are firing a TX_IRQ_THRE interrupt, therefore
+               // this read resets it
+
+               com_port->int_state &= ~INT_TX_EMPTY;
+               
+               updateIRQ(core->vm_info,com_port);
+           }
+
+           break;
+
+       case COM1_LINE_CTRL_PORT:
+       case COM2_LINE_CTRL_PORT:
+       case COM3_LINE_CTRL_PORT:
+       case COM4_LINE_CTRL_PORT:
+           PrintDebug(core->vm_info, core, "UART: read from LCR is val=0x%x - word_len=%d stop_bits=%d parity_enable=%d even_sel=%d stick_parity=%d sbr=%d dlab=%d\n",
+                      com_port->lcr.val,com_port->lcr.word_len, com_port->lcr.stop_bits,com_port->lcr.parity_enable,com_port->lcr.even_sel,com_port->lcr.stick_parity,com_port->lcr.sbr,com_port->lcr.dlab);
+           *val = com_port->lcr.val;
+           break;
+
+       case COM1_MODEM_CTRL_PORT:
+       case COM2_MODEM_CTRL_PORT:
+       case COM3_MODEM_CTRL_PORT:
+       case COM4_MODEM_CTRL_PORT:
+           PrintDebug(core->vm_info, core, "UART: read from MCR is val=0x%x - dtr=%d rts=%d out1=%d out2=%d loop=%d\n",
+                      com_port->mcr.val,com_port->mcr.dtr,com_port->mcr.rts,com_port->mcr.out1,com_port->mcr.out2,com_port->mcr.loop);
+           *val = com_port->mcr.val;
+           break;
+
+       case COM1_SCRATCH_PORT:
+       case COM2_SCRATCH_PORT:
+       case COM3_SCRATCH_PORT:
+       case COM4_SCRATCH_PORT:
+           PrintDebug(core->vm_info, core, "UART: read from SCRATCH is val=0x%x\n",com_port->scr.data);
+           *val = com_port->scr.data;
+           break;
+
+       default:
+           PrintError(core->vm_info, core, "UART: read from unknown port %d\n",port);
+           ret = -1;
+    }
+
+    v3_unlock_irqrestore(com_port->lock,irq_status);
+
+    return ret;
+}
 
-  case COM1_MODEM_CTRL_PORT:
-  case COM2_MODEM_CTRL_PORT:
-  case COM3_MODEM_CTRL_PORT:
-  case COM4_MODEM_CTRL_PORT:
+
+static int write_status_port(struct guest_info * core, uint16_t port, void * src, 
+                            uint_t length, void * priv_data) {
+    struct serial_state * state = priv_data;
+    uint8_t val = *(uint8_t *)src;
+    struct serial_port * com_port = NULL;
+    addr_t irq_status;
+    int ret;
+
+    PrintDebug(core->vm_info, core, "UART: Write to Status Port (val=0x%x)\n", val);
+
+    if (length != 1) {
+       PrintError(core->vm_info, core, "UART: Invalid Write length (%d) to status port %d\n", length, port);
+       return -1;
+    }
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError(core->vm_info, core, "UART: Could not find serial port corresponding to IO port %d\n", port);
+       return -1;
+    }
+
+    irq_status = v3_lock_irqsave(com_port->lock);
     
+    ret = 1;
 
+    switch (port) {
+       case COM1_LINE_STATUS_PORT:
+       case COM2_LINE_STATUS_PORT:
+       case COM3_LINE_STATUS_PORT:
+       case COM4_LINE_STATUS_PORT:
+           PrintDebug(core->vm_info, core, "UART: Write to LSR, old value is 0x%x - dr=%d oe=%d pe=%d fe=%d brk=%d thre=%d temt=%d fifo_err=%d\n",
+                      com_port->lsr.val,com_port->lsr.dr,com_port->lsr.oe,com_port->lsr.pe,com_port->lsr.fe,com_port->lsr.brk,com_port->lsr.thre,com_port->lsr.temt,com_port->lsr.fifo_err);
 
-  default:
-    return -1;
-  }
+           com_port->lsr.val = val;
+           // no reserved bits
 
+           PrintDebug(core->vm_info, core, "UART: Write to LSR, new value is 0x%x - dr=%d oe=%d pe=%d fe=%d brk=%d thre=%d temt=%d fifo_err=%d\n",
+                      com_port->lsr.val,com_port->lsr.dr,com_port->lsr.oe,com_port->lsr.pe,com_port->lsr.fe,com_port->lsr.brk,com_port->lsr.thre,com_port->lsr.temt,com_port->lsr.fifo_err);
 
-  return -1;
+           updateIRQ(core->vm_info, com_port);
+
+           break;
+
+       case COM1_MODEM_STATUS_PORT:
+       case COM2_MODEM_STATUS_PORT:
+       case COM3_MODEM_STATUS_PORT:
+       case COM4_MODEM_STATUS_PORT:
+
+           PrintDebug(core->vm_info,core,"UART: Write to MSR, old value is 0x%x - dcts=%d ddsr=%d teri=%d ddcd=%d cts=%d dsr=%d ri=%d dcd=%d\n",
+                      com_port->msr.val,com_port->msr.dcts,com_port->msr.ddsr,com_port->msr.teri,com_port->msr.ddcd,com_port->msr.cts,com_port->msr.dsr,com_port->msr.ri,com_port->msr.dcd);
+
+           com_port->msr.val = val;
+           // no reserved bits
+
+           PrintDebug(core->vm_info,core,"UART: Write to MSR, new value is 0x%x - dcts=%d ddsr=%d teri=%d ddcd=%d cts=%d dsr=%d ri=%d dcd=%d\n",
+                      com_port->msr.val,com_port->msr.dcts,com_port->msr.ddsr,com_port->msr.teri,com_port->msr.ddcd,com_port->msr.cts,com_port->msr.dsr,com_port->msr.ri,com_port->msr.dcd);
+           
+           updateIRQ(core->vm_info, com_port);
+
+           break;
+
+       default:
+           PrintError(core->vm_info, core, "UART: write to unsupported port %d\n",port);
+           ret = -1;
+    }
+
+    v3_unlock_irqrestore(com_port->lock,irq_status);
+
+    return ret;
 }
 
 
+static int read_status_port(struct guest_info * core, uint16_t port, void * dst, 
+                           uint_t length, void * priv_data) {
+    struct serial_state * state = priv_data;
+    uint8_t * val = (uint8_t *)dst;
+    struct serial_port * com_port = NULL;
+    addr_t irq_status;
+    int ret;
+
+    if (length==2 && (port==COM1_MODEM_STATUS_PORT ||
+                     port==COM2_MODEM_STATUS_PORT ||
+                     port==COM3_MODEM_STATUS_PORT ||
+                     port==COM4_MODEM_STATUS_PORT) ) {
+      return handle_multiple(read_status_port,core,port,dst,length, priv_data);
+    } else if (length!=1) { 
+      PrintError(core->vm_info, core, "UART: Invalid Read length (%d) from status port %d\n", length, port);
+      return -1;
+    }
+      
+    PrintDebug(core->vm_info, core, "UART: Read from Status Port 0x%x\n", port);
 
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError(core->vm_info, core, "UART: Could not find serial port corresponding to IO port %d\n", port);
+       return -1;
+    }
+    
+    irq_status = v3_lock_irqsave(com_port->lock);
+    ret = 1;
+    
+    switch (port) {
+       case COM1_LINE_STATUS_PORT:
+       case COM2_LINE_STATUS_PORT:
+       case COM3_LINE_STATUS_PORT:
+       case COM4_LINE_STATUS_PORT:
 
-int read_ctrl_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
-  char * val = (char *)dst;
-  PrintDebug("Read from Control Port\n");
+           PrintDebug(core->vm_info, core, "UART: Read from LSR is 0x%x - dr=%d oe=%d pe=%d fe=%d brk=%d thre=%d temt=%d fifo_err=%d\n",
+                      com_port->lsr.val,com_port->lsr.dr,com_port->lsr.oe,com_port->lsr.pe,com_port->lsr.fe,com_port->lsr.brk,com_port->lsr.thre,com_port->lsr.temt,com_port->lsr.fifo_err);
 
-  if (length != 1) {
-    PrintDebug("Invalid Read length to control port\n");
-    return -1;
-  }
+           *val = com_port->lsr.val;
 
-  switch (port) {
-  case COM1_IRQ_ENABLE_PORT:
-    *val = state->com1.ier;
-    break;
-  case COM2_IRQ_ENABLE_PORT:
-    *val = state->com2.ier;
-    break;
-  case COM3_IRQ_ENABLE_PORT:
-    *val = state->com3.ier;
-    break;
-  case COM4_IRQ_ENABLE_PORT:
-    *val = state->com4.ier;
-    break;
-
-  case COM1_FIFO_CTRL_PORT:
-    *val = state->com1.fcr;
-    break;
-  case COM2_FIFO_CTRL_PORT:
-    *val = state->com2.fcr;
-    break;
-  case COM3_FIFO_CTRL_PORT:
-    *val = state->com3.fcr;
-    break;
-  case COM4_FIFO_CTRL_PORT:
-    *val = state->com4.fcr;
-    break;
-
-  case COM1_LINE_CTRL_PORT:
-    *val = state->com1.lcr;
-    break;
-  case COM2_LINE_CTRL_PORT:
-    *val = state->com2.lcr;
-    break;
-  case COM3_LINE_CTRL_PORT:
-    *val = state->com3.lcr;
-    break;
-  case COM4_LINE_CTRL_PORT:
-    *val = state->com4.lcr;
-    break;
-
-  case COM1_MODEM_CTRL_PORT:
-    *val = state->com1.mcr;
-    break;
-  case COM2_MODEM_CTRL_PORT:
-    *val = state->com2.mcr;
-    break;
-  case COM3_MODEM_CTRL_PORT:
-    *val = state->com3.mcr;
-    break;
-  case COM4_MODEM_CTRL_PORT:
-    *val = state->com4.mcr;
-    break;
-
-  default:
-    return -1;
-  }
 
-  return length;
+           // Reading the LSR resets the line error state
+           com_port->lsr.oe = 0;     
+           com_port->lsr.pe = 0;     
+           com_port->lsr.fe = 0;     
+           com_port->lsr.brk = 0;     
+           com_port->lsr.fifo_err = 0;     
+
+           // and clears any interrupt we set due to it
+           com_port->int_state &= ~INT_RX_STAT;
+           
+           updateIRQ(core->vm_info,com_port);
+
+
+           break;
+
+       case COM1_MODEM_STATUS_PORT:
+       case COM2_MODEM_STATUS_PORT:
+       case COM3_MODEM_STATUS_PORT:
+       case COM4_MODEM_STATUS_PORT:
+           PrintDebug(core->vm_info,core,"UART: read of MSR is 0x%x - dcts=%d ddsr=%d teri=%d ddcd=%d cts=%d dsr=%d ri=%d dcd=%d\n",
+                      com_port->msr.val,com_port->msr.dcts,com_port->msr.ddsr,com_port->msr.teri,com_port->msr.ddcd,com_port->msr.cts,com_port->msr.dsr,com_port->msr.ri,com_port->msr.dcd);
+
+           *val = com_port->msr.val;
+
+           // MSR read resets any interrupt due to modem error
+           com_port->int_state &= ~INT_MD_STAT;
+
+           updateIRQ(core->vm_info,com_port);
+
+           break;
+
+       default:
+           PrintError(core->vm_info, core, "UART: read of unknown port %d (length = %d)\n", port, length);
+           ret = -1; 
+    }
+
+    v3_unlock_irqrestore(com_port->lock, irq_status);
+
+    return ret;
+}
+
+
+static int deinit_serial_port(struct serial_port *);
+
+static int serial_free(struct serial_state * state) {
+
+    deinit_serial_port(&(state->coms[0]));
+    deinit_serial_port(&(state->coms[1]));
+    deinit_serial_port(&(state->coms[2]));
+    deinit_serial_port(&(state->coms[3]));
+
+    V3_Free(state);
+    return 0;
 }
 
 
-int write_status_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
-  PrintDebug("Write to Status Port 0x%x (val=%x)\n", port, *(char *)src);
 
+#ifdef V3_CONFIG_CHECKPOINT
+
+#include <palacios/vmm_sprintf.h>
+
+static int serial_buffer_save(struct v3_chkpt_ctx * ctx, int port, struct serial_buffer *sb, char * bufname) {
+  
+  char keyname[128];
+  
+  snprintf(keyname,128,"COM%d_%s_HEAD", port, bufname);
+  V3_CHKPT_SAVE(ctx,keyname,sb->head,failout);
+  snprintf(keyname,128,"COM%d_%s_TAIL", port, bufname);
+  V3_CHKPT_SAVE(ctx,keyname,sb->tail,failout);
+  snprintf(keyname,128,"COM%d_%s_FULL", port, bufname);
+  V3_CHKPT_SAVE(ctx,keyname,sb->full,failout);
+  snprintf(keyname,128,"COM%d_%s_DATA", port, bufname);
+  V3_CHKPT_SAVE(ctx,keyname,sb->buffer,failout);
+  
+  return 0;
+  
+ failout:
+  PrintError(VM_NONE, VCORE_NONE, "Failed to save serial buffer\n");
   return -1;
 }
 
-int read_status_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
-  char * val = (char *)dst;
-  PrintDebug("Read from Status Port 0x%x\n", port);
+static int serial_buffer_load(struct v3_chkpt_ctx * ctx, int port,  struct serial_buffer *sb, char * bufname) {
+
+  char keyname[128];
+
+  snprintf(keyname,128,"COM%d_%s_HEAD", port, bufname);
+  V3_CHKPT_LOAD(ctx,keyname,sb->head,failout);
+  snprintf(keyname,128,"COM%d_%s_TAIL", port, bufname);
+  V3_CHKPT_LOAD(ctx,keyname,sb->tail,failout);
+  snprintf(keyname,128,"COM%d_%s_FULL", port, bufname);
+  V3_CHKPT_LOAD(ctx,keyname,sb->full,failout);
+  snprintf(keyname,128,"COM%d_%s_DATA", port, bufname);
+  V3_CHKPT_LOAD(ctx,keyname,sb->buffer,failout);
   
-  if (length != 1) {
-    PrintDebug("Invalid Read length to control port\n");
-    return -1;
-  }
+  return 0;
+  
+ failout:
+  PrintError(VM_NONE, VCORE_NONE, "Failed to load serial buffer\n");
+  return -1;
+}
+static int serial_save(struct v3_chkpt_ctx * ctx, void * private_data) {
+  struct serial_state *state = (struct serial_state *)private_data;
+  struct serial_port *serial;
+  char keyname[128];
+  int i;
   
-  switch (port) {
-  case COM1_LINE_STATUS_PORT:
-    *val = state->com1.lsr;
-    break;
-  case COM2_LINE_STATUS_PORT:
-    *val = state->com2.lsr;
-    break;
-  case COM3_LINE_STATUS_PORT:
-    *val = state->com3.lsr;
-    break;
-  case COM4_LINE_STATUS_PORT:
-    *val = state->com4.lsr;
-    break;
+  for (i=0;i<4;i++) { 
+    serial = &(state->coms[i]);
+    snprintf(keyname, 128,"COM%d_RBR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->rbr.data,failout);
+    snprintf(keyname, 128,"COM%d_THR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->thr.data,failout);
+    snprintf(keyname, 128,"COM%d_IER",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->ier.val,failout);
+    snprintf(keyname, 128,"COM%d_IIR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->iir.val,failout);
+    snprintf(keyname, 128,"COM%d_FCR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->fcr.val,failout);
+    snprintf(keyname, 128,"COM%d_LCR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->lcr.val,failout);
+    snprintf(keyname, 128,"COM%d_MCR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->mcr.val,failout);
+    snprintf(keyname, 128,"COM%d_LSR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->lsr.val,failout);
+    snprintf(keyname, 128,"COM%d_MSR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->msr.val,failout);
+    snprintf(keyname, 128,"COM%d_SCR",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->scr.data,failout);
+    snprintf(keyname, 128,"COM%d_DLL",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->dll.data,failout);
+    snprintf(keyname, 128,"COM%d_DLM",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->dlm.data,failout);
+    snprintf(keyname, 128,"COM%d_int_state",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->int_state,failout);
     
-  case COM1_MODEM_STATUS_PORT:
-    *val = state->com1.msr;
-    break;
-  case COM2_MODEM_STATUS_PORT:
-    *val = state->com2.msr;
-    break;
-  case COM3_MODEM_STATUS_PORT:
-    *val = state->com3.msr;
-    break;
-  case COM4_MODEM_STATUS_PORT:
-    *val = state->com4.msr;
-    break;
-
-  default:
-    return -1;
+    if (serial_buffer_save(ctx, i, &(serial->tx_buffer), "TX")) { 
+      PrintError(VM_NONE, VCORE_NONE, "Failed to save serial tx buffer %d\n",i);
+      goto failout;
+    }
+    
+    if (serial_buffer_save(ctx, i, &(serial->rx_buffer), "RX")) { 
+      PrintError(VM_NONE, VCORE_NONE, "Failed to save serial rx buffer %d\n",i);
+      goto failout;
+    }
+    
+    snprintf(keyname, 128,"COM%d_IRQ_NUM",i);
+    V3_CHKPT_SAVE(ctx, keyname, serial->irq_number,failout);
   }
 
+  return 0;
 
+ failout:
+  PrintError(VM_NONE, VCORE_NONE, "Failed to save serial device\n");
+  return -1;
+  
+}
+static int serial_load(struct v3_chkpt_ctx * ctx, void * private_data) {
+  struct serial_state *state = (struct serial_state *)private_data;
+  struct serial_port *serial;
+  char keyname[128];
+  int i;
+  
+  for (i=0;i<4;i++) { 
+    serial = &(state->coms[i]);
+    snprintf(keyname, 128,"COM%d_RBR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->rbr.data,failout);
+    snprintf(keyname, 128,"COM%d_THR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->thr.data,failout);
+    snprintf(keyname, 128,"COM%d_IER",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->ier.val,failout);
+    snprintf(keyname, 128,"COM%d_IIR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->iir.val,failout);
+    snprintf(keyname, 128,"COM%d_FCR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->fcr.val,failout);
+    snprintf(keyname, 128,"COM%d_LCR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->lcr.val,failout);
+    snprintf(keyname, 128,"COM%d_MCR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->mcr.val,failout);
+    snprintf(keyname, 128,"COM%d_LSR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->lsr.val,failout);
+    snprintf(keyname, 128,"COM%d_MSR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->msr.val,failout);
+    snprintf(keyname, 128,"COM%d_SCR",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->scr.data,failout);
+    snprintf(keyname, 128,"COM%d_DLL",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->dll.data,failout);
+    snprintf(keyname, 128,"COM%d_DLM",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->dlm.data,failout);
+    snprintf(keyname, 128,"COM%d_int_state",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->int_state,failout);
+
+    if (serial_buffer_load(ctx, i, &(serial->tx_buffer), "TX")) { 
+      PrintError(VM_NONE, VCORE_NONE, "Failed to load serial tx buffer %d\n",i);
+      goto failout;
+    }
+    
+    if (serial_buffer_load(ctx, i, &(serial->rx_buffer), "RX")) { 
+      PrintError(VM_NONE, VCORE_NONE, "Failed to load serial rx buffer %d\n",i);
+      goto failout;
+    }
+    
+    snprintf(keyname, 128,"COM%d_IRQ_NUM",i);
+    V3_CHKPT_LOAD(ctx, keyname, serial->irq_number,failout);
+  }
+
+  return 0;
 
-  return length;
+ failout:
+  PrintError(VM_NONE, VCORE_NONE,"Failed to load serial device\n");
+  return -1;
+  
 }
 
+#endif
 
+static struct v3_device_ops dev_ops = {
+    .free = (int (*)(void *))serial_free,
+#ifdef V3_CONFIG_CHECKPOINT
+    .save = serial_save, 
+    .load = serial_load
+#endif
+};
 
 
 
 static int init_serial_port(struct serial_port * com) {
-  //struct irq_enable_reg * ier = (struct irq_enable_reg *)&(com->ier);
-  //struct irq_id_reg * iir = (struct irq_id_reg *)&(com->iir);
-  //struct fifo_ctrl_reg * fcr = (struct fifo_ctrl_reg *)&(com->fcr);
-  //struct line_ctrl_reg * lcr = (struct line_ctrl_reg *)&(com->lcr);
-  //struct modem_ctrl_reg * mcr = (struct modem_ctrl_reg *)&(com->mcr);
-  //struct line_status_reg * lsr = (struct line_status_reg *)&(com->lsr);
-  //struct modem_status_reg * msr = (struct modem_status_reg *)&(com->msr);
-
-  com->ier = 0x00;
-  com->iir = 0x01;
-  com->fcr = 0x00;
-  com->lcr = 0x00;
-  com->mcr = 0x00;
-  com->lsr = 0x60;
-  com->msr = 0x00;
 
-  return 0;
+    // zero all
+    memset(com, 0, sizeof(*com));
+
+    com->ier.val = IER_INIT_VAL;
+    com->iir.val = IIR_INIT_VAL;
+    com->fcr.val = FCR_INIT_VAL;
+    com->lcr.val = LCR_INIT_VAL;
+    com->mcr.val = MCR_INIT_VAL;
+    com->lsr.val = LSR_INIT_VAL;
+    com->msr.val = MSR_INIT_VAL;
+
+    com->dll.data =  DLL_INIT_VAL;
+    com->dlm.data =  DLM_INIT_VAL;
+
+    flush_buffer(&(com->tx_buffer));
+    flush_buffer(&(com->rx_buffer));
+
+    v3_lock_init(&(com->lock));
+
+    com->ops = NULL;
+    com->backend_data = NULL;
+
+    return 0;
 }
 
-int serial_init(struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
-
-
-  init_serial_port(&(state->com1));
-  init_serial_port(&(state->com2));
-  init_serial_port(&(state->com3));
-  init_serial_port(&(state->com4));
-
-  dev_hook_io(dev, COM1_DATA_PORT, &read_data_port, &write_data_port);
-  dev_hook_io(dev, COM1_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM1_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM1_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM1_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM1_LINE_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM1_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM1_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
-
-  dev_hook_io(dev, COM2_DATA_PORT, &read_data_port, &write_data_port);
-  dev_hook_io(dev, COM2_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM2_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM2_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM2_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM2_LINE_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM2_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM2_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
-
-  dev_hook_io(dev, COM3_DATA_PORT, &read_data_port, &write_data_port);
-  dev_hook_io(dev, COM3_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM3_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM3_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM3_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM3_LINE_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM3_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM3_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
-
-  dev_hook_io(dev, COM4_DATA_PORT, &read_data_port, &write_data_port);
-  dev_hook_io(dev, COM4_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM4_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM4_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM4_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
-  dev_hook_io(dev, COM4_LINE_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM4_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM4_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
+static int deinit_serial_port(struct serial_port * com) {
 
-  return 0;
+    v3_lock_deinit(&(com->lock));
+
+    return 0;
 }
 
+static sint64_t serial_input(struct v3_vm_info * vm, uint8_t * buf, sint64_t len, void * priv_data){
+    struct serial_port * com_port = (struct serial_port *)priv_data;
+    int i;
+    addr_t irq_status;
+
+    irq_status = v3_lock_irqsave(com_port->lock);
+    
+    for(i = 0; i < len; i++){
+      if (queue_data(vm, com_port, &(com_port->rx_buffer), buf[i])) {
+       break;
+      }
+    }
 
-int serial_deinit(struct vm_device * dev) {
-
-
-  dev_unhook_io(dev, COM1_DATA_PORT);
-  dev_unhook_io(dev, COM1_IRQ_ENABLE_PORT);
-  dev_unhook_io(dev, COM1_FIFO_CTRL_PORT);
-  dev_unhook_io(dev, COM1_LINE_CTRL_PORT);
-  dev_unhook_io(dev, COM1_MODEM_CTRL_PORT);
-  dev_unhook_io(dev, COM1_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM1_MODEM_STATUS_PORT);
-  dev_unhook_io(dev, COM1_SCRATCH_PORT);
-
-  dev_unhook_io(dev, COM2_DATA_PORT);
-  dev_unhook_io(dev, COM2_IRQ_ENABLE_PORT);
-  dev_unhook_io(dev, COM2_FIFO_CTRL_PORT);
-  dev_unhook_io(dev, COM2_LINE_CTRL_PORT);
-  dev_unhook_io(dev, COM2_MODEM_CTRL_PORT);
-  dev_unhook_io(dev, COM2_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM2_MODEM_STATUS_PORT);
-  dev_unhook_io(dev, COM2_SCRATCH_PORT);
-
-  dev_unhook_io(dev, COM3_DATA_PORT);
-  dev_unhook_io(dev, COM3_IRQ_ENABLE_PORT);
-  dev_unhook_io(dev, COM3_FIFO_CTRL_PORT);
-  dev_unhook_io(dev, COM3_LINE_CTRL_PORT);
-  dev_unhook_io(dev, COM3_MODEM_CTRL_PORT);
-  dev_unhook_io(dev, COM3_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM3_MODEM_STATUS_PORT);
-  dev_unhook_io(dev, COM3_SCRATCH_PORT);
-
-  dev_unhook_io(dev, COM4_DATA_PORT);
-  dev_unhook_io(dev, COM4_IRQ_ENABLE_PORT);
-  dev_unhook_io(dev, COM4_FIFO_CTRL_PORT);
-  dev_unhook_io(dev, COM4_LINE_CTRL_PORT);
-  dev_unhook_io(dev, COM4_MODEM_CTRL_PORT);
-  dev_unhook_io(dev, COM4_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM4_MODEM_STATUS_PORT);
-  dev_unhook_io(dev, COM4_SCRATCH_PORT);
+    updateIRQ(vm, com_port);
 
-  return 0;
+    v3_unlock_irqrestore(com_port->lock,irq_status);
+
+    return i;
 }
 
 
+static int connect_fn(struct v3_vm_info * vm, 
+                     void * frontend_data, 
+                     struct v3_dev_char_ops * ops, 
+                     v3_cfg_tree_t * cfg, 
+                     void * private_data, 
+                     void ** push_fn_arg) {
 
-static struct vm_device_ops dev_ops = {
-  .init = serial_init,
-  .deinit = serial_deinit,
-  .reset = NULL,
-  .start = NULL,
-  .stop = NULL,
-};
+    struct serial_state * serial = (struct serial_state *)frontend_data;
+    struct serial_port * com = NULL;
+    char * com_port = v3_cfg_val(cfg, "com_port");
+    int com_idx = 0;
+
+    if (com_port == NULL) {
+        PrintError(vm, VCORE_NONE, "UART: Invalid Serial frontend config: missing \"com_port\"\n");
+       return -1;
+    }
+    
+    com_idx = atoi(com_port) - 1;
+
+    if ((com_idx > 3) || (com_idx < 0)) {
+      PrintError(vm, VCORE_NONE, "UART: Invalid Com port (%s) \n", com_port);
+       return -1;
+    }
+
+    com = &(serial->coms[com_idx]);
+
+    com->ops = ops;
+    com->backend_data = private_data;
+
+    com->ops->input = serial_input;
+    *push_fn_arg = com;
+
+    return 0;
+}
+
+static int serial_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
+    struct serial_state * state = NULL;
+    char * dev_id = v3_cfg_val(cfg, "ID");
+    int ret = 0;
+
+    state = (struct serial_state *)V3_Malloc(sizeof(struct serial_state));
+    
+    if (state == NULL) {
+        PrintError(vm,VCORE_NONE, "UART: Could not allocate Serial Device\n");
+       return -1;
+    }
+    
+    memset(state, 0, sizeof(struct serial_state));
 
+    init_serial_port(&(state->coms[0]));
+    init_serial_port(&(state->coms[1]));
+    init_serial_port(&(state->coms[2]));
+    init_serial_port(&(state->coms[3]));
 
-struct vm_device * create_serial(int num_ports) {
-  struct serial_state * state = NULL;
-  state = (struct serial_state *)V3_Malloc(sizeof(struct serial_state));
-  V3_ASSERT(state != NULL);
+    state->coms[0].irq_number = COM1_IRQ;
+    state->coms[1].irq_number = COM2_IRQ;
+    state->coms[2].irq_number = COM3_IRQ;
+    state->coms[3].irq_number = COM4_IRQ;
 
-  struct vm_device * device = create_device("Serial UART", &dev_ops, state);
 
-  return device;
+    struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
+
+    if (dev == NULL) {
+       PrintError(vm, VCORE_NONE, "UART: Could not attach device %s\n", dev_id);
+       V3_Free(state);
+       return -1;
+    }
+
+    PrintDebug(vm, VCORE_NONE, "UART: Serial device attached\n");
+
+    ret |= v3_dev_hook_io(dev, COM1_DATA_PORT, &read_data_port, &write_data_port);
+    ret |= v3_dev_hook_io(dev, COM1_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM1_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM1_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM1_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM1_LINE_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM1_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM1_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
+
+    ret |= v3_dev_hook_io(dev, COM2_DATA_PORT, &read_data_port, &write_data_port);
+    ret |= v3_dev_hook_io(dev, COM2_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM2_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM2_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM2_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM2_LINE_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM2_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM2_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
+
+    ret |= v3_dev_hook_io(dev, COM3_DATA_PORT, &read_data_port, &write_data_port);
+    ret |= v3_dev_hook_io(dev, COM3_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM3_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM3_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM3_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM3_LINE_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM3_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM3_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
+
+    ret |= v3_dev_hook_io(dev, COM4_DATA_PORT, &read_data_port, &write_data_port);
+    ret |= v3_dev_hook_io(dev, COM4_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM4_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM4_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM4_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
+    ret |= v3_dev_hook_io(dev, COM4_LINE_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM4_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
+    ret |= v3_dev_hook_io(dev, COM4_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
+
+    if (ret != 0) {
+       PrintError(vm, VCORE_NONE, "UART: Error hooking Serial IO ports\n");
+       v3_remove_device(dev);
+       return -1;
+    }
+
+    PrintDebug(vm, VCORE_NONE, "UART: Serial ports hooked\n");
+
+
+
+    if (v3_dev_add_char_frontend(vm, dev_id, connect_fn, (void *)state) == -1) {
+       PrintError(vm, VCORE_NONE, "UART: Could not register %s as frontend\n", dev_id);
+       v3_remove_device(dev);
+       return -1;
+    }
+
+
+    return 0;
 }
+
+
+
+
+
+device_register("SERIAL", serial_init)