Changes ARCH i686 -> x86 (XCC)
[akaros.git] / kern / arch / x86 / ioapic.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * See LICENSE for details.
4  */
5
6 #ifdef __SHARC__
7 #pragma nosharc
8 #endif
9 // Not currently sharc complient.
10
11 /** @file
12  * @brief Basic IOAPIC Driver.
13  *
14  * This file is responsible for the initalization of the Intel x58 IOAPIC(s)
15  * Once the IOAPIC is setup, the function ioapic_route_irq() can be used
16  * to route
17  *
18  * See Ch 17.5.26 in Intel X58 Express Chipset Datasheet
19  *
20  * @author Paul Pearce <pearce@eecs.berkeley.edu>
21  *
22  * @todo Come up with an impliment a concurrency model for use of the route/unroute functions
23  * @todo Once we begin using logical core ID's for groups, adjust route/unroute to utilize this (adjust high word)
24  * @todo Some notion of a 'initalized' flag we can check to ensure bootup call order.
25  */
26
27 #include <arch/mmu.h>
28 #include <arch/x86.h>
29 #include <arch/apic.h>
30 #include <arch/mptables.h>
31 #include <arch/pci.h>
32
33 ioapic_redirect_t ioapic_redirects[NUM_IRQS];
34
35 /**
36  * @brief Parse the entries from the mptables relevant to the IOAPIC and initalize the IOAPIC and its data structures
37  *
38  * This function will loop over the data structures created by MPTables to represent ISA and PCI interrupts
39  * and then setup the ioapic_redirects array to map IRQs->IOAPIC/Flags
40  * 
41  * This function must be called during bootup, before interrupts are rerouted, and after the PCI/MPTable initilization.
42  */
43 void ioapic_init() {
44         
45         // Set all entires invalid.
46         // We define a entry to be invalid by having an ioapic_address of NULL (0x0)
47         memset(ioapic_redirects, 0x0, sizeof(ioapic_redirects));
48         
49         extern volatile uint32_t num_cpus;
50         uint32_t num_inconsistent_pci_mappings = 0;     // Increment if we find inconsistent mappings between
51                                                                                                 //  mptables and the pci bus.
52         
53         // Pull in all the stuff we need from mptables and the pci parsing. These are all stack allocated (cant be null)
54         extern pci_int_device_t pci_int_devices[PCI_MAX_BUS][PCI_MAX_DEV];
55         extern ioapic_entry_t ioapic_entries[IOAPIC_MAX_ID];
56         extern isa_int_entry_t isa_int_entries[NUM_IRQS];
57         
58         // Setup the PCI entries
59         for (int i = 0; i < NUM_IRQS; i++) {
60                 // Bus is 16 bits as we use a sential BUS value (INVALID_BUS) to denote an invalid bus
61                 //  and this valid is out of the range 0->2^8-1
62                 uint16_t bus = irq_pci_map[i]->bus;
63                 uint8_t dev = irq_pci_map[i]->dev;
64                 uint8_t line = irq_pci_map[i]->irqpin;  // Paul's line, not the irqline
65                 
66                 if (bus == INVALID_BUS)
67                         continue;
68
69                 // We do the same trick with the dest apic ID as we do with the PCI Bus, so its wider.
70                 /* might be issues with the 'line' for INTA being 0x01 now */
71                 uint16_t dst_apic_id = pci_int_devices[bus][dev].line[line].dst_apic_id;
72                 uint8_t dst_apic_int = pci_int_devices[bus][dev].line[line].dst_apic_int;
73                 
74                 // Check if this entry has been set
75                 if (dst_apic_id == INVALID_DEST_APIC) {
76                         // If we have a valid bus in the irq->pci map, and the pic->int entry doesnt exist, we have a (probably VM) problem
77                         if (num_inconsistent_pci_mappings == 0)
78                                 printk("WARNING: INCONSISTENT IRQ->PCI AND PCI->IOAPIC MAPPINGS. Trying to cope...\n");
79                         num_inconsistent_pci_mappings++;
80                         continue;
81                 }
82                 
83                 // If the lowest bit of the apic flags is set to 0, it means the ioapic is not usable (by MP Spec)
84                 // We also use this to denote non-existent ioapics in our map
85                 if ((ioapic_entries[dst_apic_id].apic_flags & 0x1) == 0) 
86                         panic("IRQ SAYS ITS GOING TO AN IOAPIC LISTED AS INVALID, THATS BAD.");
87                                         
88                 ioapic_redirects[i].ioapic_address = ioapic_entries[dst_apic_id].apic_address;
89                 ioapic_redirects[i].ioapic_int = dst_apic_int;
90                 ioapic_redirects[i].ioapic_flags = IOAPIC_PCI_FLAGS;
91         }
92         
93         // Setup the ISA entries
94         for (int i = 0; i < NUM_IRQS; i++) {
95                 
96                 uint16_t dst_apic_id = isa_int_entries[i].dst_apic_id;
97                 uint8_t dst_apic_int = isa_int_entries[i].dst_apic_int;
98                 
99                 
100                 // Skip invalid entries
101                 if (dst_apic_id == INVALID_DEST_APIC)
102                         continue;
103                         
104                 if (ioapic_redirects[i].ioapic_address != NULL) {
105                         // This is technically a lie. We could in theory handle this, so long as
106                         //  everything agrees.... however this shouldnt ever really happen
107                         //  as this means we have both PCI and ISA claiming an interrupt
108                         panic("BOTH PCI AND ISA CLAIM TO SHARE AN IRQ. BAD");
109                 }
110                 
111                 // Code to check if this isa irq entry claims to be pci
112                 uint16_t pci_bus = irq_pci_map[i]->bus;
113                 /* TODO: this stuff probably doesn't work right anymore */
114                 if (pci_bus != INVALID_BUS) {
115                         // PCI bus had an entry for this irq, but we didn't set it during our pci run
116                         //  This means it is likely a broken mptable implimentation. this happens on bochs and kvm
117                         //  lets just set the flags as if its broken, and move on. Hopefully it will work out.
118                         ioapic_redirects[i].ioapic_flags = IOAPIC_BROKEN_PCI_FLAGS;
119                         num_inconsistent_pci_mappings--;
120                 }
121                 else {
122                         ioapic_redirects[i].ioapic_flags = IOAPIC_ISA_FLAGS;
123                 }
124                 
125
126                 ioapic_redirects[i].ioapic_address = ioapic_entries[dst_apic_id].apic_address;
127                 ioapic_redirects[i].ioapic_int = dst_apic_int;
128         }
129         
130         // Things didn't balance out when we scanned the isa bus for the missing pci devices. Die.
131         if (num_inconsistent_pci_mappings != 0) 
132                 panic("FAILED TO COPE WITH INCONSISTENT IRQ->PCI AND PCI->IOAPIC MAPPINGS!");
133         
134         // Support for other type of IRQ's goes here.
135         
136         /* Note: We do not technically ever do anything to initalize the IOAPIC
137         *   According to the x58 chipset spec, this is done for us. It starts up
138         *   usable and with everything masked, so there isn't really anything to do
139         *   besides setup our structures.
140         */
141 }
142
143
144 /** @brief Reconfigure the correct IOAPIC to route a given irq to a given dest
145   * 
146   * This function will take an irq given by 'irq' and using the interal IOAPIC
147   * strucures will adjust the IOAPIC to properly route that IRQ to a core 
148   * (or in the future group of cores) specified by the 'dest' bits.
149   *
150   * This function must be called after ioapic_init() is called.
151   *
152   * There is no notion of success besides invalid data, which casues a panic.
153   *
154   * @todo Logical partition support
155   * @todo Decide on a synchronization mechinism
156   *
157   * @param[in] irq      The IRQ we are trying to route. This is non-kernal-offseted. EX: Pit is IRQ 0, not 32.
158   * @param[in] dest     The core id we want to route irq to
159   */
160
161 void ioapic_route_irq(uint8_t irq, uint8_t dest) {
162         
163         if (((irq + KERNEL_IRQ_OFFSET) >= NUM_IRQS) || (ioapic_redirects[irq].ioapic_address == NULL)) {
164                 panic("TRYING TO REROUTE AN INVALID IRQ!");
165         }
166
167         // THIS IS A TEMP CHECK. IF WE USE LOGICAL PARTITIONS THIS MUST BE REMOVED
168         extern volatile uint32_t num_cpus;
169         if (dest >= num_cpus)
170                 panic("TRYING TO REROUTE TO AN INVALID DESTINATION!");
171         
172         if (irq == 0 && dest != 0)
173                 cprintf("WARNING: Rerouting IRQ to core != 0 may cause undefined behavior!\n");
174
175         // Bit pack our redirection entry. This is black magic based on the spec. See the x58 spec.
176         uint32_t redirect_low = KERNEL_IRQ_OFFSET + irq;
177         redirect_low = redirect_low | (ioapic_redirects[irq].ioapic_flags << 8);
178         uint32_t redirect_high = dest << 24;
179         
180         // YOU MUST MUST MUST MUST MUST MUST MUST write the high bits first. If you don't, you get interrupts going to crazy places
181         // Ask Paul about that afternoon of his life.
182         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address , IOAPIC_REDIRECT_OFFSET + 2*ioapic_redirects[irq].ioapic_int + 1);
183         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address  + IOAPIC_WRITE_WINDOW_OFFSET, redirect_high);
184         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address , IOAPIC_REDIRECT_OFFSET + 2*ioapic_redirects[irq].ioapic_int);
185         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address  + IOAPIC_WRITE_WINDOW_OFFSET, redirect_low);
186 }
187
188 /** @brief Reconfigure the correct IOAPIC to no longer route a given irq to any core
189   * 
190   * This function will take an irq given by 'irq' and using the interal IOAPIC
191   * strucures will adjust the IOAPIC to no longer route that irq to any destination
192   *
193   * This function must be called after ioapic_init() is called, but need not be called after a matching ioapic_route_irq()
194   *
195   * There is no notion of success besides invalid data, which casues a panic.
196   *
197   * @todo Decide on a synchronization mechinism
198   * 
199   * @param[in] irq      The IRQ we are trying to unroute. This is non-kernal-offseted. EX: Pit is IRQ 0, not 32.
200   */
201 void ioapic_unroute_irq(uint8_t irq) {
202
203         if (((irq + KERNEL_IRQ_OFFSET) >= NUM_IRQS) || (ioapic_redirects[irq].ioapic_address == NULL)) {
204                 panic("TRYING TO REROUTE AN INVALID IRQ!");
205         }
206         
207         // Must write low first, else we will reroute to a wrong core for a split before turning off
208         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address , IOAPIC_REDIRECT_OFFSET + 2*ioapic_redirects[irq].ioapic_int);
209         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address  + IOAPIC_WRITE_WINDOW_OFFSET, IOAPIC_UNROUTE_LOW);
210         
211         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address , IOAPIC_REDIRECT_OFFSET + 2*ioapic_redirects[irq].ioapic_int + 1);
212         write_mmreg32((uint32_t)ioapic_redirects[irq].ioapic_address  + IOAPIC_WRITE_WINDOW_OFFSET, IOAPIC_UNROUTE_HIGH);
213
214 }