nuttx/arch/x86_64/src/intel64/intel64_irq.c
p-szafonimateusz b1fd3da0f6 intel64/intel64_irq.c: support interrupts up to 255
From now all intel64 interrupts are supported.
Required step towards MSI/MSI-X

Signed-off-by: p-szafonimateusz <p-szafonimateusz@xiaomi.com>
2024-03-08 20:18:39 -03:00

621 lines
18 KiB
C

/****************************************************************************
* arch/x86_64/src/intel64/intel64_irq.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <stdint.h>
#include <string.h>
#include <debug.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <arch/arch.h>
#include <arch/irq.h>
#include <arch/io.h>
#include <arch/board/board.h>
#include <nuttx/spinlock.h>
#include "x86_64_internal.h"
#include "intel64.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define UART_BASE 0x3f8
#define IRQ_STACK_SIZE 0x2000
/****************************************************************************
* Private Types
****************************************************************************/
struct intel64_irq_priv_s
{
uint8_t busy;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void up_apic_init(void);
static void up_idtentry(unsigned int index, uint64_t base, uint16_t sel,
uint8_t flags, uint8_t ist);
static inline void up_idtinit(void);
/****************************************************************************
* Public Data
****************************************************************************/
volatile uint64_t *g_current_regs;
uint8_t g_interrupt_stack[IRQ_STACK_SIZE] aligned_data(16);
uint8_t *g_interrupt_stack_end = g_interrupt_stack + IRQ_STACK_SIZE - 16;
uint8_t g_isr_stack[IRQ_STACK_SIZE] aligned_data(16);
uint8_t *g_isr_stack_end = g_isr_stack + IRQ_STACK_SIZE - 16;
/****************************************************************************
* Private Data
****************************************************************************/
static struct idt_entry_s g_idt_entries[NR_IRQS];
static struct intel64_irq_priv_s g_irq_priv[NR_IRQS];
static spinlock_t g_irq_spin;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: up_ioapic_read
*
* Description:
* Read a IOAPIC register
*
****************************************************************************/
uint32_t up_ioapic_read(uint32_t reg)
{
mmio_write32((void *)IOAPIC_BASE + IOAPIC_REG_INDEX, reg);
return mmio_read32((void *)IOAPIC_BASE + IOAPIC_REG_DATA);
}
/****************************************************************************
* Name: up_ioapic_write
*
* Description:
* Write a IOAPIC register
*
****************************************************************************/
void up_ioapic_write(uint32_t reg, uint32_t data)
{
mmio_write32((void *)IOAPIC_BASE + IOAPIC_REG_INDEX, reg);
mmio_write32((void *)IOAPIC_BASE + IOAPIC_REG_DATA, data);
}
/****************************************************************************
* Name: up_ioapic_pin_set_vector
*
* Description:
* Bind a hardware IRQ number to APIC IRQ number
*
****************************************************************************/
void up_ioapic_pin_set_vector(unsigned int pin,
enum ioapic_trigger_mode trigger_mode,
unsigned int vector)
{
up_ioapic_write(IOAPIC_REG_TABLE + pin * 2 + 1,
up_apic_cpu_id() << (56 - 32));
up_ioapic_write(IOAPIC_REG_TABLE + pin * 2,
trigger_mode | vector);
}
/****************************************************************************
* Name: up_ioapic_mask_pin
*
* Description:
* Mask an IO-APIC interrupt
*
****************************************************************************/
void up_ioapic_mask_pin(unsigned int pin)
{
uint32_t cur;
cur = up_ioapic_read(IOAPIC_REG_TABLE + pin * 2);
up_ioapic_write(IOAPIC_REG_TABLE + pin * 2,
cur | IOAPIC_PIN_DISABLE);
}
/****************************************************************************
* Name: up_ioapic_unmask_pin
*
* Description:
* Unmask an IO-APIC interrupt
*
****************************************************************************/
void up_ioapic_unmask_pin(unsigned int pin)
{
uint32_t cur;
cur = up_ioapic_read(IOAPIC_REG_TABLE + pin * 2);
up_ioapic_write(IOAPIC_REG_TABLE + pin * 2,
cur & ~(IOAPIC_PIN_DISABLE));
}
/****************************************************************************
* Name: up_init_ist
*
* Description:
* Initialize the Interrupt Stack Table
*
****************************************************************************/
static void up_ist_init(void)
{
struct gdt_entry_s tss_l;
uint64_t tss_h;
memset(&tss_l, 0, sizeof(tss_l));
memset(&tss_h, 0, sizeof(tss_h));
tss_l.limit_low = (((104 - 1) & 0xffff)); /* Segment limit = TSS size - 1 */
tss_l.base_low = ((uintptr_t)g_ist64 & 0x00ffffff); /* Low address 1 */
tss_l.base_high = (((uintptr_t)g_ist64 & 0xff000000) >> 24); /* Low address 2 */
tss_l.P = 1;
/* Set type as IST */
tss_l.AC = 1;
tss_l.EX = 1;
tss_h = (((uintptr_t)g_ist64 >> 32) & 0xffffffff); /* High address */
g_gdt64[X86_GDT_ISTL_SEL_NUM] = tss_l;
/* memcpy used to handle type punning compiler warning */
memcpy((void *)&g_gdt64[X86_GDT_ISTH_SEL_NUM],
(void *)&tss_h, sizeof(g_gdt64[0]));
g_ist64->IST1 = (uintptr_t)g_interrupt_stack_end;
g_ist64->IST2 = (uintptr_t)g_isr_stack_end;
asm volatile ("mov $0x30, %%ax; ltr %%ax":::"memory", "rax");
}
/****************************************************************************
* Name: up_deinit_8259
*
* Description:
* Initialize the Legacy 8259 PIC
*
****************************************************************************/
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
static void up_deinit_8259(void)
{
/* First do an initialization to for any pending interrupt to vanish */
/* Starts the initialization sequence (in cascade mode) */
outb(X86_PIC_INIT, X86_IO_PORT_PIC1_CMD);
outb(X86_PIC_INIT, X86_IO_PORT_PIC2_CMD);
/* Remap the IRQ to 32~ in case of a spurious interrupt */
outb(IRQ0, X86_IO_PORT_PIC1_DATA); /* ICW2: Master PIC vector offset */
outb(IRQ0 + 8, X86_IO_PORT_PIC2_DATA); /* ICW2: Slave PIC vector offset */
/* Tell the PICs they are in cascade */
/* ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) */
outb(X86_PIC1_CASCADE, X86_IO_PORT_PIC1_DATA);
/* ICW3: tell Slave PIC its cascade identity (0000 0010) */
outb(X86_PIC2_CASCADE, X86_IO_PORT_PIC2_DATA);
/* Put the PICs into 8086 mode */
outb(X86_PIC_8086, X86_IO_PORT_PIC1_DATA);
outb(X86_PIC_8086, X86_IO_PORT_PIC2_DATA);
/* Disable the 8259 PIC by masking all the interrupt and acking them */
outb(0xff, X86_IO_PORT_PIC1_DATA);
outb(0xff, X86_IO_PORT_PIC2_DATA);
outb(X86_PIC_EOI, X86_IO_PORT_PIC1_CMD);
outb(X86_PIC_EOI, X86_IO_PORT_PIC2_CMD);
}
#endif
/****************************************************************************
* Name: up_init_apic
*
* Description:
* Initialize the APIC
*
****************************************************************************/
static void up_apic_init(void)
{
uint32_t ver;
uint32_t icrl;
uint32_t apic_base;
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
/* Enable the APIC in X2APIC MODE */
apic_base = read_msr(MSR_IA32_APIC_BASE) & 0xfffff000;
write_msr(MSR_IA32_APIC_BASE, apic_base | MSR_IA32_APIC_EN |
MSR_IA32_APIC_X2APIC | MSR_IA32_APIC_BSP);
#endif
/* Enable the APIC and setup an spurious interrupt vector */
write_msr(MSR_X2APIC_SPIV, MSR_X2APIC_SPIV_EN | IRQ_SPURIOUS);
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
/* Disable the LINT interrupt lines */
write_msr(MSR_X2APIC_LINT0, MSR_X2APIC_MASKED);
write_msr(MSR_X2APIC_LINT1, MSR_X2APIC_MASKED);
/* Disable performance counter overflow interrupts on machines tha
* provide that interrupt entry.
*/
ver = read_msr(MSR_X2APIC_VER);
if (((ver >> 16) & 0xff) >= 4)
{
write_msr(MSR_X2APIC_LVTPMR, MSR_X2APIC_MASKED);
}
/* Map error interrupt to IRQ_ERROR. */
write_msr(MSR_X2APIC_LERR, MSR_X2APIC_MASKED);
/* Clear error status register (requires back-to-back writes). */
write_msr(MSR_X2APIC_ESR, 0);
write_msr(MSR_X2APIC_ESR, 0);
/* Ack any outstanding interrupts. */
write_msr(MSR_X2APIC_EOI, 0);
/* Send an Init Level De-Assert to synchronize arbitration ID's. */
write_msr(MSR_X2APIC_ICR, MSR_X2APIC_ICR_BCAST | MSR_X2APIC_ICR_INIT |
MSR_X2APIC_ICR_LEVEL);
do
{
icrl = read_msr(MSR_X2APIC_ICR);
}
while (icrl & MSR_X2APIC_ICR_DELIVS);
/* Enable interrupts on the APIC (but not on the processor). */
write_msr(MSR_X2APIC_TPR, 0);
#endif
}
/****************************************************************************
* Name: legacy_pic_irq_handler
*
* Description:
* This function will capture will legacy 8259 PIC IRQ using virtual wire
* mode
*
****************************************************************************/
static int unused_code
legacy_pic_irq_handler(int irq, uint32_t *regs, void *arg)
{
return 0;
}
/****************************************************************************
* Name: up_init_ioapic
*
* Description:
* Initialize the IOAPIC
*
****************************************************************************/
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
static void up_ioapic_init(void)
{
int i;
up_map_region((void *)IOAPIC_BASE, HUGE_PAGE_SIZE,
X86_PAGE_PRESENT | X86_PAGE_WR | X86_PAGE_NOCACHE);
/* Setup the IO-APIC, remap the interrupt to 32~ */
uint32_t maxintr = (up_ioapic_read(IOAPIC_REG_VER) >> 16) & 0xff;
for (i = 0; i < maxintr; i++)
{
up_ioapic_pin_set_vector(i, TRIGGER_RISING_EDGE, IRQ0 + i);
up_ioapic_mask_pin(i);
}
}
#endif
/****************************************************************************
* Name: up_idtentry
*
* Description:
* Initialize one IDT entry.
*
****************************************************************************/
static void up_idtentry(unsigned int index, uint64_t base, uint16_t sel,
uint8_t flags, uint8_t ist)
{
struct idt_entry_s *entry = &g_idt_entries[index];
entry->lobase = base & 0xffff;
entry->hibase = (base >> 16) & 0xffff;
entry->xhibase = (base >> 32) & 0xffffffff;
entry->ist = ist & 0x7;
entry->sel = sel;
entry->zero = 0;
/* We must uncomment the OR below when we get to using user-mode. It sets
* the interrupt gate's privilege level to 3.
*/
entry->flags = flags; /* | 0x60 */
}
/****************************************************************************
* Name: up_idtinit
*
* Description:
* Initialize the IDT. The Interrupt Descriptor Table (IDT) is a data
* structure used by the x86 architecture to implement an interrupt vector
* table. The IDT is used by the processor to determine the correct
* response to interrupts and exceptions.
*
****************************************************************************/
static inline void up_idtinit(void)
{
size_t offset = 0;
uint64_t vector = 0;
int irq = 0;
memset(&g_idt_entries, 0, sizeof(g_idt_entries));
/* Set each ISR/IRQ to the appropriate vector with selector=8 and with
* 32-bit interrupt gate. Interrupt gate (vs. trap gate) will leave
* interrupts enabled when the IRS/IRQ handler is entered.
*/
up_idtentry(ISR0, (uint64_t)vector_isr0 , 0x08, 0x8e, 0x2);
up_idtentry(ISR1, (uint64_t)vector_isr1 , 0x08, 0x8e, 0x2);
up_idtentry(ISR2, (uint64_t)vector_isr2 , 0x08, 0x8e, 0x2);
up_idtentry(ISR3, (uint64_t)vector_isr3 , 0x08, 0x8e, 0x2);
up_idtentry(ISR4, (uint64_t)vector_isr4 , 0x08, 0x8e, 0x2);
up_idtentry(ISR5, (uint64_t)vector_isr5 , 0x08, 0x8e, 0x2);
up_idtentry(ISR6, (uint64_t)vector_isr6 , 0x08, 0x8e, 0x2);
up_idtentry(ISR7, (uint64_t)vector_isr7 , 0x08, 0x8e, 0x2);
up_idtentry(ISR8, (uint64_t)vector_isr8 , 0x08, 0x8e, 0x2);
up_idtentry(ISR9, (uint64_t)vector_isr9 , 0x08, 0x8e, 0x2);
up_idtentry(ISR10, (uint64_t)vector_isr10, 0x08, 0x8e, 0x2);
up_idtentry(ISR11, (uint64_t)vector_isr11, 0x08, 0x8e, 0x2);
up_idtentry(ISR12, (uint64_t)vector_isr12, 0x08, 0x8e, 0x2);
up_idtentry(ISR13, (uint64_t)vector_isr13, 0x08, 0x8e, 0x2);
up_idtentry(ISR14, (uint64_t)vector_isr14, 0x08, 0x8e, 0x2);
up_idtentry(ISR15, (uint64_t)vector_isr15, 0x08, 0x8e, 0x2);
up_idtentry(ISR16, (uint64_t)vector_isr16, 0x08, 0x8e, 0x2);
up_idtentry(ISR17, (uint64_t)vector_isr17, 0x08, 0x8e, 0x2);
up_idtentry(ISR18, (uint64_t)vector_isr18, 0x08, 0x8e, 0x2);
up_idtentry(ISR19, (uint64_t)vector_isr19, 0x08, 0x8e, 0x2);
up_idtentry(ISR20, (uint64_t)vector_isr20, 0x08, 0x8e, 0x2);
up_idtentry(ISR21, (uint64_t)vector_isr21, 0x08, 0x8e, 0x2);
up_idtentry(ISR22, (uint64_t)vector_isr22, 0x08, 0x8e, 0x2);
up_idtentry(ISR23, (uint64_t)vector_isr23, 0x08, 0x8e, 0x2);
up_idtentry(ISR24, (uint64_t)vector_isr24, 0x08, 0x8e, 0x2);
up_idtentry(ISR25, (uint64_t)vector_isr25, 0x08, 0x8e, 0x2);
up_idtentry(ISR26, (uint64_t)vector_isr26, 0x08, 0x8e, 0x2);
up_idtentry(ISR27, (uint64_t)vector_isr27, 0x08, 0x8e, 0x2);
up_idtentry(ISR28, (uint64_t)vector_isr28, 0x08, 0x8e, 0x2);
up_idtentry(ISR29, (uint64_t)vector_isr29, 0x08, 0x8e, 0x2);
up_idtentry(ISR30, (uint64_t)vector_isr30, 0x08, 0x8e, 0x2);
up_idtentry(ISR31, (uint64_t)vector_isr31, 0x08, 0x8e, 0x2);
/* Set all IRQ vectors */
offset = (uint64_t)vector_irq1 - (uint64_t)vector_irq0;
for (irq = IRQ0, vector = (uint64_t)vector_irq0;
irq <= IRQ255;
irq += 1, vector += offset)
{
up_idtentry(irq, (uint64_t)vector, 0x08, 0x8e, 0x1);
}
/* Then program the IDT */
setidt(&g_idt_entries, sizeof(struct idt_entry_s) * NR_IRQS - 1);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_irqinitialize
****************************************************************************/
void up_irqinitialize(void)
{
/* Initialize the IST */
up_ist_init();
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
/* Disable 8259 PIC */
up_deinit_8259();
#endif
/* Initialize the APIC */
up_apic_init();
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
/* Initialize the IOAPIC */
up_ioapic_init();
#endif
/* Initialize the IDT */
up_idtinit();
/* And finally, enable interrupts */
#ifndef CONFIG_SUPPRESS_INTERRUPTS
up_irq_restore(X86_64_RFLAGS_IF);
#endif
}
/****************************************************************************
* Name: up_disable_irq
*
* Description:
* Disable the IRQ line specified by 'irq'
*
****************************************************************************/
void up_disable_irq(int irq)
{
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
irqstate_t flags = spin_lock_irqsave(&g_irq_spin);
if (irq > IRQ255)
{
/* Not supported yet */
ASSERT(0);
}
if (g_irq_priv[irq].busy > 0)
{
g_irq_priv[irq].busy -= 1;
}
if (g_irq_priv[irq].busy == 0)
{
/* One time disable */
if (irq >= IRQ0)
{
up_ioapic_mask_pin(irq - IRQ0);
}
}
spin_unlock_irqrestore(&g_irq_spin, flags);
#endif
}
/****************************************************************************
* Name: up_enable_irq
*
* Description:
* Enable the IRQ line specified by 'irq'
*
****************************************************************************/
void up_enable_irq(int irq)
{
#ifndef CONFIG_ARCH_INTEL64_DISABLE_INT_INIT
irqstate_t flags = spin_lock_irqsave(&g_irq_spin);
# ifndef CONFIG_IRQCHAIN
/* Check if IRQ is free if we don't support IRQ chains */
if (g_irq_priv[irq].busy)
{
ASSERT(0);
}
# endif
if (irq > IRQ255)
{
/* Not supported yet */
ASSERT(0);
}
if (g_irq_priv[irq].busy == 0)
{
/* One time enable */
if (irq >= IRQ0)
{
up_ioapic_unmask_pin(irq - IRQ0);
}
}
g_irq_priv[irq].busy += 1;
spin_unlock_irqrestore(&g_irq_spin, flags);
#endif
}
/****************************************************************************
* Name: up_prioritize_irq
*
* Description:
* Set the priority of an IRQ.
*
* Since this API is not supported on all architectures, it should be
* avoided in common implementations where possible.
*
****************************************************************************/
#ifdef CONFIG_ARCH_IRQPRIO
int up_prioritize_irq(int irq, int priority)
{
#warning "Missing Logic"
return OK;
}
#endif