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.


Enlarge serial buffer size to comply to line buffering of serial console
[palacios.git] / palacios / src / devices / serial.c
index 6b3337b..d6bdc2d 100644 (file)
@@ -1,5 +1,40 @@
-#include <devices/serial.h>
+/*
+ * 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>
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+
 #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
 
 
 #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
@@ -73,256 +97,948 @@ struct irq_enable_reg {
 #define TX_IRQ_THRE             0x1
 #define MODEL_IRQ_DELTA_SET     0x0
 
-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
-};
+//COMs IRQ ID
+#define COM1_IRQ  0x4
+#define COM2_IRQ  0x3
+#define COM3_IRQ  0x4
+#define COM4_IRQ  0x3
 
-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
-};
+#define RX_BUFFER 0x1
+#define TX_BUFFER 0x2
+
+//initial value for registers
+
+#define  IER_INIT_VAL 0x3
+//receive data available interrupt and THRE interrupt are enabled
+#define  IIR_INIT_VAL 0x1
+//No Pending Interrupt bit is set.
+#define  FCR_INIT_VAL 0xc0
+//fifo control register is set to 0
+#define  LCR_INIT_VAL 0x3
+#define  MCR_INIT_VAL 0x0
+#define  LSR_INIT_VAL 0x60
+#define  MSR_INIT_VAL 0x0
+#define  DLL_INIT_VAL 0x1
+#define  DLM_INIT_VAL 0x0
 
-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
-};
 
 
-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
+//receiver buffer register
+struct rbr_register {
+    uint8_t data;
 };
 
+// transmitter holding register
+struct thr_register {
+    uint8_t data;
+};
 
-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
+//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
+           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;
 };
 
+//divisor latch LSB
+struct dll_register {
+    uint8_t data;
+};
 
-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
+//divisor latch MSB
+struct dlm_register {
+    uint8_t data;
 };
+#define SERIAL_BUF_LEN 128
 
+struct serial_buffer {
+    int head; // most recent data
+    int tail; // oldest char
+    int full;
+    uint8_t buffer[SERIAL_BUF_LEN];
+};
 
 struct serial_port {
-  struct irq_enable_reg     ier;
-  struct irq_id_reg         iid;
-  struct fifo_ctrl_reg      fcr;
-  struct line_ctrl_reg      lcr;
-  struct model_ctrl_reg     mcr;
-  struct line_status_reg    lsr;
-  struct model_status_reg   msr;
-
+    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;
+
+
+    void * backend_data;
+    struct v3_dev_char_ops * ops;
 
-  char tx_buffer[256];
-  char rx_buffer[256];
 };
 
 
 struct serial_state {
-  struct serial_port com1;
-  struct serial_port com2;
-  struct serial_port com3;
-  struct serial_port com4;
+    struct serial_port coms[4];
+
 };
 
 
-int write_data_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
-  PrintDebug("Write to Data Port\n");
 
-  return -1;
+static struct serial_port * get_com_from_port(struct serial_state * serial, uint16_t port) {
+    if ((port >= COM1_DATA_PORT) && (port <= COM1_SCRATCH_PORT)) {
+       return &(serial->coms[0]);
+    } else if ((port >= COM2_DATA_PORT) && (port <= COM2_SCRATCH_PORT)) {
+       return &(serial->coms[1]);
+    } else if ((port >= COM3_DATA_PORT) && (port <= COM3_SCRATCH_PORT)) {
+       return &(serial->coms[2]);
+    } else if ((port >= COM4_DATA_PORT) && (port <= COM4_SCRATCH_PORT)) {
+       return &(serial->coms[3]);
+    } else {
+       PrintError("Error: Could not find serial port associated with IO port %d\n", port);
+       return NULL;
+    }
 }
 
-int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
-  PrintDebug("Read from Data Port\n");
-  return -1;
-}
+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);
+    }
 
-int write_ctrl_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
-  PrintDebug("Write to Control Port\n");
+    return false;
+}
 
-  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;
+    }
 }
 
-int read_ctrl_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
-  PrintDebug("Read from Control Port\n");
-  return -1;
+static int updateIRQ(struct v3_vm_info * vm, struct serial_port * com) {
+    
+    if ( (com->ier.erbfi == 0x1) && 
+        (receive_buffer_trigger( getNumber(&(com->rx_buffer)), com->fcr.rx_trigger)) ) {
+
+       PrintDebug("UART: receive buffer interrupt(trigger level reached)\n");
+
+       com->iir.iid = RX_IRQ_TRIGGER_LEVEL;
+       v3_raise_irq(vm, com->irq_number);
+    }
+    
+    if ( (com->iir.iid == RX_IRQ_TRIGGER_LEVEL) && 
+        (!(receive_buffer_trigger( getNumber(&(com->rx_buffer)), com->fcr.rx_trigger))) ) {
+
+       com->iir.iid = 0x0;   //reset interrupt identification register
+       com->iir.pending = 0x1;
+    }
+    
+    if ( (com->iir.iid == TX_IRQ_THRE) && 
+        (getNumber(&(com->tx_buffer)) == SERIAL_BUF_LEN)) {
+
+       com->iir.iid = 0x0; //reset interrupt identification register
+       com->iir.pending = 0x1;
+
+    } else if ( (com->ier.etbei == 0x1) && 
+               (getNumber(&(com->tx_buffer)) != SERIAL_BUF_LEN )) {
+       
+       PrintDebug("UART: transmit buffer interrupt(buffer not full)\n");
+
+       com->iir.iid = TX_IRQ_THRE;
+       com->iir.pending = 0;
+
+       v3_raise_irq(vm, com->irq_number);
+    }
+
+    return 1;
 }
 
 
-int write_status_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
-  PrintDebug("Write to Status Port\n");
+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;    
+
+    if (buf->full == 1) {
+       PrintDebug("Buffer is full!\n");
+
+       if (buf == &(com->rx_buffer)) {
+           com->lsr.oe = 1; //overrun error bit set
+       }
+
+       updateIRQ(vm, com);
+
+       return 0;
+    }
+    
+    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.
+    }
+    
+    if (buf == &(com->tx_buffer)) {
+       com->lsr.thre = 0; //reset thre and temt bits.
+       com->lsr.temt = 0;
+    }
+        
+    return 0;
+}
 
-  return -1;
+static int dequeue_data(struct v3_vm_info * vm, struct serial_port * com,
+                       struct serial_buffer * buf, uint8_t * data) {
+
+    int next_tail = (buf->tail + 1) % SERIAL_BUF_LEN;
+
+
+    if ( (buf->head == buf->tail) && (buf->full != 1) ) {
+       PrintDebug("no data to delete!\n");
+       return -1;
+    }
+    
+    if (buf->full == 1) {
+       buf->full = 0;
+    }
+    
+        
+    *data = buf->buffer[next_tail];
+    buf->buffer[next_tail] = 0;
+    buf->tail = next_tail;
+    
+    if ( (buf == &(com->rx_buffer)) && (getNumber(&(com->rx_buffer)) == 0) ) {
+       com->lsr.dr = 0;
+    }
+    
+    if ((buf == &(com->tx_buffer)) && (getNumber(&(com->tx_buffer)) == 0)) {
+       com->lsr.thre = 1;
+       com->lsr.temt = 1;
+    }
+    
+    updateIRQ(vm, com);
+    
+    return 0;
 }
 
-int read_status_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
-  PrintDebug("Read from Status Port\n");
-  return -1;
+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;
+
+    PrintDebug("Write to Data Port 0x%x (val=%x)\n", port, *val);
+    
+    if (length != 1) {
+       PrintError("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("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("UART:read from NOBODY\n");
+       return -1;
+    }
+    
+
+    // dlab is always checked first
+    if (com_port->lcr.dlab == 1) {
+       com_port->dll.data = *val;
+    }  else {
+       
+
+       /* JRL: Some buffering would probably be a good idea here.... */
+       if (com_port->ops) {
+           com_port->ops->output(val, 1, com_port->backend_data);
+       } else {
+           queue_data(core->vm_info, com_port, &(com_port->tx_buffer), *val);
+           updateIRQ(core->vm_info, com_port);
+       }
+    }
+    
+    return length;
 }
 
 
 
