diff options
author | Richard Braun <rbraun@sceen.net> | 2012-10-03 01:23:21 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2012-10-03 01:26:08 +0200 |
commit | a4485f033c4ffa07955a45cad30424aedb89dff4 (patch) | |
tree | 136381bd711a9e6bd54b0c31fb347b35028911d1 /arch/x86/machine/lapic.c | |
parent | 69504fc63720b4bf2677d6074285b82256bc9b83 (diff) |
x86: new architecture
Merge 32-bit IA-32 (i386) and 64-bit AMD64 (amd64) code into one common
architecture. The amd64 variant isn't functional yet.
Diffstat (limited to 'arch/x86/machine/lapic.c')
-rw-r--r-- | arch/x86/machine/lapic.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c new file mode 100644 index 0000000..1a1236a --- /dev/null +++ b/arch/x86/machine/lapic.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2011, 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <kern/init.h> +#include <kern/panic.h> +#include <kern/param.h> +#include <kern/printk.h> +#include <lib/macros.h> +#include <lib/stdint.h> +#include <machine/cpu.h> +#include <machine/lapic.h> +#include <machine/pmap.h> +#include <machine/trap.h> +#include <vm/vm_kmem.h> + +/* + * Mask used to check that local APICS are internal. + */ +#define LAPIC_VERSION_MASK 0x10 + +/* + * Common bits for registers in the local vector table. + */ +#define LAPIC_LVT_DELIVERY_FIXED 0x00000000 +#define LAPIC_LVT_DELIVERY_NMI 0x00000400 +#define LAPIC_LVT_DELIVERY_EXTINT 0x00000700 +#define LAPIC_LVT_MASK_INTR 0x00010000 + +/* + * LVT timer register bits. + */ +#define LAPIC_LVT_TIMER_PERIODIC 0x00020000 + +/* + * Various values related to the local APIC timer. + */ +#define LAPIC_TIMER_DCR_DIV1 0x0000000b +#define LAPIC_TIMER_COUNT_MAX 0xffffffff + +/* + * The value of this duration (in microseconds) must be carefully set. + * It must divide a second (1000000) without loss of precision. It is + * recommended to use either 1s or 100ms. The former gives the best + * results, as it renders the time used for accounting operations + * negligible, but is slightly longer. + */ +#define LAPIC_TIMER_CAL_DELAY 1000000 + +/* + * Spurious-interrupt vector register bits. + */ +#define LAPIC_SVR_SOFT_EN 0x00000100 + +/* + * Interrupt command register (lower word) bits. + */ +#define LAPIC_ICR_VECTOR_MASK 0x000000ff +#define LAPIC_ICR_DELIVERY_INIT 0x00000500 +#define LAPIC_ICR_DELIVERY_STARTUP 0x00000600 +#define LAPIC_ICR_STATUS_PENDING 0x00001000 +#define LAPIC_ICR_LEVEL_ASSERT 0x00004000 +#define LAPIC_ICR_TRIGGER_LEVEL 0x00008000 +#define LAPIC_ICR_DEST_SELF 0x00040000 +#define LAPIC_ICR_DEST_ALL_WITH_SELF 0x00080000 +#define LAPIC_ICR_DEST_ALL_EXCEPT_SELF 0x000c0000 +#define LAPIC_ICR_DEST_MASK 0x000c0000 +#define LAPIC_ICR_RESERVED 0xfff32000 + +/* + * ICR destination shift and mask. + */ +#define LAPIC_DEST_SHIFT 24 +#define LAPIC_DEST_MASK 0xff000000 + +/* + * Local APIC registers are accessed with 32-bits loads/stores aligned on + * 128 bits. + */ +struct lapic_register { + uint32_t reg; + uint32_t reserved0; + uint32_t reserved1; + uint32_t reserved2; +} __packed; + +/* + * Local APIC register map. + */ +struct lapic_map { + const struct lapic_register reserved0; + const struct lapic_register reserved1; + + /* + * Some processors don't allow writing to this register, and the + * specification explicitely discourages modifications. Consider it + * read only. + */ + const struct lapic_register id; + const struct lapic_register version; + const struct lapic_register reserved2; + const struct lapic_register reserved3; + const struct lapic_register reserved4; + const struct lapic_register reserved5; + struct lapic_register tpr; + const struct lapic_register reserved6; /* APR */ + const struct lapic_register ppr; + struct lapic_register eoi; + const struct lapic_register reserved7; /* RRD */ + struct lapic_register ldr; + struct lapic_register dfr; + struct lapic_register svr; + const struct lapic_register isr0; + const struct lapic_register isr1; + const struct lapic_register isr2; + const struct lapic_register isr3; + const struct lapic_register isr4; + const struct lapic_register isr5; + const struct lapic_register isr6; + const struct lapic_register isr7; + const struct lapic_register tmr0; + const struct lapic_register tmr1; + const struct lapic_register tmr2; + const struct lapic_register tmr3; + const struct lapic_register tmr4; + const struct lapic_register tmr5; + const struct lapic_register tmr6; + const struct lapic_register tmr7; + const struct lapic_register irr0; + const struct lapic_register irr1; + const struct lapic_register irr2; + const struct lapic_register irr3; + const struct lapic_register irr4; + const struct lapic_register irr5; + const struct lapic_register irr6; + const struct lapic_register irr7; + struct lapic_register esr; + const struct lapic_register reserved8; + const struct lapic_register reserved9; + const struct lapic_register reserved10; + const struct lapic_register reserved11; + const struct lapic_register reserved12; + const struct lapic_register reserved13; + struct lapic_register lvt_cmci; + struct lapic_register icr_low; + struct lapic_register icr_high; + struct lapic_register lvt_timer; + const struct lapic_register reserved14; /* Thermal sensor register */ + const struct lapic_register reserved15; /* Performance counters register */ + struct lapic_register lvt_lint0; + struct lapic_register lvt_lint1; + struct lapic_register lvt_error; + struct lapic_register timer_icr; + const struct lapic_register timer_ccr; + const struct lapic_register reserved16; + const struct lapic_register reserved17; + const struct lapic_register reserved18; + const struct lapic_register reserved19; + struct lapic_register timer_dcr; + const struct lapic_register reserved20; +} __packed; + +/* + * Address where local APIC registers are mapped. + */ +static volatile struct lapic_map *lapic_map; + +/* + * Base frequency of the local APIC timer. + */ +static uint32_t lapic_bus_freq; + +static uint32_t +lapic_read(const volatile struct lapic_register *r) +{ + return r->reg; +} + +static void +lapic_write(volatile struct lapic_register *r, uint32_t value) +{ + r->reg = value; +} + +static void __init +lapic_setup_timer(void) +{ + uint32_t c1, c2; + + lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1); + + /* The APIC timer counter should never wrap around here */ + lapic_write(&lapic_map->timer_icr, LAPIC_TIMER_COUNT_MAX); + c1 = lapic_read(&lapic_map->timer_ccr); + cpu_delay(LAPIC_TIMER_CAL_DELAY); + c2 = lapic_read(&lapic_map->timer_ccr); + lapic_bus_freq = (c1 - c2) * (1000000 / LAPIC_TIMER_CAL_DELAY); + printk("lapic: bus frequency: %u.%02u MHz\n", lapic_bus_freq / 1000000, + lapic_bus_freq % 1000000); + lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); +} + +static void +lapic_eoi(void) +{ + lapic_write(&lapic_map->eoi, 0); +} + +static void __init +lapic_setup_registers(void) +{ + /* + * LVT mask bits can only be cleared when the local APIC is enabled. + */ + lapic_write(&lapic_map->svr, LAPIC_SVR_SOFT_EN | T_APIC_SPURIOUS_INTR); + lapic_write(&lapic_map->tpr, 0); + lapic_write(&lapic_map->eoi, 0); + lapic_write(&lapic_map->esr, 0); + lapic_write(&lapic_map->lvt_timer, LAPIC_LVT_TIMER_PERIODIC + | T_APIC_TIMER_INTR); + lapic_write(&lapic_map->lvt_lint0, LAPIC_LVT_MASK_INTR); + lapic_write(&lapic_map->lvt_lint1, LAPIC_LVT_MASK_INTR); + lapic_write(&lapic_map->lvt_error, T_APIC_ERROR_INTR); + lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1); + lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); +} + +void __init +lapic_setup(uint32_t map_addr) +{ + uint32_t value; + + lapic_map = vm_kmem_map_pa(map_addr, sizeof(*lapic_map), NULL, NULL); + + if (lapic_map == NULL) + panic("lapic: unable to map registers in kernel map"); + + value = lapic_read(&lapic_map->version); + + if ((value & LAPIC_VERSION_MASK) != LAPIC_VERSION_MASK) + panic("lapic: external local APIC not supported"); + + lapic_setup_registers(); + lapic_setup_timer(); +} + +void __init +lapic_ap_setup(void) +{ + lapic_setup_registers(); +} + +static void +lapic_ipi(uint32_t dest, uint32_t icr) +{ + uint32_t value; + + if ((icr & LAPIC_ICR_DEST_MASK) == 0) { + value = lapic_read(&lapic_map->icr_high); + value &= ~LAPIC_DEST_MASK; + value |= dest << LAPIC_DEST_SHIFT; + lapic_write(&lapic_map->icr_high, value); + } + + value = lapic_read(&lapic_map->icr_low); + value &= LAPIC_ICR_RESERVED; + value |= icr; + lapic_write(&lapic_map->icr_low, value); +} + +static void +lapic_ipi_wait(void) +{ + uint32_t value; + + do { + value = lapic_read(&lapic_map->icr_low); + cpu_pause(); + } while (value & LAPIC_ICR_STATUS_PENDING); +} + +void +lapic_ipi_init_assert(uint32_t dest) +{ + lapic_ipi(dest, LAPIC_ICR_TRIGGER_LEVEL | LAPIC_ICR_LEVEL_ASSERT + | LAPIC_ICR_DELIVERY_INIT); + lapic_ipi_wait(); +} + +void +lapic_ipi_init_deassert(uint32_t dest) +{ + lapic_ipi(dest, LAPIC_ICR_TRIGGER_LEVEL | LAPIC_ICR_DELIVERY_INIT); + lapic_ipi_wait(); +} + +void +lapic_ipi_startup(uint32_t dest, uint32_t vector) +{ + lapic_ipi(dest, LAPIC_ICR_DELIVERY_STARTUP + | (vector & LAPIC_ICR_VECTOR_MASK)); + lapic_ipi_wait(); +} + +void +lapic_timer_intr(void) +{ + lapic_eoi(); +} |