metal-cos/sys/amd64/lapic.c
2014-07-10 14:01:15 -07:00

128 lines
3.2 KiB
C

/*
* LAPIC
*/
#include <stdint.h>
#include <kassert.h>
#include "amd64.h"
#include "amd64op.h"
#include "trap.h"
#define CPUID_FLAG_APIC 0x100
#define IA32_APIC_BASE_MSR 0x1B
#define IA32_APIC_BASE_MSR_BSP 0x100
#define IA32_APIC_BASE_MSR_ENABLE 0x800
#define LAPIC_ID 0x0020 /* CPU ID */
#define LAPIC_VERSION 0x0030 /* Version */
#define LAPIC_TPR 0x0080 /* Task Priority Register */
#define LAPIC_EOI 0x00B0 /* End of Interrupt */
#define LAPIC_SIV 0x00F0 /* Spurious Interrupt Vector */
#define LAPIC_SIV_ENABLE 0x100
#define LAPIC_ESR 0x0280 /* Error Status Register */
#define LAPIC_LVT_CMCI 0x02F0 /* LVT CMCI */
#define LAPIC_LVT_TIMER 0x0320 /* LVT Timer */
#define LAPIC_LVT_TIMER_ONESHOT 0x00000000
#define LAPIC_LVT_TIMER_PERIODIC 0x00020000
#define LAPIC_LVT_TIMER_TSCDEADLINE 0x00040000
#define LAPIC_LVT_THERMAL 0x0330 /* LVT Thermal Sensor */
#define LAPIC_LVT_PMCR 0x0340 /* LVT Performance Monitoring Counter */
#define LAPIC_LVT_LINT0 0x0350 /* LVT LINT0 */
#define LAPIC_LVT_LINT1 0x0360 /* LVT LINT1 */
#define LAPIC_LVT_ERROR 0x0370 /* LVT Error */
#define LAPIC_LVT_FLAG_MASKED 0x00010000 /* Masked */
#define LAPIC_TICR 0x0380 /* Timer Initial Count Register */
#define LAPIC_TCCR 0x0390 /* Timer Currnet Count Register */
#define LAPIC_TDCR 0x03E0 /* Time Divide Configuration Register */
#define LAPIC_TDCR_X1 0x000B /* Divide counts by 1 */
static uint32_t *
LAPIC_GetBase()
{
uint64_t base = rdmsr(IA32_APIC_BASE_MSR) & 0xFFFFFFFFFFFFF000ULL;
return (uint32_t *)base;
}
uint32_t
LAPIC_Read(uint16_t reg)
{
uint32_t volatile *lapic = (uint32_t volatile *) LAPIC_GetBase();
return lapic[reg >> 2];
}
void
LAPIC_Write(uint16_t reg, uint32_t val)
{
uint32_t volatile *lapic = (uint32_t volatile *)LAPIC_GetBase();
lapic[reg >> 2] = val;
lapic[LAPIC_ID >> 2];
}
uint32_t
LAPIC_CPU()
{
return LAPIC_Read(LAPIC_ID) >> 24;
}
void
LAPIC_SendEOI()
{
LAPIC_Write(LAPIC_EOI, 0);
}
void
LAPIC_Periodic(uint64_t rate)
{
LAPIC_Write(LAPIC_TDCR, LAPIC_TDCR_X1);
LAPIC_Write(LAPIC_LVT_TIMER, LAPIC_LVT_TIMER_PERIODIC | T_IRQ_TIMER);
LAPIC_Write(LAPIC_TICR, rate);
}
void
LAPIC_Init()
{
uint32_t edx;
uint64_t base;
cpuid(1, NULL, NULL, NULL, &edx);
if ((edx & CPUID_FLAG_APIC) == 0)
Panic("APIC is required!\n");
// Enable LAPIC
base = rdmsr(IA32_APIC_BASE_MSR);
wrmsr(IA32_APIC_BASE_MSR, base | IA32_APIC_BASE_MSR_ENABLE);
kprintf("LAPIC: CPU %d found at 0x%016llx\n", LAPIC_CPU(), base);
// Enable interrupts
LAPIC_Write(LAPIC_SIV, LAPIC_SIV_ENABLE | T_IRQ_SPURIOUS);
// Clear any remaining errors
LAPIC_Write(LAPIC_ESR, 0);
// Enable error and thermal interrupts
LAPIC_Write(LAPIC_LVT_ERROR, T_IRQ_ERROR);
LAPIC_Write(LAPIC_LVT_THERMAL, T_IRQ_THERMAL);
// Disable LINT0/1, PMC
LAPIC_Write(LAPIC_LVT_LINT0, LAPIC_LVT_FLAG_MASKED);
LAPIC_Write(LAPIC_LVT_LINT1, LAPIC_LVT_FLAG_MASKED);
LAPIC_Write(LAPIC_LVT_PMCR, LAPIC_LVT_FLAG_MASKED);
LAPIC_Periodic(10000000);
LAPIC_SendEOI();
LAPIC_Write(LAPIC_TPR, 0);
}