+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;
+
+    PrintDebug("Read from Data Port 0x%x\n", port);
+    
+    if (length != 1) {
+       PrintError("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("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("UART:read from NOBODY\n");
+       return -1;
+    }
+    
+    if (com_port->lcr.dlab == 1) {
+       *val = com_port->dll.data;
+    } else {
+       dequeue_data(core->vm_info, com_port, &(com_port->rx_buffer), val);
+    }    
+       
+    return length;
+}
 
 
 
+static int handle_fcr_write(struct serial_port * com, uint8_t value) {
+
+    com->fcr.enable = value & 0x1;
+    
+    if (com->fcr.enable == 0x1) {
+       com->fcr.val = value;
+
+       com->fcr.enable = 1; // Do we need to set this??
+
+       //if rfres set, clear receive buffer.
+       if (com->fcr.rfres == 0x1) {
+           com->rx_buffer.head = 0;
+           com->rx_buffer.tail = 0;
+           com->rx_buffer.full = 0;
+           memset(com->rx_buffer.buffer, 0, SERIAL_BUF_LEN);
+           com->fcr.rfres = 0;
+       }
+
+       //if xfres set, clear transmit buffer.
+       if (com->fcr.xfres == 0x1) {
+           com->tx_buffer.head = 0;
+           com->tx_buffer.tail = 0;
+           com->tx_buffer.full = 0;
+           memset(com->tx_buffer.buffer, 0, SERIAL_BUF_LEN);
+           com->fcr.xfres = 0;
+       }
+    } else {
+       //clear both buffers.
+       com->tx_buffer.head = 0;
+       com->tx_buffer.tail = 0;
+       com->tx_buffer.full = 0;
+       com->rx_buffer.head = 0;
+       com->rx_buffer.tail = 0;
+       com->rx_buffer.full = 0;
+       
+       memset(com->rx_buffer.buffer, 0, SERIAL_BUF_LEN);
+       memset(com->tx_buffer.buffer, 0, SERIAL_BUF_LEN);
+    }
+    
+    return 1;
+}
 
