New files are from the upstream QEMU (0.10.5), and the code is based on
the following commit (dated 2009-06-19, see CHANGES.TXT):

             d2e9fd8f703203c2eeeed120b1ef6c3a6574e0ab

    new file:   hw/apic.c
    new file:   hw/fdc.h
    new file:   hw/fw_cfg.c
    new file:   hw/fw_cfg.h
    new file:   hw/i8254.c
    new file:   hw/i8259.c
    new file:   hw/ioapic.c
    new file:   hw/mc146818rtc.c
    new file:   hw/ne2000.c
    new file:   hw/pc.c
    new file:   hw/pckbd.c
    new file:   hw/piix_pci.c
    new file:   hw/ps2.c
    new file:   hw/ps2.h
    new file:   hw/smbios.c
    new file:   target-i386/TODO
    new file:   target-i386/cpu.h
    new file:   target-i386/exec.h
    new file:   target-i386/helper.c
    new file:   target-i386/helper.h
    new file:   target-i386/helper_template.h
    new file:   target-i386/kvm.c
    new file:   target-i386/machine.c
    new file:   target-i386/op_helper.c
    new file:   target-i386/ops_sse.h
    new file:   target-i386/ops_sse_header.h
    new file:   target-i386/svm.h
    new file:   target-i386/translate.c

