x86: detect CMOS aliasing on ports other then 0x70/0x71 ... in order to also intercept accesses through the alias ports. Also stop intercepting accesses to the CMOS ports if we won't ourselves use the CMOS RTC. Signed-off-by: Jan Beulich --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -1544,42 +1544,6 @@ int __hwdom_init xen_in_range(unsigned l return 0; } -static int __hwdom_init io_bitmap_cb(unsigned long s, unsigned long e, - void *ctx) -{ - struct domain *d = ctx; - unsigned int i; - - ASSERT(e <= INT_MAX); - for ( i = s; i <= e; i++ ) - __clear_bit(i, d->arch.hvm_domain.io_bitmap); - - return 0; -} - -void __hwdom_init setup_io_bitmap(struct domain *d) -{ - int rc; - - if ( has_hvm_container_domain(d) ) - { - bitmap_fill(d->arch.hvm_domain.io_bitmap, 0x10000); - rc = rangeset_report_ranges(d->arch.ioport_caps, 0, 0x10000, - io_bitmap_cb, d); - BUG_ON(rc); - /* - * NB: we need to trap accesses to 0xcf8 in order to intercept - * 4 byte accesses, that need to be handled by Xen in order to - * keep consistency. - * Access to 1 byte RTC ports also needs to be trapped in order - * to keep consistency with PV. - */ - __set_bit(0xcf8, d->arch.hvm_domain.io_bitmap); - __set_bit(RTC_PORT(0), d->arch.hvm_domain.io_bitmap); - __set_bit(RTC_PORT(1), d->arch.hvm_domain.io_bitmap); - } -} - /* * Local variables: * mode: C --- a/xen/arch/x86/time.c +++ b/xen/arch/x86/time.c @@ -736,7 +736,10 @@ static unsigned long get_cmos_time(void) if ( seconds < 60 ) { if ( rtc.sec != seconds ) + { cmos_rtc_probe = 0; + acpi_gbl_FADT.boot_flags &= ~ACPI_FADT_NO_CMOS_RTC; + } break; } --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,8 @@ idt_entry_t *idt_tables[NR_CPUS] __read_ void (*ioemul_handle_quirk)( u8 opcode, char *io_emul_stub, struct cpu_user_regs *regs); +static unsigned int __read_mostly cmos_alias_mask; + static int debug_stack_lines = 20; integer_param("debug_stack_lines", debug_stack_lines); @@ -1769,8 +1772,12 @@ static bool_t admin_io_okay(unsigned int if ( (port == 0xcf8) && (bytes == 4) ) return 0; - /* We also never permit direct access to the RTC/CMOS registers. */ - if ( ((port & ~1) == RTC_PORT(0)) ) + /* + * We also never permit direct access to the RTC/CMOS registers + * if we may be accessing the RTC ones ourselves. + */ + if ( !(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) && + ((port & ~(cmos_alias_mask | 1)) == RTC_PORT(0)) ) return 0; return ioports_access_permitted(d, port, port + bytes - 1); @@ -1842,18 +1849,19 @@ uint32_t guest_io_read(unsigned int port { sub_data = pv_pit_handler(port, 0, 0); } - else if ( (port == RTC_PORT(0)) ) + else if ( ((port & ~cmos_alias_mask) == RTC_PORT(0)) ) { sub_data = currd->arch.cmos_idx; } - else if ( (port == RTC_PORT(1)) && - ioports_access_permitted(currd, RTC_PORT(0), RTC_PORT(1)) ) + else if ( ((port & ~cmos_alias_mask) == RTC_PORT(1)) && + ioports_access_permitted(currd, port - 1, port) ) { unsigned long flags; spin_lock_irqsave(&rtc_lock, flags); - outb(currd->arch.cmos_idx & 0x7f, RTC_PORT(0)); - sub_data = inb(RTC_PORT(1)); + outb(currd->arch.cmos_idx & (0xff >> (port == RTC_PORT(1))), + port - 1); + sub_data = inb(port); spin_unlock_irqrestore(&rtc_lock, flags); } else if ( (port == 0xcf8) && (bytes == 4) ) @@ -1911,20 +1919,22 @@ void guest_io_write(unsigned int port, u { pv_pit_handler(port, (uint8_t)data, 1); } - else if ( (port == RTC_PORT(0)) ) + else if ( ((port & ~cmos_alias_mask) == RTC_PORT(0)) ) { currd->arch.cmos_idx = data; } - else if ( (port == RTC_PORT(1)) && - ioports_access_permitted(currd, RTC_PORT(0), RTC_PORT(1)) ) + else if ( ((port & ~cmos_alias_mask) == RTC_PORT(1)) && + ioports_access_permitted(currd, port - 1, port) ) { + unsigned int idx = currd->arch.cmos_idx & + (0xff >> (port == RTC_PORT(1))); unsigned long flags; if ( pv_rtc_handler ) - pv_rtc_handler(currd->arch.cmos_idx & 0x7f, data); + pv_rtc_handler(idx, data); spin_lock_irqsave(&rtc_lock, flags); - outb(currd->arch.cmos_idx & 0x7f, RTC_PORT(0)); - outb(data, RTC_PORT(1)); + outb(idx, port - 1); + outb(data, port); spin_unlock_irqrestore(&rtc_lock, flags); } else if ( (port == 0xcf8) && (bytes == 4) ) @@ -3727,6 +3737,84 @@ void __init trap_init(void) open_softirq(PCI_SERR_SOFTIRQ, pci_serr_softirq); } +static int __init probe_cmos_alias(void) +{ + unsigned int i, offs; + + if ( acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC ) + return 0; + + for ( offs = 2; offs < 8; offs <<= 1 ) + { + bool_t read = 1; + + for ( i = RTC_REG_D + 1; i < 0x80; ++i ) + { + uint8_t normal, alt; + unsigned long flags; + + if ( i == acpi_gbl_FADT.century ) + continue; + + spin_lock_irqsave(&rtc_lock, flags); + + normal = CMOS_READ(i); + if ( inb(RTC_PORT(offs)) != i ) + read = 0; + + alt = inb(RTC_PORT(offs + 1)); + + spin_unlock_irqrestore(&rtc_lock, flags); + + if ( normal != alt ) + break; + + process_pending_softirqs(); + } + if ( i == 0x80 ) + { + cmos_alias_mask |= offs; + printk(XENLOG_INFO "CMOS aliased at %02x, index %s\n", + RTC_PORT(offs), read ? "r/w" : "w/o"); + } + } + + return 0; +} +__initcall(probe_cmos_alias); + +static int __hwdom_init io_bitmap_cb(unsigned long s, unsigned long e, + void *ctx) +{ + const struct domain *d = ctx; + unsigned int i; + + ASSERT(e <= INT_MAX); + for ( i = s; i <= e; i++ ) + if ( admin_io_okay(i, 1, d) ) + __clear_bit(i, d->arch.hvm_domain.io_bitmap); + + return 0; +} + +void __hwdom_init setup_io_bitmap(struct domain *d) +{ + if ( !has_hvm_container_domain(d) ) + return; + + bitmap_fill(d->arch.hvm_domain.io_bitmap, 0x10000); + if ( rangeset_report_ranges(d->arch.ioport_caps, 0, 0x10000, + io_bitmap_cb, d) ) + BUG(); + + /* + * We need to trap 4-byte accesses to 0xcf8 (see admin_io_okay(), + * guest_io_read(), and guest_io_write()), which isn't covered by + * the admin_io_okay() check in io_bitmap_cb(). + */ + __set_bit(0xcf8, d->arch.hvm_domain.io_bitmap); +} + long register_guest_nmi_callback(unsigned long address) { struct vcpu *v = current;