X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=kitten%2Farch%2Fx86_64%2Fkernel%2Fioapic.c;fp=kitten%2Farch%2Fx86_64%2Fkernel%2Fioapic.c;h=bf38e202c6cec46daacb3c8ad6a69af831bab4b3;hb=66a1a4c7a9edcd7d8bc207aca093d694a6e6b5b2;hp=0000000000000000000000000000000000000000;hpb=f7cf9c19ecb0a589dd45ae0d2c91814bd3c2acc2;p=palacios.git diff --git a/kitten/arch/x86_64/kernel/ioapic.c b/kitten/arch/x86_64/kernel/ioapic.c new file mode 100644 index 0000000..bf38e20 --- /dev/null +++ b/kitten/arch/x86_64/kernel/ioapic.c @@ -0,0 +1,355 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Lock that protects access to the IO APICs in the system. + * There is only one lock for all IO APICs. + */ +static DEFINE_SPINLOCK(ioapic_lock); + +/** + * Number of IO APICs in the system. + */ +unsigned int ioapic_num; + +/** + * Array containing the IDs of the IO APICs in the system. + * The array is indexed by ioapic_index. + */ +unsigned int ioapic_id[MAX_IO_APICS] = { 1 }; + +/** + * Addresses of the IO APICs in the system. + * The array is indexed by ioapic_index. + */ +uintptr_t ioapic_phys_addr[MAX_IO_APICS] = { 0xFEC00000 }; + +/** + * Resource entries for the IO APIC memory mapping. + */ +static struct resource *ioapic_resources; + +/** + * Structure used to map IO APIC registers. + */ +struct ioapic { + uint32_t index; + uint32_t unused[3]; + uint32_t data; +}; + +/** + * Union used to map an IO APIC routing entry register. + */ +union ioapic_entry_union { + struct { uint32_t low_word, high_word; }; + struct IO_APIC_route_entry entry; +}; + +/** + * Returns the base kernel virtual address of the specified IO APIC's + * kernel mapping. + */ +static struct ioapic * +ioapic_base_addr(int ioapic_index) +{ + return (void *) __fix_to_virt(FIX_IO_APIC_BASE_0 + ioapic_index) + + (ioapic_phys_addr[ioapic_index] & ~PAGE_MASK); +} + +/** + * Reads a value from an IO APIC register. + */ +static uint32_t +ioapic_read(unsigned int ioapic_index, uint32_t reg) +{ + struct ioapic *ioapic = ioapic_base_addr(ioapic_index); + writel(reg, &ioapic->index); + return readl(&ioapic->data); +} + +/** + * Writes a value to an IO APIC register. + */ +static void +ioapic_write(unsigned int ioapic_index, uint32_t reg, uint32_t value) +{ + struct ioapic *ioapic = ioapic_base_addr(ioapic_index); + writel(reg, &ioapic->index); + writel(value, &ioapic->data); +} + +/** + * Reads an IO APIC pin routing entry. + */ +static struct IO_APIC_route_entry +ioapic_read_pin( + unsigned int ioapic_index, + unsigned int pin +) +{ + union ioapic_entry_union eu; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + eu.low_word = ioapic_read(ioapic_index, 0x10 + 2 * pin); + eu.high_word = ioapic_read(ioapic_index, 0x11 + 2 * pin); + spin_unlock_irqrestore(&ioapic_lock, flags); + + return eu.entry; +} + +/** + * Writes an IO APIC pin routing entry. + * + * When we write a new IO APIC routing entry, we need to write the high word + * first. This is because the mask/enable bit is in the low word and we do not + * want to enable the entry before it is fully populated. + */ +static void +ioapic_write_pin( + unsigned int ioapic_index, + unsigned int pin, + struct IO_APIC_route_entry pin_config +) +{ + union ioapic_entry_union eu; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + eu.entry = pin_config; + ioapic_write(ioapic_index, 0x11 + 2 * pin, eu.high_word); + ioapic_write(ioapic_index, 0x10 + 2 * pin, eu.low_word); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +/** + * Masks (disables) an IO APIC input pin. + */ +static void +ioapic_mask_pin( + unsigned int ioapic_index, + unsigned int pin +) +{ + struct IO_APIC_route_entry pin_config = + ioapic_read_pin(ioapic_index, pin); + pin_config.mask = 1; + ioapic_write_pin(ioapic_index, pin, pin_config); +} + +/** + * Unmasks (enables) an IO APIC input pin. + */ +static void +ioapic_unmask_pin( + unsigned int ioapic_index, + unsigned int pin +) +{ + struct IO_APIC_route_entry pin_config = + ioapic_read_pin(ioapic_index, pin); + pin_config.mask = 0; + ioapic_write_pin(ioapic_index, pin, pin_config); +} + +/** + * Returns the number of input pins provided by the specified IO APIC. + */ +static unsigned int +ioapic_get_num_pins(unsigned int ioapic_index) +{ + union IO_APIC_reg_01 reg_01; + + reg_01.raw = ioapic_read(ioapic_index, 1); + return reg_01.bits.entries + 1; +} + +/** + * Initializes the primary IO APIC (the one connected to the ISA IRQs). + */ +static void __init +ioapic_init_primary( + unsigned int ioapic_index +) +{ + unsigned int pin; + unsigned int num_pins = ioapic_get_num_pins(ioapic_index); + struct IO_APIC_route_entry cfg; + + if (num_pins != 24) + panic("Expected IOAPIC to have 24 pins, has %u.", num_pins); + + /* Mask (disable) all pins */ + for (pin = 0; pin < num_pins; pin++) { + ioapic_mask_pin(ioapic_index, pin); + } + + /* + * Configure ISA IRQs. + * (Assuming pins [1,15] are the standard ISA IRQs) + * (Assuming pin 2 is hooked to the timer interrupt) + * (Assuming pin 0 is hooked to the old i8259 PIC... don't use it) + */ + for (pin = 1; pin <= 15; pin++) { + cfg = ioapic_read_pin(ioapic_index, pin); + + cfg.delivery_mode = ioapic_fixed; + cfg.dest_mode = ioapic_physical_dest; + cfg.polarity = (pin == 8) + ? ioapic_active_low + : ioapic_active_high; + cfg.trigger = ioapic_edge_sensitive; + cfg.dest = (uint8_t) cpu_info[0].physical_id; + cfg.vector = IRQ0_VECTOR + pin; + + ioapic_write_pin(ioapic_index, pin, cfg); + } + + /* + * Configure PCI IRQs. + * (Assuming pins [16,19] are PCI INTA, INTB, INTC, and INTD) + */ + for (pin = 16; pin <= 19; pin++) { + cfg = ioapic_read_pin(ioapic_index, pin); + + cfg.delivery_mode = ioapic_fixed; + cfg.dest_mode = ioapic_physical_dest; + cfg.polarity = ioapic_active_low; + cfg.trigger = ioapic_level_sensitive; + cfg.dest = (uint8_t) cpu_info[0].physical_id; + cfg.vector = IRQ0_VECTOR + pin; + + ioapic_write_pin(ioapic_index, pin, cfg); + } + + /* Unmask (enable) all of the pins that have been configured */ + for (pin = 1; pin < 19; pin++) { + ioapic_unmask_pin(ioapic_index, pin); + } +} + +/** + * Creates a kernel mapping for all IO APICs in the system. + */ +void __init +ioapic_map(void) +{ + unsigned int i; + const int name_size = 16; + char *name; + + if (ioapic_num == 0) + { + printk( "%s: Faking IO APIC\n", __func__ ); + ioapic_num = 1; + } + + /* + * Allocate enough memory for one resource structure per detected IO + * APIC in the system. Memory for the resource name strings is tacked + * onto the end of the allocation (name_size*ioapic_num bytes). + */ + ioapic_resources = alloc_bootmem(ioapic_num * + (sizeof(struct resource) + name_size)); + name = ((char *)ioapic_resources) + ioapic_num*sizeof(struct resource); + + for (i = 0; i < ioapic_num; i++) { + /* Reserve the physical memory used by the IO APIC */ + sprintf(name, "IO APIC %u", i); + ioapic_resources[i].name = name; + ioapic_resources[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY; + ioapic_resources[i].start = ioapic_phys_addr[i]; + ioapic_resources[i].end = ioapic_phys_addr[i] + 4096 - 1; + request_resource(&iomem_resource, &ioapic_resources[i]); + name += name_size; + + /* Map the IO APIC into the kernel */ + set_fixmap_nocache(FIX_IO_APIC_BASE_0 + i, ioapic_phys_addr[i]); + + printk(KERN_DEBUG + "IO APIC mapped to virtual address 0x%016lx\n", + __fix_to_virt(FIX_IO_APIC_BASE_0 + i) + ); + } +} + +/** + * Initializes all IO APICs in the system. + */ +void __init +ioapic_init(void) +{ + if (ioapic_num == 0) + return; + +/* TODO: FIX THIS... NEED TO PARSE MPTABLE OR SOMETHING ELSE */ +#ifdef CONFIG_PC + /* TODO: For now, only initializes the first one. */ + ioapic_init_primary(0); + ioapic_dump(); +#endif +} + +/** + * Dumps the current state of all IO APICs in the system. + */ +void __init +ioapic_dump(void) +{ + unsigned int ioapic_index, pin; + union IO_APIC_reg_00 reg_00; + union IO_APIC_reg_01 reg_01; + union IO_APIC_reg_02 reg_02; + struct IO_APIC_route_entry entry; + unsigned long flags; + + for (ioapic_index = 0; ioapic_index < ioapic_num; ioapic_index++) { + + spin_lock_irqsave(&ioapic_lock, flags); + reg_00.raw = ioapic_read(ioapic_index, 0); + reg_01.raw = ioapic_read(ioapic_index, 1); + if (reg_01.bits.version >= 0x10) + reg_02.raw = ioapic_read(ioapic_index, 2); + spin_unlock_irqrestore(&ioapic_lock, flags); + + printk(KERN_DEBUG "Dump of IO APIC %u (physical id %u):\n", + ioapic_index, ioapic_id[ioapic_index]); + printk(KERN_DEBUG " register #00: %08X\n", reg_00.raw); + printk(KERN_DEBUG " physical APIC id: %02u\n", reg_00.bits.ID); + printk(KERN_DEBUG " register #01: %08X\n", *(int *)®_01); + printk(KERN_DEBUG " max redirection entries: %04X\n", reg_01.bits.entries); + printk(KERN_DEBUG " PRQ implemented: %X\n", reg_01.bits.PRQ); + printk(KERN_DEBUG " IO APIC version: %04X\n", reg_01.bits.version); + if (reg_01.bits.version >= 0x10) { + printk(KERN_DEBUG " register #02: %08X\n", reg_02.raw); + printk(KERN_DEBUG " arbitration: %02X\n", reg_02.bits.arbitration); + } + + printk(KERN_DEBUG " Interrupt Redirection Table:\n"); + for (pin = 0; pin <= reg_01.bits.entries; pin++) { + entry = ioapic_read_pin(ioapic_index, pin); + printk(KERN_DEBUG + " %02u: vector=%u dest=%03u mask=%1d " + "trigger=%1d irr=%1d polarity=%1d\n", + pin, entry.vector, entry.dest, entry.mask, + entry.trigger, entry.irr, entry.polarity); + printk(KERN_DEBUG + " dest_mode=%1d delivery_mode=%1d " + "delivery_status=%1d\n", + entry.dest_mode, entry.delivery_mode, + entry.delivery_status); + } + } +} +