-void serial_init(struct vm_device * dev) {
-  struct serial_state * state = (struct serial_state *)dev->private_data;
 
-  state->com1.ier.rsvd = 0;
-  state->com1.iir.rsvd = 0;
-  state->com1.fcr.rsvd = 0;
-  state->com1.mcr.rsvd = 0;
-  state->com1.iir.pending = 1;
 
-  state->com2.ier.rsvd = 0;
-  state->com2.iir.rsvd = 0;
-  state->com2.fcr.rsvd = 0;
-  state->com2.mcr.rsvd = 0;
-  state->com2.iir.pending = 1;
 
-  state->com3.ier.rsvd = 0;
-  state->com3.iir.rsvd = 0;
-  state->com3.fcr.rsvd = 0;
-  state->com3.mcr.rsvd = 0;
-  state->com3.iir.pending = 1;
 
-  state->com4.ier.rsvd = 0;
-  state->com4.iir.rsvd = 0;
-  state->com4.fcr.rsvd = 0;
-  state->com4.mcr.rsvd = 0;
-  state->com4.iir.pending = 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;
+
+    PrintDebug("UART:Write to Control Port (val=%x)\n", val);
+    
+    if (length != 1) {
+       PrintError("UART:Invalid Write length to control port%d\n", port);
+       return -1;
+    }
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError("Could not find serial port corresponding to IO port %d\n", port);
+       return -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("UART:Write to IER/LATCH port: dlab is %x\n", com_port->lcr.dlab);
+
+           if (com_port->lcr.dlab == 1) {
+               com_port->dlm.data = val;
+           } else {
+               com_port->ier.val = val;
+           }
+
+           break;
+       }           
+       case COM1_FIFO_CTRL_PORT:
+       case COM2_FIFO_CTRL_PORT:
+       case COM3_FIFO_CTRL_PORT:
+       case COM4_FIFO_CTRL_PORT: {
+           PrintDebug("UART:Write to FCR\n");
+
+           if (handle_fcr_write(com_port, val) == -1) {
+               return -1;
+           }
+
+           break;
+       }
+       case COM1_LINE_CTRL_PORT:
+       case COM2_LINE_CTRL_PORT:
+       case COM3_LINE_CTRL_PORT:
+       case COM4_LINE_CTRL_PORT: {
+           PrintDebug("UART:Write to LCR\n");
+           com_port->lcr.val = val;
+           break;
+       }
+       case COM1_MODEM_CTRL_PORT:
+       case COM2_MODEM_CTRL_PORT:
+       case COM3_MODEM_CTRL_PORT:
+       case COM4_MODEM_CTRL_PORT: {
+           PrintDebug("UART:Write to MCR\n");
+           com_port->mcr.val = val;
+           break;
+       }
+       case COM1_SCRATCH_PORT:
+       case COM2_SCRATCH_PORT:
+       case COM3_SCRATCH_PORT:
+       case COM4_SCRATCH_PORT: {
+           PrintDebug("UART:Write to SCRATCH\n");
+           com_port->scr.data = val;
+           break;
+       }
+       default:
+           PrintError("UART:Write to NOBODY, ERROR\n");
+           return -1;
+    }
+    
+
+    return length;
+}
 
 
-  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_MODEL_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_MODEL_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_MODEL_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_MODEL_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_MODEL_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_MODEL_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM3_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
+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;
+
+    PrintDebug("Read from Control Port\n");
+    
+    if (length != 1) {
+       PrintError("Invalid Read length to control port\n");
+       return -1;
+    }
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError("Could not find serial port corresponding to IO port %d\n", port);
+       return -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("UART:read from IER\n");
+
+           if (com_port->lcr.dlab == 1) {
+               *val = com_port->dlm.data;
+           } else {
+               *val = com_port->ier.val;
+           }
+           break;
+       }
+
+       case COM1_IIR_PORT:
+       case COM2_IIR_PORT:
+       case COM3_IIR_PORT:
+       case COM4_IIR_PORT:
+           PrintDebug("UART:read from IIR\n");
+           *val = com_port->iir.val;
+           break;
+
+       case COM1_LINE_CTRL_PORT:
+       case COM2_LINE_CTRL_PORT:
+       case COM3_LINE_CTRL_PORT:
+       case COM4_LINE_CTRL_PORT:
+           PrintDebug("UART:read from LCR\n");
+           *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("UART:read from MCR\n");
+           *val = com_port->mcr.val;
+           break;
+
+       case COM1_SCRATCH_PORT:
+       case COM2_SCRATCH_PORT:
+       case COM3_SCRATCH_PORT:
+       case COM4_SCRATCH_PORT:
+           PrintDebug("UART:read from SCRATCH\n");
+           *val = com_port->scr.data;
+           break;
+
+       default:
+           PrintError("UART:read from NOBODY\n");
+           return -1;
+    }
+
+    return length;
+}
 
