From aa98b3064906a03778e92f9cd90d0cc769d0b59f Mon Sep 17 00:00:00 2001 From: Dmitry Fleytman <dmitry@daynix.com> Date: Mon, 9 Sep 2013 16:51:40 +0300 Subject: [PATCH] XAPIC support implemented XAPIC is supported as a fall-back when X2APIC is not available --- arch/x64/apic.cc | 176 +++++++++++++++++++++++++++++++---------------- arch/x64/apic.hh | 30 +++++--- 2 files changed, 137 insertions(+), 69 deletions(-) diff --git a/arch/x64/apic.cc b/arch/x64/apic.cc index 904e3bfac..d98c45ea2 100644 --- a/arch/x64/apic.cc +++ b/arch/x64/apic.cc @@ -4,7 +4,7 @@ #include <osv/percpu.hh> #include <cpuid.hh> #include <processor.hh> -#include <mmu.hh> +#include "debug.hh" namespace processor { @@ -27,10 +27,7 @@ inline bool try_fast_eoi() class x2apic : public apic_driver { public: - explicit x2apic(); - virtual void init_on_ap(); - virtual u32 read(apicreg reg); - virtual void write(apicreg reg, u32 value); + x2apic() : apic_driver() { enable(); } virtual void self_ipi(unsigned vector); virtual void ipi(unsigned apic_id, unsigned vector); virtual void init_ipi(unsigned apic_id, unsigned vector); @@ -38,88 +35,105 @@ public: virtual void nmi_allbutself(); virtual void eoi(); virtual u32 id(); -private: - void enable(); + + virtual u32 read(apicreg reg) + { return rdmsr(0x800 + unsigned(reg) / 0x10); } + virtual void write(apicreg reg, u32 value) + { wrmsr(0x800 + unsigned(reg) / 0x10, value); } + +protected: + virtual void enable(); }; -apic_driver::~apic_driver() -{ -} +class xapic final : public apic_driver { +public: + xapic(); -void apic_driver::software_enable() -{ - write(apicreg::LVT0, 0); - write(apicreg::SPIV, 0x1ff); // FIXME: allocate real vector -} + virtual void self_ipi(unsigned vector) + { xapic::ipi(APIC_ICR_TYPE_FIXED | APIC_SHORTHAND_SELF, vector); } -x2apic::x2apic() - : apic_driver() -{ - enable(); - software_enable(); -} + virtual void init_ipi(unsigned apic_id, unsigned vector) + { xapic::ipi(apic_id << 24, vector);} -void x2apic::init_on_ap() -{ - enable(); -} + virtual void ipi_allbutself(unsigned vector) + { xapic::ipi(APIC_ICR_TYPE_FIXED | APIC_SHORTHAND_ALLBUTSELF, vector); } -void x2apic::enable() -{ - processor::wrmsr(msr::IA32_APIC_BASE, _apic_base_lo | (3 << 10)); - software_enable(); -} + virtual void nmi_allbutself() + { xapic::ipi(APIC_ICR_TYPE_FIXED | APIC_SHORTHAND_ALLBUTSELF, + apic_delivery(NMI_DELIVERY)); } -void x2apic::self_ipi(unsigned vector) + virtual void ipi(unsigned apic_id, unsigned vector); + + virtual void eoi(); + virtual u32 id(); + + virtual void write(apicreg reg, u32 value) { *reg_ptr(reg) = value; } + virtual u32 read(apicreg reg) { return *reg_ptr(reg); } + +protected: + virtual void enable(); + +private: + u32* reg_ptr(apicreg r) + {return reinterpret_cast<u32*>(&_base_virt[static_cast<unsigned>(r)]);} + + u8* const _base_virt = mmu::phys_cast<u8>(_apic_base); + + static constexpr unsigned ICR2_DESTINATION_SHIFT = 24; + static constexpr unsigned XAPIC_ID_SHIFT = 24; +}; + +void apic_driver::software_enable() { - wrmsr(msr::X2APIC_SELF_IPI, vector); + write(apicreg::LVT0, 0); + write(apicreg::SPIV, 0x1ff); // FIXME: allocate real vector } -void x2apic::ipi(unsigned apic_id, unsigned vector) +void apic_driver::read_base() { - wrmsr(msr::X2APIC_ICR, vector | (u64(apic_id) << 32) | (1 << 14)); + static constexpr u64 base_addr_mask = 0xFFFFFF000; + _apic_base = rdmsr(msr::IA32_APIC_BASE) & base_addr_mask; + + debug(fmt("APIC base %p\n") % _apic_base); } -void x2apic::init_ipi(unsigned apic_id, unsigned vector) +xapic::xapic() + : apic_driver() { - wrmsr_safe(msr::X2APIC_ICR, vector | (u64(apic_id) << 32)); + mmu::linear_map(static_cast<void*>(_base_virt), _apic_base, 4096, 4096); + xapic::enable(); } -static constexpr unsigned APIC_SHORTHAND_SELF = 0x40000; -static constexpr unsigned APIC_SHORTHAND_ALL = 0x80000; -static constexpr unsigned APIC_SHORTHAND_ALLBUTSELF = 0xC0000; - -void x2apic::ipi_allbutself(unsigned vector) +void xapic::enable() { - wrmsr(msr::X2APIC_ICR, vector | APIC_SHORTHAND_ALLBUTSELF | (1 << 14)); + wrmsr(msr::IA32_APIC_BASE, _apic_base | APIC_BASE_GLOBAL_ENABLE); + software_enable(); } -void x2apic::nmi_allbutself() +void xapic::ipi(unsigned apic_id, unsigned vector) { - wrmsr(msr::X2APIC_ICR, APIC_SHORTHAND_ALLBUTSELF | (4 << 8) | (1 << 14)); + xapic::write(apicreg::ICR2, apic_id << ICR2_DESTINATION_SHIFT); + xapic::write(apicreg::ICR, vector | APIC_ICR_LEVEL_ASSERT); } -void x2apic::eoi() +void xapic::eoi() { if (try_fast_eoi()) { return; } - wrmsr(msr::X2APIC_EOI, 0); -} -u32 x2apic::read(apicreg reg) -{ - return processor::rdmsr(0x800 + unsigned(reg) / 0x10); + xapic::write(apicreg::EOR, 0); } -void x2apic::write(apicreg reg, u32 value) +u32 xapic::id() { - processor::wrmsr(0x800 + unsigned(reg) / 0x10, value); + return xapic::read(apicreg::ID) >> XAPIC_ID_SHIFT; } u32 x2apic::id() { - u32 id = processor::rdmsr(msr::X2APIC_ID); + u32 id = read(apicreg::ID); + if (!is_xen()) return id; @@ -139,19 +153,60 @@ u32 x2apic::id() return id; } -apic_driver* create_apic_driver() +void x2apic::enable() +{ + wrmsr(msr::IA32_APIC_BASE, _apic_base | APIC_BASE_GLOBAL_ENABLE | (1 << 10)); + software_enable(); +} + +void x2apic::self_ipi(unsigned vector) { - // FIXME: detect - return new x2apic; + wrmsr(msr::X2APIC_SELF_IPI, vector); } -apic_driver* apic = create_apic_driver(); +void x2apic::ipi(unsigned apic_id, unsigned vector) +{ + wrmsr(msr::X2APIC_ICR, vector | (u64(apic_id) << 32) | APIC_ICR_LEVEL_ASSERT); +} + +void x2apic::init_ipi(unsigned apic_id, unsigned vector) +{ + wrmsr_safe(msr::X2APIC_ICR, vector | (u64(apic_id) << 32)); +} + +void x2apic::ipi_allbutself(unsigned vector) +{ + wrmsr(msr::X2APIC_ICR, vector | APIC_SHORTHAND_ALLBUTSELF | APIC_ICR_LEVEL_ASSERT); +} + +void x2apic::nmi_allbutself() +{ + wrmsr(msr::X2APIC_ICR, APIC_SHORTHAND_ALLBUTSELF | + apic_delivery(NMI_DELIVERY) | + APIC_ICR_LEVEL_ASSERT); +} -void apic_driver::set_lvt(apiclvt source, unsigned vector) +void x2apic::eoi() { - write(static_cast<apicreg>(source), vector); + if (try_fast_eoi()) { + return; + } + wrmsr(msr::X2APIC_EOI, 0); } +apic_driver* create_apic_driver() +{ + // TODO: Some Xen versions do not expose x2apic CPU feature + // but still support it. Should we do more precise detection? + if (features().x2apic) { + return new x2apic; + } else { + return new xapic; + }; +} + +apic_driver* apic = create_apic_driver(); + msi_message apic_driver::compose_msix(u8 vector, u8 dest_id) { msi_message msg; @@ -161,8 +216,7 @@ msi_message apic_driver::compose_msix(u8 vector, u8 dest_id) } msg._addr = - ( (u64)_apic_base_hi << 32 ) | - ( _apic_base_lo & 0xFFF00000 ) | + ( _apic_base & 0xFFF00000 ) | ( dest_id << 12 ); msg._data = diff --git a/arch/x64/apic.hh b/arch/x64/apic.hh index d320a4885..72293593e 100644 --- a/arch/x64/apic.hh +++ b/arch/x64/apic.hh @@ -2,6 +2,7 @@ #define APIC_HH_ #include <osv/types.h> +#include <mmu.hh> namespace processor { @@ -75,9 +76,9 @@ struct msi_message { class apic_driver { public: - apic_driver() : _apic_base_lo(0xfee00000), _apic_base_hi(0) {} - virtual ~apic_driver(); - virtual void init_on_ap() = 0; + apic_driver() { read_base(); } + virtual ~apic_driver() {} + virtual void init_on_ap() { enable(); } virtual void self_ipi(unsigned vector) = 0; virtual void ipi(unsigned apic_id, unsigned vector) = 0; virtual void init_ipi(unsigned apic_id, unsigned vector) = 0; @@ -87,15 +88,28 @@ public: virtual u32 read(apicreg reg) = 0; virtual void write(apicreg reg, u32 value) = 0; virtual u32 id() = 0; - void set_lvt(apiclvt reg, unsigned vector); + // vector should be above 31, below 15 will fail // dest_id is the apic id, if using an io_apic. msi_message compose_msix(u8 vector, u8 dest_id); protected: - void software_enable(); -protected: - u32 _apic_base_lo; - u32 _apic_base_hi; + virtual void software_enable(); + virtual void enable() = 0; + + mmu::phys _apic_base; + + static constexpr unsigned APIC_SHORTHAND_SELF = 0x40000; + static constexpr unsigned APIC_SHORTHAND_ALL = 0x80000; + static constexpr unsigned APIC_SHORTHAND_ALLBUTSELF = 0xC0000; + static constexpr unsigned APIC_ICR_TYPE_FIXED = 0x00000; + static constexpr unsigned APIC_ICR_LEVEL_ASSERT = 1 << 14; + static constexpr unsigned APIC_BASE_GLOBAL_ENABLE = 1 << 11; + + static u32 apic_delivery(u32 mode) { return mode << DELIVERY_SHIFT; } + +private: + void read_base(); + static constexpr unsigned DELIVERY_SHIFT = 8; }; extern apic_driver* apic; -- GitLab