Change-Id: I55c62ed7516f002c882705545e7c21997ece9927
diff --git a/hw/apic.c b/hw/apic.c
new file mode 100644
index 0000000..b059185
--- /dev/null
+++ b/hw/apic.c
@@ -0,0 +1,967 @@
+/*
+ *  APIC support
+ *
+ *  Copyright (c) 2004-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+#include "hw.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+
+//#define DEBUG_APIC
+
+/* APIC Local Vector Table */
+#define APIC_LVT_TIMER   0
+#define APIC_LVT_THERMAL 1
+#define APIC_LVT_PERFORM 2
+#define APIC_LVT_LINT0   3
+#define APIC_LVT_LINT1   4
+#define APIC_LVT_ERROR   5
+#define APIC_LVT_NB      6
+
+/* APIC delivery modes */
+#define APIC_DM_FIXED	0
+#define APIC_DM_LOWPRI	1
+#define APIC_DM_SMI	2
+#define APIC_DM_NMI	4
+#define APIC_DM_INIT	5
+#define APIC_DM_SIPI	6
+#define APIC_DM_EXTINT	7
+
+/* APIC destination mode */
+#define APIC_DESTMODE_FLAT	0xf
+#define APIC_DESTMODE_CLUSTER	1
+
+#define APIC_TRIGGER_EDGE  0
+#define APIC_TRIGGER_LEVEL 1
+
+#define	APIC_LVT_TIMER_PERIODIC		(1<<17)
+#define	APIC_LVT_MASKED			(1<<16)
+#define	APIC_LVT_LEVEL_TRIGGER		(1<<15)
+#define	APIC_LVT_REMOTE_IRR		(1<<14)
+#define	APIC_INPUT_POLARITY		(1<<13)
+#define	APIC_SEND_PENDING		(1<<12)
+
+#define ESR_ILLEGAL_ADDRESS (1 << 7)
+
+#define APIC_SV_ENABLE (1 << 8)
+
+#define MAX_APICS 255
+#define MAX_APIC_WORDS 8
+
+typedef struct APICState {
+    CPUState *cpu_env;
+    uint32_t apicbase;
+    uint8_t id;
+    uint8_t arb_id;
+    uint8_t tpr;
+    uint32_t spurious_vec;
+    uint8_t log_dest;
+    uint8_t dest_mode;
+    uint32_t isr[8];  /* in service register */
+    uint32_t tmr[8];  /* trigger mode register */
+    uint32_t irr[8]; /* interrupt request register */
+    uint32_t lvt[APIC_LVT_NB];
+    uint32_t esr; /* error register */
+    uint32_t icr[2];
+
+    uint32_t divide_conf;
+    int count_shift;
+    uint32_t initial_count;
+    int64_t initial_count_load_time, next_time;
+    uint32_t idx;
+    QEMUTimer *timer;
+    int sipi_vector;
+    int wait_for_sipi;
+} APICState;
+
+static int apic_io_memory;
+static APICState *local_apics[MAX_APICS + 1];
+static int last_apic_idx = 0;
+static int apic_irq_delivered;
+
+
+static void apic_set_irq(APICState *s, int vector_num, int trigger_mode);
+static void apic_update_irq(APICState *s);
+static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
+                                      uint8_t dest, uint8_t dest_mode);
+
+/* Find first bit starting from msb */
+static int fls_bit(uint32_t value)
+{
+    return 31 - clz32(value);
+}
+
+/* Find first bit starting from lsb */
+static int ffs_bit(uint32_t value)
+{
+    return ctz32(value);
+}
+
+static inline void set_bit(uint32_t *tab, int index)
+{
+    int i, mask;
+    i = index >> 5;
+    mask = 1 << (index & 0x1f);
+    tab[i] |= mask;
+}
+
+static inline void reset_bit(uint32_t *tab, int index)
+{
+    int i, mask;
+    i = index >> 5;
+    mask = 1 << (index & 0x1f);
+    tab[i] &= ~mask;
+}
+
+static inline int get_bit(uint32_t *tab, int index)
+{
+    int i, mask;
+    i = index >> 5;
+    mask = 1 << (index & 0x1f);
+    return !!(tab[i] & mask);
+}
+
+static void apic_local_deliver(CPUState *env, int vector)
+{
+    APICState *s = env->apic_state;
+    uint32_t lvt = s->lvt[vector];
+    int trigger_mode;
+
+    if (lvt & APIC_LVT_MASKED)
+        return;
+
+    switch ((lvt >> 8) & 7) {
+    case APIC_DM_SMI:
+        cpu_interrupt(env, CPU_INTERRUPT_SMI);
+        break;
+
+    case APIC_DM_NMI:
+        cpu_interrupt(env, CPU_INTERRUPT_NMI);
+        break;
+
+    case APIC_DM_EXTINT:
+        cpu_interrupt(env, CPU_INTERRUPT_HARD);
+        break;
+
+    case APIC_DM_FIXED:
+        trigger_mode = APIC_TRIGGER_EDGE;
+        if ((vector == APIC_LVT_LINT0 || vector == APIC_LVT_LINT1) &&
+            (lvt & APIC_LVT_LEVEL_TRIGGER))
+            trigger_mode = APIC_TRIGGER_LEVEL;
+        apic_set_irq(s, lvt & 0xff, trigger_mode);
+    }
+}
+
+void apic_deliver_pic_intr(CPUState *env, int level)
+{
+    if (level)
+        apic_local_deliver(env, APIC_LVT_LINT0);
+    else {
+        APICState *s = env->apic_state;
+        uint32_t lvt = s->lvt[APIC_LVT_LINT0];
+
+        switch ((lvt >> 8) & 7) {
+        case APIC_DM_FIXED:
+            if (!(lvt & APIC_LVT_LEVEL_TRIGGER))
+                break;
+            reset_bit(s->irr, lvt & 0xff);
+            /* fall through */
+        case APIC_DM_EXTINT:
+            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+            break;
+        }
+    }
+}
+
+#define foreach_apic(apic, deliver_bitmask, code) \
+{\
+    int __i, __j, __mask;\
+    for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
+        __mask = deliver_bitmask[__i];\
+        if (__mask) {\
+            for(__j = 0; __j < 32; __j++) {\
+                if (__mask & (1 << __j)) {\
+                    apic = local_apics[__i * 32 + __j];\
+                    if (apic) {\
+                        code;\
+                    }\
+                }\
+            }\
+        }\
+    }\
+}
+
+static void apic_bus_deliver(const uint32_t *deliver_bitmask,
+                             uint8_t delivery_mode,
+                             uint8_t vector_num, uint8_t polarity,
+                             uint8_t trigger_mode)
+{
+    APICState *apic_iter;
+
+    switch (delivery_mode) {
+        case APIC_DM_LOWPRI:
+            /* XXX: search for focus processor, arbitration */
+            {
+                int i, d;
+                d = -1;
+                for(i = 0; i < MAX_APIC_WORDS; i++) {
+                    if (deliver_bitmask[i]) {
+                        d = i * 32 + ffs_bit(deliver_bitmask[i]);
+                        break;
+                    }
+                }
+                if (d >= 0) {
+                    apic_iter = local_apics[d];
+                    if (apic_iter) {
+                        apic_set_irq(apic_iter, vector_num, trigger_mode);
+                    }
+                }
+            }
+            return;
+
+        case APIC_DM_FIXED:
+            break;
+
+        case APIC_DM_SMI:
+            foreach_apic(apic_iter, deliver_bitmask,
+                cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_SMI) );
+            return;
+
+        case APIC_DM_NMI:
+            foreach_apic(apic_iter, deliver_bitmask,
+                cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_NMI) );
+            return;
+
+        case APIC_DM_INIT:
+            /* normal INIT IPI sent to processors */
+            foreach_apic(apic_iter, deliver_bitmask,
+                         cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_INIT) );
+            return;
+
+        case APIC_DM_EXTINT:
+            /* handled in I/O APIC code */
+            break;
+
+        default:
+            return;
+    }
+
+    foreach_apic(apic_iter, deliver_bitmask,
+                 apic_set_irq(apic_iter, vector_num, trigger_mode) );
+}
+
+void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
+                      uint8_t delivery_mode, uint8_t vector_num,
+                      uint8_t polarity, uint8_t trigger_mode)
+{
+    uint32_t deliver_bitmask[MAX_APIC_WORDS];
+
+    apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
+    apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
+                     trigger_mode);
+}
+
+void cpu_set_apic_base(CPUState *env, uint64_t val)
+{
+    APICState *s = env->apic_state;
+#ifdef DEBUG_APIC
+    printf("cpu_set_apic_base: %016" PRIx64 "\n", val);
+#endif
+    if (!s)
+        return;
+    s->apicbase = (val & 0xfffff000) |
+        (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
+    /* if disabled, cannot be enabled again */
+    if (!(val & MSR_IA32_APICBASE_ENABLE)) {
+        s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
+        env->cpuid_features &= ~CPUID_APIC;
+        s->spurious_vec &= ~APIC_SV_ENABLE;
+    }
+}
+
+uint64_t cpu_get_apic_base(CPUState *env)
+{
+    APICState *s = env->apic_state;
+#ifdef DEBUG_APIC
+    printf("cpu_get_apic_base: %016" PRIx64 "\n",
+           s ? (uint64_t)s->apicbase: 0);
+#endif
+    return s ? s->apicbase : 0;
+}
+
+void cpu_set_apic_tpr(CPUX86State *env, uint8_t val)
+{
+    APICState *s = env->apic_state;
+    if (!s)
+        return;
+    s->tpr = (val & 0x0f) << 4;
+    apic_update_irq(s);
+}
+
+uint8_t cpu_get_apic_tpr(CPUX86State *env)
+{
+    APICState *s = env->apic_state;
+    return s ? s->tpr >> 4 : 0;
+}
+
+/* return -1 if no bit is set */
+static int get_highest_priority_int(uint32_t *tab)
+{
+    int i;
+    for(i = 7; i >= 0; i--) {
+        if (tab[i] != 0) {
+            return i * 32 + fls_bit(tab[i]);
+        }
+    }
+    return -1;
+}
+
+static int apic_get_ppr(APICState *s)
+{
+    int tpr, isrv, ppr;
+
+    tpr = (s->tpr >> 4);
+    isrv = get_highest_priority_int(s->isr);
+    if (isrv < 0)
+        isrv = 0;
+    isrv >>= 4;
+    if (tpr >= isrv)
+        ppr = s->tpr;
+    else
+        ppr = isrv << 4;
+    return ppr;
+}
+
+static int apic_get_arb_pri(APICState *s)
+{
+    /* XXX: arbitration */
+    return 0;
+}
+
+/* signal the CPU if an irq is pending */
+static void apic_update_irq(APICState *s)
+{
+    int irrv, ppr;
+    if (!(s->spurious_vec & APIC_SV_ENABLE))
+        return;
+    irrv = get_highest_priority_int(s->irr);
+    if (irrv < 0)
+        return;
+    ppr = apic_get_ppr(s);
+    if (ppr && (irrv & 0xf0) <= (ppr & 0xf0))
+        return;
+    cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+}
+
+void apic_reset_irq_delivered(void)
+{
+    apic_irq_delivered = 0;
+}
+
+int apic_get_irq_delivered(void)
+{
+    return apic_irq_delivered;
+}
+
+static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
+{
+    apic_irq_delivered += !get_bit(s->irr, vector_num);
+
+    set_bit(s->irr, vector_num);
+    if (trigger_mode)
+        set_bit(s->tmr, vector_num);
+    else
+        reset_bit(s->tmr, vector_num);
+    apic_update_irq(s);
+}
+
+static void apic_eoi(APICState *s)
+{
+    int isrv;
+    isrv = get_highest_priority_int(s->isr);
+    if (isrv < 0)
+        return;
+    reset_bit(s->isr, isrv);
+    /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to
+            set the remote IRR bit for level triggered interrupts. */
+    apic_update_irq(s);
+}
+
+static int apic_find_dest(uint8_t dest)
+{
+    APICState *apic = local_apics[dest];
+    int i;
+
+    if (apic && apic->id == dest)
+        return dest;  /* shortcut in case apic->id == apic->idx */
+
+    for (i = 0; i < MAX_APICS; i++) {
+        apic = local_apics[i];
+	if (apic && apic->id == dest)
+            return i;
+    }
+
+    return -1;
+}
+
+static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
+                                      uint8_t dest, uint8_t dest_mode)
+{
+    APICState *apic_iter;
+    int i;
+
+    if (dest_mode == 0) {
+        if (dest == 0xff) {
+            memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t));
+        } else {
+            int idx = apic_find_dest(dest);
+            memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
+            if (idx >= 0)
+                set_bit(deliver_bitmask, idx);
+        }
+    } else {
+        /* XXX: cluster mode */
+        memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
+        for(i = 0; i < MAX_APICS; i++) {
+            apic_iter = local_apics[i];
+            if (apic_iter) {
+                if (apic_iter->dest_mode == 0xf) {
+                    if (dest & apic_iter->log_dest)
+                        set_bit(deliver_bitmask, i);
+                } else if (apic_iter->dest_mode == 0x0) {
+                    if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
+                        (dest & apic_iter->log_dest & 0x0f)) {
+                        set_bit(deliver_bitmask, i);
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+void apic_init_reset(CPUState *env)
+{
+    APICState *s = env->apic_state;
+    int i;
+
+    if (!s)
+        return;
+
+    s->tpr = 0;
+    s->spurious_vec = 0xff;
+    s->log_dest = 0;
+    s->dest_mode = 0xf;
+    memset(s->isr, 0, sizeof(s->isr));
+    memset(s->tmr, 0, sizeof(s->tmr));
+    memset(s->irr, 0, sizeof(s->irr));
+    for(i = 0; i < APIC_LVT_NB; i++)
+        s->lvt[i] = 1 << 16; /* mask LVT */
+    s->esr = 0;
+    memset(s->icr, 0, sizeof(s->icr));
+    s->divide_conf = 0;
+    s->count_shift = 0;
+    s->initial_count = 0;
+    s->initial_count_load_time = 0;
+    s->next_time = 0;
+    s->wait_for_sipi = 1;
+
+    env->halted = !(s->apicbase & MSR_IA32_APICBASE_BSP);
+}
+
+static void apic_startup(APICState *s, int vector_num)
+{
+    s->sipi_vector = vector_num;
+    cpu_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI);
+}
+
+void apic_sipi(CPUState *env)
+{
+    APICState *s = env->apic_state;
+
+    cpu_reset_interrupt(env, CPU_INTERRUPT_SIPI);
+
+    if (!s->wait_for_sipi)
+        return;
+
+    env->eip = 0;
+    cpu_x86_load_seg_cache(env, R_CS, s->sipi_vector << 8, s->sipi_vector << 12,
+                           0xffff, 0);
+    env->halted = 0;
+    s->wait_for_sipi = 0;
+}
+
+static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode,
+                         uint8_t delivery_mode, uint8_t vector_num,
+                         uint8_t polarity, uint8_t trigger_mode)
+{
+    uint32_t deliver_bitmask[MAX_APIC_WORDS];
+    int dest_shorthand = (s->icr[0] >> 18) & 3;
+    APICState *apic_iter;
+
+    switch (dest_shorthand) {
+    case 0:
+        apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
+        break;
+    case 1:
+        memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
+        set_bit(deliver_bitmask, s->idx);
+        break;
+    case 2:
+        memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
+        break;
+    case 3:
+        memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
+        reset_bit(deliver_bitmask, s->idx);
+        break;
+    }
+
+    switch (delivery_mode) {
+        case APIC_DM_INIT:
+            {
+                int trig_mode = (s->icr[0] >> 15) & 1;
+                int level = (s->icr[0] >> 14) & 1;
+                if (level == 0 && trig_mode == 1) {
+                    foreach_apic(apic_iter, deliver_bitmask,
+                                 apic_iter->arb_id = apic_iter->id );
+                    return;
+                }
+            }
+            break;
+
+        case APIC_DM_SIPI:
+            foreach_apic(apic_iter, deliver_bitmask,
+                         apic_startup(apic_iter, vector_num) );
+            return;
+    }
+
+    apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
+                     trigger_mode);
+}
+
+int apic_get_interrupt(CPUState *env)
+{
+    APICState *s = env->apic_state;
+    int intno;
+
+    /* if the APIC is installed or enabled, we let the 8259 handle the
+       IRQs */
+    if (!s)
+        return -1;
+    if (!(s->spurious_vec & APIC_SV_ENABLE))
+        return -1;
+
+    /* XXX: spurious IRQ handling */
+    intno = get_highest_priority_int(s->irr);
+    if (intno < 0)
+        return -1;
+    if (s->tpr && intno <= s->tpr)
+        return s->spurious_vec & 0xff;
+    reset_bit(s->irr, intno);
+    set_bit(s->isr, intno);
+    apic_update_irq(s);
+    return intno;
+}
+
+int apic_accept_pic_intr(CPUState *env)
+{
+    APICState *s = env->apic_state;
+    uint32_t lvt0;
+
+    if (!s)
+        return -1;
+
+    lvt0 = s->lvt[APIC_LVT_LINT0];
+
+    if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 ||
+        (lvt0 & APIC_LVT_MASKED) == 0)
+        return 1;
+
+    return 0;
+}
+
+static uint32_t apic_get_current_count(APICState *s)
+{
+    int64_t d;
+    uint32_t val;
+    d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >>
+        s->count_shift;
+    if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+        /* periodic */
+        val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
+    } else {
+        if (d >= s->initial_count)
+            val = 0;
+        else
+            val = s->initial_count - d;
+    }
+    return val;
+}
+
+static void apic_timer_update(APICState *s, int64_t current_time)
+{
+    int64_t next_time, d;
+
+    if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
+        d = (current_time - s->initial_count_load_time) >>
+            s->count_shift;
+        if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+            if (!s->initial_count)
+                goto no_timer;
+            d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1);
+        } else {
+            if (d >= s->initial_count)
+                goto no_timer;
+            d = (uint64_t)s->initial_count + 1;
+        }
+        next_time = s->initial_count_load_time + (d << s->count_shift);
+        qemu_mod_timer(s->timer, next_time);
+        s->next_time = next_time;
+    } else {
+    no_timer:
+        qemu_del_timer(s->timer);
+    }
+}
+
+static void apic_timer(void *opaque)
+{
+    APICState *s = opaque;
+
+    apic_local_deliver(s->cpu_env, APIC_LVT_TIMER);
+    apic_timer_update(s, s->next_time);
+}
+
+static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    return 0;
+}
+
+static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+    return 0;
+}
+
+static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    CPUState *env;
+    APICState *s;
+    uint32_t val;
+    int index;
+
+    env = cpu_single_env;
+    if (!env)
+        return 0;
+    s = env->apic_state;
+
+    index = (addr >> 4) & 0xff;
+    switch(index) {
+    case 0x02: /* id */
+        val = s->id << 24;
+        break;
+    case 0x03: /* version */
+        val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
+        break;
+    case 0x08:
+        val = s->tpr;
+        break;
+    case 0x09:
+        val = apic_get_arb_pri(s);
+        break;
+    case 0x0a:
+        /* ppr */
+        val = apic_get_ppr(s);
+        break;
+    case 0x0b:
+        val = 0;
+        break;
+    case 0x0d:
+        val = s->log_dest << 24;
+        break;
+    case 0x0e:
+        val = s->dest_mode << 28;
+        break;
+    case 0x0f:
+        val = s->spurious_vec;
+        break;
+    case 0x10 ... 0x17:
+        val = s->isr[index & 7];
+        break;
+    case 0x18 ... 0x1f:
+        val = s->tmr[index & 7];
+        break;
+    case 0x20 ... 0x27:
+        val = s->irr[index & 7];
+        break;
+    case 0x28:
+        val = s->esr;
+        break;
+    case 0x30:
+    case 0x31:
+        val = s->icr[index & 1];
+        break;
+    case 0x32 ... 0x37:
+        val = s->lvt[index - 0x32];
+        break;
+    case 0x38:
+        val = s->initial_count;
+        break;
+    case 0x39:
+        val = apic_get_current_count(s);
+        break;
+    case 0x3e:
+        val = s->divide_conf;
+        break;
+    default:
+        s->esr |= ESR_ILLEGAL_ADDRESS;
+        val = 0;
+        break;
+    }
+#ifdef DEBUG_APIC
+    printf("APIC read: %08x = %08x\n", (uint32_t)addr, val);
+#endif
+    return val;
+}
+
+static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CPUState *env;
+    APICState *s;
+    int index;
+
+    env = cpu_single_env;
+    if (!env)
+        return;
+    s = env->apic_state;
+
+#ifdef DEBUG_APIC
+    printf("APIC write: %08x = %08x\n", (uint32_t)addr, val);
+#endif
+
+    index = (addr >> 4) & 0xff;
+    switch(index) {
+    case 0x02:
+        s->id = (val >> 24);
+        break;
+    case 0x03:
+        break;
+    case 0x08:
+        s->tpr = val;
+        apic_update_irq(s);
+        break;
+    case 0x09:
+    case 0x0a:
+        break;
+    case 0x0b: /* EOI */
+        apic_eoi(s);
+        break;
+    case 0x0d:
+        s->log_dest = val >> 24;
+        break;
+    case 0x0e:
+        s->dest_mode = val >> 28;
+        break;
+    case 0x0f:
+        s->spurious_vec = val & 0x1ff;
+        apic_update_irq(s);
+        break;
+    case 0x10 ... 0x17:
+    case 0x18 ... 0x1f:
+    case 0x20 ... 0x27:
+    case 0x28:
+        break;
+    case 0x30:
+        s->icr[0] = val;
+        apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
+                     (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff),
+                     (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1);
+        break;
+    case 0x31:
+        s->icr[1] = val;
+        break;
+    case 0x32 ... 0x37:
+        {
+            int n = index - 0x32;
+            s->lvt[n] = val;
+            if (n == APIC_LVT_TIMER)
+                apic_timer_update(s, qemu_get_clock(vm_clock));
+        }
+        break;
+    case 0x38:
+        s->initial_count = val;
+        s->initial_count_load_time = qemu_get_clock(vm_clock);
+        apic_timer_update(s, s->initial_count_load_time);
+        break;
+    case 0x39:
+        break;
+    case 0x3e:
+        {
+            int v;
+            s->divide_conf = val & 0xb;
+            v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
+            s->count_shift = (v + 1) & 7;
+        }
+        break;
+    default:
+        s->esr |= ESR_ILLEGAL_ADDRESS;
+        break;
+    }
+}
+
+static void apic_save(QEMUFile *f, void *opaque)
+{
+    APICState *s = opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->apicbase);
+    qemu_put_8s(f, &s->id);
+    qemu_put_8s(f, &s->arb_id);
+    qemu_put_8s(f, &s->tpr);
+    qemu_put_be32s(f, &s->spurious_vec);
+    qemu_put_8s(f, &s->log_dest);
+    qemu_put_8s(f, &s->dest_mode);
+    for (i = 0; i < 8; i++) {
+        qemu_put_be32s(f, &s->isr[i]);
+        qemu_put_be32s(f, &s->tmr[i]);
+        qemu_put_be32s(f, &s->irr[i]);
+    }
+    for (i = 0; i < APIC_LVT_NB; i++) {
+        qemu_put_be32s(f, &s->lvt[i]);
+    }
+    qemu_put_be32s(f, &s->esr);
+    qemu_put_be32s(f, &s->icr[0]);
+    qemu_put_be32s(f, &s->icr[1]);
+    qemu_put_be32s(f, &s->divide_conf);
+    qemu_put_be32(f, s->count_shift);
+    qemu_put_be32s(f, &s->initial_count);
+    qemu_put_be64(f, s->initial_count_load_time);
+    qemu_put_be64(f, s->next_time);
+
+    qemu_put_timer(f, s->timer);
+}
+
+static int apic_load(QEMUFile *f, void *opaque, int version_id)
+{
+    APICState *s = opaque;
+    int i;
+
+    if (version_id > 2)
+        return -EINVAL;
+
+    /* XXX: what if the base changes? (registered memory regions) */
+    qemu_get_be32s(f, &s->apicbase);
+    qemu_get_8s(f, &s->id);
+    qemu_get_8s(f, &s->arb_id);
+    qemu_get_8s(f, &s->tpr);
+    qemu_get_be32s(f, &s->spurious_vec);
+    qemu_get_8s(f, &s->log_dest);
+    qemu_get_8s(f, &s->dest_mode);
+    for (i = 0; i < 8; i++) {
+        qemu_get_be32s(f, &s->isr[i]);
+        qemu_get_be32s(f, &s->tmr[i]);
+        qemu_get_be32s(f, &s->irr[i]);
+    }
+    for (i = 0; i < APIC_LVT_NB; i++) {
+        qemu_get_be32s(f, &s->lvt[i]);
+    }
+    qemu_get_be32s(f, &s->esr);
+    qemu_get_be32s(f, &s->icr[0]);
+    qemu_get_be32s(f, &s->icr[1]);
+    qemu_get_be32s(f, &s->divide_conf);
+    s->count_shift=qemu_get_be32(f);
+    qemu_get_be32s(f, &s->initial_count);
+    s->initial_count_load_time=qemu_get_be64(f);
+    s->next_time=qemu_get_be64(f);
+
+    if (version_id >= 2)
+        qemu_get_timer(f, s->timer);
+    return 0;
+}
+
+static void apic_reset(void *opaque)
+{
+    APICState *s = opaque;
+    int bsp = cpu_is_bsp(s->cpu_env);
+
+    s->apicbase = 0xfee00000 |
+        (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
+
+    cpu_reset(s->cpu_env);
+    apic_init_reset(s->cpu_env);
+
+    if (bsp) {
+        /*
+         * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization
+         * time typically by BIOS, so PIC interrupt can be delivered to the
+         * processor when local APIC is enabled.
+         */
+        s->lvt[APIC_LVT_LINT0] = 0x700;
+    }
+}
+
+static CPUReadMemoryFunc *apic_mem_read[3] = {
+    apic_mem_readb,
+    apic_mem_readw,
+    apic_mem_readl,
+};
+
+static CPUWriteMemoryFunc *apic_mem_write[3] = {
+    apic_mem_writeb,
+    apic_mem_writew,
+    apic_mem_writel,
+};
+
+int apic_init(CPUState *env)
+{
+    APICState *s;
+
+    if (last_apic_idx >= MAX_APICS)
+        return -1;
+    s = qemu_mallocz(sizeof(APICState));
+    env->apic_state = s;
+    s->idx = last_apic_idx++;
+    s->id = env->cpuid_apic_id;
+    s->cpu_env = env;
+
+    apic_reset(s);
+
+    /* XXX: mapping more APICs at the same memory location */
+    if (apic_io_memory == 0) {
+        /* NOTE: the APIC is directly connected to the CPU - it is not
+           on the global memory bus. */
+        apic_io_memory = cpu_register_io_memory(apic_mem_read,
+                                                apic_mem_write, NULL);
+        cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000,
+                                     apic_io_memory);
+    }
+    s->timer = qemu_new_timer(vm_clock, apic_timer, s);
+
+    register_savevm("apic", s->idx, 2, apic_save, apic_load, s);
+    qemu_register_reset(apic_reset, 0, s);
+
+    local_apics[s->idx] = s;
+    return 0;
+}
+
diff --git a/hw/fdc.h b/hw/fdc.h
new file mode 100644
index 0000000..7b6a9de
--- /dev/null
+++ b/hw/fdc.h
@@ -0,0 +1,11 @@
+/* fdc.c */
+#define MAX_FD 2
+
+typedef struct fdctrl_t fdctrl_t;
+
+fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped,
+                       target_phys_addr_t io_base,
+                       BlockDriverState **fds);
+fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
+                             BlockDriverState **fds, qemu_irq *fdc_tc);
+int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num);
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
new file mode 100644
index 0000000..276c396
--- /dev/null
+++ b/hw/fw_cfg.c
@@ -0,0 +1,288 @@
+/*
+ * QEMU Firmware configuration device emulation
+ *
+ * Copyright (c) 2008 Gleb Natapov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "sysemu.h"
+#include "isa.h"
+#include "fw_cfg.h"
+
+/* debug firmware config */
+//#define DEBUG_FW_CFG
+
+#ifdef DEBUG_FW_CFG
+#define FW_CFG_DPRINTF(fmt, ...)                        \
+    do { printf("FW_CFG: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define FW_CFG_DPRINTF(fmt, ...)
+#endif
+
+#define FW_CFG_SIZE 2
+
+typedef struct _FWCfgEntry {
+    uint16_t len;
+    uint8_t *data;
+    void *callback_opaque;
+    FWCfgCallback callback;
+} FWCfgEntry;
+
+typedef struct _FWCfgState {
+    FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
+    uint16_t cur_entry;
+    uint16_t cur_offset;
+} FWCfgState;
+
+static void fw_cfg_write(FWCfgState *s, uint8_t value)
+{
+    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+
+    FW_CFG_DPRINTF("write %d\n", value);
+
+    if (s->cur_entry & FW_CFG_WRITE_CHANNEL && s->cur_offset < e->len) {
+        e->data[s->cur_offset++] = value;
+        if (s->cur_offset == e->len) {
+            e->callback(e->callback_opaque, e->data);
+            s->cur_offset = 0;
+        }
+    }
+}
+
+static int fw_cfg_select(FWCfgState *s, uint16_t key)
+{
+    int ret;
+
+    s->cur_offset = 0;
+    if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
+        s->cur_entry = FW_CFG_INVALID;
+        ret = 0;
+    } else {
+        s->cur_entry = key;
+        ret = 1;
+    }
+
+    FW_CFG_DPRINTF("select key %d (%sfound)\n", key, ret ? "" : "not ");
+
+    return ret;
+}
+
+static uint8_t fw_cfg_read(FWCfgState *s)
+{
+    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+    uint8_t ret;
+
+    if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
+        ret = 0;
+    else
+        ret = e->data[s->cur_offset++];
+
+    FW_CFG_DPRINTF("read %d\n", ret);
+
+    return ret;
+}
+
+static uint32_t fw_cfg_io_readb(void *opaque, uint32_t addr)
+{
+    return fw_cfg_read(opaque);
+}
+
+static void fw_cfg_io_writeb(void *opaque, uint32_t addr, uint32_t value)
+{
+    fw_cfg_write(opaque, (uint8_t)value);
+}
+
+static void fw_cfg_io_writew(void *opaque, uint32_t addr, uint32_t value)
+{
+    fw_cfg_select(opaque, (uint16_t)value);
+}
+
+static uint32_t fw_cfg_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    return fw_cfg_read(opaque);
+}
+
+static void fw_cfg_mem_writeb(void *opaque, target_phys_addr_t addr,
+                              uint32_t value)
+{
+    fw_cfg_write(opaque, (uint8_t)value);
+}
+
+static void fw_cfg_mem_writew(void *opaque, target_phys_addr_t addr,
+                              uint32_t value)
+{
+    fw_cfg_select(opaque, (uint16_t)value);
+}
+
+static CPUReadMemoryFunc *fw_cfg_ctl_mem_read[3] = {
+    NULL,
+    NULL,
+    NULL,
+};
+
+static CPUWriteMemoryFunc *fw_cfg_ctl_mem_write[3] = {
+    NULL,
+    fw_cfg_mem_writew,
+    NULL,
+};
+
+static CPUReadMemoryFunc *fw_cfg_data_mem_read[3] = {
+    fw_cfg_mem_readb,
+    NULL,
+    NULL,
+};
+
+static CPUWriteMemoryFunc *fw_cfg_data_mem_write[3] = {
+    fw_cfg_mem_writeb,
+    NULL,
+    NULL,
+};
+
+static void fw_cfg_reset(void *opaque)
+{
+    FWCfgState *s = opaque;
+
+    fw_cfg_select(s, 0);
+}
+
+static void fw_cfg_save(QEMUFile *f, void *opaque)
+{
+    FWCfgState *s = opaque;
+
+    qemu_put_be16s(f, &s->cur_entry);
+    qemu_put_be16s(f, &s->cur_offset);
+}
+
+static int fw_cfg_load(QEMUFile *f, void *opaque, int version_id)
+{
+    FWCfgState *s = opaque;
+
+    if (version_id > 1)
+        return -EINVAL;
+
+    qemu_get_be16s(f, &s->cur_entry);
+    qemu_get_be16s(f, &s->cur_offset);
+
+    return 0;
+}
+
+int fw_cfg_add_bytes(void *opaque, uint16_t key, uint8_t *data, uint16_t len)
+{
+    FWCfgState *s = opaque;
+    int arch = !!(key & FW_CFG_ARCH_LOCAL);
+
+    key &= FW_CFG_ENTRY_MASK;
+
+    if (key >= FW_CFG_MAX_ENTRY)
+        return 0;
+
+    s->entries[arch][key].data = data;
+    s->entries[arch][key].len = len;
+
+    return 1;
+}
+
+int fw_cfg_add_i16(void *opaque, uint16_t key, uint16_t value)
+{
+    uint16_t *copy;
+
+    copy = qemu_malloc(sizeof(value));
+    *copy = cpu_to_le16(value);
+    return fw_cfg_add_bytes(opaque, key, (uint8_t *)copy, sizeof(value));
+}
+
+int fw_cfg_add_i32(void *opaque, uint16_t key, uint32_t value)
+{
+    uint32_t *copy;
+
+    copy = qemu_malloc(sizeof(value));
+    *copy = cpu_to_le32(value);
+    return fw_cfg_add_bytes(opaque, key, (uint8_t *)copy, sizeof(value));
+}
+
+int fw_cfg_add_i64(void *opaque, uint16_t key, uint64_t value)
+{
+    uint64_t *copy;
+
+    copy = qemu_malloc(sizeof(value));
+    *copy = cpu_to_le64(value);
+    return fw_cfg_add_bytes(opaque, key, (uint8_t *)copy, sizeof(value));
+}
+
+int fw_cfg_add_callback(void *opaque, uint16_t key, FWCfgCallback callback,
+                        void *callback_opaque, uint8_t *data, size_t len)
+{
+    FWCfgState *s = opaque;
+    int arch = !!(key & FW_CFG_ARCH_LOCAL);
+
+    if (!(key & FW_CFG_WRITE_CHANNEL))
+        return 0;
+
+    key &= FW_CFG_ENTRY_MASK;
+
+    if (key >= FW_CFG_MAX_ENTRY || len > 65535)
+        return 0;
+
+    s->entries[arch][key].data = data;
+    s->entries[arch][key].len = len;
+    s->entries[arch][key].callback_opaque = callback_opaque;
+    s->entries[arch][key].callback = callback;
+
+    return 1;
+}
+
+void *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
+		target_phys_addr_t ctl_addr, target_phys_addr_t data_addr)
+{
+    FWCfgState *s;
+    int io_ctl_memory, io_data_memory;
+
+    s = qemu_mallocz(sizeof(FWCfgState));
+
+    if (ctl_port) {
+        register_ioport_write(ctl_port, 2, 2, fw_cfg_io_writew, s);
+    }
+    if (data_port) {
+        register_ioport_read(data_port, 1, 1, fw_cfg_io_readb, s);
+        register_ioport_write(data_port, 1, 1, fw_cfg_io_writeb, s);
+    }
+    if (ctl_addr) {
+        io_ctl_memory = cpu_register_io_memory(fw_cfg_ctl_mem_read,
+                                           fw_cfg_ctl_mem_write, s);
+        cpu_register_physical_memory(ctl_addr, FW_CFG_SIZE, io_ctl_memory);
+    }
+    if (data_addr) {
+        io_data_memory = cpu_register_io_memory(fw_cfg_data_mem_read,
+                                           fw_cfg_data_mem_write, s);
+        cpu_register_physical_memory(data_addr, FW_CFG_SIZE, io_data_memory);
+    }
+    fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (uint8_t *)"QEMU", 4);
+    fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
+    fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
+    fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
+
+    register_savevm("fw_cfg", -1, 1, fw_cfg_save, fw_cfg_load, s);
+    qemu_register_reset(fw_cfg_reset, 0, s);
+    fw_cfg_reset(s);
+
+    return s;
+}
diff --git a/hw/fw_cfg.h b/hw/fw_cfg.h
new file mode 100644
index 0000000..f616ed2
--- /dev/null
+++ b/hw/fw_cfg.h
@@ -0,0 +1,40 @@
+#ifndef FW_CFG_H
+#define FW_CFG_H
+
+#define FW_CFG_SIGNATURE        0x00
+#define FW_CFG_ID               0x01
+#define FW_CFG_UUID             0x02
+#define FW_CFG_RAM_SIZE         0x03
+#define FW_CFG_NOGRAPHIC        0x04
+#define FW_CFG_NB_CPUS          0x05
+#define FW_CFG_MACHINE_ID       0x06
+#define FW_CFG_KERNEL_ADDR      0x07
+#define FW_CFG_KERNEL_SIZE      0x08
+#define FW_CFG_KERNEL_CMDLINE   0x09
+#define FW_CFG_INITRD_ADDR      0x0a
+#define FW_CFG_INITRD_SIZE      0x0b
+#define FW_CFG_BOOT_DEVICE      0x0c
+#define FW_CFG_NUMA             0x0d
+#define FW_CFG_MAX_ENTRY        0x10
+
+#define FW_CFG_WRITE_CHANNEL    0x4000
+#define FW_CFG_ARCH_LOCAL       0x8000
+#define FW_CFG_ENTRY_MASK       ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
+
+#define FW_CFG_INVALID          0xffff
+
+#ifndef NO_QEMU_PROTOS
+typedef void (*FWCfgCallback)(void *opaque, uint8_t *data);
+
+int fw_cfg_add_bytes(void *opaque, uint16_t key, uint8_t *data, uint16_t len);
+int fw_cfg_add_i16(void *opaque, uint16_t key, uint16_t value);
+int fw_cfg_add_i32(void *opaque, uint16_t key, uint32_t value);
+int fw_cfg_add_i64(void *opaque, uint16_t key, uint64_t value);
+int fw_cfg_add_callback(void *opaque, uint16_t key, FWCfgCallback callback,
+                        void *callback_opaque, uint8_t *data, size_t len);
+void *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
+		target_phys_addr_t crl_addr, target_phys_addr_t data_addr);
+
+#endif /* NO_QEMU_PROTOS */
+
+#endif
diff --git a/hw/i8254.c b/hw/i8254.c
new file mode 100644
index 0000000..c202c9c
--- /dev/null
+++ b/hw/i8254.c
@@ -0,0 +1,507 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qemu-timer.h"
+
+//#define DEBUG_PIT
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+typedef struct PITChannelState {
+    int count; /* can be 65536 */
+    uint16_t latched_count;
+    uint8_t count_latched;
+    uint8_t status_latched;
+    uint8_t status;
+    uint8_t read_state;
+    uint8_t write_state;
+    uint8_t write_latch;
+    uint8_t rw_mode;
+    uint8_t mode;
+    uint8_t bcd; /* not supported */
+    uint8_t gate; /* timer start */
+    int64_t count_load_time;
+    /* irq handling */
+    int64_t next_transition_time;
+    QEMUTimer *irq_timer;
+    qemu_irq irq;
+} PITChannelState;
+
+struct PITState {
+    PITChannelState channels[3];
+};
+
+static PITState pit_state;
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
+
+static int pit_get_count(PITChannelState *s)
+{
+    uint64_t d;
+    int counter;
+
+    d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, get_ticks_per_sec());
+    switch(s->mode) {
+    case 0:
+    case 1:
+    case 4:
+    case 5:
+        counter = (s->count - d) & 0xffff;
+        break;
+    case 3:
+        /* XXX: may be incorrect for odd counts */
+        counter = s->count - ((2 * d) % s->count);
+        break;
+    default:
+        counter = s->count - (d % s->count);
+        break;
+    }
+    return counter;
+}
+
+/* get pit output bit */
+static int pit_get_out1(PITChannelState *s, int64_t current_time)
+{
+    uint64_t d;
+    int out;
+
+    d = muldiv64(current_time - s->count_load_time, PIT_FREQ, get_ticks_per_sec());
+    switch(s->mode) {
+    default:
+    case 0:
+        out = (d >= s->count);
+        break;
+    case 1:
+        out = (d < s->count);
+        break;
+    case 2:
+        if ((d % s->count) == 0 && d != 0)
+            out = 1;
+        else
+            out = 0;
+        break;
+    case 3:
+        out = (d % s->count) < ((s->count + 1) >> 1);
+        break;
+    case 4:
+    case 5:
+        out = (d == s->count);
+        break;
+    }
+    return out;
+}
+
+int pit_get_out(PITState *pit, int channel, int64_t current_time)
+{
+    PITChannelState *s = &pit->channels[channel];
+    return pit_get_out1(s, current_time);
+}
+
+/* return -1 if no transition will occur.  */
+static int64_t pit_get_next_transition_time(PITChannelState *s,
+                                            int64_t current_time)
+{
+    uint64_t d, next_time, base;
+    int period2;
+
+    d = muldiv64(current_time - s->count_load_time, PIT_FREQ, get_ticks_per_sec());
+    switch(s->mode) {
+    default:
+    case 0:
+    case 1:
+        if (d < s->count)
+            next_time = s->count;
+        else
+            return -1;
+        break;
+    case 2:
+        base = (d / s->count) * s->count;
+        if ((d - base) == 0 && d != 0)
+            next_time = base + s->count;
+        else
+            next_time = base + s->count + 1;
+        break;
+    case 3:
+        base = (d / s->count) * s->count;
+        period2 = ((s->count + 1) >> 1);
+        if ((d - base) < period2)
+            next_time = base + period2;
+        else
+            next_time = base + s->count;
+        break;
+    case 4:
+    case 5:
+        if (d < s->count)
+            next_time = s->count;
+        else if (d == s->count)
+            next_time = s->count + 1;
+        else
+            return -1;
+        break;
+    }
+    /* convert to timer units */
+    next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(), PIT_FREQ);
+    /* fix potential rounding problems */
+    /* XXX: better solution: use a clock at PIT_FREQ Hz */
+    if (next_time <= current_time)
+        next_time = current_time + 1;
+    return next_time;
+}
+
+/* val must be 0 or 1 */
+void pit_set_gate(PITState *pit, int channel, int val)
+{
+    PITChannelState *s = &pit->channels[channel];
+
+    switch(s->mode) {
+    default:
+    case 0:
+    case 4:
+        /* XXX: just disable/enable counting */
+        break;
+    case 1:
+    case 5:
+        if (s->gate < val) {
+            /* restart counting on rising edge */
+            s->count_load_time = qemu_get_clock(vm_clock);
+            pit_irq_timer_update(s, s->count_load_time);
+        }
+        break;
+    case 2:
+    case 3:
+        if (s->gate < val) {
+            /* restart counting on rising edge */
+            s->count_load_time = qemu_get_clock(vm_clock);
+            pit_irq_timer_update(s, s->count_load_time);
+        }
+        /* XXX: disable/enable counting */
+        break;
+    }
+    s->gate = val;
+}
+
+int pit_get_gate(PITState *pit, int channel)
+{
+    PITChannelState *s = &pit->channels[channel];
+    return s->gate;
+}
+
+int pit_get_initial_count(PITState *pit, int channel)
+{
+    PITChannelState *s = &pit->channels[channel];
+    return s->count;
+}
+
+int pit_get_mode(PITState *pit, int channel)
+{
+    PITChannelState *s = &pit->channels[channel];
+    return s->mode;
+}
+
+static inline void pit_load_count(PITChannelState *s, int val)
+{
+    if (val == 0)
+        val = 0x10000;
+    s->count_load_time = qemu_get_clock(vm_clock);
+    s->count = val;
+    pit_irq_timer_update(s, s->count_load_time);
+}
+
+/* if already latched, do not latch again */
+static void pit_latch_count(PITChannelState *s)
+{
+    if (!s->count_latched) {
+        s->latched_count = pit_get_count(s);
+        s->count_latched = s->rw_mode;
+    }
+}
+
+static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    PITState *pit = opaque;
+    int channel, access;
+    PITChannelState *s;
+
+    addr &= 3;
+    if (addr == 3) {
+        channel = val >> 6;
+        if (channel == 3) {
+            /* read back command */
+            for(channel = 0; channel < 3; channel++) {
+                s = &pit->channels[channel];
+                if (val & (2 << channel)) {
+                    if (!(val & 0x20)) {
+                        pit_latch_count(s);
+                    }
+                    if (!(val & 0x10) && !s->status_latched) {
+                        /* status latch */
+                        /* XXX: add BCD and null count */
+                        s->status =  (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
+                            (s->rw_mode << 4) |
+                            (s->mode << 1) |
+                            s->bcd;
+                        s->status_latched = 1;
+                    }
+                }
+            }
+        } else {
+            s = &pit->channels[channel];
+            access = (val >> 4) & 3;
+            if (access == 0) {
+                pit_latch_count(s);
+            } else {
+                s->rw_mode = access;
+                s->read_state = access;
+                s->write_state = access;
+
+                s->mode = (val >> 1) & 7;
+                s->bcd = val & 1;
+                /* XXX: update irq timer ? */
+            }
+        }
+    } else {
+        s = &pit->channels[addr];
+        switch(s->write_state) {
+        default:
+        case RW_STATE_LSB:
+            pit_load_count(s, val);
+            break;
+        case RW_STATE_MSB:
+            pit_load_count(s, val << 8);
+            break;
+        case RW_STATE_WORD0:
+            s->write_latch = val;
+            s->write_state = RW_STATE_WORD1;
+            break;
+        case RW_STATE_WORD1:
+            pit_load_count(s, s->write_latch | (val << 8));
+            s->write_state = RW_STATE_WORD0;
+            break;
+        }
+    }
+}
+
+static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
+{
+    PITState *pit = opaque;
+    int ret, count;
+    PITChannelState *s;
+
+    addr &= 3;
+    s = &pit->channels[addr];
+    if (s->status_latched) {
+        s->status_latched = 0;
+        ret = s->status;
+    } else if (s->count_latched) {
+        switch(s->count_latched) {
+        default:
+        case RW_STATE_LSB:
+            ret = s->latched_count & 0xff;
+            s->count_latched = 0;
+            break;
+        case RW_STATE_MSB:
+            ret = s->latched_count >> 8;
+            s->count_latched = 0;
+            break;
+        case RW_STATE_WORD0:
+            ret = s->latched_count & 0xff;
+            s->count_latched = RW_STATE_MSB;
+            break;
+        }
+    } else {
+        switch(s->read_state) {
+        default:
+        case RW_STATE_LSB:
+            count = pit_get_count(s);
+            ret = count & 0xff;
+            break;
+        case RW_STATE_MSB:
+            count = pit_get_count(s);
+            ret = (count >> 8) & 0xff;
+            break;
+        case RW_STATE_WORD0:
+            count = pit_get_count(s);
+            ret = count & 0xff;
+            s->read_state = RW_STATE_WORD1;
+            break;
+        case RW_STATE_WORD1:
+            count = pit_get_count(s);
+            ret = (count >> 8) & 0xff;
+            s->read_state = RW_STATE_WORD0;
+            break;
+        }
+    }
+    return ret;
+}
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
+{
+    int64_t expire_time;
+    int irq_level;
+
+    if (!s->irq_timer)
+        return;
+    expire_time = pit_get_next_transition_time(s, current_time);
+    irq_level = pit_get_out1(s, current_time);
+    qemu_set_irq(s->irq, irq_level);
+#ifdef DEBUG_PIT
+    printf("irq_level=%d next_delay=%f\n",
+           irq_level,
+           (double)(expire_time - current_time) / get_ticks_per_sec());
+#endif
+    s->next_transition_time = expire_time;
+    if (expire_time != -1)
+        qemu_mod_timer(s->irq_timer, expire_time);
+    else
+        qemu_del_timer(s->irq_timer);
+}
+
+static void pit_irq_timer(void *opaque)
+{
+    PITChannelState *s = opaque;
+
+    pit_irq_timer_update(s, s->next_transition_time);
+}
+
+static void pit_save(QEMUFile *f, void *opaque)
+{
+    PITState *pit = opaque;
+    PITChannelState *s;
+    int i;
+
+    for(i = 0; i < 3; i++) {
+        s = &pit->channels[i];
+        qemu_put_be32(f, s->count);
+        qemu_put_be16s(f, &s->latched_count);
+        qemu_put_8s(f, &s->count_latched);
+        qemu_put_8s(f, &s->status_latched);
+        qemu_put_8s(f, &s->status);
+        qemu_put_8s(f, &s->read_state);
+        qemu_put_8s(f, &s->write_state);
+        qemu_put_8s(f, &s->write_latch);
+        qemu_put_8s(f, &s->rw_mode);
+        qemu_put_8s(f, &s->mode);
+        qemu_put_8s(f, &s->bcd);
+        qemu_put_8s(f, &s->gate);
+        qemu_put_be64(f, s->count_load_time);
+        if (s->irq_timer) {
+            qemu_put_be64(f, s->next_transition_time);
+            qemu_put_timer(f, s->irq_timer);
+        }
+    }
+}
+
+static int pit_load(QEMUFile *f, void *opaque, int version_id)
+{
+    PITState *pit = opaque;
+    PITChannelState *s;
+    int i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    for(i = 0; i < 3; i++) {
+        s = &pit->channels[i];
+        s->count=qemu_get_be32(f);
+        qemu_get_be16s(f, &s->latched_count);
+        qemu_get_8s(f, &s->count_latched);
+        qemu_get_8s(f, &s->status_latched);
+        qemu_get_8s(f, &s->status);
+        qemu_get_8s(f, &s->read_state);
+        qemu_get_8s(f, &s->write_state);
+        qemu_get_8s(f, &s->write_latch);
+        qemu_get_8s(f, &s->rw_mode);
+        qemu_get_8s(f, &s->mode);
+        qemu_get_8s(f, &s->bcd);
+        qemu_get_8s(f, &s->gate);
+        s->count_load_time=qemu_get_be64(f);
+        if (s->irq_timer) {
+            s->next_transition_time=qemu_get_be64(f);
+            qemu_get_timer(f, s->irq_timer);
+        }
+    }
+    return 0;
+}
+
+static void pit_reset(void *opaque)
+{
+    PITState *pit = opaque;
+    PITChannelState *s;
+    int i;
+
+    for(i = 0;i < 3; i++) {
+        s = &pit->channels[i];
+        s->mode = 3;
+        s->gate = (i != 2);
+        pit_load_count(s, 0);
+    }
+}
+
+/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
+void hpet_pit_disable(void) {
+    PITChannelState *s;
+    s = &pit_state.channels[0];
+    if (s->irq_timer)
+        qemu_del_timer(s->irq_timer);
+}
+
+/* When HPET is reset or leaving legacy mode, it must reenable i8254
+ * timer 0
+ */
+
+void hpet_pit_enable(void)
+{
+    PITState *pit = &pit_state;
+    PITChannelState *s;
+    s = &pit->channels[0];
+    s->mode = 3;
+    s->gate = 1;
+    pit_load_count(s, 0);
+}
+
+PITState *pit_init(int base, qemu_irq irq)
+{
+    PITState *pit = &pit_state;
+    PITChannelState *s;
+
+    s = &pit->channels[0];
+    /* the timer 0 is connected to an IRQ */
+    s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
+    s->irq = irq;
+
+    register_savevm("i8254", base, 1, pit_save, pit_load, pit);
+
+    qemu_register_reset(pit_reset, 0, pit);
+    register_ioport_write(base, 4, 1, pit_ioport_write, pit);
+    register_ioport_read(base, 3, 1, pit_ioport_read, pit);
+
+    pit_reset(pit);
+
+    return pit;
+}
diff --git a/hw/i8259.c b/hw/i8259.c
new file mode 100644
index 0000000..091ba7a
--- /dev/null
+++ b/hw/i8259.c
@@ -0,0 +1,570 @@
+/*
+ * QEMU 8259 interrupt controller emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "monitor.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+//#define DEBUG_IRQ_LATENCY
+//#define DEBUG_IRQ_COUNT
+
+typedef struct PicState {
+    uint8_t last_irr; /* edge detection */
+    uint8_t irr; /* interrupt request register */
+    uint8_t imr; /* interrupt mask register */
+    uint8_t isr; /* interrupt service register */
+    uint8_t priority_add; /* highest irq priority */
+    uint8_t irq_base;
+    uint8_t read_reg_select;
+    uint8_t poll;
+    uint8_t special_mask;
+    uint8_t init_state;
+    uint8_t auto_eoi;
+    uint8_t rotate_on_auto_eoi;
+    uint8_t special_fully_nested_mode;
+    uint8_t init4; /* true if 4 byte init */
+    uint8_t single_mode; /* true if slave pic is not initialized */
+    uint8_t elcr; /* PIIX edge/trigger selection*/
+    uint8_t elcr_mask;
+    PicState2 *pics_state;
+} PicState;
+
+struct PicState2 {
+    /* 0 is master pic, 1 is slave pic */
+    /* XXX: better separation between the two pics */
+    PicState pics[2];
+    qemu_irq parent_irq;
+    void *irq_request_opaque;
+    /* IOAPIC callback support */
+    SetIRQFunc *alt_irq_func;
+    void *alt_irq_opaque;
+};
+
+#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT)
+static int irq_level[16];
+#endif
+#ifdef DEBUG_IRQ_COUNT
+static uint64_t irq_count[16];
+#endif
+
+/* set irq level. If an edge is detected, then the IRR is set to 1 */
+static inline void pic_set_irq1(PicState *s, int irq, int level)
+{
+    int mask;
+    mask = 1 << irq;
+    if (s->elcr & mask) {
+        /* level triggered */
+        if (level) {
+            s->irr |= mask;
+            s->last_irr |= mask;
+        } else {
+            s->irr &= ~mask;
+            s->last_irr &= ~mask;
+        }
+    } else {
+        /* edge triggered */
+        if (level) {
+            if ((s->last_irr & mask) == 0)
+                s->irr |= mask;
+            s->last_irr |= mask;
+        } else {
+            s->last_irr &= ~mask;
+        }
+    }
+}
+
+/* return the highest priority found in mask (highest = smallest
+   number). Return 8 if no irq */
+static inline int get_priority(PicState *s, int mask)
+{
+    int priority;
+    if (mask == 0)
+        return 8;
+    priority = 0;
+    while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
+        priority++;
+    return priority;
+}
+
+/* return the pic wanted interrupt. return -1 if none */
+static int pic_get_irq(PicState *s)
+{
+    int mask, cur_priority, priority;
+
+    mask = s->irr & ~s->imr;
+    priority = get_priority(s, mask);
+    if (priority == 8)
+        return -1;
+    /* compute current priority. If special fully nested mode on the
+       master, the IRQ coming from the slave is not taken into account
+       for the priority computation. */
+    mask = s->isr;
+    if (s->special_mask)
+        mask &= ~s->imr;
+    if (s->special_fully_nested_mode && s == &s->pics_state->pics[0])
+        mask &= ~(1 << 2);
+    cur_priority = get_priority(s, mask);
+    if (priority < cur_priority) {
+        /* higher priority found: an irq should be generated */
+        return (priority + s->priority_add) & 7;
+    } else {
+        return -1;
+    }
+}
+
+/* raise irq to CPU if necessary. must be called every time the active
+   irq may change */
+/* XXX: should not export it, but it is needed for an APIC kludge */
+void pic_update_irq(PicState2 *s)
+{
+    int irq2, irq;
+
+    /* first look at slave pic */
+    irq2 = pic_get_irq(&s->pics[1]);
+    if (irq2 >= 0) {
+        /* if irq request by slave pic, signal master PIC */
+        pic_set_irq1(&s->pics[0], 2, 1);
+        pic_set_irq1(&s->pics[0], 2, 0);
+    }
+    /* look at requested irq */
+    irq = pic_get_irq(&s->pics[0]);
+    if (irq >= 0) {
+#if defined(DEBUG_PIC)
+        {
+            int i;
+            for(i = 0; i < 2; i++) {
+                printf("pic%d: imr=%x irr=%x padd=%d\n",
+                       i, s->pics[i].imr, s->pics[i].irr,
+                       s->pics[i].priority_add);
+
+            }
+        }
+        printf("pic: cpu_interrupt\n");
+#endif
+        qemu_irq_raise(s->parent_irq);
+    }
+
+/* all targets should do this rather than acking the IRQ in the cpu */
+#if defined(TARGET_MIPS) || defined(TARGET_PPC) || defined(TARGET_ALPHA)
+    else {
+        qemu_irq_lower(s->parent_irq);
+    }
+#endif
+}
+
+#ifdef DEBUG_IRQ_LATENCY
+int64_t irq_time[16];
+#endif
+
+static void i8259_set_irq(void *opaque, int irq, int level)
+{
+    PicState2 *s = opaque;
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+    if (level != irq_level[irq]) {
+#if defined(DEBUG_PIC)
+        printf("i8259_set_irq: irq=%d level=%d\n", irq, level);
+#endif
+        irq_level[irq] = level;
+#ifdef DEBUG_IRQ_COUNT
+	if (level == 1)
+	    irq_count[irq]++;
+#endif
+    }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+    if (level) {
+        irq_time[irq] = qemu_get_clock(vm_clock);
+    }
+#endif
+    pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
+    /* used for IOAPIC irqs */
+    if (s->alt_irq_func)
+        s->alt_irq_func(s->alt_irq_opaque, irq, level);
+    pic_update_irq(s);
+}
+
+/* acknowledge interrupt 'irq' */
+static inline void pic_intack(PicState *s, int irq)
+{
+    if (s->auto_eoi) {
+        if (s->rotate_on_auto_eoi)
+            s->priority_add = (irq + 1) & 7;
+    } else {
+        s->isr |= (1 << irq);
+    }
+    /* We don't clear a level sensitive interrupt here */
+    if (!(s->elcr & (1 << irq)))
+        s->irr &= ~(1 << irq);
+}
+
+int pic_read_irq(PicState2 *s)
+{
+    int irq, irq2, intno;
+
+    irq = pic_get_irq(&s->pics[0]);
+    if (irq >= 0) {
+        pic_intack(&s->pics[0], irq);
+        if (irq == 2) {
+            irq2 = pic_get_irq(&s->pics[1]);
+            if (irq2 >= 0) {
+                pic_intack(&s->pics[1], irq2);
+            } else {
+                /* spurious IRQ on slave controller */
+                irq2 = 7;
+            }
+            intno = s->pics[1].irq_base + irq2;
+            irq = irq2 + 8;
+        } else {
+            intno = s->pics[0].irq_base + irq;
+        }
+    } else {
+        /* spurious IRQ on host controller */
+        irq = 7;
+        intno = s->pics[0].irq_base + irq;
+    }
+    pic_update_irq(s);
+
+#ifdef DEBUG_IRQ_LATENCY
+    printf("IRQ%d latency=%0.3fus\n",
+           irq,
+           (double)(qemu_get_clock(vm_clock) - irq_time[irq]) * 1000000.0 / get_ticks_per_sec);
+#endif
+#if defined(DEBUG_PIC)
+    printf("pic_interrupt: irq=%d\n", irq);
+#endif
+    return intno;
+}
+
+static void pic_reset(void *opaque)
+{
+    PicState *s = opaque;
+
+    s->last_irr = 0;
+    s->irr = 0;
+    s->imr = 0;
+    s->isr = 0;
+    s->priority_add = 0;
+    s->irq_base = 0;
+    s->read_reg_select = 0;
+    s->poll = 0;
+    s->special_mask = 0;
+    s->init_state = 0;
+    s->auto_eoi = 0;
+    s->rotate_on_auto_eoi = 0;
+    s->special_fully_nested_mode = 0;
+    s->init4 = 0;
+    s->single_mode = 0;
+    /* Note: ELCR is not reset */
+}
+
+static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    PicState *s = opaque;
+    int priority, cmd, irq;
+
+#ifdef DEBUG_PIC
+    printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+    addr &= 1;
+    if (addr == 0) {
+        if (val & 0x10) {
+            /* init */
+            pic_reset(s);
+            /* deassert a pending interrupt */
+            qemu_irq_lower(s->pics_state->parent_irq);
+            s->init_state = 1;
+            s->init4 = val & 1;
+            s->single_mode = val & 2;
+            if (val & 0x08)
+                hw_error("level sensitive irq not supported");
+        } else if (val & 0x08) {
+            if (val & 0x04)
+                s->poll = 1;
+            if (val & 0x02)
+                s->read_reg_select = val & 1;
+            if (val & 0x40)
+                s->special_mask = (val >> 5) & 1;
+        } else {
+            cmd = val >> 5;
+            switch(cmd) {
+            case 0:
+            case 4:
+                s->rotate_on_auto_eoi = cmd >> 2;
+                break;
+            case 1: /* end of interrupt */
+            case 5:
+                priority = get_priority(s, s->isr);
+                if (priority != 8) {
+                    irq = (priority + s->priority_add) & 7;
+                    s->isr &= ~(1 << irq);
+                    if (cmd == 5)
+                        s->priority_add = (irq + 1) & 7;
+                    pic_update_irq(s->pics_state);
+                }
+                break;
+            case 3:
+                irq = val & 7;
+                s->isr &= ~(1 << irq);
+                pic_update_irq(s->pics_state);
+                break;
+            case 6:
+                s->priority_add = (val + 1) & 7;
+                pic_update_irq(s->pics_state);
+                break;
+            case 7:
+                irq = val & 7;
+                s->isr &= ~(1 << irq);
+                s->priority_add = (irq + 1) & 7;
+                pic_update_irq(s->pics_state);
+                break;
+            default:
+                /* no operation */
+                break;
+            }
+        }
+    } else {
+        switch(s->init_state) {
+        case 0:
+            /* normal mode */
+            s->imr = val;
+            pic_update_irq(s->pics_state);
+            break;
+        case 1:
+            s->irq_base = val & 0xf8;
+            s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
+            break;
+        case 2:
+            if (s->init4) {
+                s->init_state = 3;
+            } else {
+                s->init_state = 0;
+            }
+            break;
+        case 3:
+            s->special_fully_nested_mode = (val >> 4) & 1;
+            s->auto_eoi = (val >> 1) & 1;
+            s->init_state = 0;
+            break;
+        }
+    }
+}
+
+static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
+{
+    int ret;
+
+    ret = pic_get_irq(s);
+    if (ret >= 0) {
+        if (addr1 >> 7) {
+            s->pics_state->pics[0].isr &= ~(1 << 2);
+            s->pics_state->pics[0].irr &= ~(1 << 2);
+        }
+        s->irr &= ~(1 << ret);
+        s->isr &= ~(1 << ret);
+        if (addr1 >> 7 || ret != 2)
+            pic_update_irq(s->pics_state);
+    } else {
+        ret = 0x07;
+        pic_update_irq(s->pics_state);
+    }
+
+    return ret;
+}
+
+static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
+{
+    PicState *s = opaque;
+    unsigned int addr;
+    int ret;
+
+    addr = addr1;
+    addr &= 1;
+    if (s->poll) {
+        ret = pic_poll_read(s, addr1);
+        s->poll = 0;
+    } else {
+        if (addr == 0) {
+            if (s->read_reg_select)
+                ret = s->isr;
+            else
+                ret = s->irr;
+        } else {
+            ret = s->imr;
+        }
+    }
+#ifdef DEBUG_PIC
+    printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret);
+#endif
+    return ret;
+}
+
+/* memory mapped interrupt status */
+/* XXX: may be the same than pic_read_irq() */
+uint32_t pic_intack_read(PicState2 *s)
+{
+    int ret;
+
+    ret = pic_poll_read(&s->pics[0], 0x00);
+    if (ret == 2)
+        ret = pic_poll_read(&s->pics[1], 0x80) + 8;
+    /* Prepare for ISR read */
+    s->pics[0].read_reg_select = 1;
+
+    return ret;
+}
+
+static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    PicState *s = opaque;
+    s->elcr = val & s->elcr_mask;
+}
+
+static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
+{
+    PicState *s = opaque;
+    return s->elcr;
+}
+
+static void pic_save(QEMUFile *f, void *opaque)
+{
+    PicState *s = opaque;
+
+    qemu_put_8s(f, &s->last_irr);
+    qemu_put_8s(f, &s->irr);
+    qemu_put_8s(f, &s->imr);
+    qemu_put_8s(f, &s->isr);
+    qemu_put_8s(f, &s->priority_add);
+    qemu_put_8s(f, &s->irq_base);
+    qemu_put_8s(f, &s->read_reg_select);
+    qemu_put_8s(f, &s->poll);
+    qemu_put_8s(f, &s->special_mask);
+    qemu_put_8s(f, &s->init_state);
+    qemu_put_8s(f, &s->auto_eoi);
+    qemu_put_8s(f, &s->rotate_on_auto_eoi);
+    qemu_put_8s(f, &s->special_fully_nested_mode);
+    qemu_put_8s(f, &s->init4);
+    qemu_put_8s(f, &s->single_mode);
+    qemu_put_8s(f, &s->elcr);
+}
+
+static int pic_load(QEMUFile *f, void *opaque, int version_id)
+{
+    PicState *s = opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_8s(f, &s->last_irr);
+    qemu_get_8s(f, &s->irr);
+    qemu_get_8s(f, &s->imr);
+    qemu_get_8s(f, &s->isr);
+    qemu_get_8s(f, &s->priority_add);
+    qemu_get_8s(f, &s->irq_base);
+    qemu_get_8s(f, &s->read_reg_select);
+    qemu_get_8s(f, &s->poll);
+    qemu_get_8s(f, &s->special_mask);
+    qemu_get_8s(f, &s->init_state);
+    qemu_get_8s(f, &s->auto_eoi);
+    qemu_get_8s(f, &s->rotate_on_auto_eoi);
+    qemu_get_8s(f, &s->special_fully_nested_mode);
+    qemu_get_8s(f, &s->init4);
+    qemu_get_8s(f, &s->single_mode);
+    qemu_get_8s(f, &s->elcr);
+    return 0;
+}
+
+/* XXX: add generic master/slave system */
+static void pic_init1(int io_addr, int elcr_addr, PicState *s)
+{
+    register_ioport_write(io_addr, 2, 1, pic_ioport_write, s);
+    register_ioport_read(io_addr, 2, 1, pic_ioport_read, s);
+    if (elcr_addr >= 0) {
+        register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s);
+        register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s);
+    }
+    register_savevm("i8259", io_addr, 1, pic_save, pic_load, s);
+    qemu_register_reset(pic_reset, 0, s);
+}
+
+void pic_info(Monitor *mon)
+{
+    int i;
+    PicState *s;
+
+    if (!isa_pic)
+        return;
+
+    for(i=0;i<2;i++) {
+        s = &isa_pic->pics[i];
+        monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
+                       "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
+                       i, s->irr, s->imr, s->isr, s->priority_add,
+                       s->irq_base, s->read_reg_select, s->elcr,
+                       s->special_fully_nested_mode);
+    }
+}
+
+void irq_info(Monitor *mon)
+{
+#ifndef DEBUG_IRQ_COUNT
+    monitor_printf(mon, "irq statistic code not compiled.\n");
+#else
+    int i;
+    int64_t count;
+
+    monitor_printf(mon, "IRQ statistics:\n");
+    for (i = 0; i < 16; i++) {
+        count = irq_count[i];
+        if (count > 0)
+            monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
+    }
+#endif
+}
+
+qemu_irq *i8259_init(qemu_irq parent_irq)
+{
+    PicState2 *s;
+
+    s = qemu_mallocz(sizeof(PicState2));
+    pic_init1(0x20, 0x4d0, &s->pics[0]);
+    pic_init1(0xa0, 0x4d1, &s->pics[1]);
+    s->pics[0].elcr_mask = 0xf8;
+    s->pics[1].elcr_mask = 0xde;
+    s->parent_irq = parent_irq;
+    s->pics[0].pics_state = s;
+    s->pics[1].pics_state = s;
+    isa_pic = s;
+    return qemu_allocate_irqs(i8259_set_irq, s, 16);
+}
+
+void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func,
+                          void *alt_irq_opaque)
+{
+    s->alt_irq_func = alt_irq_func;
+    s->alt_irq_opaque = alt_irq_opaque;
+}
diff --git a/hw/ioapic.c b/hw/ioapic.c
new file mode 100644
index 0000000..b179e6e
--- /dev/null
+++ b/hw/ioapic.c
@@ -0,0 +1,261 @@
+/*
+ *  ioapic.c IOAPIC emulation logic
+ *
+ *  Copyright (c) 2004-2005 Fabrice Bellard
+ *
+ *  Split the ioapic logic from apic.c
+ *  Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+
+//#define DEBUG_IOAPIC
+
+#define IOAPIC_NUM_PINS			0x18
+#define IOAPIC_LVT_MASKED 		(1<<16)
+
+#define IOAPIC_TRIGGER_EDGE		0
+#define IOAPIC_TRIGGER_LEVEL		1
+
+/*io{apic,sapic} delivery mode*/
+#define IOAPIC_DM_FIXED			0x0
+#define IOAPIC_DM_LOWEST_PRIORITY	0x1
+#define IOAPIC_DM_PMI			0x2
+#define IOAPIC_DM_NMI			0x4
+#define IOAPIC_DM_INIT			0x5
+#define IOAPIC_DM_SIPI			0x5
+#define IOAPIC_DM_EXTINT		0x7
+
+struct IOAPICState {
+    uint8_t id;
+    uint8_t ioregsel;
+
+    uint32_t irr;
+    uint64_t ioredtbl[IOAPIC_NUM_PINS];
+};
+
+static void ioapic_service(IOAPICState *s)
+{
+    uint8_t i;
+    uint8_t trig_mode;
+    uint8_t vector;
+    uint8_t delivery_mode;
+    uint32_t mask;
+    uint64_t entry;
+    uint8_t dest;
+    uint8_t dest_mode;
+    uint8_t polarity;
+
+    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+        mask = 1 << i;
+        if (s->irr & mask) {
+            entry = s->ioredtbl[i];
+            if (!(entry & IOAPIC_LVT_MASKED)) {
+                trig_mode = ((entry >> 15) & 1);
+                dest = entry >> 56;
+                dest_mode = (entry >> 11) & 1;
+                delivery_mode = (entry >> 8) & 7;
+                polarity = (entry >> 13) & 1;
+                if (trig_mode == IOAPIC_TRIGGER_EDGE)
+                    s->irr &= ~mask;
+                if (delivery_mode == IOAPIC_DM_EXTINT)
+                    vector = pic_read_irq(isa_pic);
+                else
+                    vector = entry & 0xff;
+
+                apic_deliver_irq(dest, dest_mode, delivery_mode,
+                                 vector, polarity, trig_mode);
+            }
+        }
+    }
+}
+
+void ioapic_set_irq(void *opaque, int vector, int level)
+{
+    IOAPICState *s = opaque;
+
+    /* ISA IRQs map to GSI 1-1 except for IRQ0 which maps
+     * to GSI 2.  GSI maps to ioapic 1-1.  This is not
+     * the cleanest way of doing it but it should work. */
+
+    if (vector == 0)
+        vector = 2;
+
+    if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
+        uint32_t mask = 1 << vector;
+        uint64_t entry = s->ioredtbl[vector];
+
+        if ((entry >> 15) & 1) {
+            /* level triggered */
+            if (level) {
+                s->irr |= mask;
+                ioapic_service(s);
+            } else {
+                s->irr &= ~mask;
+            }
+        } else {
+            /* edge triggered */
+            if (level) {
+                s->irr |= mask;
+                ioapic_service(s);
+            }
+        }
+    }
+}
+
+static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    IOAPICState *s = opaque;
+    int index;
+    uint32_t val = 0;
+
+    addr &= 0xff;
+    if (addr == 0x00) {
+        val = s->ioregsel;
+    } else if (addr == 0x10) {
+        switch (s->ioregsel) {
+            case 0x00:
+                val = s->id << 24;
+                break;
+            case 0x01:
+                val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */
+                break;
+            case 0x02:
+                val = 0;
+                break;
+            default:
+                index = (s->ioregsel - 0x10) >> 1;
+                if (index >= 0 && index < IOAPIC_NUM_PINS) {
+                    if (s->ioregsel & 1)
+                        val = s->ioredtbl[index] >> 32;
+                    else
+                        val = s->ioredtbl[index] & 0xffffffff;
+                }
+        }
+#ifdef DEBUG_IOAPIC
+        printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val);
+#endif
+    }
+    return val;
+}
+
+static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    IOAPICState *s = opaque;
+    int index;
+
+    addr &= 0xff;
+    if (addr == 0x00)  {
+        s->ioregsel = val;
+        return;
+    } else if (addr == 0x10) {
+#ifdef DEBUG_IOAPIC
+        printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val);
+#endif
+        switch (s->ioregsel) {
+            case 0x00:
+                s->id = (val >> 24) & 0xff;
+                return;
+            case 0x01:
+            case 0x02:
+                return;
+            default:
+                index = (s->ioregsel - 0x10) >> 1;
+                if (index >= 0 && index < IOAPIC_NUM_PINS) {
+                    if (s->ioregsel & 1) {
+                        s->ioredtbl[index] &= 0xffffffff;
+                        s->ioredtbl[index] |= (uint64_t)val << 32;
+                    } else {
+                        s->ioredtbl[index] &= ~0xffffffffULL;
+                        s->ioredtbl[index] |= val;
+                    }
+                    ioapic_service(s);
+                }
+        }
+    }
+}
+
+static void ioapic_save(QEMUFile *f, void *opaque)
+{
+    IOAPICState *s = opaque;
+    int i;
+
+    qemu_put_8s(f, &s->id);
+    qemu_put_8s(f, &s->ioregsel);
+    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+        qemu_put_be64s(f, &s->ioredtbl[i]);
+    }
+}
+
+static int ioapic_load(QEMUFile *f, void *opaque, int version_id)
+{
+    IOAPICState *s = opaque;
+    int i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_8s(f, &s->id);
+    qemu_get_8s(f, &s->ioregsel);
+    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+        qemu_get_be64s(f, &s->ioredtbl[i]);
+    }
+    return 0;
+}
+
+static void ioapic_reset(void *opaque)
+{
+    IOAPICState *s = opaque;
+    int i;
+
+    memset(s, 0, sizeof(*s));
+    for(i = 0; i < IOAPIC_NUM_PINS; i++)
+        s->ioredtbl[i] = 1 << 16; /* mask LVT */
+}
+
+static CPUReadMemoryFunc *ioapic_mem_read[3] = {
+    ioapic_mem_readl,
+    ioapic_mem_readl,
+    ioapic_mem_readl,
+};
+
+static CPUWriteMemoryFunc *ioapic_mem_write[3] = {
+    ioapic_mem_writel,
+    ioapic_mem_writel,
+    ioapic_mem_writel,
+};
+
+IOAPICState *ioapic_init(void)
+{
+    IOAPICState *s;
+    int io_memory;
+
+    s = qemu_mallocz(sizeof(IOAPICState));
+    ioapic_reset(s);
+
+    io_memory = cpu_register_io_memory(ioapic_mem_read,
+                                       ioapic_mem_write, s);
+    cpu_register_physical_memory(0xfec00000, 0x1000, io_memory);
+
+    register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s);
+    qemu_register_reset(ioapic_reset, 0, s);
+
+    return s;
+}
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
new file mode 100644
index 0000000..f93a3cb
--- /dev/null
+++ b/hw/mc146818rtc.c
@@ -0,0 +1,754 @@
+/*
+ * QEMU MC146818 RTC emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "pc.h"
+#include "isa.h"
+//#include "hpet_emul.h"
+
+//#define DEBUG_CMOS
+
+#define RTC_SECONDS             0
+#define RTC_SECONDS_ALARM       1
+#define RTC_MINUTES             2
+#define RTC_MINUTES_ALARM       3
+#define RTC_HOURS               4
+#define RTC_HOURS_ALARM         5
+#define RTC_ALARM_DONT_CARE    0xC0
+
+#define RTC_DAY_OF_WEEK         6
+#define RTC_DAY_OF_MONTH        7
+#define RTC_MONTH               8
+#define RTC_YEAR                9
+
+#define RTC_REG_A               10
+#define RTC_REG_B               11
+#define RTC_REG_C               12
+#define RTC_REG_D               13
+
+#define REG_A_UIP 0x80
+
+#define REG_B_SET  0x80
+#define REG_B_PIE  0x40
+#define REG_B_AIE  0x20
+#define REG_B_UIE  0x10
+#define REG_B_SQWE 0x08
+#define REG_B_DM   0x04
+
+#define REG_C_UF   0x10
+#define REG_C_IRQF 0x80
+#define REG_C_PF   0x40
+#define REG_C_AF   0x20
+
+struct RTCState {
+    uint8_t cmos_data[128];
+    uint8_t cmos_index;
+    struct tm current_tm;
+    int base_year;
+    qemu_irq irq;
+    qemu_irq sqw_irq;
+    int it_shift;
+    /* periodic timer */
+    QEMUTimer *periodic_timer;
+    int64_t next_periodic_time;
+    /* second update */
+    int64_t next_second_time;
+#ifdef TARGET_I386
+    uint32_t irq_coalesced;
+    uint32_t period;
+    QEMUTimer *coalesced_timer;
+#endif
+    QEMUTimer *second_timer;
+    QEMUTimer *second_timer2;
+};
+
+static void rtc_irq_raise(qemu_irq irq) {
+    /* When HPET is operating in legacy mode, RTC interrupts are disabled
+     * We block qemu_irq_raise, but not qemu_irq_lower, in case legacy
+     * mode is established while interrupt is raised. We want it to
+     * be lowered in any case
+     */
+#ifndef CONFIG_ANDROID
+#if defined TARGET_I386 || defined TARGET_X86_64
+    if (!hpet_in_legacy_mode())
+#endif
+#endif
+        qemu_irq_raise(irq);
+}
+
+static void rtc_set_time(RTCState *s);
+static void rtc_copy_date(RTCState *s);
+
+#ifdef TARGET_I386
+static void rtc_coalesced_timer_update(RTCState *s)
+{
+    if (s->irq_coalesced == 0) {
+        qemu_del_timer(s->coalesced_timer);
+    } else {
+        /* divide each RTC interval to 2 - 8 smaller intervals */
+        int c = MIN(s->irq_coalesced, 7) + 1; 
+        int64_t next_clock = qemu_get_clock(vm_clock) +
+		muldiv64(s->period / c, get_ticks_per_sec(), 32768);
+        qemu_mod_timer(s->coalesced_timer, next_clock);
+    }
+}
+
+static void rtc_coalesced_timer(void *opaque)
+{
+    RTCState *s = opaque;
+
+    if (s->irq_coalesced != 0) {
+        apic_reset_irq_delivered();
+        s->cmos_data[RTC_REG_C] |= 0xc0;
+        rtc_irq_raise(s->irq);
+        if (apic_get_irq_delivered()) {
+            s->irq_coalesced--;
+        }
+    }
+
+    rtc_coalesced_timer_update(s);
+}
+#endif
+
+static void rtc_timer_update(RTCState *s, int64_t current_time)
+{
+    int period_code, period;
+    int64_t cur_clock, next_irq_clock;
+    int enable_pie;
+
+    period_code = s->cmos_data[RTC_REG_A] & 0x0f;
+#ifndef CONFIG_ANDROID
+#if defined TARGET_I386 || defined TARGET_X86_64
+    /* disable periodic timer if hpet is in legacy mode, since interrupts are
+     * disabled anyway.
+     */
+    enable_pie = !hpet_in_legacy_mode();
+#else
+    enable_pie = 1;
+#endif
+#endif
+    enable_pie = 1;
+	
+    if (period_code != 0
+        && (((s->cmos_data[RTC_REG_B] & REG_B_PIE) && enable_pie)
+            || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) {
+        if (period_code <= 2)
+            period_code += 7;
+        /* period in 32 Khz cycles */
+        period = 1 << (period_code - 1);
+#ifdef TARGET_I386
+        if(period != s->period)
+            s->irq_coalesced = (s->irq_coalesced * s->period) / period;
+        s->period = period;
+#endif
+        /* compute 32 khz clock */
+        cur_clock = muldiv64(current_time, 32768, get_ticks_per_sec());
+        next_irq_clock = (cur_clock & ~(period - 1)) + period;
+        s->next_periodic_time = muldiv64(next_irq_clock, get_ticks_per_sec(), 32768) + 1;
+        qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
+    } else {
+#ifdef TARGET_I386
+        s->irq_coalesced = 0;
+#endif
+        qemu_del_timer(s->periodic_timer);
+    }
+}
+
+static void rtc_periodic_timer(void *opaque)
+{
+    RTCState *s = opaque;
+
+    rtc_timer_update(s, s->next_periodic_time);
+    if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
+        s->cmos_data[RTC_REG_C] |= 0xc0;
+#ifdef TARGET_I386
+        if(rtc_td_hack) {
+            apic_reset_irq_delivered();
+            rtc_irq_raise(s->irq);
+            if (!apic_get_irq_delivered()) {
+                s->irq_coalesced++;
+                rtc_coalesced_timer_update(s);
+            }
+        } else
+#endif
+        rtc_irq_raise(s->irq);
+    }
+    if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) {
+        /* Not square wave at all but we don't want 2048Hz interrupts!
+           Must be seen as a pulse.  */
+        qemu_irq_raise(s->sqw_irq);
+    }
+}
+
+static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    RTCState *s = opaque;
+
+    if ((addr & 1) == 0) {
+        s->cmos_index = data & 0x7f;
+    } else {
+#ifdef DEBUG_CMOS
+        printf("cmos: write index=0x%02x val=0x%02x\n",
+               s->cmos_index, data);
+#endif
+        switch(s->cmos_index) {
+        case RTC_SECONDS_ALARM:
+        case RTC_MINUTES_ALARM:
+        case RTC_HOURS_ALARM:
+            /* XXX: not supported */
+            s->cmos_data[s->cmos_index] = data;
+            break;
+        case RTC_SECONDS:
+        case RTC_MINUTES:
+        case RTC_HOURS:
+        case RTC_DAY_OF_WEEK:
+        case RTC_DAY_OF_MONTH:
+        case RTC_MONTH:
+        case RTC_YEAR:
+            s->cmos_data[s->cmos_index] = data;
+            /* if in set mode, do not update the time */
+            if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+                rtc_set_time(s);
+            }
+            break;
+        case RTC_REG_A:
+            /* UIP bit is read only */
+            s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
+                (s->cmos_data[RTC_REG_A] & REG_A_UIP);
+            rtc_timer_update(s, qemu_get_clock(vm_clock));
+            break;
+        case RTC_REG_B:
+            if (data & REG_B_SET) {
+                /* set mode: reset UIP mode */
+                s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+                data &= ~REG_B_UIE;
+            } else {
+                /* if disabling set mode, update the time */
+                if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+                    rtc_set_time(s);
+                }
+            }
+            s->cmos_data[RTC_REG_B] = data;
+            rtc_timer_update(s, qemu_get_clock(vm_clock));
+            break;
+        case RTC_REG_C:
+        case RTC_REG_D:
+            /* cannot write to them */
+            break;
+        default:
+            s->cmos_data[s->cmos_index] = data;
+            break;
+        }
+    }
+}
+
+static inline int rtc_to_bcd(RTCState *s, int a)
+{
+    if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
+        return a;
+    } else {
+        return ((a / 10) << 4) | (a % 10);
+    }
+}
+
+static inline int rtc_from_bcd(RTCState *s, int a)
+{
+    if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
+        return a;
+    } else {
+        return ((a >> 4) * 10) + (a & 0x0f);
+    }
+}
+
+static void rtc_set_time(RTCState *s)
+{
+    struct tm *tm = &s->current_tm;
+
+    tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
+    tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
+    tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
+    if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
+        (s->cmos_data[RTC_HOURS] & 0x80)) {
+        tm->tm_hour += 12;
+    }
+    tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1;
+    tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
+    tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
+    tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900;
+}
+
+static void rtc_copy_date(RTCState *s)
+{
+    const struct tm *tm = &s->current_tm;
+    int year;
+
+    s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec);
+    s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min);
+    if (s->cmos_data[RTC_REG_B] & 0x02) {
+        /* 24 hour format */
+        s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour);
+    } else {
+        /* 12 hour format */
+        s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour % 12);
+        if (tm->tm_hour >= 12)
+            s->cmos_data[RTC_HOURS] |= 0x80;
+    }
+    s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1);
+    s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday);
+    s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1);
+    year = (tm->tm_year - s->base_year) % 100;
+    if (year < 0)
+        year += 100;
+    s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year);
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+    static const int days_tab[12] = {
+        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    int d;
+    if ((unsigned )month >= 12)
+        return 31;
+    d = days_tab[month];
+    if (month == 1) {
+        if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
+            d++;
+    }
+    return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+    int days_in_month;
+
+    tm->tm_sec++;
+    if ((unsigned)tm->tm_sec >= 60) {
+        tm->tm_sec = 0;
+        tm->tm_min++;
+        if ((unsigned)tm->tm_min >= 60) {
+            tm->tm_min = 0;
+            tm->tm_hour++;
+            if ((unsigned)tm->tm_hour >= 24) {
+                tm->tm_hour = 0;
+                /* next day */
+                tm->tm_wday++;
+                if ((unsigned)tm->tm_wday >= 7)
+                    tm->tm_wday = 0;
+                days_in_month = get_days_in_month(tm->tm_mon,
+                                                  tm->tm_year + 1900);
+                tm->tm_mday++;
+                if (tm->tm_mday < 1) {
+                    tm->tm_mday = 1;
+                } else if (tm->tm_mday > days_in_month) {
+                    tm->tm_mday = 1;
+                    tm->tm_mon++;
+                    if (tm->tm_mon >= 12) {
+                        tm->tm_mon = 0;
+                        tm->tm_year++;
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+static void rtc_update_second(void *opaque)
+{
+    RTCState *s = opaque;
+    int64_t delay;
+
+    /* if the oscillator is not in normal operation, we do not update */
+    if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
+        s->next_second_time += get_ticks_per_sec();
+        qemu_mod_timer(s->second_timer, s->next_second_time);
+    } else {
+        rtc_next_second(&s->current_tm);
+
+        if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+            /* update in progress bit */
+            s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+        }
+        /* should be 244 us = 8 / 32768 seconds, but currently the
+           timers do not have the necessary resolution. */
+        delay = (get_ticks_per_sec() * 1) / 100;
+        if (delay < 1)
+            delay = 1;
+        qemu_mod_timer(s->second_timer2,
+                       s->next_second_time + delay);
+    }
+}
+
+static void rtc_update_second2(void *opaque)
+{
+    RTCState *s = opaque;
+
+    if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+        rtc_copy_date(s);
+    }
+
+    /* check alarm */
+    if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+        if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
+             s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
+            ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
+             s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
+            ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
+             s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
+
+            s->cmos_data[RTC_REG_C] |= 0xa0;
+            rtc_irq_raise(s->irq);
+        }
+    }
+
+    /* update ended interrupt */
+    if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
+        s->cmos_data[RTC_REG_C] |= 0x90;
+        rtc_irq_raise(s->irq);
+    }
+
+    /* clear update in progress bit */
+    s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+
+    s->next_second_time += get_ticks_per_sec();
+    qemu_mod_timer(s->second_timer, s->next_second_time);
+}
+
+static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
+{
+    RTCState *s = opaque;
+    int ret;
+    if ((addr & 1) == 0) {
+        return 0xff;
+    } else {
+        switch(s->cmos_index) {
+        case RTC_SECONDS:
+        case RTC_MINUTES:
+        case RTC_HOURS:
+        case RTC_DAY_OF_WEEK:
+        case RTC_DAY_OF_MONTH:
+        case RTC_MONTH:
+        case RTC_YEAR:
+            ret = s->cmos_data[s->cmos_index];
+            break;
+        case RTC_REG_A:
+            ret = s->cmos_data[s->cmos_index];
+            break;
+        case RTC_REG_C:
+            ret = s->cmos_data[s->cmos_index];
+            qemu_irq_lower(s->irq);
+            s->cmos_data[RTC_REG_C] = 0x00;
+            break;
+        default:
+            ret = s->cmos_data[s->cmos_index];
+            break;
+        }
+#ifdef DEBUG_CMOS
+        printf("cmos: read index=0x%02x val=0x%02x\n",
+               s->cmos_index, ret);
+#endif
+        return ret;
+    }
+}
+
+void rtc_set_memory(RTCState *s, int addr, int val)
+{
+    if (addr >= 0 && addr <= 127)
+        s->cmos_data[addr] = val;
+}
+
+void rtc_set_date(RTCState *s, const struct tm *tm)
+{
+    s->current_tm = *tm;
+    rtc_copy_date(s);
+}
+
+/* PC cmos mappings */
+#define REG_IBM_CENTURY_BYTE        0x32
+#define REG_IBM_PS2_CENTURY_BYTE    0x37
+
+static void rtc_set_date_from_host(RTCState *s)
+{
+    struct tm tm;
+    int val;
+
+    /* set the CMOS date */
+    qemu_get_timedate(&tm, 0);
+    rtc_set_date(s, &tm);
+
+    val = rtc_to_bcd(s, (tm.tm_year / 100) + 19);
+    rtc_set_memory(s, REG_IBM_CENTURY_BYTE, val);
+    rtc_set_memory(s, REG_IBM_PS2_CENTURY_BYTE, val);
+}
+
+static void rtc_save(QEMUFile *f, void *opaque)
+{
+    RTCState *s = opaque;
+
+    qemu_put_buffer(f, s->cmos_data, 128);
+    qemu_put_8s(f, &s->cmos_index);
+
+    qemu_put_be32(f, s->current_tm.tm_sec);
+    qemu_put_be32(f, s->current_tm.tm_min);
+    qemu_put_be32(f, s->current_tm.tm_hour);
+    qemu_put_be32(f, s->current_tm.tm_wday);
+    qemu_put_be32(f, s->current_tm.tm_mday);
+    qemu_put_be32(f, s->current_tm.tm_mon);
+    qemu_put_be32(f, s->current_tm.tm_year);
+
+    qemu_put_timer(f, s->periodic_timer);
+    qemu_put_be64(f, s->next_periodic_time);
+
+    qemu_put_be64(f, s->next_second_time);
+    qemu_put_timer(f, s->second_timer);
+    qemu_put_timer(f, s->second_timer2);
+}
+
+static int rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+    RTCState *s = opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_buffer(f, s->cmos_data, 128);
+    qemu_get_8s(f, &s->cmos_index);
+
+    s->current_tm.tm_sec=qemu_get_be32(f);
+    s->current_tm.tm_min=qemu_get_be32(f);
+    s->current_tm.tm_hour=qemu_get_be32(f);
+    s->current_tm.tm_wday=qemu_get_be32(f);
+    s->current_tm.tm_mday=qemu_get_be32(f);
+    s->current_tm.tm_mon=qemu_get_be32(f);
+    s->current_tm.tm_year=qemu_get_be32(f);
+
+    qemu_get_timer(f, s->periodic_timer);
+    s->next_periodic_time=qemu_get_be64(f);
+
+    s->next_second_time=qemu_get_be64(f);
+    qemu_get_timer(f, s->second_timer);
+    qemu_get_timer(f, s->second_timer2);
+    return 0;
+}
+
+#ifdef TARGET_I386
+static void rtc_save_td(QEMUFile *f, void *opaque)
+{
+    RTCState *s = opaque;
+
+    qemu_put_be32(f, s->irq_coalesced);
+    qemu_put_be32(f, s->period);
+}
+
+static int rtc_load_td(QEMUFile *f, void *opaque, int version_id)
+{
+    RTCState *s = opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->irq_coalesced = qemu_get_be32(f);
+    s->period = qemu_get_be32(f);
+    rtc_coalesced_timer_update(s);
+    return 0;
+}
+#endif
+
+static void rtc_reset(void *opaque)
+{
+    RTCState *s = opaque;
+
+    s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE);
+    s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF);
+
+    qemu_irq_lower(s->irq);
+
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+	    s->irq_coalesced = 0;
+#endif
+}
+
+RTCState *rtc_init_sqw(int base, qemu_irq irq, qemu_irq sqw_irq, int base_year)
+{
+    RTCState *s;
+
+    s = qemu_mallocz(sizeof(RTCState));
+
+    s->irq = irq;
+    s->sqw_irq = sqw_irq;
+    s->cmos_data[RTC_REG_A] = 0x26;
+    s->cmos_data[RTC_REG_B] = 0x02;
+    s->cmos_data[RTC_REG_C] = 0x00;
+    s->cmos_data[RTC_REG_D] = 0x80;
+
+    s->base_year = base_year;
+    rtc_set_date_from_host(s);
+
+    s->periodic_timer = qemu_new_timer(vm_clock,
+                                       rtc_periodic_timer, s);
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+        s->coalesced_timer = qemu_new_timer(vm_clock, rtc_coalesced_timer, s);
+#endif
+    s->second_timer = qemu_new_timer(vm_clock,
+                                     rtc_update_second, s);
+    s->second_timer2 = qemu_new_timer(vm_clock,
+                                      rtc_update_second2, s);
+
+    s->next_second_time = qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 99) / 100;
+    qemu_mod_timer(s->second_timer2, s->next_second_time);
+
+    register_ioport_write(base, 2, 1, cmos_ioport_write, s);
+    register_ioport_read(base, 2, 1, cmos_ioport_read, s);
+
+    register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+        register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
+#endif
+    qemu_register_reset(rtc_reset, 0, s);
+
+    return s;
+}
+
+RTCState *rtc_init(int base, qemu_irq irq, int base_year)
+{
+    return rtc_init_sqw(base, irq, NULL, base_year);
+}
+
+/* Memory mapped interface */
+static uint32_t cmos_mm_readb (void *opaque, target_phys_addr_t addr)
+{
+    RTCState *s = opaque;
+
+    return cmos_ioport_read(s, addr >> s->it_shift) & 0xFF;
+}
+
+static void cmos_mm_writeb (void *opaque,
+                            target_phys_addr_t addr, uint32_t value)
+{
+    RTCState *s = opaque;
+
+    cmos_ioport_write(s, addr >> s->it_shift, value & 0xFF);
+}
+
+static uint32_t cmos_mm_readw (void *opaque, target_phys_addr_t addr)
+{
+    RTCState *s = opaque;
+    uint32_t val;
+
+    val = cmos_ioport_read(s, addr >> s->it_shift) & 0xFFFF;
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap16(val);
+#endif
+    return val;
+}
+
+static void cmos_mm_writew (void *opaque,
+                            target_phys_addr_t addr, uint32_t value)
+{
+    RTCState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    value = bswap16(value);
+#endif
+    cmos_ioport_write(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t cmos_mm_readl (void *opaque, target_phys_addr_t addr)
+{
+    RTCState *s = opaque;
+    uint32_t val;
+
+    val = cmos_ioport_read(s, addr >> s->it_shift);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap32(val);
+#endif
+    return val;
+}
+
+static void cmos_mm_writel (void *opaque,
+                            target_phys_addr_t addr, uint32_t value)
+{
+    RTCState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    value = bswap32(value);
+#endif
+    cmos_ioport_write(s, addr >> s->it_shift, value);
+}
+
+static CPUReadMemoryFunc *rtc_mm_read[] = {
+    &cmos_mm_readb,
+    &cmos_mm_readw,
+    &cmos_mm_readl,
+};
+
+static CPUWriteMemoryFunc *rtc_mm_write[] = {
+    &cmos_mm_writeb,
+    &cmos_mm_writew,
+    &cmos_mm_writel,
+};
+
+RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq,
+                      int base_year)
+{
+    RTCState *s;
+    int io_memory;
+
+    s = qemu_mallocz(sizeof(RTCState));
+
+    s->irq = irq;
+    s->cmos_data[RTC_REG_A] = 0x26;
+    s->cmos_data[RTC_REG_B] = 0x02;
+    s->cmos_data[RTC_REG_C] = 0x00;
+    s->cmos_data[RTC_REG_D] = 0x80;
+
+    s->base_year = base_year;
+    rtc_set_date_from_host(s);
+
+    s->periodic_timer = qemu_new_timer(vm_clock,
+                                       rtc_periodic_timer, s);
+    s->second_timer = qemu_new_timer(vm_clock,
+                                     rtc_update_second, s);
+    s->second_timer2 = qemu_new_timer(vm_clock,
+                                      rtc_update_second2, s);
+
+    s->next_second_time = qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 99) / 100;
+    qemu_mod_timer(s->second_timer2, s->next_second_time);
+
+    io_memory = cpu_register_io_memory(rtc_mm_read, rtc_mm_write, s);
+    cpu_register_physical_memory(base, 2 << it_shift, io_memory);
+
+    register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+        register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
+#endif
+    qemu_register_reset(rtc_reset, 0, s);
+    return s;
+}
diff --git a/hw/ne2000.c b/hw/ne2000.c
new file mode 100644
index 0000000..66ae9ab
--- /dev/null
+++ b/hw/ne2000.c
@@ -0,0 +1,840 @@
+/*
+ * QEMU NE2000 emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pci.h"
+#include "pc.h"
+#include "net.h"
+
+/* debug NE2000 card */
+//#define DEBUG_NE2000
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+#define E8390_CMD	0x00  /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO	0x01	/* Low byte of current local dma addr  RD */
+#define EN0_STARTPG	0x01	/* Starting page of ring bfr WR */
+#define EN0_CLDAHI	0x02	/* High byte of current local dma addr  RD */
+#define EN0_STOPPG	0x02	/* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY	0x03	/* Boundary page of ring bfr RD WR */
+#define EN0_TSR		0x04	/* Transmit status reg RD */
+#define EN0_TPSR	0x04	/* Transmit starting page WR */
+#define EN0_NCR		0x05	/* Number of collision reg RD */
+#define EN0_TCNTLO	0x05	/* Low  byte of tx byte count WR */
+#define EN0_FIFO	0x06	/* FIFO RD */
+#define EN0_TCNTHI	0x06	/* High byte of tx byte count WR */
+#define EN0_ISR		0x07	/* Interrupt status reg RD WR */
+#define EN0_CRDALO	0x08	/* low byte of current remote dma address RD */
+#define EN0_RSARLO	0x08	/* Remote start address reg 0 */
+#define EN0_CRDAHI	0x09	/* high byte, current remote dma address RD */
+#define EN0_RSARHI	0x09	/* Remote start address reg 1 */
+#define EN0_RCNTLO	0x0a	/* Remote byte count reg WR */
+#define EN0_RTL8029ID0	0x0a	/* Realtek ID byte #1 RD */
+#define EN0_RCNTHI	0x0b	/* Remote byte count reg WR */
+#define EN0_RTL8029ID1	0x0b	/* Realtek ID byte #2 RD */
+#define EN0_RSR		0x0c	/* rx status reg RD */
+#define EN0_RXCR	0x0c	/* RX configuration reg WR */
+#define EN0_TXCR	0x0d	/* TX configuration reg WR */
+#define EN0_COUNTER0	0x0d	/* Rcv alignment error counter RD */
+#define EN0_DCFG	0x0e	/* Data configuration reg WR */
+#define EN0_COUNTER1	0x0e	/* Rcv CRC error counter RD */
+#define EN0_IMR		0x0f	/* Interrupt mask reg WR */
+#define EN0_COUNTER2	0x0f	/* Rcv missed frame error counter RD */
+
+#define EN1_PHYS        0x11
+#define EN1_CURPAG      0x17
+#define EN1_MULT        0x18
+
+#define EN2_STARTPG	0x21	/* Starting page of ring bfr RD */
+#define EN2_STOPPG	0x22	/* Ending page +1 of ring bfr RD */
+
+#define EN3_CONFIG0	0x33
+#define EN3_CONFIG1	0x34
+#define EN3_CONFIG2	0x35
+#define EN3_CONFIG3	0x36
+
+/*  Register accessed at EN_CMD, the 8390 base addr.  */
+#define E8390_STOP	0x01	/* Stop and reset the chip */
+#define E8390_START	0x02	/* Start the chip, clear reset */
+#define E8390_TRANS	0x04	/* Transmit a frame */
+#define E8390_RREAD	0x08	/* Remote read */
+#define E8390_RWRITE	0x10	/* Remote write  */
+#define E8390_NODMA	0x20	/* Remote DMA */
+#define E8390_PAGE0	0x00	/* Select page chip registers */
+#define E8390_PAGE1	0x40	/* using the two high-order bits */
+#define E8390_PAGE2	0x80	/* Page 3 is invalid. */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX	0x01	/* Receiver, no error */
+#define ENISR_TX	0x02	/* Transmitter, no error */
+#define ENISR_RX_ERR	0x04	/* Receiver, with error */
+#define ENISR_TX_ERR	0x08	/* Transmitter, with error */
+#define ENISR_OVER	0x10	/* Receiver overwrote the ring */
+#define ENISR_COUNTERS	0x20	/* Counters need emptying */
+#define ENISR_RDC	0x40	/* remote dma complete */
+#define ENISR_RESET	0x80	/* Reset completed */
+#define ENISR_ALL	0x3f	/* Interrupts we will enable */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK	0x01	/* Received a good packet */
+#define ENRSR_CRC	0x02	/* CRC error */
+#define ENRSR_FAE	0x04	/* frame alignment error */
+#define ENRSR_FO	0x08	/* FIFO overrun */
+#define ENRSR_MPA	0x10	/* missed pkt */
+#define ENRSR_PHY	0x20	/* physical/multicast address */
+#define ENRSR_DIS	0x40	/* receiver disable. set in monitor mode */
+#define ENRSR_DEF	0x80	/* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01	/* Packet transmitted without error */
+#define ENTSR_ND  0x02	/* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04	/* The transmit collided at least once. */
+#define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10	/* The carrier sense was lost. */
+#define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40	/* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
+
+#define NE2000_PMEM_SIZE    (32*1024)
+#define NE2000_PMEM_START   (16*1024)
+#define NE2000_PMEM_END     (NE2000_PMEM_SIZE+NE2000_PMEM_START)
+#define NE2000_MEM_SIZE     NE2000_PMEM_END
+
+typedef struct NE2000State {
+    uint8_t cmd;
+    uint32_t start;
+    uint32_t stop;
+    uint8_t boundary;
+    uint8_t tsr;
+    uint8_t tpsr;
+    uint16_t tcnt;
+    uint16_t rcnt;
+    uint32_t rsar;
+    uint8_t rsr;
+    uint8_t rxcr;
+    uint8_t isr;
+    uint8_t dcfg;
+    uint8_t imr;
+    uint8_t phys[6]; /* mac address */
+    uint8_t curpag;
+    uint8_t mult[8]; /* multicast mask array */
+    qemu_irq irq;
+    int isa_io_base;
+    PCIDevice *pci_dev;
+    VLANClientState *vc;
+    uint8_t macaddr[6];
+    uint8_t mem[NE2000_MEM_SIZE];
+} NE2000State;
+
+static void ne2000_reset(NE2000State *s)
+{
+    int i;
+
+    s->isr = ENISR_RESET;
+    memcpy(s->mem, s->macaddr, 6);
+    s->mem[14] = 0x57;
+    s->mem[15] = 0x57;
+
+    /* duplicate prom data */
+    for(i = 15;i >= 0; i--) {
+        s->mem[2 * i] = s->mem[i];
+        s->mem[2 * i + 1] = s->mem[i];
+    }
+}
+
+static void ne2000_update_irq(NE2000State *s)
+{
+    int isr;
+    isr = (s->isr & s->imr) & 0x7f;
+#if defined(DEBUG_NE2000)
+    printf("NE2000: Set IRQ to %d (%02x %02x)\n",
+	   isr ? 1 : 0, s->isr, s->imr);
+#endif
+    qemu_set_irq(s->irq, (isr != 0));
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+/* XXX: optimize */
+static int compute_mcast_idx(const uint8_t *ep)
+{
+    uint32_t crc;
+    int carry, i, j;
+    uint8_t b;
+
+    crc = 0xffffffff;
+    for (i = 0; i < 6; i++) {
+        b = *ep++;
+        for (j = 0; j < 8; j++) {
+            carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+            crc <<= 1;
+            b >>= 1;
+            if (carry)
+                crc = ((crc ^ POLYNOMIAL) | carry);
+        }
+    }
+    return (crc >> 26);
+}
+
+static int ne2000_buffer_full(NE2000State *s)
+{
+    int avail, index, boundary;
+
+    index = s->curpag << 8;
+    boundary = s->boundary << 8;
+    if (index < boundary)
+        avail = boundary - index;
+    else
+        avail = (s->stop - s->start) - (index - boundary);
+    if (avail < (MAX_ETH_FRAME_SIZE + 4))
+        return 1;
+    return 0;
+}
+
+static int ne2000_can_receive(VLANClientState *vc)
+{
+    NE2000State *s = vc->opaque;
+
+    if (s->cmd & E8390_STOP)
+        return 1;
+    return !ne2000_buffer_full(s);
+}
+
+#define MIN_BUF_SIZE 60
+
+static ssize_t ne2000_receive(VLANClientState *vc, const uint8_t *buf, size_t size_)
+{
+    NE2000State *s = vc->opaque;
+    int size = size_;
+    uint8_t *p;
+    unsigned int total_len, next, avail, len, index, mcast_idx;
+    uint8_t buf1[60];
+    static const uint8_t broadcast_macaddr[6] =
+        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#if defined(DEBUG_NE2000)
+    printf("NE2000: received len=%d\n", size);
+#endif
+
+    if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
+        return -1;
+
+    /* XXX: check this */
+    if (s->rxcr & 0x10) {
+        /* promiscuous: receive all */
+    } else {
+        if (!memcmp(buf,  broadcast_macaddr, 6)) {
+            /* broadcast address */
+            if (!(s->rxcr & 0x04))
+                return size;
+        } else if (buf[0] & 0x01) {
+            /* multicast */
+            if (!(s->rxcr & 0x08))
+                return size;
+            mcast_idx = compute_mcast_idx(buf);
+            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+                return size;
+        } else if (s->mem[0] == buf[0] &&
+                   s->mem[2] == buf[1] &&
+                   s->mem[4] == buf[2] &&
+                   s->mem[6] == buf[3] &&
+                   s->mem[8] == buf[4] &&
+                   s->mem[10] == buf[5]) {
+            /* match */
+        } else {
+            return size;
+        }
+    }
+
+
+    /* if too small buffer, then expand it */
+    if (size < MIN_BUF_SIZE) {
+        memcpy(buf1, buf, size);
+        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+        buf = buf1;
+        size = MIN_BUF_SIZE;
+    }
+
+    index = s->curpag << 8;
+    /* 4 bytes for header */
+    total_len = size + 4;
+    /* address for next packet (4 bytes for CRC) */
+    next = index + ((total_len + 4 + 255) & ~0xff);
+    if (next >= s->stop)
+        next -= (s->stop - s->start);
+    /* prepare packet header */
+    p = s->mem + index;
+    s->rsr = ENRSR_RXOK; /* receive status */
+    /* XXX: check this */
+    if (buf[0] & 0x01)
+        s->rsr |= ENRSR_PHY;
+    p[0] = s->rsr;
+    p[1] = next >> 8;
+    p[2] = total_len;
+    p[3] = total_len >> 8;
+    index += 4;
+
+    /* write packet data */
+    while (size > 0) {
+        if (index <= s->stop)
+            avail = s->stop - index;
+        else
+            avail = 0;
+        len = size;
+        if (len > avail)
+            len = avail;
+        memcpy(s->mem + index, buf, len);
+        buf += len;
+        index += len;
+        if (index == s->stop)
+            index = s->start;
+        size -= len;
+    }
+    s->curpag = next >> 8;
+
+    /* now we can signal we have received something */
+    s->isr |= ENISR_RX;
+    ne2000_update_irq(s);
+
+    return size_;
+}
+
+static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = opaque;
+    int offset, page, index;
+
+    addr &= 0xf;
+#ifdef DEBUG_NE2000
+    printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+    if (addr == E8390_CMD) {
+        /* control register */
+        s->cmd = val;
+        if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */
+            s->isr &= ~ENISR_RESET;
+            /* test specific case: zero length transfer */
+            if ((val & (E8390_RREAD | E8390_RWRITE)) &&
+                s->rcnt == 0) {
+                s->isr |= ENISR_RDC;
+                ne2000_update_irq(s);
+            }
+            if (val & E8390_TRANS) {
+                index = (s->tpsr << 8);
+                /* XXX: next 2 lines are a hack to make netware 3.11 work */
+                if (index >= NE2000_PMEM_END)
+                    index -= NE2000_PMEM_SIZE;
+                /* fail safe: check range on the transmitted length  */
+                if (index + s->tcnt <= NE2000_PMEM_END) {
+                    qemu_send_packet(s->vc, s->mem + index, s->tcnt);
+                }
+                /* signal end of transfer */
+                s->tsr = ENTSR_PTX;
+                s->isr |= ENISR_TX;
+                s->cmd &= ~E8390_TRANS;
+                ne2000_update_irq(s);
+            }
+        }
+    } else {
+        page = s->cmd >> 6;
+        offset = addr | (page << 4);
+        switch(offset) {
+        case EN0_STARTPG:
+            s->start = val << 8;
+            break;
+        case EN0_STOPPG:
+            s->stop = val << 8;
+            break;
+        case EN0_BOUNDARY:
+            s->boundary = val;
+            break;
+        case EN0_IMR:
+            s->imr = val;
+            ne2000_update_irq(s);
+            break;
+        case EN0_TPSR:
+            s->tpsr = val;
+            break;
+        case EN0_TCNTLO:
+            s->tcnt = (s->tcnt & 0xff00) | val;
+            break;
+        case EN0_TCNTHI:
+            s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
+            break;
+        case EN0_RSARLO:
+            s->rsar = (s->rsar & 0xff00) | val;
+            break;
+        case EN0_RSARHI:
+            s->rsar = (s->rsar & 0x00ff) | (val << 8);
+            break;
+        case EN0_RCNTLO:
+            s->rcnt = (s->rcnt & 0xff00) | val;
+            break;
+        case EN0_RCNTHI:
+            s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
+            break;
+        case EN0_RXCR:
+            s->rxcr = val;
+            break;
+        case EN0_DCFG:
+            s->dcfg = val;
+            break;
+        case EN0_ISR:
+            s->isr &= ~(val & 0x7f);
+            ne2000_update_irq(s);
+            break;
+        case EN1_PHYS ... EN1_PHYS + 5:
+            s->phys[offset - EN1_PHYS] = val;
+            break;
+        case EN1_CURPAG:
+            s->curpag = val;
+            break;
+        case EN1_MULT ... EN1_MULT + 7:
+            s->mult[offset - EN1_MULT] = val;
+            break;
+        }
+    }
+}
+
+static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    int offset, page, ret;
+
+    addr &= 0xf;
+    if (addr == E8390_CMD) {
+        ret = s->cmd;
+    } else {
+        page = s->cmd >> 6;
+        offset = addr | (page << 4);
+        switch(offset) {
+        case EN0_TSR:
+            ret = s->tsr;
+            break;
+        case EN0_BOUNDARY:
+            ret = s->boundary;
+            break;
+        case EN0_ISR:
+            ret = s->isr;
+            break;
+	case EN0_RSARLO:
+	    ret = s->rsar & 0x00ff;
+	    break;
+	case EN0_RSARHI:
+	    ret = s->rsar >> 8;
+	    break;
+        case EN1_PHYS ... EN1_PHYS + 5:
+            ret = s->phys[offset - EN1_PHYS];
+            break;
+        case EN1_CURPAG:
+            ret = s->curpag;
+            break;
+        case EN1_MULT ... EN1_MULT + 7:
+            ret = s->mult[offset - EN1_MULT];
+            break;
+        case EN0_RSR:
+            ret = s->rsr;
+            break;
+        case EN2_STARTPG:
+            ret = s->start >> 8;
+            break;
+        case EN2_STOPPG:
+            ret = s->stop >> 8;
+            break;
+	case EN0_RTL8029ID0:
+	    ret = 0x50;
+	    break;
+	case EN0_RTL8029ID1:
+	    ret = 0x43;
+	    break;
+	case EN3_CONFIG0:
+	    ret = 0;		/* 10baseT media */
+	    break;
+	case EN3_CONFIG2:
+	    ret = 0x40;		/* 10baseT active */
+	    break;
+	case EN3_CONFIG3:
+	    ret = 0x40;		/* Full duplex */
+	    break;
+        default:
+            ret = 0x00;
+            break;
+        }
+    }
+#ifdef DEBUG_NE2000
+    printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
+#endif
+    return ret;
+}
+
+static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
+                                     uint32_t val)
+{
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        s->mem[addr] = val;
+    }
+}
+
+static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
+                                     uint32_t val)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        *(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
+    }
+}
+
+static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
+                                     uint32_t val)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
+    }
+}
+
+static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
+{
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        return s->mem[addr];
+    } else {
+        return 0xff;
+    }
+}
+
+static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        return le16_to_cpu(*(uint16_t *)(s->mem + addr));
+    } else {
+        return 0xffff;
+    }
+}
+
+static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        return le32_to_cpupu((uint32_t *)(s->mem + addr));
+    } else {
+        return 0xffffffff;
+    }
+}
+
+static inline void ne2000_dma_update(NE2000State *s, int len)
+{
+    s->rsar += len;
+    /* wrap */
+    /* XXX: check what to do if rsar > stop */
+    if (s->rsar == s->stop)
+        s->rsar = s->start;
+
+    if (s->rcnt <= len) {
+        s->rcnt = 0;
+        /* signal end of transfer */
+        s->isr |= ENISR_RDC;
+        ne2000_update_irq(s);
+    } else {
+        s->rcnt -= len;
+    }
+}
+
+static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic write val=0x%04x\n", val);
+#endif
+    if (s->rcnt == 0)
+        return;
+    if (s->dcfg & 0x01) {
+        /* 16 bit access */
+        ne2000_mem_writew(s, s->rsar, val);
+        ne2000_dma_update(s, 2);
+    } else {
+        /* 8 bit access */
+        ne2000_mem_writeb(s, s->rsar, val);
+        ne2000_dma_update(s, 1);
+    }
+}
+
+static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    int ret;
+
+    if (s->dcfg & 0x01) {
+        /* 16 bit access */
+        ret = ne2000_mem_readw(s, s->rsar);
+        ne2000_dma_update(s, 2);
+    } else {
+        /* 8 bit access */
+        ret = ne2000_mem_readb(s, s->rsar);
+        ne2000_dma_update(s, 1);
+    }
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic read val=0x%04x\n", ret);
+#endif
+    return ret;
+}
+
+static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic writel val=0x%04x\n", val);
+#endif
+    if (s->rcnt == 0)
+        return;
+    /* 32 bit access */
+    ne2000_mem_writel(s, s->rsar, val);
+    ne2000_dma_update(s, 4);
+}
+
+static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    int ret;
+
+    /* 32 bit access */
+    ret = ne2000_mem_readl(s, s->rsar);
+    ne2000_dma_update(s, 4);
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic readl val=0x%04x\n", ret);
+#endif
+    return ret;
+}
+
+static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    /* nothing to do (end of reset pulse) */
+}
+
+static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    ne2000_reset(s);
+    return 0;
+}
+
+static void ne2000_save(QEMUFile* f,void* opaque)
+{
+	NE2000State* s=(NE2000State*)opaque;
+        uint32_t tmp;
+
+        if (s->pci_dev)
+            pci_device_save(s->pci_dev, f);
+
+        qemu_put_8s(f, &s->rxcr);
+
+	qemu_put_8s(f, &s->cmd);
+	qemu_put_be32s(f, &s->start);
+	qemu_put_be32s(f, &s->stop);
+	qemu_put_8s(f, &s->boundary);
+	qemu_put_8s(f, &s->tsr);
+	qemu_put_8s(f, &s->tpsr);
+	qemu_put_be16s(f, &s->tcnt);
+	qemu_put_be16s(f, &s->rcnt);
+	qemu_put_be32s(f, &s->rsar);
+	qemu_put_8s(f, &s->rsr);
+	qemu_put_8s(f, &s->isr);
+	qemu_put_8s(f, &s->dcfg);
+	qemu_put_8s(f, &s->imr);
+	qemu_put_buffer(f, s->phys, 6);
+	qemu_put_8s(f, &s->curpag);
+	qemu_put_buffer(f, s->mult, 8);
+        tmp = 0;
+	qemu_put_be32s(f, &tmp); /* ignored, was irq */
+	qemu_put_buffer(f, s->mem, NE2000_MEM_SIZE);
+}
+
+static int ne2000_load(QEMUFile* f,void* opaque,int version_id)
+{
+	NE2000State* s=(NE2000State*)opaque;
+        int ret;
+        uint32_t tmp;
+
+        if (version_id > 3)
+            return -EINVAL;
+
+        if (s->pci_dev && version_id >= 3) {
+            ret = pci_device_load(s->pci_dev, f);
+            if (ret < 0)
+                return ret;
+        }
+
+        if (version_id >= 2) {
+            qemu_get_8s(f, &s->rxcr);
+        } else {
+            s->rxcr = 0x0c;
+        }
+
+	qemu_get_8s(f, &s->cmd);
+	qemu_get_be32s(f, &s->start);
+	qemu_get_be32s(f, &s->stop);
+	qemu_get_8s(f, &s->boundary);
+	qemu_get_8s(f, &s->tsr);
+	qemu_get_8s(f, &s->tpsr);
+	qemu_get_be16s(f, &s->tcnt);
+	qemu_get_be16s(f, &s->rcnt);
+	qemu_get_be32s(f, &s->rsar);
+	qemu_get_8s(f, &s->rsr);
+	qemu_get_8s(f, &s->isr);
+	qemu_get_8s(f, &s->dcfg);
+	qemu_get_8s(f, &s->imr);
+	qemu_get_buffer(f, s->phys, 6);
+	qemu_get_8s(f, &s->curpag);
+	qemu_get_buffer(f, s->mult, 8);
+	qemu_get_be32s(f, &tmp); /* ignored */
+	qemu_get_buffer(f, s->mem, NE2000_MEM_SIZE);
+
+	return 0;
+}
+
+static void isa_ne2000_cleanup(VLANClientState *vc)
+{
+    NE2000State *s = vc->opaque;
+
+    unregister_savevm("ne2000", s);
+
+    isa_unassign_ioport(s->isa_io_base, 16);
+    isa_unassign_ioport(s->isa_io_base + 0x10, 2);
+    isa_unassign_ioport(s->isa_io_base + 0x1f, 1);
+
+    qemu_free(s);
+}
+
+void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd)
+{
+    NE2000State *s;
+
+    qemu_check_nic_model(nd, "ne2k_isa");
+
+    s = qemu_mallocz(sizeof(NE2000State));
+
+    register_ioport_write(base, 16, 1, ne2000_ioport_write, s);
+    register_ioport_read(base, 16, 1, ne2000_ioport_read, s);
+
+    register_ioport_write(base + 0x10, 1, 1, ne2000_asic_ioport_write, s);
+    register_ioport_read(base + 0x10, 1, 1, ne2000_asic_ioport_read, s);
+    register_ioport_write(base + 0x10, 2, 2, ne2000_asic_ioport_write, s);
+    register_ioport_read(base + 0x10, 2, 2, ne2000_asic_ioport_read, s);
+
+    register_ioport_write(base + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
+    register_ioport_read(base + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
+    s->isa_io_base = base;
+    s->irq = irq;
+    memcpy(s->macaddr, nd->macaddr, 6);
+
+    ne2000_reset(s);
+
+    s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
+                                 ne2000_can_receive, ne2000_receive, NULL,
+                                 isa_ne2000_cleanup, s);
+
+    qemu_format_nic_info_str(s->vc, s->macaddr);
+
+    register_savevm("ne2000", -1, 2, ne2000_save, ne2000_load, s);
+}
+
+/***********************************************************/
+/* PCI NE2000 definitions */
+
+typedef struct PCINE2000State {
+    PCIDevice dev;
+    NE2000State ne2000;
+} PCINE2000State;
+
+static void ne2000_map(PCIDevice *pci_dev, int region_num,
+                       uint32_t addr, uint32_t size, int type)
+{
+    PCINE2000State *d = (PCINE2000State *)pci_dev;
+    NE2000State *s = &d->ne2000;
+
+    register_ioport_write(addr, 16, 1, ne2000_ioport_write, s);
+    register_ioport_read(addr, 16, 1, ne2000_ioport_read, s);
+
+    register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s);
+    register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s);
+    register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s);
+    register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s);
+    register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s);
+    register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s);
+
+    register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
+    register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
+}
+
+static void ne2000_cleanup(VLANClientState *vc)
+{
+    NE2000State *s = vc->opaque;
+
+    unregister_savevm("ne2000", s);
+}
+
+static void pci_ne2000_init(PCIDevice *pci_dev)
+{
+    PCINE2000State *d = (PCINE2000State *)pci_dev;
+    NE2000State *s;
+    uint8_t *pci_conf;
+
+    pci_conf = d->dev.config;
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8029);
+    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+    pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type
+    pci_conf[0x3d] = 1; // interrupt pin 0
+
+    pci_register_bar(&d->dev, 0, 0x100,
+                           PCI_ADDRESS_SPACE_IO, ne2000_map);
+    s = &d->ne2000;
+    s->irq = d->dev.irq[0];
+    s->pci_dev = (PCIDevice *)d;
+    qdev_get_macaddr(&d->dev.qdev, s->macaddr);
+    ne2000_reset(s);
+    s->vc = qdev_get_vlan_client(&d->dev.qdev,
+                                 ne2000_can_receive, ne2000_receive, NULL,
+                                 ne2000_cleanup, s);
+
+    qemu_format_nic_info_str(s->vc, s->macaddr);
+
+    register_savevm("ne2000", -1, 3, ne2000_save, ne2000_load, s);
+}
+
+static void ne2000_register_devices(void)
+{
+    pci_qdev_register("ne2k_pci", sizeof(PCINE2000State), pci_ne2000_init);
+}
+
+device_init(ne2000_register_devices)
diff --git a/hw/pc.c b/hw/pc.c
new file mode 100644
index 0000000..7c32211
--- /dev/null
+++ b/hw/pc.c
@@ -0,0 +1,1310 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "pci.h"
+#include "block.h"
+#include "sysemu.h"
+#include "blockdev.h"
+#include "audio/audio.h"
+#include "net.h"
+//#include "smbus.h"
+#include "boards.h"
+#include "android/globals.h"
+#include "monitor.h"
+#include "fw_cfg.h"
+//#include "hpet_emul.h"
+#include "watchdog.h"
+#include "smbios.h"
+#include "console.h"
+
+#include "goldfish_device.h"
+
+char* audio_input_source = NULL;
+/* output Bochs bios info messages */
+//#define DEBUG_BIOS
+
+#define BIOS_FILENAME "bios.bin"
+#define VGABIOS_FILENAME "vgabios.bin"
+#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+
+#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
+
+/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables.  */
+#define ACPI_DATA_SIZE       0x10000
+#define BIOS_CFG_IOPORT 0x510
+#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
+#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
+
+#define MAX_IDE_BUS 2
+#ifndef CONFIG_ANDROID
+static fdctrl_t *floppy_controller;
+#endif
+static RTCState *rtc_state;
+static PITState *pit;
+static IOAPICState *ioapic;
+static PCIDevice *i440fx_state;
+
+typedef struct rom_reset_data {
+    uint8_t *data;
+    target_phys_addr_t addr;
+    unsigned size;
+} RomResetData;
+
+static void option_rom_reset(void *_rrd)
+{
+    RomResetData *rrd = _rrd;
+
+    cpu_physical_memory_write_rom(rrd->addr, rrd->data, rrd->size);
+}
+
+static void option_rom_setup_reset(target_phys_addr_t addr, unsigned size)
+{
+    RomResetData *rrd = qemu_malloc(sizeof *rrd);
+
+    rrd->data = qemu_malloc(size);
+    cpu_physical_memory_read(addr, rrd->data, size);
+    rrd->addr = addr;
+    rrd->size = size;
+    qemu_register_reset(option_rom_reset, 0, rrd);
+}
+
+static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
+{
+}
+
+/* MSDOS compatibility mode FPU exception support */
+static qemu_irq ferr_irq;
+/* XXX: add IGNNE support */
+void cpu_set_ferr(CPUX86State *s)
+{
+    qemu_irq_raise(ferr_irq);
+}
+
+static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    qemu_irq_lower(ferr_irq);
+}
+
+/* TSC handling */
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+    /* Note: when using kqemu, it is more logical to return the host TSC
+       because kqemu does not trap the RDTSC instruction for
+       performance reasons */
+#ifdef CONFIG_KQEMU
+    if (env->kqemu_enabled) {
+        return cpu_get_real_ticks();
+    } else
+#endif
+    {
+        return cpu_get_ticks();
+    }
+}
+
+/* SMM support */
+void cpu_smm_update(CPUState *env)
+{
+    if (i440fx_state && env == first_cpu)
+        i440fx_set_smm(i440fx_state, (env->hflags >> HF_SMM_SHIFT) & 1);
+}
+
+
+/* IRQ handling */
+int cpu_get_pic_interrupt(CPUState *env)
+{
+    int intno;
+
+    intno = apic_get_interrupt(env);
+    if (intno >= 0) {
+        /* set irq request if a PIC irq is still pending */
+        /* XXX: improve that */
+        pic_update_irq(isa_pic);
+        return intno;
+    }
+    /* read the irq from the PIC */
+    if (!apic_accept_pic_intr(env))
+        return -1;
+
+    intno = pic_read_irq(isa_pic);
+    return intno;
+}
+
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+    CPUState *env = first_cpu;
+
+    if (env->apic_state) {
+        while (env) {
+            if (apic_accept_pic_intr(env))
+                apic_deliver_pic_intr(env, level);
+            env = env->next_cpu;
+        }
+    } else {
+        if (level)
+            cpu_interrupt(env, CPU_INTERRUPT_HARD);
+        else
+            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+    }
+}
+
+/* PC cmos mappings */
+
+#define REG_EQUIPMENT_BYTE          0x14
+
+#ifndef CONFIG_ANDROID
+static int cmos_get_fd_drive_type(int fd0)
+{
+    int val;
+
+    switch (fd0) {
+    case 0:
+        /* 1.44 Mb 3"5 drive */
+        val = 4;
+        break;
+    case 1:
+        /* 2.88 Mb 3"5 drive */
+        val = 5;
+        break;
+    case 2:
+        /* 1.2 Mb 5"5 drive */
+        val = 2;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    return val;
+}
+#endif
+
+static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+{
+    RTCState *s = rtc_state;
+    int cylinders, heads, sectors;
+    bdrv_get_geometry_hint(hd, &cylinders, &heads, &sectors);
+    rtc_set_memory(s, type_ofs, 47);
+    rtc_set_memory(s, info_ofs, cylinders);
+    rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
+    rtc_set_memory(s, info_ofs + 2, heads);
+    rtc_set_memory(s, info_ofs + 3, 0xff);
+    rtc_set_memory(s, info_ofs + 4, 0xff);
+    rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
+    rtc_set_memory(s, info_ofs + 6, cylinders);
+    rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
+    rtc_set_memory(s, info_ofs + 8, sectors);
+}
+
+/* convert boot_device letter to something recognizable by the bios */
+static int boot_device2nibble(char boot_device)
+{
+    switch(boot_device) {
+    case 'a':
+    case 'b':
+        return 0x01; /* floppy boot */
+    case 'c':
+        return 0x02; /* hard drive boot */
+    case 'd':
+        return 0x03; /* CD-ROM boot */
+    case 'n':
+        return 0x04; /* Network boot */
+    }
+    return 0;
+}
+
+/* copy/pasted from cmos_init, should be made a general function
+ and used there as well */
+static int pc_boot_set(void *opaque, const char *boot_device)
+{
+    Monitor *mon = cur_mon;
+#define PC_MAX_BOOT_DEVICES 3
+    RTCState *s = (RTCState *)opaque;
+    int nbds, bds[3] = { 0, };
+    int i;
+
+    nbds = strlen(boot_device);
+    if (nbds > PC_MAX_BOOT_DEVICES) {
+        monitor_printf(mon, "Too many boot devices for PC\n");
+        return(1);
+    }
+    for (i = 0; i < nbds; i++) {
+        bds[i] = boot_device2nibble(boot_device[i]);
+        if (bds[i] == 0) {
+            monitor_printf(mon, "Invalid boot device for PC: '%c'\n",
+                           boot_device[i]);
+            return(1);
+        }
+    }
+    rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+    rtc_set_memory(s, 0x38, (bds[2] << 4));
+    return(0);
+}
+
+/* hd_table must contain 4 block drivers */
+static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+                      const char *boot_device, BlockDriverState **hd_table)
+{
+    RTCState *s = rtc_state;
+    int nbds, bds[3] = { 0, };
+    int val;
+#ifndef CONFIG_ANDROID
+    int fd0, fd1, nb;
+#endif
+    int i;
+
+    /* various important CMOS locations needed by PC/Bochs bios */
+
+    /* memory size */
+    val = 640; /* base memory in K */
+    rtc_set_memory(s, 0x15, val);
+    rtc_set_memory(s, 0x16, val >> 8);
+
+    val = (ram_size / 1024) - 1024;
+    if (val > 65535)
+        val = 65535;
+    rtc_set_memory(s, 0x17, val);
+    rtc_set_memory(s, 0x18, val >> 8);
+    rtc_set_memory(s, 0x30, val);
+    rtc_set_memory(s, 0x31, val >> 8);
+
+    if (above_4g_mem_size) {
+        rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16);
+        rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
+        rtc_set_memory(s, 0x5d, (uint64_t)above_4g_mem_size >> 32);
+    }
+
+    if (ram_size > (16 * 1024 * 1024))
+        val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
+    else
+        val = 0;
+    if (val > 65535)
+        val = 65535;
+    rtc_set_memory(s, 0x34, val);
+    rtc_set_memory(s, 0x35, val >> 8);
+
+    /* set the number of CPU */
+    rtc_set_memory(s, 0x5f, smp_cpus - 1);
+
+    /* set boot devices, and disable floppy signature check if requested */
+#define PC_MAX_BOOT_DEVICES 3
+    nbds = strlen(boot_device);
+    if (nbds > PC_MAX_BOOT_DEVICES) {
+        fprintf(stderr, "Too many boot devices for PC\n");
+        exit(1);
+    }
+    for (i = 0; i < nbds; i++) {
+        bds[i] = boot_device2nibble(boot_device[i]);
+        if (bds[i] == 0) {
+            fprintf(stderr, "Invalid boot device for PC: '%c'\n",
+                    boot_device[i]);
+            exit(1);
+        }
+    }
+    rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+    rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ?  0x0 : 0x1));
+
+    /* floppy type */
+
+#ifndef CONFIG_ANDROID
+    fd0 = fdctrl_get_drive_type(floppy_controller, 0);
+    fd1 = fdctrl_get_drive_type(floppy_controller, 1);
+
+    val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
+    rtc_set_memory(s, 0x10, val);
+
+    val = 0;
+    nb = 0;
+    if (fd0 < 3)
+        nb++;
+    if (fd1 < 3)
+        nb++;
+    switch (nb) {
+    case 0:
+        break;
+    case 1:
+        val |= 0x01; /* 1 drive, ready for boot */
+        break;
+    case 2:
+        val |= 0x41; /* 2 drives, ready for boot */
+        break;
+    }
+    val |= 0x02; /* FPU is there */
+    val |= 0x04; /* PS/2 mouse installed */
+    rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
+#endif
+
+    /* hard drives */
+
+    rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
+    if (hd_table[0])
+        cmos_init_hd(0x19, 0x1b, hd_table[0]);
+    if (hd_table[1])
+        cmos_init_hd(0x1a, 0x24, hd_table[1]);
+
+    val = 0;
+    for (i = 0; i < 4; i++) {
+        if (hd_table[i]) {
+            int cylinders, heads, sectors, translation;
+            /* NOTE: bdrv_get_geometry_hint() returns the physical
+                geometry.  It is always such that: 1 <= sects <= 63, 1
+                <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+                geometry can be different if a translation is done. */
+            translation = bdrv_get_translation_hint(hd_table[i]);
+            if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+                bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, &sectors);
+                if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
+                    /* No translation. */
+                    translation = 0;
+                } else {
+                    /* LBA translation. */
+                    translation = 1;
+                }
+            } else {
+                translation--;
+            }
+            val |= translation << (i * 2);
+        }
+    }
+    rtc_set_memory(s, 0x39, val);
+}
+
+void ioport_set_a20(int enable)
+{
+    /* XXX: send to all CPUs ? */
+    cpu_x86_set_a20(first_cpu, enable);
+}
+
+int ioport_get_a20(void)
+{
+    return ((first_cpu->a20_mask >> 20) & 1);
+}
+
+static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    ioport_set_a20((val >> 1) & 1);
+    /* XXX: bit 0 is fast reset */
+}
+
+static uint32_t ioport92_read(void *opaque, uint32_t addr)
+{
+    return ioport_get_a20() << 1;
+}
+
+/***********************************************************/
+/* Bochs BIOS debug ports */
+
+static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    static const char shutdown_str[8] = "Shutdown";
+    static int shutdown_index = 0;
+
+    switch(addr) {
+        /* Bochs BIOS messages */
+    case 0x400:
+    case 0x401:
+        fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
+        exit(1);
+    case 0x402:
+    case 0x403:
+#ifdef DEBUG_BIOS
+        fprintf(stderr, "%c", val);
+#endif
+        break;
+    case 0x8900:
+        /* same as Bochs power off */
+        if (val == shutdown_str[shutdown_index]) {
+            shutdown_index++;
+            if (shutdown_index == 8) {
+                shutdown_index = 0;
+                qemu_system_shutdown_request();
+            }
+        } else {
+            shutdown_index = 0;
+        }
+        break;
+
+        /* LGPL'ed VGA BIOS messages */
+    case 0x501:
+    case 0x502:
+        fprintf(stderr, "VGA BIOS panic, line %d\n", val);
+        exit(1);
+    case 0x500:
+    case 0x503:
+#ifdef DEBUG_BIOS
+        fprintf(stderr, "%c", val);
+#endif
+        break;
+    }
+}
+
+extern uint64_t node_cpumask[MAX_NODES];
+
+static void bochs_bios_init(void)
+{
+    void *fw_cfg;
+    uint8_t *smbios_table;
+    size_t smbios_len;
+    uint64_t *numa_fw_cfg;
+    int i, j;
+
+    register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
+    register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
+    register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
+
+    register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
+    register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
+
+    fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+    fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+#ifndef CONFIG_ANDROID
+    fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, (uint8_t *)acpi_tables,
+                     acpi_tables_len);
+#endif
+    smbios_table = smbios_get_table(&smbios_len);
+    if (smbios_table)
+        fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
+                         smbios_table, smbios_len);
+
+    /* allocate memory for the NUMA channel: one (64bit) word for the number
+     * of nodes, one word for each VCPU->node and one word for each node to
+     * hold the amount of memory.
+     */
+    numa_fw_cfg = qemu_mallocz((1 + smp_cpus + nb_numa_nodes) * 8);
+    numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
+    for (i = 0; i < smp_cpus; i++) {
+        for (j = 0; j < nb_numa_nodes; j++) {
+            if (node_cpumask[j] & (1 << i)) {
+                numa_fw_cfg[i + 1] = cpu_to_le64(j);
+                break;
+            }
+        }
+    }
+    for (i = 0; i < nb_numa_nodes; i++) {
+        numa_fw_cfg[smp_cpus + 1 + i] = cpu_to_le64(node_mem[i]);
+    }
+    fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, (uint8_t *)numa_fw_cfg,
+                     (1 + smp_cpus + nb_numa_nodes) * 8);
+}
+
+/* Generate an initial boot sector which sets state and jump to
+   a specified vector */
+static void generate_bootsect(target_phys_addr_t option_rom,
+                              uint32_t gpr[8], uint16_t segs[6], uint16_t ip)
+{
+    uint8_t rom[512], *p, *reloc;
+    uint8_t sum;
+    int i;
+
+    memset(rom, 0, sizeof(rom));
+
+    p = rom;
+    /* Make sure we have an option rom signature */
+    *p++ = 0x55;
+    *p++ = 0xaa;
+
+    /* ROM size in sectors*/
+    *p++ = 1;
+
+    /* Hook int19 */
+
+    *p++ = 0x50;		/* push ax */
+    *p++ = 0x1e;		/* push ds */
+    *p++ = 0x31; *p++ = 0xc0;	/* xor ax, ax */
+    *p++ = 0x8e; *p++ = 0xd8;	/* mov ax, ds */
+
+    *p++ = 0xc7; *p++ = 0x06;   /* movvw _start,0x64 */
+    *p++ = 0x64; *p++ = 0x00;
+    reloc = p;
+    *p++ = 0x00; *p++ = 0x00;
+
+    *p++ = 0x8c; *p++ = 0x0e;   /* mov cs,0x66 */
+    *p++ = 0x66; *p++ = 0x00;
+
+    *p++ = 0x1f;		/* pop ds */
+    *p++ = 0x58;		/* pop ax */
+    *p++ = 0xcb;		/* lret */
+    
+    /* Actual code */
+    *reloc = (p - rom);
+
+    *p++ = 0xfa;		/* CLI */
+    *p++ = 0xfc;		/* CLD */
+
+    for (i = 0; i < 6; i++) {
+	if (i == 1)		/* Skip CS */
+	    continue;
+
+	*p++ = 0xb8;		/* MOV AX,imm16 */
+	*p++ = segs[i];
+	*p++ = segs[i] >> 8;
+	*p++ = 0x8e;		/* MOV <seg>,AX */
+	*p++ = 0xc0 + (i << 3);
+    }
+
+    for (i = 0; i < 8; i++) {
+	*p++ = 0x66;		/* 32-bit operand size */
+	*p++ = 0xb8 + i;	/* MOV <reg>,imm32 */
+	*p++ = gpr[i];
+	*p++ = gpr[i] >> 8;
+	*p++ = gpr[i] >> 16;
+	*p++ = gpr[i] >> 24;
+    }
+
+    *p++ = 0xea;		/* JMP FAR */
+    *p++ = ip;			/* IP */
+    *p++ = ip >> 8;
+    *p++ = segs[1];		/* CS */
+    *p++ = segs[1] >> 8;
+
+    /* sign rom */
+    sum = 0;
+    for (i = 0; i < (sizeof(rom) - 1); i++)
+        sum += rom[i];
+    rom[sizeof(rom) - 1] = -sum;
+
+    cpu_physical_memory_write_rom(option_rom, rom, sizeof(rom));
+    option_rom_setup_reset(option_rom, sizeof (rom));
+}
+
+static long get_file_size(FILE *f)
+{
+    long where, size;
+
+    /* XXX: on Unix systems, using fstat() probably makes more sense */
+
+    where = ftell(f);
+    fseek(f, 0, SEEK_END);
+    size = ftell(f);
+    fseek(f, where, SEEK_SET);
+
+    return size;
+}
+
+static void load_linux(target_phys_addr_t option_rom,
+                       const char *kernel_filename,
+		       const char *initrd_filename,
+		       const char *kernel_cmdline,
+               target_phys_addr_t max_ram_size)
+{
+    uint16_t protocol;
+    uint32_t gpr[8];
+    uint16_t seg[6];
+    uint16_t real_seg;
+    int setup_size, kernel_size, initrd_size = 0, cmdline_size;
+    uint32_t initrd_max;
+    uint8_t header[1024];
+    target_phys_addr_t real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
+    FILE *f, *fi;
+
+    /* Align to 16 bytes as a paranoia measure */
+    cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
+
+    /* load the kernel header */
+    f = fopen(kernel_filename, "rb");
+    if (!f || !(kernel_size = get_file_size(f)) ||
+	fread(header, 1, 1024, f) != 1024) {
+	fprintf(stderr, "qemu: could not load kernel '%s'\n",
+		kernel_filename);
+	exit(1);
+    }
+
+    /* kernel protocol version */
+#if 0
+    fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
+#endif
+    if (ldl_p(header+0x202) == 0x53726448)
+	protocol = lduw_p(header+0x206);
+    else
+	protocol = 0;
+
+    if (protocol < 0x200 || !(header[0x211] & 0x01)) {
+	/* Low kernel */
+	real_addr    = 0x90000;
+	cmdline_addr = 0x9a000 - cmdline_size;
+	prot_addr    = 0x10000;
+    } else if (protocol < 0x202) {
+	/* High but ancient kernel */
+	real_addr    = 0x90000;
+	cmdline_addr = 0x9a000 - cmdline_size;
+	prot_addr    = 0x100000;
+    } else {
+	/* High and recent kernel */
+	real_addr    = 0x10000;
+	cmdline_addr = 0x20000;
+	prot_addr    = 0x100000;
+    }
+
+#if 0
+    fprintf(stderr,
+	    "qemu: real_addr     = 0x" TARGET_FMT_plx "\n"
+	    "qemu: cmdline_addr  = 0x" TARGET_FMT_plx "\n"
+	    "qemu: prot_addr     = 0x" TARGET_FMT_plx "\n",
+	    real_addr,
+	    cmdline_addr,
+	    prot_addr);
+#endif
+
+    /* highest address for loading the initrd */
+    if (protocol >= 0x203)
+	initrd_max = ldl_p(header+0x22c);
+    else
+	initrd_max = 0x37ffffff;
+
+    if (initrd_max >= max_ram_size-ACPI_DATA_SIZE)
+    	initrd_max = max_ram_size-ACPI_DATA_SIZE-1;
+
+    /* kernel command line */
+    pstrcpy_targphys(cmdline_addr, 4096, kernel_cmdline);
+
+    if (protocol >= 0x202) {
+	stl_p(header+0x228, cmdline_addr);
+    } else {
+	stw_p(header+0x20, 0xA33F);
+	stw_p(header+0x22, cmdline_addr-real_addr);
+    }
+
+    /* loader type */
+    /* High nybble = B reserved for Qemu; low nybble is revision number.
+       If this code is substantially changed, you may want to consider
+       incrementing the revision. */
+    if (protocol >= 0x200)
+	header[0x210] = 0xB0;
+
+    /* heap */
+    if (protocol >= 0x201) {
+	header[0x211] |= 0x80;	/* CAN_USE_HEAP */
+	stw_p(header+0x224, cmdline_addr-real_addr-0x200);
+    }
+
+    /* load initrd */
+    if (initrd_filename) {
+	if (protocol < 0x200) {
+	    fprintf(stderr, "qemu: linux kernel too old to load a ram disk %s, %s, %s\n",
+			kernel_filename, initrd_filename, kernel_cmdline);
+	    exit(1);
+	}
+
+	fi = fopen(initrd_filename, "rb");
+	if (!fi) {
+	    fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+		    initrd_filename);
+	    exit(1);
+	}
+
+	initrd_size = get_file_size(fi);
+	initrd_addr = (initrd_max-initrd_size) & ~4095;
+
+	if (!fread_targphys_ok(initrd_addr, initrd_size, fi)) {
+	    fprintf(stderr, "qemu: read error on initial ram disk '%s'\n",
+		    initrd_filename);
+	    exit(1);
+	}
+	fclose(fi);
+
+	stl_p(header+0x218, initrd_addr);
+	stl_p(header+0x21c, initrd_size);
+    }
+
+    /* store the finalized header and load the rest of the kernel */
+    cpu_physical_memory_write(real_addr, header, 1024);
+
+    setup_size = header[0x1f1];
+    if (setup_size == 0)
+	setup_size = 4;
+
+    setup_size = (setup_size+1)*512;
+    kernel_size -= setup_size;	/* Size of protected-mode code */
+
+    if (!fread_targphys_ok(real_addr+1024, setup_size-1024, f) ||
+	!fread_targphys_ok(prot_addr, kernel_size, f)) {
+	fprintf(stderr, "qemu: read error on kernel '%s'\n",
+		kernel_filename);
+	exit(1);
+    }
+    fclose(f);
+
+    /* generate bootsector to set up the initial register state */
+    real_seg = real_addr >> 4;
+    seg[0] = seg[2] = seg[3] = seg[4] = seg[4] = real_seg;
+    seg[1] = real_seg+0x20;	/* CS */
+    memset(gpr, 0, sizeof gpr);
+    gpr[4] = cmdline_addr-real_addr-16;	/* SP (-16 is paranoia) */
+
+    option_rom_setup_reset(real_addr, setup_size);
+    option_rom_setup_reset(prot_addr, kernel_size);
+    option_rom_setup_reset(cmdline_addr, cmdline_size);
+    if (initrd_filename)
+        option_rom_setup_reset(initrd_addr, initrd_size);
+
+    generate_bootsect(option_rom, gpr, seg, 0);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+    cpu_reset(env);
+}
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+#define NE2000_NB_MAX 6
+
+static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
+static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+/* static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; */
+
+#ifdef HAS_AUDIO
+static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
+{
+    struct soundhw *c;
+
+    for (c = soundhw; c->name; ++c) {
+        if (c->enabled) {
+            if (c->isa) {
+                c->init.init_isa(pic);
+            } else {
+                if (pci_bus) {
+                    c->init.init_pci(pci_bus);
+                }
+            }
+        }
+    }
+}
+#endif
+
+static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic)
+{
+    static int nb_ne2k = 0;
+
+    if (nb_ne2k == NE2000_NB_MAX)
+        return;
+    isa_ne2000_init(ne2000_io[nb_ne2k], pic[ne2000_irq[nb_ne2k]], nd);
+    nb_ne2k++;
+}
+
+static int load_option_rom(const char *oprom, target_phys_addr_t start,
+                           target_phys_addr_t end)
+{
+        int size;
+        char *filename;
+
+        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, oprom);
+        if (filename) {
+            size = get_image_size(filename);
+            if (size > 0 && start + size > end) {
+                fprintf(stderr, "Not enough space to load option rom '%s'\n",
+                        oprom);
+                exit(1);
+            }
+            size = load_image_targphys(filename, start, end - start);
+            qemu_free(filename);
+        } else {
+            size = -1;
+        }
+        if (size < 0) {
+            fprintf(stderr, "Could not load option rom '%s'\n", oprom);
+            exit(1);
+        }
+        /* Round up optiom rom size to the next 2k boundary */
+        size = (size + 2047) & ~2047;
+        option_rom_setup_reset(start, size);
+        return size;
+}
+
+int cpu_is_bsp(CPUState *env)
+{
+	return env->cpuid_apic_id == 0;
+}
+
+static struct goldfish_device event0_device = {
+    .name = "goldfish_events",
+    .id = 0,
+    .size = 0x1000,
+    .irq_count = 1
+};
+
+static struct goldfish_device nand_device = {
+    .name = "goldfish_nand",
+    .id = 0,
+    .size = 0x1000
+};
+
+void goldfish_memlog_init(uint32_t base);
+
+/* PC hardware initialisation */
+static void pc_init1(ram_addr_t ram_size,
+                     const char *boot_device,
+                     const char *kernel_filename, const char *kernel_cmdline,
+                     const char *initrd_filename,
+                     int pci_enabled, const char *cpu_model)
+{
+    char *filename;
+    int ret, linux_boot, i;
+    ram_addr_t ram_addr, bios_offset, option_rom_offset;
+    ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
+    int bios_size, isa_bios_size, oprom_area_size;
+    PCIBus *pci_bus;
+    int piix3_devfn = -1;
+    CPUState *env;
+    qemu_irq *cpu_irq;
+    qemu_irq *i8259;
+#ifndef CONFIG_ANDROID
+    int index;
+#endif
+    BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+#ifndef CONFIG_ANDROID
+    BlockDriverState *fd[MAX_FD];
+#endif
+    int using_vga = cirrus_vga_enabled || std_vga_enabled || vmsvga_enabled;
+
+    if (ram_size >= 0xe0000000 ) {
+        above_4g_mem_size = ram_size - 0xe0000000;
+        below_4g_mem_size = 0xe0000000;
+    } else {
+        below_4g_mem_size = ram_size;
+    }
+
+    linux_boot = (kernel_filename != NULL);
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+#ifdef TARGET_X86_64
+        cpu_model = "qemu64";
+#else
+        cpu_model = "qemu32";
+#endif
+    }
+    
+    for(i = 0; i < smp_cpus; i++) {
+        env = cpu_init(cpu_model);
+        if (!env) {
+            fprintf(stderr, "Unable to find x86 CPU definition\n");
+            exit(1);
+        }
+        if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) {
+            env->cpuid_apic_id = env->cpu_index;
+            apic_init(env);
+        }
+        qemu_register_reset(main_cpu_reset, 0, env);
+    }
+#ifndef CONFIG_ANDROID
+    vmport_init();
+#endif
+    /* allocate RAM */
+    ram_addr = qemu_ram_alloc(0xa0000);
+    cpu_register_physical_memory(0, 0xa0000, ram_addr);
+
+    /* Allocate, even though we won't register, so we don't break the
+     * phys_ram_base + PA assumption. This range includes vga (0xa0000 - 0xc0000),
+     * and some bios areas, which will be registered later
+     */
+    ram_addr = qemu_ram_alloc(0x100000 - 0xa0000);
+    ram_addr = qemu_ram_alloc(below_4g_mem_size - 0x100000);
+    cpu_register_physical_memory(0x100000,
+                 below_4g_mem_size - 0x100000,
+                 ram_addr);
+
+    /* above 4giga memory allocation */
+    if (above_4g_mem_size > 0) {
+#if TARGET_PHYS_ADDR_BITS == 32
+        hw_error("To much RAM for 32-bit physical address");
+#else
+        ram_addr = qemu_ram_alloc(above_4g_mem_size);
+        cpu_register_physical_memory(0x100000000ULL,
+                                     above_4g_mem_size,
+                                     ram_addr);
+#endif
+    }
+
+
+    /* BIOS load */
+    if (bios_name == NULL)
+        bios_name = BIOS_FILENAME;
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+    if (filename) {
+        bios_size = get_image_size(filename);
+    } else {
+        bios_size = -1;
+    }
+    if (bios_size <= 0 ||
+        (bios_size % 65536) != 0) {
+        goto bios_error;
+    }
+    bios_offset = qemu_ram_alloc(bios_size);
+    ret = load_image(filename, qemu_get_ram_ptr(bios_offset));
+    if (ret != bios_size) {
+    bios_error:
+        fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
+        exit(1);
+    }
+    if (filename) {
+        qemu_free(filename);
+    }
+    /* map the last 128KB of the BIOS in ISA space */
+    isa_bios_size = bios_size;
+    if (isa_bios_size > (128 * 1024))
+        isa_bios_size = 128 * 1024;
+    cpu_register_physical_memory(0x100000 - isa_bios_size,
+                                 isa_bios_size,
+                                 (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
+
+
+
+    option_rom_offset = qemu_ram_alloc(0x20000);
+    oprom_area_size = 0;
+    cpu_register_physical_memory(0xc0000, 0x20000, option_rom_offset);
+
+    if (using_vga) {
+        const char *vgabios_filename;
+        /* VGA BIOS load */
+        if (cirrus_vga_enabled) {
+            vgabios_filename = VGABIOS_CIRRUS_FILENAME;
+        } else {
+            vgabios_filename = VGABIOS_FILENAME;
+        }
+        oprom_area_size = load_option_rom(vgabios_filename, 0xc0000, 0xe0000);
+    }
+    /* Although video roms can grow larger than 0x8000, the area between
+     * 0xc0000 - 0xc8000 is reserved for them. It means we won't be looking
+     * for any other kind of option rom inside this area */
+    if (oprom_area_size < 0x8000)
+        oprom_area_size = 0x8000;
+
+    if (linux_boot) {
+        load_linux(0xc0000 + oprom_area_size,
+                   kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
+        oprom_area_size += 2048;
+    }
+
+    for (i = 0; i < nb_option_roms; i++) {
+        oprom_area_size += load_option_rom(option_rom[i],
+                                           0xc0000 + oprom_area_size, 0xe0000);
+    }
+
+    /* map all the bios at the top of memory */
+    cpu_register_physical_memory((uint32_t)(-bios_size),
+                                 bios_size, bios_offset | IO_MEM_ROM);
+
+    bochs_bios_init();
+
+    cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
+    i8259 = i8259_init(cpu_irq[0]);
+    ferr_irq = i8259[13];
+
+#define IRQ_PDEV_BUS 4
+    goldfish_device_init(i8259, 0xff010000, 0x7f0000, 5, 5);
+    goldfish_device_bus_init(0xff001000, IRQ_PDEV_BUS);
+
+    if (android_hw->hw_battery)
+        goldfish_battery_init();
+
+    goldfish_memlog_init(0);
+
+#ifdef CONFIG_NAND
+    goldfish_add_device_no_io(&nand_device);
+    nand_dev_init(nand_device.base);
+#endif
+
+    {
+        DriveInfo* info = drive_get( IF_IDE, 0, 0 );
+        if (info != NULL) {
+            goldfish_mmc_init(0xff005000, 0, info->bdrv);
+        }
+    }
+
+    if (pci_enabled) {
+        pci_bus = i440fx_init(&i440fx_state, i8259);
+        piix3_devfn = piix3_init(pci_bus, -1);
+    } else {
+        pci_bus = NULL;
+    }
+
+    /* init basic PC hardware */
+    register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
+
+    register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
+
+#ifndef CONFIG_ANDROID
+    if (cirrus_vga_enabled) {
+        if (pci_enabled) {
+            pci_cirrus_vga_init(pci_bus);
+        } else {
+            isa_cirrus_vga_init();
+        }
+    } else if (vmsvga_enabled) {
+        if (pci_enabled)
+            pci_vmsvga_init(pci_bus);
+        else
+            fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+    } else if (std_vga_enabled) {
+        if (pci_enabled) {
+            pci_vga_init(pci_bus, 0, 0);
+        } else {
+            isa_vga_init();
+        }
+    }
+#endif
+
+    rtc_state = rtc_init(0x70, i8259[8], 2000);
+
+    qemu_register_boot_set(pc_boot_set, rtc_state);
+
+    register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
+    register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+
+    if (pci_enabled) {
+        ioapic = ioapic_init();
+    }
+    pit = pit_init(0x40, i8259[0]);
+
+#ifndef CONFIG_ANDROID
+    pcspk_init(pit);
+
+    if (!no_hpet) {
+        hpet_init(i8259);
+    }
+#endif
+
+    if (pci_enabled) {
+        pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
+    }
+
+    goldfish_tty_add(serial_hds[0], 0, 0, 0);
+    for(i = 1; i < MAX_SERIAL_PORTS; i++) {
+        if(serial_hds[i]) {
+            goldfish_tty_add(serial_hds[i], i, 0, 0);
+        }
+    }
+
+#ifndef CONFIG_ANDROID
+    for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+        if (serial_hds[i]) {
+            serial_init(serial_io[i], i8259[serial_irq[i]], 115200,
+                        serial_hds[i]);
+        }
+    }
+
+    for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+        if (parallel_hds[i]) {
+            parallel_init(parallel_io[i], i8259[parallel_irq[i]],
+                          parallel_hds[i]);
+        }
+    }
+#endif
+
+    watchdog_pc_init(pci_bus);
+
+    for(i = 0; i < nb_nics; i++) {
+        NICInfo *nd = &nd_table[i];
+
+        if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
+            pc_init_ne2k_isa(nd, i8259);
+        else
+            pci_nic_init(pci_bus, nd, -1, "ne2k_pci");
+    }
+
+#ifdef CONFIG_ANDROID
+    for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++)
+       hd[i] = NULL;
+#else
+    qemu_system_hot_add_init();
+
+    if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+        fprintf(stderr, "qemu: too many IDE bus\n");
+        exit(1);
+    }
+
+    for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+        index = drive_get_index(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+	if (index != -1)
+	    hd[i] = drives_table[index].bdrv;
+	else
+	    hd[i] = NULL;
+    }
+
+    if (pci_enabled) {
+        pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1, i8259);
+    } else {
+        for(i = 0; i < MAX_IDE_BUS; i++) {
+            isa_ide_init(ide_iobase[i], ide_iobase2[i], i8259[ide_irq[i]],
+	                 hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
+        }
+    }
+#endif
+
+    i8042_init(i8259[1], i8259[12], 0x60);
+    DMA_init(0);
+
+    goldfish_fb_init(0);
+
+    goldfish_add_device_no_io(&event0_device);
+    events_dev_init(event0_device.base, i8259[event0_device.irq]);
+
+#ifdef HAS_AUDIO
+    audio_init(pci_enabled ? pci_bus : NULL, i8259);
+#endif
+
+#ifndef CONFIG_ANDROID
+    for(i = 0; i < MAX_FD; i++) {
+        index = drive_get_index(IF_FLOPPY, 0, i);
+	if (index != -1)
+	    fd[i] = drives_table[index].bdrv;
+	else
+	    fd[i] = NULL;
+    }
+
+    floppy_controller = fdctrl_init(i8259[6], 2, 0, 0x3f0, fd);
+#endif
+
+    cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, hd);
+
+#ifndef CONFIG_ANDROID
+    if (pci_enabled && usb_enabled) {
+        usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+    }
+
+    if (pci_enabled && acpi_enabled) {
+        uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+        i2c_bus *smbus;
+
+        /* TODO: Populate SPD eeprom data.  */
+        smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, i8259[9]);
+        for (i = 0; i < 8; i++) {
+            DeviceState *eeprom;
+            eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+            qdev_set_prop_int(eeprom, "address", 0x50 + i);
+            qdev_set_prop_ptr(eeprom, "data", eeprom_buf + (i * 256));
+            qdev_init(eeprom);
+        }
+    }
+#endif
+
+    if (i440fx_state) {
+        i440fx_init_memory_mappings(i440fx_state);
+    }
+
+    if (pci_enabled) {
+	int max_bus;
+        int bus;
+
+        max_bus = drive_get_max_bus(IF_SCSI);
+	for (bus = 0; bus <= max_bus; bus++) {
+            pci_create_simple(pci_bus, -1, "lsi53c895a");
+        }
+    }
+#ifndef CONFIG_ANDROID
+    /* Add virtio block devices */
+    if (pci_enabled) {
+        int index;
+        int unit_id = 0;
+
+        while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) {
+            pci_create_simple(pci_bus, -1, "virtio-blk-pci");
+            unit_id++;
+        }
+    }
+
+    /* Add virtio balloon device */
+    if (pci_enabled && !no_virtio_balloon) {
+        pci_create_simple(pci_bus, -1, "virtio-balloon-pci");
+    }
+
+    /* Add virtio console devices */
+    if (pci_enabled) {
+        for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
+            if (virtcon_hds[i]) {
+                pci_create_simple(pci_bus, -1, "virtio-console-pci");
+            }
+        }
+    }
+#endif
+}
+
+static void pc_init_pci(ram_addr_t ram_size,
+                        const char *boot_device,
+                        const char *kernel_filename,
+                        const char *kernel_cmdline,
+                        const char *initrd_filename,
+                        const char *cpu_model)
+{
+    pc_init1(ram_size, boot_device,
+             kernel_filename, kernel_cmdline,
+             initrd_filename, 1, cpu_model);
+}
+
+static void pc_init_isa(ram_addr_t ram_size,
+                        const char *boot_device,
+                        const char *kernel_filename,
+                        const char *kernel_cmdline,
+                        const char *initrd_filename,
+                        const char *cpu_model)
+{
+    pc_init1(ram_size, boot_device,
+             kernel_filename, kernel_cmdline,
+             initrd_filename, 0, cpu_model);
+}
+
+/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+   BIOS will read it and start S3 resume at POST Entry */
+void cmos_set_s3_resume(void)
+{
+    if (rtc_state)
+        rtc_set_memory(rtc_state, 0xF, 0xFE);
+}
+
+static QEMUMachine pc_machine = {
+    .name = "pc",
+    .desc = "Standard PC",
+    .init = pc_init_pci,
+    .max_cpus = 255,
+    .is_default = 1,
+};
+
+static QEMUMachine isapc_machine = {
+    .name = "isapc",
+    .desc = "ISA-only PC",
+    .init = pc_init_isa,
+    .max_cpus = 1,
+};
+
+static void pc_machine_init(void)
+{
+    qemu_register_machine(&pc_machine);
+    qemu_register_machine(&isapc_machine);
+}
+
+machine_init(pc_machine_init);
diff --git a/hw/pckbd.c b/hw/pckbd.c
new file mode 100644
index 0000000..e1c6d40
--- /dev/null
+++ b/hw/pckbd.c
@@ -0,0 +1,446 @@
+/*
+ * QEMU PC keyboard emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "ps2.h"
+#include "sysemu.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/*	Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE	0x20	/* Read mode bits */
+#define KBD_CCMD_WRITE_MODE	0x60	/* Write mode bits */
+#define KBD_CCMD_GET_VERSION	0xA1	/* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE	0xA7	/* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE	0xA8	/* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE	0xA9	/* Mouse interface test */
+#define KBD_CCMD_SELF_TEST	0xAA	/* Controller self test */
+#define KBD_CCMD_KBD_TEST	0xAB	/* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE	0xAD	/* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE	0xAE	/* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT    0xC0    /* read input port */
+#define KBD_CCMD_READ_OUTPORT	0xD0    /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT	0xD1    /* write output port */
+#define KBD_CCMD_WRITE_OBUF	0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF	0xD3    /* Write to output buffer as if
+					   initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE	0xD4	/* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20    0xDD    /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20     0xDF    /* HP vectra only ? */
+#define KBD_CCMD_RESET	        0xFE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
+#define KBD_CMD_ECHO     	0xEE
+#define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
+#define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
+#define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
+#define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
+#define KBD_CMD_RESET		0xFF	/* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR		0xAA	/* Power on reset */
+#define KBD_REPLY_ACK		0xFA	/* Command ACK */
+#define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
+
+/* Status Register Bits */
+#define KBD_STAT_OBF 		0x01	/* Keyboard output buffer full */
+#define KBD_STAT_IBF 		0x02	/* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST	0x04	/* Self test successful */
+#define KBD_STAT_CMD		0x08	/* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED	0x10	/* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF	0x20	/* Mouse output buffer full */
+#define KBD_STAT_GTO 		0x40	/* General receive/xmit timeout */
+#define KBD_STAT_PERR 		0x80	/* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT	0x01	/* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT	0x02	/* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 		0x04	/* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK	0x08	/* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD	0x10	/* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE	0x20	/* Disable mouse interface */
+#define KBD_MODE_KCC 		0x40	/* Scan code conversion to PC format */
+#define KBD_MODE_RFU		0x80
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
+#define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
+#define AUX_SET_RES		0xE8	/* Set resolution */
+#define AUX_GET_SCALE		0xE9	/* Get scaling factor */
+#define AUX_SET_STREAM		0xEA	/* Set stream mode */
+#define AUX_POLL		0xEB	/* Poll */
+#define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
+#define AUX_SET_WRAP		0xEE	/* Set wrap mode */
+#define AUX_SET_REMOTE		0xF0	/* Set remote mode */
+#define AUX_GET_TYPE		0xF2	/* Get type */
+#define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
+#define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
+#define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
+#define AUX_SET_DEFAULT		0xF6
+#define AUX_RESET		0xFF	/* Reset aux device */
+#define AUX_ACK			0xFA	/* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE     0x40
+#define MOUSE_STATUS_ENABLED    0x20
+#define MOUSE_STATUS_SCALE21    0x10
+
+#define KBD_PENDING_KBD         1
+#define KBD_PENDING_AUX         2
+
+typedef struct KBDState {
+    uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
+    uint8_t status;
+    uint8_t mode;
+    /* Bitmask of devices with data available.  */
+    uint8_t pending;
+    void *kbd;
+    void *mouse;
+
+    qemu_irq irq_kbd;
+    qemu_irq irq_mouse;
+    target_phys_addr_t mask;
+} KBDState;
+
+static KBDState kbd_state;
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
+   incorrect, but it avoids having to simulate exact delays */
+static void kbd_update_irq(KBDState *s)
+{
+    int irq_kbd_level, irq_mouse_level;
+
+    irq_kbd_level = 0;
+    irq_mouse_level = 0;
+    s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+    if (s->pending) {
+        s->status |= KBD_STAT_OBF;
+        /* kbd data takes priority over aux data.  */
+        if (s->pending == KBD_PENDING_AUX) {
+            s->status |= KBD_STAT_MOUSE_OBF;
+            if (s->mode & KBD_MODE_MOUSE_INT)
+                irq_mouse_level = 1;
+        } else {
+            if ((s->mode & KBD_MODE_KBD_INT) &&
+                !(s->mode & KBD_MODE_DISABLE_KBD))
+                irq_kbd_level = 1;
+        }
+    }
+    qemu_set_irq(s->irq_kbd, irq_kbd_level);
+    qemu_set_irq(s->irq_mouse, irq_mouse_level);
+}
+
+static void kbd_update_kbd_irq(void *opaque, int level)
+{
+    KBDState *s = (KBDState *)opaque;
+
+    if (level)
+        s->pending |= KBD_PENDING_KBD;
+    else
+        s->pending &= ~KBD_PENDING_KBD;
+    kbd_update_irq(s);
+}
+
+static void kbd_update_aux_irq(void *opaque, int level)
+{
+    KBDState *s = (KBDState *)opaque;
+
+    if (level)
+        s->pending |= KBD_PENDING_AUX;
+    else
+        s->pending &= ~KBD_PENDING_AUX;
+    kbd_update_irq(s);
+}
+
+static uint32_t kbd_read_status(void *opaque, uint32_t addr)
+{
+    KBDState *s = opaque;
+    int val;
+    val = s->status;
+#if defined(DEBUG_KBD)
+    printf("kbd: read status=0x%02x\n", val);
+#endif
+    return val;
+}
+
+static void kbd_queue(KBDState *s, int b, int aux)
+{
+    if (aux)
+        ps2_queue(s->mouse, b);
+    else
+        ps2_queue(s->kbd, b);
+}
+
+static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
+{
+    KBDState *s = opaque;
+
+#ifdef DEBUG_KBD
+    printf("kbd: write cmd=0x%02x\n", val);
+#endif
+    switch(val) {
+    case KBD_CCMD_READ_MODE:
+        kbd_queue(s, s->mode, 0);
+        break;
+    case KBD_CCMD_WRITE_MODE:
+    case KBD_CCMD_WRITE_OBUF:
+    case KBD_CCMD_WRITE_AUX_OBUF:
+    case KBD_CCMD_WRITE_MOUSE:
+    case KBD_CCMD_WRITE_OUTPORT:
+        s->write_cmd = val;
+        break;
+    case KBD_CCMD_MOUSE_DISABLE:
+        s->mode |= KBD_MODE_DISABLE_MOUSE;
+        break;
+    case KBD_CCMD_MOUSE_ENABLE:
+        s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+        break;
+    case KBD_CCMD_TEST_MOUSE:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_SELF_TEST:
+        s->status |= KBD_STAT_SELFTEST;
+        kbd_queue(s, 0x55, 0);
+        break;
+    case KBD_CCMD_KBD_TEST:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_KBD_DISABLE:
+        s->mode |= KBD_MODE_DISABLE_KBD;
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_KBD_ENABLE:
+        s->mode &= ~KBD_MODE_DISABLE_KBD;
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_READ_INPORT:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_READ_OUTPORT:
+        /* XXX: check that */
+#ifdef TARGET_I386
+        val = 0x01 | (ioport_get_a20() << 1);
+#else
+        val = 0x01;
+#endif
+        if (s->status & KBD_STAT_OBF)
+            val |= 0x10;
+        if (s->status & KBD_STAT_MOUSE_OBF)
+            val |= 0x20;
+        kbd_queue(s, val, 0);
+        break;
+#ifdef TARGET_I386
+    case KBD_CCMD_ENABLE_A20:
+        ioport_set_a20(1);
+        break;
+    case KBD_CCMD_DISABLE_A20:
+        ioport_set_a20(0);
+        break;
+#endif
+    case KBD_CCMD_RESET:
+        qemu_system_reset_request();
+        break;
+    case 0xff:
+        /* ignore that - I don't know what is its use */
+        break;
+    default:
+        fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
+        break;
+    }
+}
+
+static uint32_t kbd_read_data(void *opaque, uint32_t addr)
+{
+    KBDState *s = opaque;
+    uint32_t val;
+
+    if (s->pending == KBD_PENDING_AUX)
+        val = ps2_read_data(s->mouse);
+    else
+        val = ps2_read_data(s->kbd);
+
+#if defined(DEBUG_KBD)
+    printf("kbd: read data=0x%02x\n", val);
+#endif
+    return val;
+}
+
+static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+    KBDState *s = opaque;
+
+#ifdef DEBUG_KBD
+    printf("kbd: write data=0x%02x\n", val);
+#endif
+
+    switch(s->write_cmd) {
+    case 0:
+        ps2_write_keyboard(s->kbd, val);
+        break;
+    case KBD_CCMD_WRITE_MODE:
+        s->mode = val;
+        ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
+        /* ??? */
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_WRITE_OBUF:
+        kbd_queue(s, val, 0);
+        break;
+    case KBD_CCMD_WRITE_AUX_OBUF:
+        kbd_queue(s, val, 1);
+        break;
+    case KBD_CCMD_WRITE_OUTPORT:
+#ifdef TARGET_I386
+        ioport_set_a20((val >> 1) & 1);
+#endif
+        if (!(val & 1)) {
+            qemu_system_reset_request();
+        }
+        break;
+    case KBD_CCMD_WRITE_MOUSE:
+        ps2_write_mouse(s->mouse, val);
+        break;
+    default:
+        break;
+    }
+    s->write_cmd = 0;
+}
+
+static void kbd_reset(void *opaque)
+{
+    KBDState *s = opaque;
+
+    s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+    s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+}
+
+static void kbd_save(QEMUFile* f, void* opaque)
+{
+    KBDState *s = (KBDState*)opaque;
+
+    qemu_put_8s(f, &s->write_cmd);
+    qemu_put_8s(f, &s->status);
+    qemu_put_8s(f, &s->mode);
+    qemu_put_8s(f, &s->pending);
+}
+
+static int kbd_load(QEMUFile* f, void* opaque, int version_id)
+{
+    KBDState *s = (KBDState*)opaque;
+
+    if (version_id != 3)
+        return -EINVAL;
+    qemu_get_8s(f, &s->write_cmd);
+    qemu_get_8s(f, &s->status);
+    qemu_get_8s(f, &s->mode);
+    qemu_get_8s(f, &s->pending);
+    return 0;
+}
+
+void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base)
+{
+    KBDState *s = &kbd_state;
+
+    s->irq_kbd = kbd_irq;
+    s->irq_mouse = mouse_irq;
+
+    kbd_reset(s);
+    register_savevm("pckbd", 0, 3, kbd_save, kbd_load, s);
+    register_ioport_read(io_base, 1, 1, kbd_read_data, s);
+    register_ioport_write(io_base, 1, 1, kbd_write_data, s);
+    register_ioport_read(io_base + 4, 1, 1, kbd_read_status, s);
+    register_ioport_write(io_base + 4, 1, 1, kbd_write_command, s);
+
+    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+#ifndef CONFIG_ANDROID
+#ifdef TARGET_I386
+    vmmouse_init(s->mouse);
+#endif
+#endif
+    qemu_register_reset(kbd_reset, 0, s);
+}
+
+/* Memory mapped interface */
+static uint32_t kbd_mm_readb (void *opaque, target_phys_addr_t addr)
+{
+    KBDState *s = opaque;
+
+    if (addr & s->mask)
+        return kbd_read_status(s, 0) & 0xff;
+    else
+        return kbd_read_data(s, 0) & 0xff;
+}
+
+static void kbd_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    KBDState *s = opaque;
+
+    if (addr & s->mask)
+        kbd_write_command(s, 0, value & 0xff);
+    else
+        kbd_write_data(s, 0, value & 0xff);
+}
+
+static CPUReadMemoryFunc *kbd_mm_read[] = {
+    &kbd_mm_readb,
+    &kbd_mm_readb,
+    &kbd_mm_readb,
+};
+
+static CPUWriteMemoryFunc *kbd_mm_write[] = {
+    &kbd_mm_writeb,
+    &kbd_mm_writeb,
+    &kbd_mm_writeb,
+};
+
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+                   target_phys_addr_t base, ram_addr_t size,
+                   target_phys_addr_t mask)
+{
+    KBDState *s = &kbd_state;
+    int s_io_memory;
+
+    s->irq_kbd = kbd_irq;
+    s->irq_mouse = mouse_irq;
+    s->mask = mask;
+
+    kbd_reset(s);
+    register_savevm("pckbd", 0, 3, kbd_save, kbd_load, s);
+    s_io_memory = cpu_register_io_memory(kbd_mm_read, kbd_mm_write, s);
+    cpu_register_physical_memory(base, size, s_io_memory);
+
+    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+#ifndef CONFIG_ANDROID
+#ifdef TARGET_I386
+    vmmouse_init(s->mouse);
+#endif
+#endif
+    qemu_register_reset(kbd_reset, 0, s);
+}
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
new file mode 100644
index 0000000..67e6309
--- /dev/null
+++ b/hw/piix_pci.c
@@ -0,0 +1,374 @@
+/*
+ * QEMU i440FX/PIIX3 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+
+typedef uint32_t pci_addr_t;
+#include "pci_host.h"
+
+typedef PCIHostState I440FXState;
+
+static void i440fx_addr_writel(void* opaque, uint32_t addr, uint32_t val)
+{
+    I440FXState *s = opaque;
+    s->config_reg = val;
+}
+
+static uint32_t i440fx_addr_readl(void* opaque, uint32_t addr)
+{
+    I440FXState *s = opaque;
+    return s->config_reg;
+}
+
+static void piix3_set_irq(qemu_irq *pic, int irq_num, int level);
+
+/* return the global irq number corresponding to a given device irq
+   pin. We could also use the bus number to have a more precise
+   mapping. */
+static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
+{
+    int slot_addend;
+    slot_addend = (pci_dev->devfn >> 3) - 1;
+    return (irq_num + slot_addend) & 3;
+}
+
+static target_phys_addr_t isa_page_descs[384 / 4];
+static uint8_t smm_enabled;
+static int pci_irq_levels[4];
+
+static void update_pam(PCIDevice *d, uint32_t start, uint32_t end, int r)
+{
+    uint32_t addr;
+
+    //    printf("ISA mapping %08x-0x%08x: %d\n", start, end, r);
+    switch(r) {
+    case 3:
+        /* RAM */
+        cpu_register_physical_memory(start, end - start,
+                                     start);
+        break;
+    case 1:
+        /* ROM (XXX: not quite correct) */
+        cpu_register_physical_memory(start, end - start,
+                                     start | IO_MEM_ROM);
+        break;
+    case 2:
+    case 0:
+        /* XXX: should distinguish read/write cases */
+        for(addr = start; addr < end; addr += 4096) {
+            cpu_register_physical_memory(addr, 4096,
+                                         isa_page_descs[(addr - 0xa0000) >> 12]);
+        }
+        break;
+    }
+}
+
+static void i440fx_update_memory_mappings(PCIDevice *d)
+{
+    int i, r;
+    uint32_t smram, addr;
+
+    update_pam(d, 0xf0000, 0x100000, (d->config[0x59] >> 4) & 3);
+    for(i = 0; i < 12; i++) {
+        r = (d->config[(i >> 1) + 0x5a] >> ((i & 1) * 4)) & 3;
+        update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r);
+    }
+    smram = d->config[0x72];
+    if ((smm_enabled && (smram & 0x08)) || (smram & 0x40)) {
+        cpu_register_physical_memory(0xa0000, 0x20000, 0xa0000);
+    } else {
+        for(addr = 0xa0000; addr < 0xc0000; addr += 4096) {
+            cpu_register_physical_memory(addr, 4096,
+                                         isa_page_descs[(addr - 0xa0000) >> 12]);
+        }
+    }
+}
+
+void i440fx_set_smm(PCIDevice *d, int val)
+{
+    val = (val != 0);
+    if (smm_enabled != val) {
+        smm_enabled = val;
+        i440fx_update_memory_mappings(d);
+    }
+}
+
+
+/* XXX: suppress when better memory API. We make the assumption that
+   no device (in particular the VGA) changes the memory mappings in
+   the 0xa0000-0x100000 range */
+void i440fx_init_memory_mappings(PCIDevice *d)
+{
+    int i;
+    for(i = 0; i < 96; i++) {
+        isa_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000);
+    }
+}
+
+static void i440fx_write_config(PCIDevice *d,
+                                uint32_t address, uint32_t val, int len)
+{
+    /* XXX: implement SMRAM.D_LOCK */
+    pci_default_write_config(d, address, val, len);
+    if ((address >= 0x59 && address <= 0x5f) || address == 0x72)
+        i440fx_update_memory_mappings(d);
+}
+
+static void i440fx_save(QEMUFile* f, void *opaque)
+{
+    PCIDevice *d = opaque;
+    int i;
+
+    pci_device_save(d, f);
+    qemu_put_8s(f, &smm_enabled);
+
+    for (i = 0; i < 4; i++)
+        qemu_put_be32(f, pci_irq_levels[i]);
+}
+
+static int i440fx_load(QEMUFile* f, void *opaque, int version_id)
+{
+    PCIDevice *d = opaque;
+    int ret, i;
+
+    if (version_id > 2)
+        return -EINVAL;
+    ret = pci_device_load(d, f);
+    if (ret < 0)
+        return ret;
+    i440fx_update_memory_mappings(d);
+    qemu_get_8s(f, &smm_enabled);
+
+    if (version_id >= 2)
+        for (i = 0; i < 4; i++)
+            pci_irq_levels[i] = qemu_get_be32(f);
+
+    return 0;
+}
+
+PCIBus *i440fx_init(PCIDevice **pi440fx_state, qemu_irq *pic)
+{
+    PCIBus *b;
+    PCIDevice *d;
+    I440FXState *s;
+
+    s = qemu_mallocz(sizeof(I440FXState));
+    b = pci_register_bus(NULL, "pci", 
+                         piix3_set_irq, pci_slot_get_pirq, pic, 0, 4);
+    s->bus = b;
+
+    register_ioport_write(0xcf8, 4, 4, i440fx_addr_writel, s);
+    register_ioport_read(0xcf8, 4, 4, i440fx_addr_readl, s);
+
+    register_ioport_write(0xcfc, 4, 1, pci_host_data_writeb, s);
+    register_ioport_write(0xcfc, 4, 2, pci_host_data_writew, s);
+    register_ioport_write(0xcfc, 4, 4, pci_host_data_writel, s);
+    register_ioport_read(0xcfc, 4, 1, pci_host_data_readb, s);
+    register_ioport_read(0xcfc, 4, 2, pci_host_data_readw, s);
+    register_ioport_read(0xcfc, 4, 4, pci_host_data_readl, s);
+
+    d = pci_register_device(b, "i440FX", sizeof(PCIDevice), 0,
+                            NULL, i440fx_write_config);
+
+    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_82441);
+    d->config[0x08] = 0x02; // revision
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+    d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type
+
+    d->config[0x72] = 0x02; /* SMRAM */
+
+    register_savevm("I440FX", 0, 2, i440fx_save, i440fx_load, d);
+    *pi440fx_state = d;
+    return b;
+}
+
+/* PIIX3 PCI to ISA bridge */
+
+static PCIDevice *piix3_dev;
+PCIDevice *piix4_dev;
+
+static void piix3_set_irq(qemu_irq *pic, int irq_num, int level)
+{
+    int i, pic_irq, pic_level;
+
+    pci_irq_levels[irq_num] = level;
+
+    /* now we change the pic irq level according to the piix irq mappings */
+    /* XXX: optimize */
+    pic_irq = piix3_dev->config[0x60 + irq_num];
+    if (pic_irq < 16) {
+        /* The pic level is the logical OR of all the PCI irqs mapped
+           to it */
+        pic_level = 0;
+        for (i = 0; i < 4; i++) {
+            if (pic_irq == piix3_dev->config[0x60 + i])
+                pic_level |= pci_irq_levels[i];
+        }
+        qemu_set_irq(pic[pic_irq], pic_level);
+    }
+}
+
+static void piix3_reset(void *opaque)
+{
+    PCIDevice *d = opaque;
+    uint8_t *pci_conf = d->config;
+
+    pci_conf[0x04] = 0x07; // master, memory and I/O
+    pci_conf[0x05] = 0x00;
+    pci_conf[0x06] = 0x00;
+    pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
+    pci_conf[0x4c] = 0x4d;
+    pci_conf[0x4e] = 0x03;
+    pci_conf[0x4f] = 0x00;
+    pci_conf[0x60] = 0x80;
+    pci_conf[0x61] = 0x80;
+    pci_conf[0x62] = 0x80;
+    pci_conf[0x63] = 0x80;
+    pci_conf[0x69] = 0x02;
+    pci_conf[0x70] = 0x80;
+    pci_conf[0x76] = 0x0c;
+    pci_conf[0x77] = 0x0c;
+    pci_conf[0x78] = 0x02;
+    pci_conf[0x79] = 0x00;
+    pci_conf[0x80] = 0x00;
+    pci_conf[0x82] = 0x00;
+    pci_conf[0xa0] = 0x08;
+    pci_conf[0xa2] = 0x00;
+    pci_conf[0xa3] = 0x00;
+    pci_conf[0xa4] = 0x00;
+    pci_conf[0xa5] = 0x00;
+    pci_conf[0xa6] = 0x00;
+    pci_conf[0xa7] = 0x00;
+    pci_conf[0xa8] = 0x0f;
+    pci_conf[0xaa] = 0x00;
+    pci_conf[0xab] = 0x00;
+    pci_conf[0xac] = 0x00;
+    pci_conf[0xae] = 0x00;
+
+    memset(pci_irq_levels, 0, sizeof(pci_irq_levels));
+}
+
+static void piix4_reset(void *opaque)
+{
+    PCIDevice *d = opaque;
+    uint8_t *pci_conf = d->config;
+
+    pci_conf[0x04] = 0x07; // master, memory and I/O
+    pci_conf[0x05] = 0x00;
+    pci_conf[0x06] = 0x00;
+    pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
+    pci_conf[0x4c] = 0x4d;
+    pci_conf[0x4e] = 0x03;
+    pci_conf[0x4f] = 0x00;
+    pci_conf[0x60] = 0x0a; // PCI A -> IRQ 10
+    pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10
+    pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11
+    pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11
+    pci_conf[0x69] = 0x02;
+    pci_conf[0x70] = 0x80;
+    pci_conf[0x76] = 0x0c;
+    pci_conf[0x77] = 0x0c;
+    pci_conf[0x78] = 0x02;
+    pci_conf[0x79] = 0x00;
+    pci_conf[0x80] = 0x00;
+    pci_conf[0x82] = 0x00;
+    pci_conf[0xa0] = 0x08;
+    pci_conf[0xa2] = 0x00;
+    pci_conf[0xa3] = 0x00;
+    pci_conf[0xa4] = 0x00;
+    pci_conf[0xa5] = 0x00;
+    pci_conf[0xa6] = 0x00;
+    pci_conf[0xa7] = 0x00;
+    pci_conf[0xa8] = 0x0f;
+    pci_conf[0xaa] = 0x00;
+    pci_conf[0xab] = 0x00;
+    pci_conf[0xac] = 0x00;
+    pci_conf[0xae] = 0x00;
+
+    memset(pci_irq_levels, 0, sizeof(pci_irq_levels));
+}
+
+static void piix_save(QEMUFile* f, void *opaque)
+{
+    PCIDevice *d = opaque;
+    pci_device_save(d, f);
+}
+
+static int piix_load(QEMUFile* f, void *opaque, int version_id)
+{
+    PCIDevice *d = opaque;
+    if (version_id != 2)
+        return -EINVAL;
+    return pci_device_load(d, f);
+}
+
+int piix3_init(PCIBus *bus, int devfn)
+{
+    PCIDevice *d;
+    uint8_t *pci_conf;
+
+    d = pci_register_device(bus, "PIIX3", sizeof(PCIDevice),
+                                    devfn, NULL, NULL);
+    register_savevm("PIIX3", 0, 2, piix_save, piix_load, d);
+
+    piix3_dev = d;
+    pci_conf = d->config;
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
+    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
+    pci_conf[PCI_HEADER_TYPE] =
+        PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic
+
+    piix3_reset(d);
+    qemu_register_reset(piix3_reset, 0, d);
+    return d->devfn;
+}
+
+int piix4_init(PCIBus *bus, int devfn)
+{
+    PCIDevice *d;
+    uint8_t *pci_conf;
+
+    d = pci_register_device(bus, "PIIX4", sizeof(PCIDevice),
+                                    devfn, NULL, NULL);
+    register_savevm("PIIX4", 0, 2, piix_save, piix_load, d);
+
+    piix4_dev = d;
+    pci_conf = d->config;
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_0); // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge
+    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
+    pci_conf[PCI_HEADER_TYPE] =
+        PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic
+
+
+    piix4_reset(d);
+    qemu_register_reset(piix4_reset, 0, d);
+    return d->devfn;
+}
diff --git a/hw/ps2.c b/hw/ps2.c
new file mode 100644
index 0000000..9149598
--- /dev/null
+++ b/hw/ps2.c
@@ -0,0 +1,611 @@
+/*
+ * QEMU PS/2 keyboard/mouse emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ps2.h"
+#include "console.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
+#define KBD_CMD_ECHO     	0xEE
+#define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
+#define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
+#define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
+#define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
+#define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
+#define KBD_CMD_RESET		0xFF	/* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR		0xAA	/* Power on reset */
+#define KBD_REPLY_ID		0xAB	/* Keyboard ID */
+#define KBD_REPLY_ACK		0xFA	/* Command ACK */
+#define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
+#define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
+#define AUX_SET_RES		0xE8	/* Set resolution */
+#define AUX_GET_SCALE		0xE9	/* Get scaling factor */
+#define AUX_SET_STREAM		0xEA	/* Set stream mode */
+#define AUX_POLL		0xEB	/* Poll */
+#define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
+#define AUX_SET_WRAP		0xEE	/* Set wrap mode */
+#define AUX_SET_REMOTE		0xF0	/* Set remote mode */
+#define AUX_GET_TYPE		0xF2	/* Get type */
+#define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
+#define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
+#define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
+#define AUX_SET_DEFAULT		0xF6
+#define AUX_RESET		0xFF	/* Reset aux device */
+#define AUX_ACK			0xFA	/* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE     0x40
+#define MOUSE_STATUS_ENABLED    0x20
+#define MOUSE_STATUS_SCALE21    0x10
+
+#define PS2_QUEUE_SIZE 256
+
+typedef struct {
+    uint8_t data[PS2_QUEUE_SIZE];
+    int rptr, wptr, count;
+} PS2Queue;
+
+typedef struct {
+    PS2Queue queue;
+    int32_t write_cmd;
+    void (*update_irq)(void *, int);
+    void *update_arg;
+} PS2State;
+
+typedef struct {
+    PS2State common;
+    int scan_enabled;
+    /* Qemu uses translated PC scancodes internally.  To avoid multiple
+       conversions we do the translation (if any) in the PS/2 emulation
+       not the keyboard controller.  */
+    int translate;
+    int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
+} PS2KbdState;
+
+typedef struct {
+    PS2State common;
+    uint8_t mouse_status;
+    uint8_t mouse_resolution;
+    uint8_t mouse_sample_rate;
+    uint8_t mouse_wrap;
+    uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
+    uint8_t mouse_detect_state;
+    int mouse_dx; /* current values, needed for 'poll' mode */
+    int mouse_dy;
+    int mouse_dz;
+    uint8_t mouse_buttons;
+} PS2MouseState;
+
+/* Table to convert from PC scancodes to raw scancodes.  */
+static const unsigned char ps2_raw_keycode[128] = {
+          0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+         21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+         35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+         50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
+         11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
+        114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
+         71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+         19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+void ps2_queue(void *opaque, int b)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q = &s->queue;
+
+    if (q->count >= PS2_QUEUE_SIZE)
+        return;
+    q->data[q->wptr] = b;
+    if (++q->wptr == PS2_QUEUE_SIZE)
+        q->wptr = 0;
+    q->count++;
+    s->update_irq(s->update_arg, 1);
+}
+
+/*
+   keycode is expressed as follow:
+   bit 7    - 0 key pressed, 1 = key released
+   bits 6-0 - translated scancode set 2
+ */
+static void ps2_put_keycode(void *opaque, int keycode)
+{
+    PS2KbdState *s = opaque;
+
+    /* XXX: add support for scancode sets 1 and 3 */
+    if (!s->translate && keycode < 0xe0 && s->scancode_set == 2)
+      {
+        if (keycode & 0x80)
+            ps2_queue(&s->common, 0xf0);
+        keycode = ps2_raw_keycode[keycode & 0x7f];
+      }
+    ps2_queue(&s->common, keycode);
+}
+
+uint32_t ps2_read_data(void *opaque)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q;
+    int val, index;
+
+    q = &s->queue;
+    if (q->count == 0) {
+        /* NOTE: if no data left, we return the last keyboard one
+           (needed for EMM386) */
+        /* XXX: need a timer to do things correctly */
+        index = q->rptr - 1;
+        if (index < 0)
+            index = PS2_QUEUE_SIZE - 1;
+        val = q->data[index];
+    } else {
+        val = q->data[q->rptr];
+        if (++q->rptr == PS2_QUEUE_SIZE)
+            q->rptr = 0;
+        q->count--;
+        /* reading deasserts IRQ */
+        s->update_irq(s->update_arg, 0);
+        /* reassert IRQs if data left */
+        s->update_irq(s->update_arg, q->count != 0);
+    }
+    return val;
+}
+
+static void ps2_reset_keyboard(PS2KbdState *s)
+{
+    s->scan_enabled = 1;
+    s->scancode_set = 2;
+}
+
+void ps2_write_keyboard(void *opaque, int val)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+
+    switch(s->common.write_cmd) {
+    default:
+    case -1:
+        switch(val) {
+        case 0x00:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case 0x05:
+            ps2_queue(&s->common, KBD_REPLY_RESEND);
+            break;
+        case KBD_CMD_GET_ID:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            /* We emulate a MF2 AT keyboard here */
+            ps2_queue(&s->common, KBD_REPLY_ID);
+            if (s->translate)
+                ps2_queue(&s->common, 0x41);
+            else
+                ps2_queue(&s->common, 0x83);
+            break;
+        case KBD_CMD_ECHO:
+            ps2_queue(&s->common, KBD_CMD_ECHO);
+            break;
+        case KBD_CMD_ENABLE:
+            s->scan_enabled = 1;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_SCANCODE:
+        case KBD_CMD_SET_LEDS:
+        case KBD_CMD_SET_RATE:
+            s->common.write_cmd = val;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET_DISABLE:
+            ps2_reset_keyboard(s);
+            s->scan_enabled = 0;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET_ENABLE:
+            ps2_reset_keyboard(s);
+            s->scan_enabled = 1;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET:
+            ps2_reset_keyboard(s);
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            ps2_queue(&s->common, KBD_REPLY_POR);
+            break;
+        default:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        }
+        break;
+    case KBD_CMD_SCANCODE:
+        if (val == 0) {
+            if (s->scancode_set == 1)
+                ps2_put_keycode(s, 0x43);
+            else if (s->scancode_set == 2)
+                ps2_put_keycode(s, 0x41);
+            else if (s->scancode_set == 3)
+                ps2_put_keycode(s, 0x3f);
+        } else {
+            if (val >= 1 && val <= 3)
+                s->scancode_set = val;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+        }
+        s->common.write_cmd = -1;
+        break;
+    case KBD_CMD_SET_LEDS:
+        ps2_queue(&s->common, KBD_REPLY_ACK);
+        s->common.write_cmd = -1;
+        break;
+    case KBD_CMD_SET_RATE:
+        ps2_queue(&s->common, KBD_REPLY_ACK);
+        s->common.write_cmd = -1;
+        break;
+    }
+}
+
+/* Set the scancode translation mode.
+   0 = raw scancodes.
+   1 = translated scancodes (used by qemu internally).  */
+
+void ps2_keyboard_set_translation(void *opaque, int mode)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+    s->translate = mode;
+}
+
+static void ps2_mouse_send_packet(PS2MouseState *s)
+{
+    unsigned int b;
+    int dx1, dy1, dz1;
+
+    dx1 = s->mouse_dx;
+    dy1 = s->mouse_dy;
+    dz1 = s->mouse_dz;
+    /* XXX: increase range to 8 bits ? */
+    if (dx1 > 127)
+        dx1 = 127;
+    else if (dx1 < -127)
+        dx1 = -127;
+    if (dy1 > 127)
+        dy1 = 127;
+    else if (dy1 < -127)
+        dy1 = -127;
+    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+    ps2_queue(&s->common, b);
+    ps2_queue(&s->common, dx1 & 0xff);
+    ps2_queue(&s->common, dy1 & 0xff);
+    /* extra byte for IMPS/2 or IMEX */
+    switch(s->mouse_type) {
+    default:
+        break;
+    case 3:
+        if (dz1 > 127)
+            dz1 = 127;
+        else if (dz1 < -127)
+                dz1 = -127;
+        ps2_queue(&s->common, dz1 & 0xff);
+        break;
+    case 4:
+        if (dz1 > 7)
+            dz1 = 7;
+        else if (dz1 < -7)
+            dz1 = -7;
+        b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+        ps2_queue(&s->common, b);
+        break;
+    }
+
+    /* update deltas */
+    s->mouse_dx -= dx1;
+    s->mouse_dy -= dy1;
+    s->mouse_dz -= dz1;
+}
+
+static void ps2_mouse_event(void *opaque,
+                            int dx, int dy, int dz, int buttons_state)
+{
+    PS2MouseState *s = opaque;
+
+    /* check if deltas are recorded when disabled */
+    if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+        return;
+
+    s->mouse_dx += dx;
+    s->mouse_dy -= dy;
+    s->mouse_dz += dz;
+    /* XXX: SDL sometimes generates nul events: we delete them */
+    if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
+        s->mouse_buttons == buttons_state)
+	return;
+    s->mouse_buttons = buttons_state;
+
+    if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
+        (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
+        for(;;) {
+            /* if not remote, send event. Multiple events are sent if
+               too big deltas */
+            ps2_mouse_send_packet(s);
+            if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+                break;
+        }
+    }
+}
+
+void ps2_mouse_fake_event(void *opaque)
+{
+    ps2_mouse_event(opaque, 1, 0, 0, 0);
+}
+
+void ps2_write_mouse(void *opaque, int val)
+{
+    PS2MouseState *s = (PS2MouseState *)opaque;
+#ifdef DEBUG_MOUSE
+    printf("kbd: write mouse 0x%02x\n", val);
+#endif
+    switch(s->common.write_cmd) {
+    default:
+    case -1:
+        /* mouse command */
+        if (s->mouse_wrap) {
+            if (val == AUX_RESET_WRAP) {
+                s->mouse_wrap = 0;
+                ps2_queue(&s->common, AUX_ACK);
+                return;
+            } else if (val != AUX_RESET) {
+                ps2_queue(&s->common, val);
+                return;
+            }
+        }
+        switch(val) {
+        case AUX_SET_SCALE11:
+            s->mouse_status &= ~MOUSE_STATUS_SCALE21;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_SCALE21:
+            s->mouse_status |= MOUSE_STATUS_SCALE21;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_STREAM:
+            s->mouse_status &= ~MOUSE_STATUS_REMOTE;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_WRAP:
+            s->mouse_wrap = 1;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_REMOTE:
+            s->mouse_status |= MOUSE_STATUS_REMOTE;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_GET_TYPE:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, s->mouse_type);
+            break;
+        case AUX_SET_RES:
+        case AUX_SET_SAMPLE:
+            s->common.write_cmd = val;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_GET_SCALE:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, s->mouse_status);
+            ps2_queue(&s->common, s->mouse_resolution);
+            ps2_queue(&s->common, s->mouse_sample_rate);
+            break;
+        case AUX_POLL:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_mouse_send_packet(s);
+            break;
+        case AUX_ENABLE_DEV:
+            s->mouse_status |= MOUSE_STATUS_ENABLED;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_DISABLE_DEV:
+            s->mouse_status &= ~MOUSE_STATUS_ENABLED;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_DEFAULT:
+            s->mouse_sample_rate = 100;
+            s->mouse_resolution = 2;
+            s->mouse_status = 0;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_RESET:
+            s->mouse_sample_rate = 100;
+            s->mouse_resolution = 2;
+            s->mouse_status = 0;
+            s->mouse_type = 0;
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, 0xaa);
+            ps2_queue(&s->common, s->mouse_type);
+            break;
+        default:
+            break;
+        }
+        break;
+    case AUX_SET_SAMPLE:
+        s->mouse_sample_rate = val;
+        /* detect IMPS/2 or IMEX */
+        switch(s->mouse_detect_state) {
+        default:
+        case 0:
+            if (val == 200)
+                s->mouse_detect_state = 1;
+            break;
+        case 1:
+            if (val == 100)
+                s->mouse_detect_state = 2;
+            else if (val == 200)
+                s->mouse_detect_state = 3;
+            else
+                s->mouse_detect_state = 0;
+            break;
+        case 2:
+            if (val == 80)
+                s->mouse_type = 3; /* IMPS/2 */
+            s->mouse_detect_state = 0;
+            break;
+        case 3:
+            if (val == 80)
+                s->mouse_type = 4; /* IMEX */
+            s->mouse_detect_state = 0;
+            break;
+        }
+        ps2_queue(&s->common, AUX_ACK);
+        s->common.write_cmd = -1;
+        break;
+    case AUX_SET_RES:
+        s->mouse_resolution = val;
+        ps2_queue(&s->common, AUX_ACK);
+        s->common.write_cmd = -1;
+        break;
+    }
+}
+
+static void ps2_reset(void *opaque)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q;
+    s->write_cmd = -1;
+    q = &s->queue;
+    q->rptr = 0;
+    q->wptr = 0;
+    q->count = 0;
+    s->update_irq(s->update_arg, 0);
+}
+
+static void ps2_common_save (QEMUFile *f, PS2State *s)
+{
+    qemu_put_be32 (f, s->write_cmd);
+    qemu_put_be32 (f, s->queue.rptr);
+    qemu_put_be32 (f, s->queue.wptr);
+    qemu_put_be32 (f, s->queue.count);
+    qemu_put_buffer (f, s->queue.data, sizeof (s->queue.data));
+}
+
+static void ps2_common_load (QEMUFile *f, PS2State *s)
+{
+    s->write_cmd=qemu_get_be32 (f);
+    s->queue.rptr=qemu_get_be32 (f);
+    s->queue.wptr=qemu_get_be32 (f);
+    s->queue.count=qemu_get_be32 (f);
+    qemu_get_buffer (f, s->queue.data, sizeof (s->queue.data));
+}
+
+static void ps2_kbd_save(QEMUFile* f, void* opaque)
+{
+    PS2KbdState *s = (PS2KbdState*)opaque;
+
+    ps2_common_save (f, &s->common);
+    qemu_put_be32(f, s->scan_enabled);
+    qemu_put_be32(f, s->translate);
+    qemu_put_be32(f, s->scancode_set);
+}
+
+static void ps2_mouse_save(QEMUFile* f, void* opaque)
+{
+    PS2MouseState *s = (PS2MouseState*)opaque;
+
+    ps2_common_save (f, &s->common);
+    qemu_put_8s(f, &s->mouse_status);
+    qemu_put_8s(f, &s->mouse_resolution);
+    qemu_put_8s(f, &s->mouse_sample_rate);
+    qemu_put_8s(f, &s->mouse_wrap);
+    qemu_put_8s(f, &s->mouse_type);
+    qemu_put_8s(f, &s->mouse_detect_state);
+    qemu_put_be32(f, s->mouse_dx);
+    qemu_put_be32(f, s->mouse_dy);
+    qemu_put_be32(f, s->mouse_dz);
+    qemu_put_8s(f, &s->mouse_buttons);
+}
+
+static int ps2_kbd_load(QEMUFile* f, void* opaque, int version_id)
+{
+    PS2KbdState *s = (PS2KbdState*)opaque;
+
+    if (version_id != 2 && version_id != 3)
+        return -EINVAL;
+
+    ps2_common_load (f, &s->common);
+    s->scan_enabled=qemu_get_be32(f);
+    s->translate=qemu_get_be32(f);
+    if (version_id == 3)
+        s->scancode_set=qemu_get_be32(f);
+    else
+        s->scancode_set=2;
+    return 0;
+}
+
+static int ps2_mouse_load(QEMUFile* f, void* opaque, int version_id)
+{
+    PS2MouseState *s = (PS2MouseState*)opaque;
+
+    if (version_id != 2)
+        return -EINVAL;
+
+    ps2_common_load (f, &s->common);
+    qemu_get_8s(f, &s->mouse_status);
+    qemu_get_8s(f, &s->mouse_resolution);
+    qemu_get_8s(f, &s->mouse_sample_rate);
+    qemu_get_8s(f, &s->mouse_wrap);
+    qemu_get_8s(f, &s->mouse_type);
+    qemu_get_8s(f, &s->mouse_detect_state);
+    s->mouse_dx=qemu_get_be32(f);
+    s->mouse_dy=qemu_get_be32(f);
+    s->mouse_dz=qemu_get_be32(f);
+    qemu_get_8s(f, &s->mouse_buttons);
+    return 0;
+}
+
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
+{
+    PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState));
+
+    s->common.update_irq = update_irq;
+    s->common.update_arg = update_arg;
+    s->scancode_set = 2;
+    ps2_reset(&s->common);
+    register_savevm("ps2kbd", 0, 3, ps2_kbd_save, ps2_kbd_load, s);
+    //qemu_add_kbd_event_handler(ps2_put_keycode, s);
+    qemu_register_reset(ps2_reset, 0, &s->common);
+    return s;
+}
+
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
+{
+    PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState));
+
+    s->common.update_irq = update_irq;
+    s->common.update_arg = update_arg;
+    ps2_reset(&s->common);
+    register_savevm("ps2mouse", 0, 2, ps2_mouse_save, ps2_mouse_load, s);
+    //qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
+    qemu_register_reset(ps2_reset, 0, &s->common);
+    return s;
+}
diff --git a/hw/ps2.h b/hw/ps2.h
new file mode 100644
index 0000000..32a4231
--- /dev/null
+++ b/hw/ps2.h
@@ -0,0 +1,9 @@
+/* ps2.c */
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
+void ps2_write_mouse(void *, int val);
+void ps2_write_keyboard(void *, int val);
+uint32_t ps2_read_data(void *);
+void ps2_queue(void *, int b);
+void ps2_keyboard_set_translation(void *opaque, int mode);
+void ps2_mouse_fake_event(void *opaque);
diff --git a/hw/smbios.c b/hw/smbios.c
new file mode 100644
index 0000000..ced90ce
--- /dev/null
+++ b/hw/smbios.c
@@ -0,0 +1,224 @@
+/*
+ * SMBIOS Support
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors:
+ *  Alex Williamson <alex.williamson@hp.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sysemu.h"
+#include "smbios.h"
+
+/*
+ * Structures shared with the BIOS
+ */
+struct smbios_header {
+    uint16_t length;
+    uint8_t type;
+} __attribute__((__packed__));
+
+struct smbios_field {
+    struct smbios_header header;
+    uint8_t type;
+    uint16_t offset;
+    uint8_t data[];
+} __attribute__((__packed__));
+
+struct smbios_table {
+    struct smbios_header header;
+    uint8_t data[];
+} __attribute__((__packed__));
+
+#define SMBIOS_FIELD_ENTRY 0
+#define SMBIOS_TABLE_ENTRY 1
+
+
+static uint8_t *smbios_entries;
+static size_t smbios_entries_len;
+
+uint8_t *smbios_get_table(size_t *length)
+{
+    *length = smbios_entries_len;
+    return smbios_entries;
+}
+
+/*
+ * To avoid unresolvable overlaps in data, don't allow both
+ * tables and fields for the same smbios type.
+ */
+static void smbios_check_collision(int type, int entry)
+{
+    uint16_t *num_entries = (uint16_t *)smbios_entries;
+    struct smbios_header *header;
+    char *p;
+    int i;
+
+    if (!num_entries)
+        return;
+
+    p = (char *)(num_entries + 1);
+
+    for (i = 0; i < *num_entries; i++) {
+        header = (struct smbios_header *)p;
+        if (entry == SMBIOS_TABLE_ENTRY && header->type == SMBIOS_FIELD_ENTRY) {
+            struct smbios_field *field = (void *)header;
+            if (type == field->type) {
+                fprintf(stderr, "SMBIOS type %d field already defined, "
+                                "cannot add table\n", type);
+                exit(1);
+            }
+        } else if (entry == SMBIOS_FIELD_ENTRY &&
+                   header->type == SMBIOS_TABLE_ENTRY) {
+            struct smbios_structure_header *table = (void *)(header + 1);
+            if (type == table->type) {
+                fprintf(stderr, "SMBIOS type %d table already defined, "
+                                "cannot add field\n", type);
+                exit(1);
+            }
+        }
+        p += le16_to_cpu(header->length);
+    }
+}
+
+void smbios_add_field(int type, int offset, int len, void *data)
+{
+    struct smbios_field *field;
+
+    smbios_check_collision(type, SMBIOS_FIELD_ENTRY);
+
+    if (!smbios_entries) {
+        smbios_entries_len = sizeof(uint16_t);
+        smbios_entries = qemu_mallocz(smbios_entries_len);
+    }
+    smbios_entries = qemu_realloc(smbios_entries, smbios_entries_len +
+                                                  sizeof(*field) + len);
+    field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
+    field->header.type = SMBIOS_FIELD_ENTRY;
+    field->header.length = cpu_to_le16(sizeof(*field) + len);
+
+    field->type = type;
+    field->offset = cpu_to_le16(offset);
+    memcpy(field->data, data, len);
+
+    smbios_entries_len += sizeof(*field) + len;
+    (*(uint16_t *)smbios_entries) =
+            cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+}
+
+static void smbios_build_type_0_fields(const char *t)
+{
+    char buf[1024];
+
+    if (get_param_value(buf, sizeof(buf), "vendor", t))
+        smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "version", t))
+        smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "date", t))
+        smbios_add_field(0, offsetof(struct smbios_type_0,
+                                     bios_release_date_str),
+                                     strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "release", t)) {
+        int major, minor;
+        sscanf(buf, "%d.%d", &major, &minor);
+        smbios_add_field(0, offsetof(struct smbios_type_0,
+                                     system_bios_major_release), 1, &major);
+        smbios_add_field(0, offsetof(struct smbios_type_0,
+                                     system_bios_minor_release), 1, &minor);
+    }
+}
+
+static void smbios_build_type_1_fields(const char *t)
+{
+    char buf[1024];
+
+    if (get_param_value(buf, sizeof(buf), "manufacturer", t))
+        smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "product", t))
+        smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "version", t))
+        smbios_add_field(1, offsetof(struct smbios_type_1, version_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "serial", t))
+        smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "uuid", t)) {
+        if (qemu_uuid_parse(buf, qemu_uuid) != 0) {
+            fprintf(stderr, "Invalid SMBIOS UUID string\n");
+            exit(1);
+        }
+    }
+    if (get_param_value(buf, sizeof(buf), "sku", t))
+        smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str),
+                         strlen(buf) + 1, buf);
+    if (get_param_value(buf, sizeof(buf), "family", t))
+        smbios_add_field(1, offsetof(struct smbios_type_1, family_str),
+                         strlen(buf) + 1, buf);
+}
+
+int smbios_entry_add(const char *t)
+{
+    char buf[1024];
+
+    if (get_param_value(buf, sizeof(buf), "file", t)) {
+        struct smbios_structure_header *header;
+        struct smbios_table *table;
+        int size = get_image_size(buf);
+
+        if (size < sizeof(struct smbios_structure_header)) {
+            fprintf(stderr, "Cannot read smbios file %s", buf);
+            exit(1);
+        }
+
+        if (!smbios_entries) {
+            smbios_entries_len = sizeof(uint16_t);
+            smbios_entries = qemu_mallocz(smbios_entries_len);
+        }
+
+        smbios_entries = qemu_realloc(smbios_entries, smbios_entries_len +
+                                                      sizeof(*table) + size);
+        table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
+        table->header.type = SMBIOS_TABLE_ENTRY;
+        table->header.length = cpu_to_le16(sizeof(*table) + size);
+
+        if (load_image(buf, table->data) != size) {
+            fprintf(stderr, "Failed to load smbios file %s", buf);
+            exit(1);
+        }
+
+        header = (struct smbios_structure_header *)(table->data);
+        smbios_check_collision(header->type, SMBIOS_TABLE_ENTRY);
+
+        smbios_entries_len += sizeof(*table) + size;
+        (*(uint16_t *)smbios_entries) =
+                cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+        return 0;
+    }
+
+    if (get_param_value(buf, sizeof(buf), "type", t)) {
+        unsigned long type = strtoul(buf, NULL, 0);
+        switch (type) {
+        case 0:
+            smbios_build_type_0_fields(t);
+            return 0;
+        case 1:
+            smbios_build_type_1_fields(t);
+            return 0;
+        default:
+            fprintf(stderr, "Don't know how to build fields for SMBIOS type "
+                    "%ld\n", type);
+            exit(1);
+        }
+    }
+
+    fprintf(stderr, "smbios: must specify type= or file=\n");
+    return -1;
+}