Skip to contentSkip to conversation

Add HPET emulation to qemu (v4)

PATCH In review Beth Kon 2 participants 3 messages Nov 13, 2008 – Nov 14, 2008 Kbuild

lore
Share & tools
lore

Beth Kon

author
#0 lore raw

PATCH 1/2 Add HPET emulation to qemu (v4)

This version includes -

- required Interrupt Source Override changes
- incorporation of comments from v3
- added -no-hpet option
- added info hpet to monitor for checking whether hpet is active
- bugfix for system reset (handoff between pit/hpet)
- enabled 64-bit capability (after discovering that OS is warned in HPET spec about possibility that a 64-bit read may be implemented as two 32-bit reads).
- added wrap interrupt for one-shot 32 bit timers, as dictated by HPET spec. (Interrupt on wrap in addition to interrupt on timer expiration)

One area that needs special attention:

The HPET Timer N Configuration and Capability register contains a field that advertises which IOAPIC interrupts are available for routing of the timer's interrupt. I hunted around in the QEMU code and found that pretty much all of those that map to both PIC and IOAPIC are already in use. In legacy mode, timer 0 uses INTI2 and timer 1 uses INTI8, replacing PIT and RTC. But when not in legacy mode, I assume I need to advertise at least 3 values. I chose 5, 10 and 11 (trying to choose more obscure options), but am not sure that will work for all configurations.

As I understand the code, IOAPIC_NUM_PINS = 18, and we are only mapping the PIC IRQs, 0-15, so maybe we need to enable the use of pins 16 and 17? Or do we need to increase the size of the IOAPIC? (HPET requires interrupt routing to the IOAPIC but not to the 8259). Comments?

The first patch contains changes to QEMU, the second patch contains changes to BOCHS BIOS.

Signed-off-by Beth Kon <[email protected]>

Signature · 4 lines
Elizabeth Kon (Beth)
IBM Linux Technology Center
Open Hypervisor Team
email: [email protected]
---
 Makefile.target  |    2 +-
 hw/apic.c        |    7 +
 hw/hpet.c        |  590 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/hpet_emul.h   |   85 ++++++++
 hw/i8254.c       |   21 ++
 hw/mc146818rtc.c |   29 +++-
 hw/pc.c          |    4 +
 hw/pc.h          |    3 +
 monitor.c        |    7 +
 vl.c             |    7 +
 10 files changed, 749 insertions(+), 6 deletions(-)
Makefile.target +1 -1
index d844d9c..f0256e0 100644
@@ -663,7 +663,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
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
hw/apic.c +7 -0
index a2915f8..f6950ca 100644
@@ -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];
hw/hpet.c new file +590 -0

590 lines not on this page · raw message

hw/hpet_emul.h new file +85 -0

85 lines not on this page · raw message

hw/i8254.c +21 -0
index 4813b03..1625787 100644
@@ -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;
hw/mc146818rtc.c +24 -5
index 30bb044..a561230 100644
@@ -26,6 +26,7 @@
 #include "sysemu.h"
 #include "pc.h"
 #include "isa.h"
+#include "hpet_emul.h"
 
 //#define DEBUG_CMOS
 
@@ -70,6 +71,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);
 
@@ -79,8 +92,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 */
@@ -101,7 +120,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)
@@ -320,14 +339,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 */
hw/pc.c +4 -0
index 1486b68..29d7c30 100644
@@ -33,6 +33,7 @@
 #include "boards.h"
 #include "console.h"
 #include "fw_cfg.h"
+#include "hpet_emul.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -975,6 +976,9 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
     }
     pit = pit_init(0x40, i8259[0]);
     pcspk_init(pit);
+    if (hpet_enabled) {
+        hpet_init(i8259);
+    }
     if (pci_enabled) {
         pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
     }
hw/pc.h +3 -0
index d64d8a6..7244856 100644
@@ -95,6 +95,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 hpet_enabled;
+
 /* pcspk.c */
 void pcspk_init(PITState *);
 int pcspk_audio_init(AudioState *, qemu_irq *pic);
monitor.c +7 -0
index 8fff3aa..09a6200 100644
@@ -251,6 +251,11 @@ static void do_info_name(void)
         term_printf("%s\n", qemu_name);
 }
 
+static void do_info_hpet(void)
+{
+    term_printf("HPET is %s \n", (hpet_enabled) ? "active" : "inactive");
+}
+
 static void do_info_uuid(void)
 {
     term_printf(UUID_FMT "\n", qemu_uuid[0], qemu_uuid[1], qemu_uuid[2],
@@ -1506,6 +1511,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 (active/inactive)", },
 #endif
     { "jit", "", do_info_jit,
       "", "show dynamic compiler info", },
vl.c +7 -0
index daad89f..fb39bf4 100644
@@ -222,6 +222,7 @@ int usb_enabled = 0;
 int smp_cpus = 1;
 const char *vnc_display;
 int acpi_enabled = 1;
+int hpet_enabled = 1;
 int fd_bootchk = 1;
 int no_reboot = 0;
 int no_shutdown = 0;
@@ -5076,6 +5077,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"
@@ -5187,6 +5189,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,
@@ -5300,6 +5303,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 },
@@ -6135,6 +6139,9 @@ int main(int argc, char **argv)
             case QEMU_OPTION_no_acpi:
                 acpi_enabled = 0;
                 break;
+            case QEMU_OPTION_no_hpet:
+                hpet_enabled = 0;
+                break;
             case QEMU_OPTION_no_reboot:
                 no_reboot = 1;
                 break;
#1 lore raw
Quoted 3 lines · Beth Kon

- enabled 64-bit capability (after discovering that OS is warned in HPET spec about possibility that a 64-bit read may be implemented as two 32-bit reads).

It's in the spec, but are there any real x86_64 machines where 64-bit HPET read would be translated into two 32-bit reads? I'm wondering if some OSes ssume reading HPET with a 64-bit read on x86_64 is atomic, because that might be true. Linux seems to use HPET in 32-bit mode so that's safe (I checked Linux 2.6.26).

-- Jamie

author
#2 lore raw
Quoted 5 lines · Jamie Lokier
Quoted 3 lines · Beth Kon

- enabled 64-bit capability (after discovering that OS is warned in HPET spec about possibility that a 64-bit read may be implemented as two 32-bit reads).

Quoted 8 lines

It's in the spec, but are there any real x86_64 machines where 64-bit HPET read would be translated into two 32-bit reads? I'm wondering if some OSes ssume reading HPET with a 64-bit read on x86_64 is atomic, because that might be true. Linux seems to use HPET in 32-bit mode so that's safe (I checked Linux 2.6.26).

-- Jamie

Hmm, good point. Maybe it would be safer to leave it at 32 bits.

Quoted 1 line
Signature · 4 lines
Elizabeth Kon (Beth)
IBM Linux Technology Center
Open Hypervisor Team
email: [email protected]