summaryrefslogtreecommitdiff
path: root/Kernel/Arch
diff options
context:
space:
mode:
authorTom <tomut@yahoo.com>2019-10-16 10:27:00 -0600
committerAndreas Kling <awesomekling@gmail.com>2019-10-16 19:14:02 +0200
commit00a7c48d6e60b7a89e3ac621997795d4314ca293 (patch)
tree4ffa184a408f911fec1293c31bc5888322de1422 /Kernel/Arch
parent4c8341d0806bd86553c1939c9910c1f3f2aff6cf (diff)
downloadserenity-00a7c48d6e60b7a89e3ac621997795d4314ca293.zip
APIC: Enable APIC and start APs
Diffstat (limited to 'Kernel/Arch')
-rw-r--r--Kernel/Arch/i386/APIC.cpp193
-rw-r--r--Kernel/Arch/i386/APIC.h10
-rw-r--r--Kernel/Arch/i386/CPU.cpp1
-rw-r--r--Kernel/Arch/i386/CPU.h50
4 files changed, 254 insertions, 0 deletions
diff --git a/Kernel/Arch/i386/APIC.cpp b/Kernel/Arch/i386/APIC.cpp
new file mode 100644
index 0000000000..fc60da2dbd
--- /dev/null
+++ b/Kernel/Arch/i386/APIC.cpp
@@ -0,0 +1,193 @@
+#include <AK/Assertions.h>
+#include <AK/Types.h>
+#include <Kernel/Arch/i386/CPU.h>
+#include <Kernel/Arch/i386/APIC.h>
+#include <Kernel/IO.h>
+#include <Kernel/VM/MemoryManager.h>
+
+#define IRQ_APIC_SPURIOUS 0x1f
+
+#define APIC_BASE_MSR 0x1b
+
+#define APIC_REG_LD 0xd0
+#define APIC_REG_DF 0xe0
+#define APIC_REG_SIV 0xf0
+#define APIC_REG_ICR_LOW 0x300
+#define APIC_REG_ICR_HIGH 0x310
+#define APIC_REG_LVT_TIMER 0x320
+#define APIC_REG_LVT_THERMAL 0x330
+#define APIC_REG_LVT_PERFORMANCE_COUNTER 0x340
+#define APIC_REG_LVT_LINT0 0x350
+#define APIC_REG_LVT_LINT1 0x360
+#define APIC_REG_LVT_ERR 0x370
+
+extern "C" void apic_spurious_interrupt_entry();
+
+asm(
+ ".globl apic_spurious_interrupt_entry \n"
+ "apic_spurious_interrupt_entry: \n"
+ " iret\n");
+
+namespace APIC {
+
+class ICRReg
+{
+ u32 m_reg{0};
+public:
+ enum DeliveryMode
+ {
+ Fixed = 0x0,
+ LowPriority = 0x1,
+ SMI = 0x2,
+ NMI = 0x4,
+ INIT = 0x5,
+ StartUp = 0x6,
+ };
+ enum DestinationMode
+ {
+ Physical = 0x0,
+ Logical = 0x0,
+ };
+ enum Level
+ {
+ DeAssert = 0x0,
+ Assert = 0x1
+ };
+ enum class TriggerMode
+ {
+ Edge = 0x0,
+ Level = 0x1,
+ };
+ enum DestinationShorthand
+ {
+ NoShorthand = 0x0,
+ Self = 0x1,
+ AllIncludingSelf = 0x2,
+ AllExcludingSelf = 0x3,
+ };
+
+ ICRReg(u8 vector, DeliveryMode delivery_mode, DestinationMode destination_mode, Level level, TriggerMode trigger_mode, DestinationShorthand destination):
+ m_reg(vector | (delivery_mode << 8) | (destination_mode << 11) | (level << 14) | (static_cast<u32>(trigger_mode) << 15) | (destination << 18))
+ {
+ }
+
+ u32 low() const { return m_reg; }
+ u32 high() const { return 0; }
+};
+
+static volatile u8* g_apic_base = nullptr;
+
+static PhysicalAddress get_base()
+{
+ u32 lo, hi;
+ MSR msr(APIC_BASE_MSR);
+ msr.get(lo, hi);
+ return PhysicalAddress(lo & 0xfffff000);
+}
+
+static void set_base(const PhysicalAddress& base)
+{
+ u32 hi = 0;
+ u32 lo = base.get() | 0x800;
+ MSR msr(APIC_BASE_MSR);
+ msr.set(lo, hi);
+}
+
+static u32 apic_read(u32 off)
+{
+ return *(volatile u32*)(&g_apic_base[off]);
+}
+
+static void apic_write(u32 off, u32 val)
+{
+ *reinterpret_cast<volatile u32*>(&g_apic_base[off]) = val;
+}
+
+static void apic_write_icr(const ICRReg& icr)
+{
+ apic_write(APIC_REG_ICR_HIGH, icr.high());
+ apic_write(APIC_REG_ICR_LOW, icr.low());
+}
+
+#define APIC_LVT_MASKED (1 << 15)
+#define APIC_LVT_TRIGGER_LEVEL (1 << 14)
+#define APIC_LVT(iv, dm) ((iv & 0xff) | ((dm & 0x7) << 8))
+
+asm(
+ ".globl apic_ap_start \n"
+ ".type apic_ap_start, @function \n"
+ "apic_ap_start: \n"
+ ".set begin_apic_ap_start, . \n"
+ " jmp apic_ap_start\n" // TODO: implement
+ ".set end_apic_ap_start, . \n"
+ "\n"
+ ".globl apic_ap_start_size \n"
+ "apic_ap_start_size: \n"
+ ".word end_apic_ap_start - begin_apic_ap_start \n");
+
+extern "C" void apic_ap_start(void);
+extern "C" u16 apic_ap_start_size;
+
+bool init()
+{
+ if (!MSR::have())
+ return false;
+
+ // check if we support local apic
+ CPUID id(1);
+ if ((id.edx() & (1 << 9)) == 0)
+ return false;
+
+ PhysicalAddress apic_base = get_base();
+ kprintf("Initializing APIC, base: P%x\n", apic_base);
+ set_base(apic_base);
+
+ MM.map_for_kernel(VirtualAddress(apic_base.get()), apic_base, true); // Map memory, disable cache!
+ g_apic_base = apic_base.as_ptr();
+
+ // copy ap init code to P8000
+ MM.map_for_kernel(VirtualAddress(0x8000), PhysicalAddress(0x8000));
+ memcpy(reinterpret_cast<u8*>(0x8000), reinterpret_cast<const u8*>(apic_ap_start), apic_ap_start_size);
+ return true;
+}
+
+void enable(u32 cpu)
+{
+ kprintf("Enabling local APIC for cpu #%u\n", cpu);
+
+ // set spurious interrupt vector
+ apic_write(APIC_REG_SIV, apic_read(APIC_REG_SIV) | 0x100);
+
+ // local destination mode (flat mode)
+ apic_write(APIC_REG_DF, 0xf000000);
+
+ // set destination id (note that this limits it to 8 cpus)
+ apic_write(APIC_REG_LD, (1 << cpu) << 24);
+
+ register_interrupt_handler(IRQ_APIC_SPURIOUS, apic_spurious_interrupt_entry);
+
+ apic_write(APIC_REG_LVT_TIMER, APIC_LVT(0xff, 0) | APIC_LVT_MASKED);
+ apic_write(APIC_REG_LVT_THERMAL, APIC_LVT(0xff, 0) | APIC_LVT_MASKED);
+ apic_write(APIC_REG_LVT_PERFORMANCE_COUNTER, APIC_LVT(0xff, 0) | APIC_LVT_MASKED);
+ apic_write(APIC_REG_LVT_LINT0, APIC_LVT(0x1f, 7) | APIC_LVT_MASKED);
+ apic_write(APIC_REG_LVT_LINT1, APIC_LVT(0xff, 4) | APIC_LVT_TRIGGER_LEVEL); // nmi
+ apic_write(APIC_REG_LVT_ERR, APIC_LVT(0xe3, 0) | APIC_LVT_MASKED);
+
+ if (cpu == 0) {
+ static volatile u32 foo = 0;
+
+ // INIT
+ apic_write_icr(ICRReg(0, ICRReg::INIT, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf));
+
+ for (foo = 0; foo < 0x800000; foo++); // TODO: 10 millisecond delay
+
+ for (int i = 0; i < 2; i++) {
+ // SIPI
+ apic_write_icr(ICRReg(0x08, ICRReg::StartUp, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf)); // start execution at P8000
+
+ for (foo = 0; foo < 0x80000; foo++); // TODO: 200 microsecond delay
+ }
+ }
+}
+
+}
diff --git a/Kernel/Arch/i386/APIC.h b/Kernel/Arch/i386/APIC.h
new file mode 100644
index 0000000000..3dd98a097e
--- /dev/null
+++ b/Kernel/Arch/i386/APIC.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <AK/Types.h>
+
+namespace APIC {
+
+bool init();
+void enable(u32 cpu);
+
+}
diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp
index a7e4d112f5..834a612a3e 100644
--- a/Kernel/Arch/i386/CPU.cpp
+++ b/Kernel/Arch/i386/CPU.cpp
@@ -1,6 +1,7 @@
#include "Assertions.h"
#include "IRQHandler.h"
#include "PIC.h"
+#include "APIC.h"
#include "Process.h"
#include "Scheduler.h"
#include <AK/Types.h>
diff --git a/Kernel/Arch/i386/CPU.h b/Kernel/Arch/i386/CPU.h
index d0fa59a824..5cdcfa4467 100644
--- a/Kernel/Arch/i386/CPU.h
+++ b/Kernel/Arch/i386/CPU.h
@@ -238,6 +238,24 @@ inline u32 cpu_flags()
return flags;
}
+inline u32 read_fs_u32(u32 offset)
+{
+ u32 val;
+ asm volatile(
+ "movl %%fs:%a[off], %k[val]"
+ : [val] "=r" (val)
+ : [off] "ir" (offset));
+ return val;
+}
+
+inline void write_fs_u32(u32 offset, u32 val)
+{
+ asm volatile(
+ "movl %k[val], %%fs:%a[off]"
+ :: [off] "ir" (offset), [val] "ir" (val)
+ : "memory");
+}
+
inline bool are_interrupts_enabled()
{
return cpu_flags() & 0x200;
@@ -419,3 +437,35 @@ private:
const char* m_name { nullptr };
SplitQword m_start;
};
+
+class MSR {
+ uint32_t m_msr;
+
+public:
+ static bool have()
+ {
+ CPUID id(1);
+ return (id.edx() & (1 << 5)) != 0;
+ }
+
+ MSR(const MSR&) = delete;
+ MSR& operator=(const MSR&) = delete;
+
+ MSR(uint32_t msr):
+ m_msr(msr)
+ {
+ }
+
+ void get(u32& low, u32& high)
+ {
+ asm volatile("rdmsr"
+ : "=a"(low), "=d"(high)
+ : "c"(m_msr));
+ }
+
+ void set(u32 low, u32 high)
+ {
+ asm volatile("wrmsr"
+ :: "a"(low), "d"(high), "c"(m_msr));
+ }
+};