-  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_MODEL_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_MODEL_STATUS_PORT, &read_status_port, &write_status_port);
-  dev_hook_io(dev, COM4_SCRATCH_PORT, &read_ctrl_port, &write_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;
+
+    PrintDebug("Write to Status Port (val=%x)\n", val);
+
+    if (length != 1) {
+       PrintError("Invalid Write length to status port %d\n", port);
+       return -1;
+    }
+
+    com_port = get_com_from_port(state, port);
+
+    if (com_port == NULL) {
+       PrintError("Could not find serial port corresponding to IO port %d\n", port);
+       return -1;
+    }
+
+    switch (port) {
+       case COM1_LINE_STATUS_PORT:
+       case COM2_LINE_STATUS_PORT:
+       case COM3_LINE_STATUS_PORT:
+       case COM4_LINE_STATUS_PORT:
+           PrintDebug("UART:write to LSR\n");
+           com_port->lsr.val = val;
+           break;
+
+       case COM1_MODEM_STATUS_PORT:
+       case COM2_MODEM_STATUS_PORT:
+       case COM3_MODEM_STATUS_PORT:
+       case COM4_MODEM_STATUS_PORT:
+           PrintDebug("UART:write to MSR\n");
+           com_port->msr.val = val;
+           break;
+
+       default:
+           PrintError("UART:write to NOBODY\n");
+           return -1;
+    }
+
+    return length;
 }
 
+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;
+
+    PrintDebug("Read from Status Port 0x%x\n", port);
+
+    com_port = get_com_from_port(state, port);
 
-void 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_MODEL_CTRL_PORT);
-  dev_unhook_io(dev, COM1_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM1_MODEL_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_MODEL_CTRL_PORT);
-  dev_unhook_io(dev, COM2_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM2_MODEL_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_MODEL_CTRL_PORT);
-  dev_unhook_io(dev, COM3_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM3_MODEL_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_MODEL_CTRL_PORT);
-  dev_unhook_io(dev, COM4_LINE_STATUS_PORT);
-  dev_unhook_io(dev, COM4_MODEL_STATUS_PORT);
-  dev_unhook_io(dev, COM4_SCRATCH_PORT);
+    if (com_port == NULL) {
+       PrintError("Could not find serial port corresponding to IO port %d\n", port);
+       return -1;
+    }
 
