Add HPET emulation to qemu (Beth Kon)

This patch adds HPET emulation.  It can be disabled with -disable-hpet.  An hpet
provides a more finely granular clocksource than otherwise available on PC.
This means that latency-dependent applications (e.g. multimedia) will generally
be smoother when using the HPET.

Signed-off-by: Beth Kon <eak@us.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6081 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
aliguori
2008-12-17 23:28:44 +00:00
parent 0bacd1300d
commit 16b29ae180
13 changed files with 938 additions and 6 deletions
+1 -1
View File
@@ -635,7 +635,7 @@ ifeq ($(TARGET_BASE_ARCH), i386)
OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
# virtio support
OBJS+= virtio.o virtio-blk.o virtio-balloon.o virtio-net.o
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
+7
View File
@@ -945,6 +945,13 @@ 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];
+588
View File
File diff suppressed because it is too large Load Diff
+85
View File
@@ -0,0 +1,85 @@
/*
* QEMU Emulated HPET support
*
* Copyright IBM, Corp. 2008
*
* Authors:
* Beth Kon <bkon@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#ifndef QEMU_HPET_EMUL_H
#define QEMU_HPET_EMUL_H
#define HPET_BASE 0xfed00000
#define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/
#define FS_PER_NS 1000000
#define HPET_NUM_TIMERS 3
#define HPET_TIMER_TYPE_LEVEL 1
#define HPET_TIMER_TYPE_EDGE 0
#define HPET_TIMER_DELIVERY_APIC 0
#define HPET_TIMER_DELIVERY_FSB 1
#define HPET_TIMER_CAP_FSB_INT_DEL (1 << 15)
#define HPET_TIMER_CAP_PER_INT (1 << 4)
#define HPET_CFG_ENABLE 0x001
#define HPET_CFG_LEGACY 0x002
#define HPET_ID 0x000
#define HPET_PERIOD 0x004
#define HPET_CFG 0x010
#define HPET_STATUS 0x020
#define HPET_COUNTER 0x0f0
#define HPET_TN_CFG 0x000
#define HPET_TN_CMP 0x008
#define HPET_TN_ROUTE 0x010
#define HPET_TN_ENABLE 0x004
#define HPET_TN_PERIODIC 0x008
#define HPET_TN_PERIODIC_CAP 0x010
#define HPET_TN_SIZE_CAP 0x020
#define HPET_TN_SETVAL 0x040
#define HPET_TN_32BIT 0x100
#define HPET_TN_INT_ROUTE_MASK 0x3e00
#define HPET_TN_INT_ROUTE_SHIFT 9
#define HPET_TN_INT_ROUTE_CAP_SHIFT 32
#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
struct HPETState;
typedef struct HPETTimer { /* timers */
uint8_t tn; /*timer number*/
QEMUTimer *qemu_timer;
struct HPETState *state;
/* Memory-mapped, software visible timer registers */
uint64_t config; /* configuration/cap */
uint64_t cmp; /* comparator */
uint64_t fsb; /* FSB route, not supported now */
/* Hidden register state */
uint64_t period; /* Last value written to comparator */
uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit
* mode. Next pop will be actual timer expiration.
*/
} HPETTimer;
typedef struct HPETState {
uint64_t hpet_offset;
qemu_irq *irqs;
HPETTimer timer[HPET_NUM_TIMERS];
/* Memory-mapped, software visible registers */
uint64_t capability; /* capabilities */
uint64_t config; /* configuration */
uint64_t isr; /* interrupt status reg */
uint64_t hpet_counter; /* main counter */
} HPETState;
#if defined TARGET_I386 || defined TARGET_X86_64
extern uint32_t hpet_in_legacy_mode(void);
extern void hpet_init(qemu_irq *irq);
#endif
#endif
+21
View File
@@ -463,6 +463,27 @@ static void pit_reset(void *opaque)
}
}
/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
void hpet_pit_disable(void) {
PITChannelState *s;
s = &pit_state.channels[0];
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;
+24 -5
View File
@@ -26,6 +26,7 @@
#include "sysemu.h"
#include "pc.h"
#include "isa.h"
#include "hpet_emul.h"
//#define DEBUG_CMOS
@@ -69,6 +70,18 @@ struct RTCState {
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
*/
#if defined TARGET_I386 || defined TARGET_X86_64
if (!hpet_in_legacy_mode())
#endif
qemu_irq_raise(irq);
}
static void rtc_set_time(RTCState *s);
static void rtc_copy_date(RTCState *s);
@@ -78,8 +91,14 @@ static void rtc_timer_update(RTCState *s, int64_t current_time)
int64_t cur_clock, next_irq_clock;
period_code = s->cmos_data[RTC_REG_A] & 0x0f;
if (period_code != 0 &&
(s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
#if defined TARGET_I386 || defined TARGET_X86_64
/* disable periodic timer if hpet is in legacy mode, since interrupts are
* disabled anyway.
*/
if (period_code != 0 && (s->cmos_data[RTC_REG_B] & REG_B_PIE) && !hpet_in_legacy_mode()) {
#else
if (period_code != 0 && (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
#endif
if (period_code <= 2)
period_code += 7;
/* period in 32 Khz cycles */
@@ -100,7 +119,7 @@ static void rtc_periodic_timer(void *opaque)
rtc_timer_update(s, s->next_periodic_time);
s->cmos_data[RTC_REG_C] |= 0xc0;
qemu_irq_raise(s->irq);
rtc_irq_raise(s->irq);
}
static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
@@ -319,14 +338,14 @@ static void rtc_update_second2(void *opaque)
s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
s->cmos_data[RTC_REG_C] |= 0xa0;
qemu_irq_raise(s->irq);
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;
qemu_irq_raise(s->irq);
rtc_irq_raise(s->irq);
}
/* clear update in progress bit */
+4
View File
@@ -35,6 +35,7 @@
#include "fw_cfg.h"
#include "virtio-blk.h"
#include "virtio-balloon.h"
#include "hpet_emul.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@@ -977,6 +978,9 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
}
pit = pit_init(0x40, i8259[0]);
pcspk_init(pit);
if (!no_hpet) {
hpet_init(i8259);
}
if (pci_enabled) {
pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
}
+3
View File
@@ -97,6 +97,9 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
void acpi_bios_init(void);
/* hpet.c */
extern int no_hpet;
/* pcspk.c */
void pcspk_init(PITState *);
int pcspk_audio_init(AudioState *, qemu_irq *pic);
+7
View File
@@ -252,6 +252,11 @@ static void do_info_name(void)
term_printf("%s\n", qemu_name);
}
static void do_info_hpet(void)
{
term_printf("HPET is %s by QEMU\n", (no_hpet) ? "disabled" : "enabled");
}
static void do_info_uuid(void)
{
term_printf(UUID_FMT "\n", qemu_uuid[0], qemu_uuid[1], qemu_uuid[2],
@@ -1531,6 +1536,8 @@ static const term_cmd_t info_cmds[] = {
"", "show virtual to physical memory mappings", },
{ "mem", "", mem_info,
"", "show the active virtual memory mappings", },
{ "hpet", "", do_info_hpet,
"", "show state of HPET", },
#endif
{ "jit", "", do_info_jit,
"", "show dynamic compiler info", },
+190
View File
@@ -0,0 +1,190 @@
BOCHS BIOS changes to support HPET in QEMU.
Signed-off-by Beth Kon <eak@us.ibm.com>
Index: bochs-2.3.7/bios/acpi-dsdt.dsl
===================================================================
--- bochs-2.3.7.orig/bios/acpi-dsdt.dsl 2008-10-15 12:39:14.000000000 -0500
+++ bochs-2.3.7/bios/acpi-dsdt.dsl 2008-10-28 07:58:40.000000000 -0500
@@ -159,6 +159,26 @@
Return (MEMP)
}
}
+#ifdef BX_QEMU
+ Device(HPET) {
+ Name(_HID, EISAID("PNP0103"))
+ Name(_UID, 0)
+ Method (_STA, 0, NotSerialized) {
+ Return(0x0F)
+ }
+ Name(_CRS, ResourceTemplate() {
+ DWordMemory(
+ ResourceConsumer, PosDecode, MinFixed, MaxFixed,
+ NonCacheable, ReadWrite,
+ 0x00000000,
+ 0xFED00000,
+ 0xFED003FF,
+ 0x00000000,
+ 0x00000400 /* 1K memory: FED00000 - FED003FF */
+ )
+ })
+ }
+#endif
}
Scope(\_SB.PCI0) {
Index: bochs-2.3.7/bios/rombios32.c
===================================================================
--- bochs-2.3.7.orig/bios/rombios32.c 2008-10-15 12:39:36.000000000 -0500
+++ bochs-2.3.7/bios/rombios32.c 2008-11-12 14:41:41.000000000 -0600
@@ -1087,7 +1087,11 @@
struct rsdt_descriptor_rev1
{
ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+#ifdef BX_QEMU
+ uint32_t table_offset_entry [4]; /* Array of pointers to other */
+#else
uint32_t table_offset_entry [3]; /* Array of pointers to other */
+#endif
/* ACPI tables */
};
@@ -1227,6 +1231,32 @@
#endif
};
+#ifdef BX_QEMU
+/*
+ * * ACPI 2.0 Generic Address Space definition.
+ * */
+struct acpi_20_generic_address {
+ uint8_t address_space_id;
+ uint8_t register_bit_width;
+ uint8_t register_bit_offset;
+ uint8_t reserved;
+ uint64_t address;
+};
+
+/*
+ * * HPET Description Table
+ * */
+struct acpi_20_hpet {
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t timer_block_id;
+ struct acpi_20_generic_address addr;
+ uint8_t hpet_number;
+ uint16_t min_tick;
+ uint8_t page_protect;
+};
+#define ACPI_HPET_ADDRESS 0xFED00000UL
+#endif
+
struct madt_io_apic
{
APIC_HEADER_DEF
@@ -1237,6 +1267,17 @@
* lines start */
};
+#ifdef BX_QEMU
+struct madt_int_override
+{
+ APIC_HEADER_DEF
+ uint8_t bus; /* Identifies ISA Bus */
+ uint8_t source; /* Bus-relative interrupt source */
+ uint32_t gsi; /* GSI that source will signal */
+ uint16_t flags; /* MPS INTI flags */
+};
+#endif
+
#include "acpi-dsdt.hex"
static inline uint16_t cpu_to_le16(uint16_t x)
@@ -1342,6 +1383,10 @@
struct facs_descriptor_rev1 *facs;
struct multiple_apic_table *madt;
uint8_t *dsdt, *ssdt;
+#ifdef BX_QEMU
+ struct acpi_20_hpet *hpet;
+ uint32_t hpet_addr;
+#endif
uint32_t base_addr, rsdt_addr, fadt_addr, addr, facs_addr, dsdt_addr, ssdt_addr;
uint32_t acpi_tables_size, madt_addr, madt_size;
int i;
@@ -1384,10 +1429,21 @@
madt_addr = addr;
madt_size = sizeof(*madt) +
sizeof(struct madt_processor_apic) * smp_cpus +
+#ifdef BX_QEMU
+ sizeof(struct madt_io_apic) + sizeof(struct madt_int_override);
+#else
sizeof(struct madt_io_apic);
+#endif
madt = (void *)(addr);
addr += madt_size;
+#ifdef BX_QEMU
+ addr = (addr + 7) & ~7;
+ hpet_addr = addr;
+ hpet = (void *)(addr);
+ addr += sizeof(*hpet);
+#endif
+
acpi_tables_size = addr - base_addr;
BX_INFO("ACPI tables: RSDP addr=0x%08lx ACPI DATA addr=0x%08lx size=0x%x\n",
@@ -1410,6 +1466,9 @@
rsdt->table_offset_entry[0] = cpu_to_le32(fadt_addr);
rsdt->table_offset_entry[1] = cpu_to_le32(madt_addr);
rsdt->table_offset_entry[2] = cpu_to_le32(ssdt_addr);
+#ifdef BX_QEMU
+ rsdt->table_offset_entry[3] = cpu_to_le32(hpet_addr);
+#endif
acpi_build_table_header((struct acpi_table_header *)rsdt,
"RSDT", sizeof(*rsdt), 1);
@@ -1448,6 +1507,9 @@
{
struct madt_processor_apic *apic;
struct madt_io_apic *io_apic;
+#ifdef BX_QEMU
+ struct madt_int_override *int_override;
+#endif
memset(madt, 0, madt_size);
madt->local_apic_address = cpu_to_le32(0xfee00000);
@@ -1467,10 +1529,34 @@
io_apic->io_apic_id = smp_cpus;
io_apic->address = cpu_to_le32(0xfec00000);
io_apic->interrupt = cpu_to_le32(0);
+#ifdef BX_QEMU
+ io_apic++;
+
+ int_override = (void *)io_apic;
+ int_override->type = APIC_XRUPT_OVERRIDE;
+ int_override->length = sizeof(*int_override);
+ int_override->bus = cpu_to_le32(0);
+ int_override->source = cpu_to_le32(0);
+ int_override->gsi = cpu_to_le32(2);
+ int_override->flags = cpu_to_le32(0);
+#endif
acpi_build_table_header((struct acpi_table_header *)madt,
"APIC", madt_size, 1);
}
+
+#ifdef BX_QEMU
+ /* HPET */
+ memset(hpet, 0, sizeof(*hpet));
+ /* Note timer_block_id value must be kept in sync with value advertised by
+ * emulated hpet
+ */
+ hpet->timer_block_id = cpu_to_le32(0x8086a201);
+ hpet->addr.address = cpu_to_le32(ACPI_HPET_ADDRESS);
+ acpi_build_table_header((struct acpi_table_header *)hpet,
+ "HPET", sizeof(*hpet), 1);
+#endif
+
}
/* SMBIOS entry point -- must be written to a 16-bit aligned address
+1
View File
@@ -2,3 +2,4 @@
0002_e820-high-mem.patch
0003_smp-startup-poll.patch
0004_no-stack-protector.patch
0005_hpet.patch
BIN
View File
Binary file not shown.
+7
View File
@@ -224,6 +224,7 @@ int usb_enabled = 0;
int smp_cpus = 1;
const char *vnc_display;
int acpi_enabled = 1;
int no_hpet = 0;
int fd_bootchk = 1;
int no_reboot = 0;
int no_shutdown = 0;
@@ -3956,6 +3957,7 @@ static void help(int exitcode)
#endif
#ifdef TARGET_I386
"-no-acpi disable ACPI\n"
"-no-hpet disable HPET\n"
#endif
#ifdef CONFIG_CURSES
"-curses use a curses/ncurses interface instead of SDL\n"
@@ -4067,6 +4069,7 @@ enum {
QEMU_OPTION_smp,
QEMU_OPTION_vnc,
QEMU_OPTION_no_acpi,
QEMU_OPTION_no_hpet,
QEMU_OPTION_curses,
QEMU_OPTION_no_reboot,
QEMU_OPTION_no_shutdown,
@@ -4180,6 +4183,7 @@ static const QEMUOption qemu_options[] = {
/* temporary options */
{ "usb", 0, QEMU_OPTION_usb },
{ "no-acpi", 0, QEMU_OPTION_no_acpi },
{ "no-hpet", 0, QEMU_OPTION_no_hpet },
{ "no-reboot", 0, QEMU_OPTION_no_reboot },
{ "no-shutdown", 0, QEMU_OPTION_no_shutdown },
{ "show-cursor", 0, QEMU_OPTION_show_cursor },
@@ -5017,6 +5021,9 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_no_acpi:
acpi_enabled = 0;
break;
case QEMU_OPTION_no_hpet:
no_hpet = 1;
break;
case QEMU_OPTION_no_reboot:
no_reboot = 1;
break;