#include "pc.h"
#include "apic.h"
#include "fdc.h"
+#include "ide.h"
#include "pci.h"
#include "vmware_vga.h"
#include "monitor.h"
#include "loader.h"
#include "elf.h"
#include "multiboot.h"
+#include "mc146818rtc.h"
+#include "msix.h"
+#include "sysbus.h"
+#include "sysemu.h"
+#include "blockdev.h"
+#include "ui/qemu-spice.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
+/* debug PC/ISA interrupts */
+//#define DEBUG_IRQ
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, ...) \
+ do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
#define BIOS_FILENAME "bios.bin"
#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3)
+#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4)
+
+#define MSI_ADDR_BASE 0xfee00000
#define E820_NR_ENTRIES 16
uint64_t address;
uint64_t length;
uint32_t type;
-};
+} __attribute((__packed__, __aligned__(4)));
struct e820_table {
uint32_t count;
struct e820_entry entry[E820_NR_ENTRIES];
-};
+} __attribute((__packed__, __aligned__(4)));
static struct e820_table e820_table;
+struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
void isa_irq_handler(void *opaque, int n, int level)
{
IsaIrqState *isa = (IsaIrqState *)opaque;
+ DPRINTF("isa_irqs: %s irq %d\n", level? "raise" : "lower", n);
if (n < 16) {
qemu_set_irq(isa->i8259[n], level);
}
{
int intno;
- intno = apic_get_interrupt(env);
+ intno = apic_get_interrupt(env->apic_state);
if (intno >= 0) {
/* set irq request if a PIC irq is still pending */
/* XXX: improve that */
return intno;
}
/* read the irq from the PIC */
- if (!apic_accept_pic_intr(env))
+ if (!apic_accept_pic_intr(env->apic_state)) {
return -1;
+ }
intno = pic_read_irq(isa_pic);
return intno;
{
CPUState *env = first_cpu;
+ DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
if (env->apic_state) {
while (env) {
- if (apic_accept_pic_intr(env))
- apic_deliver_pic_intr(env, level);
+ if (apic_accept_pic_intr(env->apic_state)) {
+ apic_deliver_pic_intr(env->apic_state, level);
+ }
env = env->next_cpu;
}
} else {
#define REG_EQUIPMENT_BYTE 0x14
-static int cmos_get_fd_drive_type(int fd0)
+static int cmos_get_fd_drive_type(FDriveType fd0)
{
int val;
switch (fd0) {
- case 0:
+ case FDRIVE_DRV_144:
/* 1.44 Mb 3"5 drive */
val = 4;
break;
- case 1:
+ case FDRIVE_DRV_288:
/* 2.88 Mb 3"5 drive */
val = 5;
break;
- case 2:
+ case FDRIVE_DRV_120:
/* 1.2 Mb 5"5 drive */
val = 2;
break;
+ case FDRIVE_DRV_NONE:
default:
val = 0;
break;
}
static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd,
- RTCState *s)
+ ISADevice *s)
{
int cylinders, heads, sectors;
bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors);
return 0;
}
-static int set_boot_dev(RTCState *s, const char *boot_device, int fd_bootchk)
+static int set_boot_dev(ISADevice *s, const char *boot_device, int fd_bootchk)
{
#define PC_MAX_BOOT_DEVICES 3
int nbds, bds[3] = { 0, };
return set_boot_dev(opaque, boot_device, 0);
}
-/* hd_table must contain 4 block drivers */
-void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
- const char *boot_device, DriveInfo **hd_table,
- FDCtrl *floppy_controller, RTCState *s)
+typedef struct pc_cmos_init_late_arg {
+ ISADevice *rtc_state;
+ BusState *idebus0, *idebus1;
+} pc_cmos_init_late_arg;
+
+static void pc_cmos_init_late(void *opaque)
{
+ pc_cmos_init_late_arg *arg = opaque;
+ ISADevice *s = arg->rtc_state;
int val;
- int fd0, fd1, nb;
+ BlockDriverState *hd_table[4];
int i;
+ ide_get_bs(hd_table, arg->idebus0);
+ ide_get_bs(hd_table + 2, arg->idebus1);
+
+ 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], s);
+ if (hd_table[1])
+ cmos_init_hd(0x1a, 0x24, hd_table[1], s);
+
+ 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, §ors);
+ 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);
+
+ qemu_unregister_reset(pc_cmos_init_late, opaque);
+}
+
+void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device,
+ BusState *idebus0, BusState *idebus1,
+ ISADevice *s)
+{
+ int val, nb, nb_heads, max_track, last_sect, i;
+ FDriveType fd_type[2];
+ DriveInfo *fd[2];
+ static pc_cmos_init_late_arg arg;
+
/* various important CMOS locations needed by PC/Bochs bios */
/* memory size */
}
/* floppy type */
-
- 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);
+ for (i = 0; i < 2; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ if (fd[i] && bdrv_is_inserted(fd[i]->bdrv)) {
+ bdrv_get_floppy_geometry_hint(fd[i]->bdrv, &nb_heads, &max_track,
+ &last_sect, FDRIVE_DRV_NONE,
+ &fd_type[i]);
+ } else {
+ fd_type[i] = FDRIVE_DRV_NONE;
+ }
+ }
+ val = (cmos_get_fd_drive_type(fd_type[0]) << 4) |
+ cmos_get_fd_drive_type(fd_type[1]);
rtc_set_memory(s, 0x10, val);
val = 0;
nb = 0;
- if (fd0 < 3)
+ if (fd_type[0] < FDRIVE_DRV_NONE) {
nb++;
- if (fd1 < 3)
+ }
+ if (fd_type[1] < FDRIVE_DRV_NONE) {
nb++;
+ }
switch (nb) {
case 0:
break;
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
/* hard drives */
+ arg.rtc_state = s;
+ arg.idebus0 = idebus0;
+ arg.idebus1 = idebus1;
+ qemu_register_reset(pc_cmos_init_late, &arg);
+}
- 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]->bdrv, s);
- if (hd_table[1])
- cmos_init_hd(0x1a, 0x24, hd_table[1]->bdrv, s);
+/* port 92 stuff: could be split off */
+typedef struct Port92State {
+ ISADevice dev;
+ uint8_t outport;
+ qemu_irq *a20_out;
+} Port92State;
- 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]->bdrv);
- if (translation == BIOS_ATA_TRANSLATION_AUTO) {
- bdrv_get_geometry_hint(hd_table[i]->bdrv, &cylinders, &heads, §ors);
- if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
- /* No translation. */
- translation = 0;
- } else {
- /* LBA translation. */
- translation = 1;
- }
- } else {
- translation--;
- }
- val |= translation << (i * 2);
- }
+static void port92_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ Port92State *s = opaque;
+
+ DPRINTF("port92: write 0x%02x\n", val);
+ s->outport = val;
+ qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+ if (val & 1) {
+ qemu_system_reset_request();
}
- rtc_set_memory(s, 0x39, val);
}
-void ioport_set_a20(int enable)
+static uint32_t port92_read(void *opaque, uint32_t addr)
{
- /* XXX: send to all CPUs ? */
- cpu_x86_set_a20(first_cpu, enable);
+ Port92State *s = opaque;
+ uint32_t ret;
+
+ ret = s->outport;
+ DPRINTF("port92: read 0x%02x\n", ret);
+ return ret;
+}
+
+static void port92_init(ISADevice *dev, qemu_irq *a20_out)
+{
+ Port92State *s = DO_UPCAST(Port92State, dev, dev);
+
+ s->a20_out = a20_out;
+}
+
+static const VMStateDescription vmstate_port92_isa = {
+ .name = "port92",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(outport, Port92State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void port92_reset(DeviceState *d)
+{
+ Port92State *s = container_of(d, Port92State, dev.qdev);
+
+ s->outport &= ~1;
}
-int ioport_get_a20(void)
+static int port92_initfn(ISADevice *dev)
{
- return ((first_cpu->a20_mask >> 20) & 1);
+ Port92State *s = DO_UPCAST(Port92State, dev, dev);
+
+ register_ioport_read(0x92, 1, 1, port92_read, s);
+ register_ioport_write(0x92, 1, 1, port92_write, s);
+ isa_init_ioport(dev, 0x92);
+ s->outport = 0;
+ return 0;
}
-static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
+static ISADeviceInfo port92_info = {
+ .qdev.name = "port92",
+ .qdev.size = sizeof(Port92State),
+ .qdev.vmsd = &vmstate_port92_isa,
+ .qdev.no_user = 1,
+ .qdev.reset = port92_reset,
+ .init = port92_initfn,
+};
+
+static void port92_register(void)
{
- ioport_set_a20((val >> 1) & 1);
- /* XXX: bit 0 is fast reset */
+ isa_qdev_register(&port92_info);
}
+device_init(port92_register)
-static uint32_t ioport92_read(void *opaque, uint32_t addr)
+static void handle_a20_line_change(void *opaque, int irq, int level)
{
- return ioport_get_a20() << 1;
+ CPUState *cpu = opaque;
+
+ /* XXX: send to all CPUs ? */
+ /* XXX: add logic to handle multiple A20 line sources */
+ cpu_x86_set_a20(cpu, level);
}
/***********************************************************/
/* Bochs BIOS messages */
case 0x400:
case 0x401:
- fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
- exit(1);
+ /* used to be panic, now unused */
+ break;
case 0x402:
case 0x403:
#ifdef DEBUG_BIOS
int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
{
- int index = e820_table.count;
+ int index = le32_to_cpu(e820_table.count);
struct e820_entry *entry;
if (index >= E820_NR_ENTRIES)
return -EBUSY;
- entry = &e820_table.entry[index];
+ entry = &e820_table.entry[index++];
- entry->address = address;
- entry->length = length;
- entry->type = type;
+ entry->address = cpu_to_le64(address);
+ entry->length = cpu_to_le64(length);
+ entry->type = cpu_to_le32(type);
- e820_table.count++;
- return e820_table.count;
+ e820_table.count = cpu_to_le32(index);
+ return index;
}
static void *bochs_bios_init(void)
fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, (uint8_t *)&e820_table,
sizeof(struct e820_table));
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, (uint8_t *)&hpet_cfg,
+ sizeof(struct hpet_fw_config));
/* 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.
fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
- option_rom[nb_option_roms] = "linuxboot.bin";
+ option_rom[nb_option_roms].name = "linuxboot.bin";
+ option_rom[nb_option_roms].bootindex = 0;
nb_option_roms++;
}
static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
-#ifdef HAS_AUDIO
-void pc_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
-
void pc_init_ne2k_isa(NICInfo *nd)
{
static int nb_ne2k = 0;
return env->cpu_index == 0;
}
+DeviceState *cpu_get_current_apic(void)
+{
+ if (cpu_single_env) {
+ return cpu_single_env->apic_state;
+ } else {
+ return NULL;
+ }
+}
+
+static DeviceState *apic_init(void *env, uint8_t apic_id)
+{
+ DeviceState *dev;
+ SysBusDevice *d;
+ static int apic_mapped;
+
+ dev = qdev_create(NULL, "apic");
+ qdev_prop_set_uint8(dev, "id", apic_id);
+ qdev_prop_set_ptr(dev, "cpu_env", env);
+ qdev_init_nofail(dev);
+ d = sysbus_from_qdev(dev);
+
+ /* XXX: mapping more APICs at the same memory location */
+ if (apic_mapped == 0) {
+ /* NOTE: the APIC is directly connected to the CPU - it is not
+ on the global memory bus. */
+ /* XXX: what if the base changes? */
+ sysbus_mmio_map(d, 0, MSI_ADDR_BASE);
+ apic_mapped = 1;
+ }
+
+ msix_supported = 1;
+
+ return dev;
+}
+
/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
BIOS will read it and start S3 resume at POST Entry */
void pc_cmos_set_s3_resume(void *opaque, int irq, int level)
{
- RTCState *s = opaque;
+ ISADevice *s = opaque;
if (level) {
rtc_set_memory(s, 0xF, 0xFE);
}
}
+static void pc_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ env->halted = !cpu_is_bsp(env);
+}
+
static CPUState *pc_new_cpu(const char *cpu_model)
{
CPUState *env;
}
if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) {
env->cpuid_apic_id = env->cpu_index;
- /* APIC reset callback resets cpu */
- apic_init(env);
- } else {
- qemu_register_reset((QEMUResetHandler*)cpu_reset, env);
+ env->apic_state = apic_init(env, env->cpuid_apic_id);
}
+ qemu_register_reset(pc_cpu_reset, env);
+ pc_cpu_reset(env);
return env;
}
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;
- void **fw_cfg;
+ void *fw_cfg;
if (ram_size >= 0xe0000000 ) {
above_4g_mem_size = ram_size - 0xe0000000;
linux_boot = (kernel_filename != NULL);
/* allocate RAM */
- ram_addr = qemu_ram_alloc(below_4g_mem_size);
+ ram_addr = qemu_ram_alloc(NULL, "pc.ram",
+ below_4g_mem_size + above_4g_mem_size);
cpu_register_physical_memory(0, 0xa0000, ram_addr);
cpu_register_physical_memory(0x100000,
below_4g_mem_size - 0x100000,
ram_addr + 0x100000);
-
- /* 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
+ cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
+ ram_addr + below_4g_mem_size);
}
-
/* BIOS load */
if (bios_name == NULL)
bios_name = BIOS_FILENAME;
(bios_size % 65536) != 0) {
goto bios_error;
}
- bios_offset = qemu_ram_alloc(bios_size);
- ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size));
+ bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size);
+ ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
if (ret != 0) {
bios_error:
fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
isa_bios_size,
(bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
- option_rom_offset = qemu_ram_alloc(PC_ROM_SIZE);
+ option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE);
cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset);
/* map all the bios at the top of memory */
rom_set_fw(fw_cfg);
if (linux_boot) {
- load_linux(*fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
+ load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
}
for (i = 0; i < nb_option_roms; i++) {
- rom_add_option(option_rom[i]);
+ rom_add_option(option_rom[i].name, option_rom[i].bootindex);
}
}
isa_cirrus_vga_init();
}
} else if (vmsvga_enabled) {
+ if (pci_bus) {
+ if (!pci_vmsvga_init(pci_bus)) {
+ fprintf(stderr, "Warning: vmware_vga not available,"
+ " using standard VGA instead\n");
+ pci_vga_init(pci_bus);
+ }
+ } else {
+ fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+ }
+#ifdef CONFIG_SPICE
+ } else if (qxl_enabled) {
if (pci_bus)
- pci_vmsvga_init(pci_bus);
+ pci_create_simple(pci_bus, -1, "qxl-vga");
else
- fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+ fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
+#endif
} else if (std_vga_enabled) {
if (pci_bus) {
- pci_vga_init(pci_bus, 0, 0);
+ pci_vga_init(pci_bus);
} else {
isa_vga_init();
}
}
}
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env && level) {
+ cpu_exit(env);
+ }
+}
+
void pc_basic_device_init(qemu_irq *isa_irq,
- FDCtrl **floppy_controller,
- RTCState **rtc_state)
+ ISADevice **rtc_state)
{
int i;
DriveInfo *fd[MAX_FD];
- PITState *pit;
+ qemu_irq rtc_irq = NULL;
+ qemu_irq *a20_line;
+ ISADevice *i8042, *port92, *vmmouse, *pit;
+ qemu_irq *cpu_exit_irq;
register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
- *rtc_state = rtc_init(2000);
+ if (!no_hpet) {
+ DeviceState *hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL);
- qemu_register_boot_set(pc_boot_set, *rtc_state);
+ if (hpet) {
+ for (i = 0; i < 24; i++) {
+ sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]);
+ }
+ rtc_irq = qdev_get_gpio_in(hpet, 0);
+ }
+ }
+ *rtc_state = rtc_init(2000, rtc_irq);
- register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
- register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+ qemu_register_boot_set(pc_boot_set, *rtc_state);
- pit = pit_init(0x40, isa_reserve_irq(0));
+ pit = pit_init(0x40, 0);
pcspk_init(pit);
- if (!no_hpet) {
- hpet_init(isa_irq);
- }
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
if (serial_hds[i]) {
}
}
- isa_create_simple("i8042");
- DMA_init(0);
+ a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
+ i8042 = isa_create_simple("i8042");
+ i8042_setup_a20_line(i8042, &a20_line[0]);
+ vmport_init();
+ vmmouse = isa_try_create("vmmouse");
+ if (vmmouse) {
+ qdev_prop_set_ptr(&vmmouse->qdev, "ps2_mouse", i8042);
+ qdev_init_nofail(&vmmouse->qdev);
+ }
+ port92 = isa_create_simple("port92");
+ port92_init(port92, &a20_line[1]);
+
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
}
- *floppy_controller = fdctrl_init_isa(fd);
+ fdctrl_init_isa(fd);
}
void pc_pci_device_init(PCIBus *pci_bus)