|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v4] x86: detect CMOS aliasing on ports other than 0x70/0x71
... in order to also intercept Dom0 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 <jbeulich@xxxxxxxx>
---
v4: Also conditionally mask top bit for guest index port accesses. Add
missing adjustments to rtc_init(). Re-work to avoid recursive
read_lock(). Also adjust guest_io_{read,write}(). Re-base.
v3: Re-base over change to earlier patch.
v2: Re-base.
--- a/xen/arch/x86/hvm/rtc.c
+++ b/xen/arch/x86/hvm/rtc.c
@@ -27,7 +27,7 @@
#include <asm/hvm/vpt.h>
#include <asm/hvm/io.h>
#include <asm/hvm/save.h>
-#include <asm/current.h>
+#include <asm/iocap.h>
#include <xen/trace.h>
#include <public/hvm/params.h>
@@ -836,10 +836,18 @@ void rtc_init(struct domain *d)
if ( !has_vrtc(d) )
{
- if ( is_hardware_domain(d) )
- /* Hardware domain gets mediated access to the physical RTC. */
- register_portio_handler(d, RTC_PORT(0), 2, hw_rtc_io);
- return;
+ unsigned int port;
+
+ if ( !is_hardware_domain(d) )
+ return;
+
+ /*
+ * Hardware domain gets mediated access to the physical RTC/CMOS
+ * (of course unless we don't use it ourselves).
+ */
+ for ( port = RTC_PORT(0); port < RTC_PORT(0) + 0x10; port += 2 )
+ if ( is_cmos_port(port, 2, d) )
+ register_portio_handler(d, port, 2, hw_rtc_io);
}
spin_lock_init(&s->lock);
--- a/xen/arch/x86/include/asm/mc146818rtc.h
+++ b/xen/arch/x86/include/asm/mc146818rtc.h
@@ -9,6 +9,10 @@
extern spinlock_t rtc_lock; /* serialize CMOS RAM access */
+struct domain;
+bool is_cmos_port(unsigned int port, unsigned int bytes,
+ const struct domain *d);
+
/**********************************************************************
* register summary
**********************************************************************/
--- a/xen/arch/x86/pv/emul-priv-op.c
+++ b/xen/arch/x86/pv/emul-priv-op.c
@@ -220,7 +220,7 @@ static bool admin_io_okay(unsigned int p
return false;
/* We also never permit direct access to the RTC/CMOS registers. */
- if ( port <= RTC_PORT(1) && port + bytes > RTC_PORT(0) )
+ if ( is_cmos_port(port, bytes, d) )
return false;
return ioports_access_permitted(d, port, port + bytes - 1);
@@ -290,7 +290,7 @@ static uint32_t guest_io_read(unsigned i
{
sub_data = pv_pit_handler(port, 0, 0);
}
- else if ( port == RTC_PORT(0) || port == RTC_PORT(1) )
+ else if ( is_cmos_port(port, 1, currd) )
{
sub_data = rtc_guest_read(port);
}
@@ -436,7 +436,7 @@ static void guest_io_write(unsigned int
{
pv_pit_handler(port, (uint8_t)data, 1);
}
- else if ( port == RTC_PORT(0) || port == RTC_PORT(1) )
+ else if ( is_cmos_port(port, 1, currd) )
{
rtc_guest_write(port, data);
}
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -2072,37 +2072,36 @@ int __hwdom_init xen_in_range(unsigned l
static int __hwdom_init cf_check io_bitmap_cb(
unsigned long s, unsigned long e, void *ctx)
{
- struct domain *d = ctx;
+ const struct domain *d = ctx;
unsigned int i;
ASSERT(e <= INT_MAX);
for ( i = s; i <= e; i++ )
- __clear_bit(i, d->arch.hvm.io_bitmap);
+ /*
+ * Accesses to RTC ports also need to be trapped in order to keep
+ * consistency with PV.
+ */
+ if ( !is_cmos_port(i, 1, d) )
+ __clear_bit(i, d->arch.hvm.io_bitmap);
return 0;
}
void __hwdom_init setup_io_bitmap(struct domain *d)
{
- int rc;
+ if ( !is_hvm_domain(d) )
+ return;
- if ( is_hvm_domain(d) )
- {
- bitmap_fill(d->arch.hvm.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.io_bitmap);
- __set_bit(RTC_PORT(0), d->arch.hvm.io_bitmap);
- __set_bit(RTC_PORT(1), d->arch.hvm.io_bitmap);
- }
+ bitmap_fill(d->arch.hvm.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()).
+ */
+ __set_bit(0xcf8, d->arch.hvm.io_bitmap);
}
/*
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -1234,7 +1234,10 @@ static unsigned long get_cmos_time(void)
if ( seconds < 60 )
{
if ( rtc.sec != seconds )
+ {
cmos_rtc_probe = false;
+ acpi_gbl_FADT.boot_flags &= ~ACPI_FADT_NO_CMOS_RTC;
+ }
break;
}
@@ -1249,6 +1252,80 @@ static unsigned long get_cmos_time(void)
return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
}
+static unsigned int __ro_after_init cmos_alias_mask;
+
+static int __init cf_check 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 read = true;
+
+ 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 = false;
+
+ 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);
+
+bool is_cmos_port(unsigned int port, unsigned int bytes, const struct domain
*d)
+{
+ if ( !is_hardware_domain(d) )
+ return port <= RTC_PORT(1) && port + bytes > RTC_PORT(0);
+
+ if ( !(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) &&
+ port <= RTC_PORT(cmos_alias_mask | 1) && port + bytes > RTC_PORT(0) )
+ {
+ unsigned int cmos = RTC_PORT(0), nr = 2, step;
+
+ while ( cmos_alias_mask & nr )
+ nr <<= 1;
+ for ( step = nr << 1;
+ step < cmos_alias_mask && !(cmos_alias_mask & step); )
+ step <<= 1;
+ do {
+ if ( !(cmos & ~RTC_PORT(cmos_alias_mask)) &&
+ port <= cmos + 1 && port + bytes > cmos )
+ return true;
+ cmos += step;
+ } while ( cmos <= RTC_PORT(cmos_alias_mask) );
+ }
+
+ return false;
+}
+
/* Helpers for guest accesses to the physical RTC. */
unsigned int rtc_guest_read(unsigned int port)
{
@@ -1256,7 +1333,7 @@ unsigned int rtc_guest_read(unsigned int
unsigned long flags;
unsigned int data = ~0;
- switch ( port )
+ switch ( port & ~cmos_alias_mask )
{
case RTC_PORT(0):
/*
@@ -1264,15 +1341,16 @@ unsigned int rtc_guest_read(unsigned int
* of the first RTC port, as there's no access to the physical IO
* ports.
*/
- data = currd->arch.cmos_idx;
+ data = currd->arch.cmos_idx & (0xff >> (port == RTC_PORT(0)));
break;
case RTC_PORT(1):
- if ( !ioports_access_permitted(currd, RTC_PORT(0), RTC_PORT(1)) )
+ if ( !ioports_access_permitted(currd, port - 1, port) )
break;
spin_lock_irqsave(&rtc_lock, flags);
- outb(currd->arch.cmos_idx & 0x7f, RTC_PORT(0));
- data = inb(RTC_PORT(1));
+ outb(currd->arch.cmos_idx & (0xff >> (port == RTC_PORT(1))),
+ port - 1);
+ data = inb(port);
spin_unlock_irqrestore(&rtc_lock, flags);
break;
@@ -1288,9 +1366,10 @@ void rtc_guest_write(unsigned int port,
struct domain *currd = current->domain;
unsigned long flags;
- switch ( port )
+ switch ( port & ~cmos_alias_mask )
{
typeof(pv_rtc_handler) hook;
+ unsigned int idx;
case RTC_PORT(0):
/*
@@ -1298,20 +1377,22 @@ void rtc_guest_write(unsigned int port,
* value of the first RTC port, as there's no access to the physical IO
* ports.
*/
- currd->arch.cmos_idx = data;
+ currd->arch.cmos_idx = data & (0xff >> (port == RTC_PORT(0)));
break;
case RTC_PORT(1):
- if ( !ioports_access_permitted(currd, RTC_PORT(0), RTC_PORT(1)) )
+ if ( !ioports_access_permitted(currd, port - 1, port) )
break;
+ idx = currd->arch.cmos_idx & (0xff >> (port == RTC_PORT(1)));
+
hook = ACCESS_ONCE(pv_rtc_handler);
if ( hook )
- hook(currd->arch.cmos_idx & 0x7f, data);
+ hook(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);
break;
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |