Add HPET emulation to qemu (v4)
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
@@ -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
@@ -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
@@ -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
@@ -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 definedTARGET_I386 || definedTARGET_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 ] ®_B_PIE )) { +#if definedTARGET_I386 || definedTARGET_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 ] ®_B_PIE ) && !hpet_in_legacy_mode ()) { +#else +if (period_code !=0 && (s->cmos_data[RTC_REG_B ] ®_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 ] ®_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
@@ -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
@@ -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
@@ -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
@@ -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 ;
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
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_64machines 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 onx86_64is 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]