1 /*
2  * Copyright 2014, General Dynamics C4 Systems
3  *
4  * SPDX-License-Identifier: GPL-2.0-only
5  */
6 
7 #include <config.h>
8 
9 #include <linker.h>
10 #include <machine/io.h>
11 #include <plat/machine/hardware.h>
12 #include <plat/machine/ioapic.h>
13 
14 #define IOAPIC_REGSEL 0x00
15 #define IOAPIC_WINDOW 0x10
16 
17 #define IOAPIC_REG_IOAPICID 0x00
18 #define IOAPIC_REG_IOREDTBL 0x10
19 
20 #define IOREDTBL_LOW(reg) (IOAPIC_REG_IOREDTBL + (reg) * 2)
21 #define IOREDTBL_HIGH(reg) (IOREDTBL_LOW(reg) + 1)
22 
23 #define IOREDTBL_LOW_INTERRUPT_MASK BIT(16)
24 #define IOREDTBL_LOW_TRIGGER_MODE_LEVEL BIT(15)
25 #define IOREDTBL_LOW_TRIGGER_MODE_SHIFT    15
26 #define IOREDTBL_LOW_POLARITY_LOW BIT(13)
27 #define IOREDTBL_LOW_POLARITY_SHIFT         13
28 #define IOREDTBL_LOW_DEST_MODE_LOGCIAL BIT(11)
29 
30 #define IOAPICID_ID_BITS 4
31 #define IOAPICID_ID_OFFSET 24
32 
33 #define IOREDTBL_HIGH_RESERVED_BITS 24
34 
35 /* Cache what we believe is in the low word of the IOREDTBL. This
36  * has all the state of trigger modes etc etc */
37 static uint32_t ioredtbl_state[IOAPIC_IRQ_LINES * MAX(1, CONFIG_MAX_NUM_IOAPIC)];
38 
39 /* Number of IOAPICs in the system */
40 static uint32_t num_ioapics = 0;
41 
ioapic_write(uint32_t ioapic,word_t reg,uint32_t value)42 static void ioapic_write(uint32_t ioapic, word_t reg, uint32_t value)
43 {
44     *(volatile uint32_t *)((word_t)(PPTR_IOAPIC_START + ioapic * BIT(PAGE_BITS)) + reg) = value;
45 }
46 
ioapic_read(uint32_t ioapic,word_t reg)47 static uint32_t ioapic_read(uint32_t ioapic, word_t reg)
48 {
49     return *(volatile uint32_t *)((word_t)(PPTR_IOAPIC_START + ioapic * BIT(PAGE_BITS)) + reg);
50 }
51 
single_ioapic_init(word_t ioapic,cpu_id_t delivery_cpu)52 static void single_ioapic_init(word_t ioapic, cpu_id_t delivery_cpu)
53 {
54     uint32_t i;
55 
56     /* Mask all the IRQs. In doing so we happen to set
57      * the vector to 0, which we can assert against in
58      * mask_interrupt to ensure a vector is assigned
59      * before we unmask */
60     for (i = 0; i < IOAPIC_IRQ_LINES; i++) {
61         /* Send to desired cpu */
62         ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_HIGH(i));
63         ioapic_write(ioapic, IOAPIC_WINDOW, (ioapic_read(ioapic,
64                                                          IOAPIC_WINDOW) & MASK(IOREDTBL_HIGH_RESERVED_BITS)) | (delivery_cpu << IOREDTBL_HIGH_RESERVED_BITS));
65         /* mask and set 0 vector */
66         ioredtbl_state[i] = IOREDTBL_LOW_INTERRUPT_MASK;
67         ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(i));
68         /* The upper 16 bits are reserved, so we make sure to preserve them */
69         ioredtbl_state[i] |= ioapic_read(ioapic, IOAPIC_WINDOW) & ~MASK(16);
70         ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[i]);
71     }
72 }
73 
74 static  cpu_id_t ioapic_target_cpu = 0;
ioapic_init(uint32_t num_nodes,cpu_id_t * cpu_list,uint32_t num_ioapic)75 void ioapic_init(uint32_t num_nodes, cpu_id_t *cpu_list, uint32_t num_ioapic)
76 {
77     uint32_t ioapic;
78     num_ioapics = num_ioapic;
79     ioapic_target_cpu = cpu_list[0];
80 
81     for (ioapic = 0; ioapic < num_ioapic; ioapic++) {
82         /* Init this ioapic */
83         single_ioapic_init(ioapic, cpu_list[0]);
84     }
85 }
86 
ioapic_mask(bool_t mask,uint32_t ioapic,uint32_t pin)87 void ioapic_mask(bool_t mask, uint32_t ioapic, uint32_t pin)
88 {
89     int index = ioapic * IOAPIC_IRQ_LINES + pin;
90     if (ioapic >= num_ioapics || pin >= IOAPIC_IRQ_LINES) {
91         /* silently ignore requests to non existent parts of the interrupt space */
92         return;
93     }
94     if (mask) {
95         ioredtbl_state[index] |= IOREDTBL_LOW_INTERRUPT_MASK;
96     } else {
97         ioredtbl_state[index] &= ~IOREDTBL_LOW_INTERRUPT_MASK;
98         /* it should not be possible to be unmasking an interrupt, without
99          * it having been mapped to a vector, assert that this is the case */
100         assert((ioredtbl_state[index] & 0xff) != 0);
101     }
102     ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(pin));
103     ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[index]);
104 }
105 
ioapic_decode_map_pin_to_vector(word_t ioapic,word_t pin,word_t level,word_t polarity,word_t vector)106 exception_t ioapic_decode_map_pin_to_vector(word_t ioapic, word_t pin, word_t level,
107                                             word_t polarity, word_t vector)
108 {
109     if (num_ioapics == 0) {
110         userError("System has no IOAPICs");
111         current_syscall_error.type = seL4_IllegalOperation;
112         return EXCEPTION_SYSCALL_ERROR;
113     }
114     if (ioapic >= num_ioapics) {
115         userError("Invalid IOAPIC %ld, only have %ld", (long)ioapic, (long)num_ioapics);
116         current_syscall_error.type = seL4_RangeError;
117         current_syscall_error.rangeErrorMin = 0;
118         current_syscall_error.rangeErrorMax = num_ioapics - 1;
119         return EXCEPTION_SYSCALL_ERROR;
120     }
121     if (pin >= IOAPIC_IRQ_LINES) {
122         userError("Invalid IOAPIC pin %ld, there are %d pins", (long)pin, IOAPIC_IRQ_LINES);
123         current_syscall_error.type = seL4_RangeError;
124         current_syscall_error.rangeErrorMin = 0;
125         current_syscall_error.rangeErrorMax = IOAPIC_IRQ_LINES - 1;
126         return EXCEPTION_SYSCALL_ERROR;
127     }
128 
129     if (level != 0 && level != 1) {
130         userError("Level should be 0 or 1, not %d", (int)level);
131         current_syscall_error.type = seL4_RangeError;
132         current_syscall_error.rangeErrorMin = 0;
133         current_syscall_error.rangeErrorMax = 1;
134         return EXCEPTION_SYSCALL_ERROR;
135     }
136     if (polarity != 0 && polarity != 1) {
137         userError("Polarity should be 0 or 1, not %d", (int)polarity);
138         current_syscall_error.type = seL4_RangeError;
139         current_syscall_error.rangeErrorMin = 0;
140         current_syscall_error.rangeErrorMax = 1;
141         return EXCEPTION_SYSCALL_ERROR;
142     }
143     return EXCEPTION_NONE;
144 }
145 
ioapic_map_pin_to_vector(word_t ioapic,word_t pin,word_t level,word_t polarity,word_t vector)146 void ioapic_map_pin_to_vector(word_t ioapic, word_t pin, word_t level,
147                               word_t polarity, word_t vector)
148 {
149     uint32_t ioredtbl_high = 0;
150     uint32_t index = 0;
151 
152     index = ioapic * IOAPIC_IRQ_LINES + pin;
153     ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_HIGH(pin));
154     ioredtbl_high = ioapic_read(ioapic, IOAPIC_WINDOW) & MASK(IOREDTBL_HIGH_RESERVED_BITS);
155     /* delivery mode: physical mode only, using APIC ID */
156     ioredtbl_high |= (ioapic_target_cpu << IOREDTBL_HIGH_RESERVED_BITS);
157     ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_high);
158     /* we do not need to add IRQ_INT_OFFSET to the vector here */
159     ioredtbl_state[index] = IOREDTBL_LOW_INTERRUPT_MASK |
160                             (level << IOREDTBL_LOW_TRIGGER_MODE_SHIFT) |
161                             (polarity << IOREDTBL_LOW_POLARITY_SHIFT) |
162                             vector;
163 
164     ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(pin));
165     /* the upper 16 bits are reserved */
166     ioredtbl_state[index] |= ioapic_read(ioapic, IOAPIC_WINDOW) & ~MASK(16);
167     ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[index]);
168 }
169