+    switch (port) {
+       case COM1_LINE_STATUS_PORT:
+       case COM2_LINE_STATUS_PORT:
+       case COM3_LINE_STATUS_PORT:
+       case COM4_LINE_STATUS_PORT:
+
+           if (length != 1) {
+               PrintError("Invalid Read length to control port\n");
+               return -1;
+           }
+
+           PrintDebug("UART:read from LSR\n");
+
+           *val = com_port->lsr.val;
+           com_port->lsr.oe = 0;     // Why do we clear this??
+
+           break;
+
+       case COM1_MODEM_STATUS_PORT:
+       case COM2_MODEM_STATUS_PORT:
+       case COM3_MODEM_STATUS_PORT:
+       case COM4_MODEM_STATUS_PORT:
+           PrintDebug("UART:read from COM4 MSR (length = %d)\n", length);
+
+           if (length > 2) {
+               PrintError("Invalid Read length to MSR port\n");
+               return -1;
+           }
+
+           if (length == 2) {
+               /* Windows XP expects to be able to read this register and the next in one go */
+
+               if (read_ctrl_port(core, port + 1, val + 1, 1, priv_data) < 0) {
+                   PrintError("Error reading control port for word size read of Status register\n");
+                   return -1;
+               }
+           }
+
+           // always read low byte...
+           
+           *val = com_port->msr.val;
+           break;
+
+       default:
+           PrintError("UART:read from NOBODY (length = %d)\n", length);
+           return -1;
+    }
+
+    return length;
 }
 
+static int serial_free(struct serial_state * state) {
+
+    V3_Free(state);
+    return 0;
+}
+
+
 
 
-static struct vm_device_ops dev_ops = {
-  .init = serial_init,
-  .deinit = serial_deini,
-  .reset = NULL,
-  .start = NULL,
-  .stop = NULL,
+static struct v3_device_ops dev_ops = {
+    .free = (int (*)(void *))serial_free,
 };
 
 
-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);
 
-  struct vm_device * device = create_device("Serial UART", &dev_ops, state);
+static int init_serial_port(struct serial_port * 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;
+    
+    com->tx_buffer.head = 0;
+    com->tx_buffer.tail = 0;
+    com->tx_buffer.full = 0;
+    memset(com->tx_buffer.buffer, 0, SERIAL_BUF_LEN);
+
+    com->rx_buffer.head = 0;
+    com->rx_buffer.tail = 0;
+    com->rx_buffer.full = 0;
+    memset(com->rx_buffer.buffer, 0, SERIAL_BUF_LEN);
+    
+    com->ops = NULL;
+    com->backend_data = NULL;
+
+    return 0;
+}
+
+static uint64_t serial_input(struct v3_vm_info * vm, uint8_t * buf, uint64_t len, void * priv_data){
+    struct serial_port * com_port = (struct serial_port *)priv_data;
+    int i;
+
+    for(i = 0; i < len; i++){
+       queue_data(vm, com_port, &(com_port->rx_buffer), buf[i]);
+    }
+
+    updateIRQ(vm, com_port);
+
+    return len;
+}
+
+
+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) {
+
+    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("Invalid Serial frontend config: missing \"com_port\"\n");
+       return -1;
+    }
+    
+    com_idx = atoi(com_port) - 1;
+
+    if ((com_idx > 3) || (com_idx < 0)) {
+       PrintError("Invalid Com port (%s) \n", com_port);
+       return -1;
+    }
 
-  return device;
+    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("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]));
+
+    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 * dev = v3_add_device(vm, dev_id, &dev_ops, state);
+
+    if (dev == NULL) {
+       PrintError("Could not attach device %s\n", dev_id);
+       V3_Free(state);
+       return -1;
+    }
+
+    PrintDebug("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("Error hooking Serial IO ports\n");
+       v3_remove_device(dev);
+       return -1;
+    }
+
+    PrintDebug("Serial ports hooked\n");
+
+
+
+    if (v3_dev_add_char_frontend(vm, dev_id, connect_fn, (void *)state) == -1) {
+       PrintError("Could not register %s as frontend\n", dev_id);
+       v3_remove_device(dev);
+       return -1;
+    }
+
+
+    return 0;
 }
+
+
+
+
+
+device_register("SERIAL", serial_init)