Skip to content
Snippets Groups Projects
Commit aa98b306 authored by Dmitry Fleytman's avatar Dmitry Fleytman
Browse files

XAPIC support implemented

XAPIC is supported as a fall-back when X2APIC is not available
parent d19a8d4b
No related branches found
No related tags found
No related merge requests found
......@@ -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 =
......
......@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment