Make irq0->inti2 override in BIOS configurable from userspace
RFC PATCH 1/2 Make irq0->inti2 override in BIOS configurable from userspace
This series of patches (nearly) resolves the irq0->inti2 override issue, and gets the hpet working on kvm with and without the in-kernel irqchip (i.e., it disables userspace and in-kernel pit as needed).
- irq0->inti2
The resolution was to always use the override unless the kernel cannot do irq routing (i.e., compatibility with old kernels). So qemu checks whether the kernel is capable of irq routing. If so, qemu tells kvm to route irq0 to inti2 via the irq routing interface, and tells bios to add the irq0->inti2 override to the MADT interrupt source override table, and to the mp table (for the non-acpi case). The only outstanding problem here is that when I set acpi=off on the kernel boot line, the boot fails. Apparently linux does not like the way I implemented the override for the mp table in rombios32.c. Since I am pressed for time at the moment, I'm putting this patch set out for comments in hopes that someone else may immediately see the problem. Otherwise I'll keep looking into it as time permits.
- hpet
The hpet works with and without in-kernel irqchip. And many thanks to Marcelo for finding a bios corruption bug that was the primary source of win2k864 problems. Now the hpet works on linux (ubuntu 8.0.4), win2k832. On win2k864 it works with the in-kernel irqchip but is broken (i.e.,black screen) when -no-kvm-irqchip is specified. Though I found that it is also broken when I remove these 2 patches, so it appears to have nothing to do with hpet or irq routing. Needs more looking into.
--- bios/Makefile | 2 +- bios/rombios32.c | 40 ++++++++++++++++++++++++++++++++++++---- qemu/hw/apic.c | 5 ++--- qemu/hw/fw_cfg.c | 1 + qemu/hw/fw_cfg.h | 1 + qemu/qemu-kvm.c | 5 ++++- qemu/sysemu.h | 1 + qemu/vl.c | 10 ++++++++-- 8 files changed, 54 insertions(+), 11 deletions(-)
bios/Makefile +1 -1
@@ -48,7 +48,7 @@ LIBS = -lm RANLIB = ranlib BCC = bcc -GCC = gcc $(CFLAGS) +GCC = gcc $(CFLAGS) -fno-stack-protector HOST_CC = gcc AS86 = as86
bios/rombios32.c +36 -4
@@ -443,6 +443,7 @@ uint32_t cpuid_ext_features;unsigned long ram_size;uint64_t ram_end;uint8_t bios_uuid[16 ]; +uint8_t irq0_override;#ifdef BX_USE_EBDA_TABLES unsigned long ebda_cur_addr;#endif
@@ -475,6 +476,7 @@ void wrmsr_smp(uint32_t index, uint64_t val)#define QEMU_CFG_SIGNATURE 0x00#define QEMU_CFG_ID 0x01#define QEMU_CFG_UUID 0x02 +#define QEMU_CFG_IRQ0_OVERRIDE 0x07int qemu_cfg_port;
@@ -516,6 +518,18 @@ void uuid_probe(void)memset (bios_uuid,0 ,16 ); } +void irq0_override_probe (void ) +{ +#ifdef BX_QEMU +if (qemu_cfg_port) { +qemu_cfg_select (QEMU_CFG_IRQ0_OVERRIDE ); +qemu_cfg_read (&irq0_override,1 ); +return ; + } +#endif +memset (&irq0_override,0 ,1 ); +} +void cpu_probe (void ) {uint32_t eax, ebx, ecx, edx;
@@ -1149,6 +1163,8 @@ static void mptable_init(void)/* irqs */ for (i =0 ; i <16 ; i++) { +if (irq0_override && i ==2 ) +continue ;putb (&q,3 );/* entry type = I/O interrupt */ putb (&q,0 );/* interrupt type = vectored interrupt */ putb (&q,0 );/* flags: po=0, el=0 */
@@ -1156,7 +1172,10 @@ static void mptable_init(void)putb (&q,0 );/* source bus ID = ISA */ putb (&q, i);/* source bus IRQ */ putb (&q, ioapic_id);/* dest I/O APIC ID */ -putb (&q, i);/* dest I/O APIC interrupt in */ +if (irq0_override && i ==0 ) +putb (&q,2 );/* dest I/O APIC interrupt in */ +else +putb (&q, i);/* dest I/O APIC interrupt in */ }/* patch length */ len = q - mp_config_table;
@@ -1505,6 +1524,11 @@ void acpi_bios_init(void)sizeof (struct madt_processor_apic ) *MAX_CPUS +sizeof (struct madt_io_apic ); madt = (void *)(addr); +for (i =0 ; i <16 ; i++) +if (PCI_ISA_IRQ_MASK & (1U << i)) + madt_size +=sizeof (struct madt_intsrcovr ); +if (irq0_override) + madt_size +=sizeof (struct madt_intsrcovr ); addr += madt_size; acpi_tables_size = addr - base_addr;
@@ -1594,8 +1618,15 @@ void acpi_bios_init(void) io_apic->interrupt =cpu_to_le32 (0 ); intsrcovr = (struct madt_intsrcovr *)(io_apic +1 ); -for ( i =0 ; i <16 ; i++ ) { -if (PCI_ISA_IRQ_MASK & (1U << i) ) { +for (i =0 ; i <16 ; i++) { +if (irq0_override && i ==0 ) { +memset (intsrcovr,0 ,sizeof (*intsrcovr)); + intsrcovr->type =APIC_XRUPT_OVERRIDE ; + intsrcovr->length =sizeof (*intsrcovr); + intsrcovr->source = i; + intsrcovr->gsi =2 ; + intsrcovr->flags =0 ;//conforms to bus specifications + }else if (PCI_ISA_IRQ_MASK & (1U << i)) {memset (intsrcovr,0 ,sizeof (*intsrcovr)); intsrcovr->type =APIC_XRUPT_OVERRIDE ; intsrcovr->length =sizeof (*intsrcovr);
@@ -1607,7 +1638,6 @@ void acpi_bios_init(void)continue ; } intsrcovr++; - madt_size +=sizeof (struct madt_intsrcovr ); }acpi_build_table_header ((struct acpi_table_header *)madt,"APIC" , madt_size,1 );
@@ -2232,6 +2262,8 @@ void rombios32_init(uint32_t *s3_resume_vector, uint8_t *shutdown_flag)uuid_probe ();smbios_init (); + +irq0_override_probe ();if (acpi_enabled)acpi_bios_init ();
qemu/hw/apic.c +2 -3
@@ -19,6 +19,7 @@*/ #include "hw.h" #include "pc.h" +#include "sysemu.h" #include "qemu-timer.h" #include "host-utils.h"
@@ -1077,14 +1078,12 @@ void ioapic_set_irq(void *opaque, int vector, int level) {IOAPICState *s = opaque; -#if 0 /* 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 ) +if (vector ==0 && irq0override) vector =2 ; -#endif if (vector >=0 && vector <IOAPIC_NUM_PINS ) {uint32_t mask =1 << vector;
qemu/hw/fw_cfg.c +1 -0
@@ -287,6 +287,7 @@ void *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,fw_cfg_add_bytes (s,FW_CFG_UUID , qemu_uuid,16 );fw_cfg_add_i16 (s,FW_CFG_NOGRAPHIC , (uint16_t )nographic);fw_cfg_add_i16 (s,FW_CFG_NB_CPUS , (uint16_t )smp_cpus); +fw_cfg_add_i16 (s,FW_CFG_IRQ0_OVERRIDE , (uint16_t )irq0override);register_savevm ("fw_cfg" ,-1 ,1 , fw_cfg_save, fw_cfg_load, s);qemu_register_reset (fw_cfg_reset, s);
qemu/hw/fw_cfg.h +1 -0
@@ -8,6 +8,7 @@#define FW_CFG_NOGRAPHIC 0x04#define FW_CFG_NB_CPUS 0x05#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_IRQ0_OVERRIDE 0x07#define FW_CFG_MAX_ENTRY 0x10#define FW_CFG_WRITE_CHANNEL 0x4000
qemu/qemu-kvm.c +4 -1
@@ -815,7 +815,10 @@ int kvm_qemu_create_context(void)return r; }for (i =0 ; i <24 ; ++i) { - r =kvm_add_irq_route (kvm_context, i,KVM_IRQCHIP_IOAPIC , i); +if (i ==0 ) + r =kvm_add_irq_route (kvm_context, i,KVM_IRQCHIP_IOAPIC ,2 ); +else + r =kvm_add_irq_route (kvm_context, i,KVM_IRQCHIP_IOAPIC , i);if (r <0 )return r; }
qemu/sysemu.h +1 -0
@@ -92,6 +92,7 @@ extern int graphic_width;extern int graphic_height;extern int graphic_depth;extern int nographic; +extern int irq0override;extern const char *keyboard_layout;extern int win2k_install_hack;extern int rtc_td_hack;
qemu/vl.c +8 -2
@@ -194,6 +194,7 @@ static int vga_ram_size;enum vga_retrace_method vga_retrace_method =VGA_RETRACE_DUMB ;static DisplayState *display_state;int nographic; +int irq0override;static int curses;static int sdl;const char * keyboard_layout =NULL ;
@@ -4874,6 +4875,7 @@ int main(int argc, char **argv, char **envp)#endif snapshot =0 ; nographic =0 ; + irq0override =1 ; curses =0 ; kernel_filename =NULL ; kernel_cmdline ="" ;
@@ -5871,8 +5873,12 @@ int main(int argc, char **argv, char **envp) } } -if (kvm_enabled ()) -kvm_init_ap (); +if (kvm_enabled ()) { +kvm_init_ap (); +if (kvm_irqchip && !kvm_has_gsi_routing (kvm_context)) { + irq0override =0 ; + } + } machine->init (ram_size, vga_ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
RFC PATCH 2/2 Finish hpet implementation for KVM
- add hpet to BIOS
- add disable/enable of kernel pit when hpet enters/leaves legacy mode
bios/acpi-dsdt.dsl +18 -0
@@ -233,6 +233,24 @@ DefinitionBlock ( ,, , AddressRangeMemory, TypeStatic) }) } + 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 */ + ) + }) + } } Scope(\_SB.PCI0) {
bios/rombios32.c +43 -1
80 lines not on this page · raw message
qemu/hw/hpet.c +42 -4
72 lines not on this page · raw message
qemu/vl.c +6 -0
@@ -5877,6 +5877,12 @@ int main(int argc, char **argv, char **envp)kvm_init_ap ();if (kvm_irqchip && !kvm_has_gsi_routing (kvm_context)) { irq0override =0 ; + +/* if kernel can't do irq routing, interrupt source +* override 0->2 can not be set up as required by hpet, +* so disable hpet. +*/ + no_hpet=1 ; } }
Hi Beth,
Quoted 11 lines · Beth Kon
This series of patches (nearly) resolves the
irq0->inti2override issue, and gets the hpet working on kvm with and without the in-kernel irqchip (i.e., it disables userspace and in-kernel pit as needed).-
irq0->inti2
The resolution was to always use the override unless the kernel cannot do irq routing (i.e., compatibility with old kernels). So qemu checks whether the kernel is capable of irq routing. If so, qemu tells kvm to route irq0 to inti2 via the irq routing interface, and tells bios to add theirq0->inti2override to the MADT interrupt source override table, and to the mp table (for the non-acpi case). The only outstanding problem here is that when I set acpi=off on the kernel boot line, the boot fails. Apparently linux does not like the way I implemented the override for the mp table inrombios32.c. Since I am pressed for time at the moment, I'm putting this patch set out for comments in hopes that someone else may immediately see the problem. Otherwise I'll keep looking into it as time permits.
The mptable is not being initialized as you intented because irq0override is read from fw cfg after mptable initialization (oops).
Also the kernel irq ack notifiers lack knowledge about the routing table, assuming identity mapping.
See attached patches (qemu-kvm.c change avoids the creation of two routing entries for pin 2 pointing to different gsi's, 0 and 2).
RHEL5 guests complain about "Invalid checksum", so there's still something wrong in either acpi or mptable setup. I'll try to look into this next week if nobody beats me.
BIOS-e820: 00000000fffbc000 - 0000000100000000 (reserved) DMI 2.4 present.
Quoted 1 line
Quoted 1 line
Quoted 1 line
ERROR: Invalid checksum
No NUMA configuration found
Great work Beth, thanks!
bios/rombios32.c +2 -2
@@ -2299,14 +2299,14 @@ void rombios32_init(uint32_t *s3_resume_if (bios_table_cur_addr !=0 ) { +irq0_override_probe (); +mptable_init ();uuid_probe ();smbios_init (); -irq0_override_probe (); -if (acpi_enabled)acpi_bios_init ();
qemu/qemu-kvm.c +1 -1
@@ -817,7 +817,7 @@ int kvm_qemu_create_context(void)for (i =0 ; i <24 ; ++i) {if (i ==0 ) r =kvm_add_irq_route (kvm_context, i,KVM_IRQCHIP_IOAPIC ,2 ); -else +else if (i !=2 ) r =kvm_add_irq_route (kvm_context, i,KVM_IRQCHIP_IOAPIC , i);if (r <0 )return r;
arch/x86/kvm/i8259.c +3 -2
@@ -49,7 +49,8 @@ static void pic_unlock(struct kvm_pic *s)spin_unlock (&s->lock);while (acks) { -kvm_notify_acked_irq (kvm,__ffs (acks)); +kvm_notify_acked_irq (kvm,SELECT_PIC (__ffs (acks)), +__ffs (acks)); acks &= acks -1 ; }
@@ -232,7 +233,7 @@ int kvm_pic_read_irq(struct kvm *kvm) }pic_update_irq (s);pic_unlock (s); -kvm_notify_acked_irq (kvm, irq); +kvm_notify_acked_irq (kvm,SELECT_PIC (irq), irq);return intno; }
arch/x86/kvm/irq.h +2 -0
@@ -32,6 +32,8 @@#include "lapic.h" #define PIC_NUM_PINS 16 +#define SELECT_PIC (irq) \ + ((irq) < 8 ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE)struct kvm ;struct kvm_vcpu ;
include/linux/kvm_host.h +1 -1
@@ -352,7 +352,7 @@ void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq,void kvm_fire_mask_notifiers (struct kvm *kvm,int irq,bool mask);void kvm_set_irq (struct kvm *kvm,int irq_source_id,int irq,int level); -void kvm_notify_acked_irq (struct kvm *kvm,unsigned gsi); +void kvm_notify_acked_irq (struct kvm *kvm,unsigned irqchip,unsigned pin);void kvm_register_irq_ack_notifier (struct kvm *kvm,struct kvm_irq_ack_notifier *kian);void kvm_unregister_irq_ack_notifier (struct kvm_irq_ack_notifier *kian);
virt/kvm/ioapic.c +5 -5
@@ -293,20 +293,20 @@ void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level) } } -static void __kvm_ioapic_update_eoi (struct kvm_ioapic *ioapic,int gsi, +static void __kvm_ioapic_update_eoi (struct kvm_ioapic *ioapic,int pin,int trigger_mode) {union ioapic_redir_entry *ent; - ent = &ioapic->redirtbl[gsi]; + ent = &ioapic->redirtbl[pin]; -kvm_notify_acked_irq (ioapic->kvm, gsi); +kvm_notify_acked_irq (ioapic->kvm,KVM_IRQCHIP_IOAPIC , pin);if (trigger_mode ==IOAPIC_LEVEL_TRIG ) {ASSERT (ent->fields.trig_mode ==IOAPIC_LEVEL_TRIG ); ent->fields.remote_irr =0 ; -if (!ent->fields.mask && (ioapic->irr & (1 << gsi))) -ioapic_service (ioapic, gsi); +if (!ent->fields.mask && (ioapic->irr & (1 << pin))) +ioapic_service (ioapic, pin); } }
virt/kvm/irq_comm.c +10 -3
@@ -59,10 +59,19 @@ void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level) e->set (e, kvm, !!(*irq_state)); } -void kvm_notify_acked_irq (struct kvm *kvm,unsigned gsi) +void kvm_notify_acked_irq (struct kvm *kvm,unsigned irqchip,unsigned pin) { +struct kvm_kernel_irq_routing_entry *e;struct kvm_irq_ack_notifier *kian;struct hlist_node *n; +unsigned gsi =-1 ; + +list_for_each_entry (e, &kvm->irq_routing, link) +if (e->irqchip.irqchip == irqchip && + e->irqchip.pin == pin) { + gsi = e->gsi; +break ; + }hlist_for_each_entry (kian, n, &kvm->arch.irq_ack_notifier_list, link)if (kian->gsi == gsi)
@@ -237,8 +246,6 @@ out:#define ROUTING_ENTRY1 (irq) IOAPIC_ROUTING_ENTRY(irq)#ifdef CONFIG_X86 -#define SELECT_PIC (irq) \ - ((irq) < 8 ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE)# define PIC_ROUTING_ENTRY (irq) \ { .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP, \ .u.irqchip.irqchip = SELECT_PIC(irq), .u.irqchip.pin = (irq) % 8 }