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