aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorJun Nakajima <jnakajim@gmail.com>2011-01-29 14:24:24 -0800
committerJun Nakajima <jnakajim@gmail.com>2011-01-29 14:24:24 -0800
commit86797937017f52bff088d02edf64fb931177a7ea (patch)
treeba2447b411b62754ae3c6e6fecf5c94e936a1ccb /hw
parent00a9f5942a755f00f57d5ad9ef463c4c773222bb (diff)
downloadexternal_qemu-86797937017f52bff088d02edf64fb931177a7ea.zip
external_qemu-86797937017f52bff088d02edf64fb931177a7ea.tar.gz
external_qemu-86797937017f52bff088d02edf64fb931177a7ea.tar.bz2
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
Diffstat (limited to 'hw')
-rw-r--r--hw/apic.c967
-rw-r--r--hw/fdc.h11
-rw-r--r--hw/fw_cfg.c288
-rw-r--r--hw/fw_cfg.h40
-rw-r--r--hw/i8254.c507
-rw-r--r--hw/i8259.c570
-rw-r--r--hw/ioapic.c261
-rw-r--r--hw/mc146818rtc.c754
-rw-r--r--hw/ne2000.c840
-rw-r--r--hw/pc.c1310
-rw-r--r--hw/pckbd.c446
-rw-r--r--hw/piix_pci.c374
-rw-r--r--hw/ps2.c611
-rw-r--r--hw/ps2.h9
-rw-r--r--hw/smbios.c224
15 files changed, 7212 insertions, 0 deletions
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;
+}