x86: Provide an option to enable legacy USB
[akaros.git] / kern / arch / x86 / smp.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  */
6
7 #include <arch/arch.h>
8 #include <arch/topology.h>
9 #include <bitmask.h>
10 #include <smp.h>
11
12 #include <atomic.h>
13 #include <error.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <assert.h>
17 #include <pmap.h>
18 #include <env.h>
19 #include <trap.h>
20
21 /*************************** IPI Wrapper Stuff ********************************/
22 // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
23 // need to be global, since there is no function that will always exist for them
24 handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
25
26 static int smp_call_function(uint8_t type, uint32_t dest, isr_t handler,
27                              void *data, handler_wrapper_t **wait_wrapper)
28 {
29         int8_t state = 0;
30         uint32_t wrapper_num;
31         handler_wrapper_t* wrapper;
32         extern atomic_t outstanding_calls;
33
34         // prevents us from ever having more than NUM_HANDLER_WRAPPERS callers in
35         // the process of competing for vectors.  not decremented until both after
36         // the while(1) loop and after it's been waited on.
37         atomic_inc(&outstanding_calls);
38         if (atomic_read(&outstanding_calls) > NUM_HANDLER_WRAPPERS) {
39                 atomic_dec(&outstanding_calls);
40                 return -EBUSY;
41         }
42
43         // assumes our cores are numbered in order
44         if ((type == 4) && (dest >= num_cores))
45                 panic("Destination CPU %d does not exist!", dest);
46
47         // build the mask based on the type and destination
48         INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CORES);
49         // set checklist mask's size dynamically to the num cpus actually present
50         cpu_mask.size = num_cores;
51         switch (type) {
52                 case 1: // self
53                         SET_BITMASK_BIT(cpu_mask.bits, core_id());
54                         break;
55                 case 2: // all
56                         FILL_BITMASK(cpu_mask.bits, num_cores);
57                         break;
58                 case 3: // all but self
59                         FILL_BITMASK(cpu_mask.bits, num_cores);
60                         CLR_BITMASK_BIT(cpu_mask.bits, core_id());
61                         break;
62                 case 4: // physical mode
63                         // note this only supports sending to one specific physical id
64                         // (only sets one bit, so if multiple cores have the same phys id
65                         // the first one through will set this).
66                         SET_BITMASK_BIT(cpu_mask.bits, dest);
67                         break;
68                 case 5: // logical mode
69                         // TODO
70                         warn("Logical mode bitmask handler protection not implemented!");
71                         break;
72                 default:
73                         panic("Invalid type for cross-core function call!");
74         }
75
76         // Find an available vector/wrapper.  Starts with this core's id (mod the
77         // number of wrappers).  Walk through on conflict.
78         // Commit returns an error if it wanted to give up for some reason,
79         // like taking too long to acquire the lock or clear the mask, at which
80         // point, we try the next one.
81         // When we are done, wrapper points to the one we finally got.
82         // this wrapper_num trick doesn't work as well if you send a bunch in a row
83         // and wait, since you always check your main one (which is currently busy).
84         wrapper_num = core_id() % NUM_HANDLER_WRAPPERS;
85         while(1) {
86                 wrapper = &handler_wrappers[wrapper_num];
87                 if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask))
88                         break;
89                 wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS;
90                 /*
91                 uint32_t count = 0;
92                 // instead of deadlock, smp_call can fail with this.  makes it harder
93                 // to use (have to check your return value).  consider putting a delay
94                 // here too (like if wrapper_num == initial_wrapper_num)
95                 if (count++ > NUM_HANDLER_WRAPPERS * 1000) // note 1000 isn't enough...
96                         return -EBUSY;
97                 */
98         }
99
100         // Wanting to wait is expressed by having a non-NULL handler_wrapper_t**
101         // passed in.  Pass out our reference to wrapper, to wait later.
102         // If we don't want to wait, release the checklist (though it is still not
103         // clear, so it can't be used til everyone checks in).
104         if (wait_wrapper)
105                 *wait_wrapper = wrapper;
106         else {
107                 release_checklist(wrapper->cpu_list);
108                 atomic_dec(&outstanding_calls);
109         }
110
111         /* TODO: once we can unregister, we can reregister.  This here assumes that
112          * there is only one IRQ registered, and its the one for SMP call function.
113          * We're waiting on RCU to do a nice unregister. */
114         extern struct irq_handler *irq_handlers[];
115         if (!irq_handlers[wrapper->vector]) {
116                 register_irq(wrapper->vector, handler, data, MKBUS(BusIPI, 0, 0, 0));
117         } else {
118                 /* we're replacing the old one.  hope it was ours, and the IRQ is firing
119                  * concurrently (if it is, there's an smp_call bug)! */
120                 irq_handlers[wrapper->vector]->isr = handler;
121                 irq_handlers[wrapper->vector]->data = data;
122         }
123
124         // WRITE MEMORY BARRIER HERE
125         enable_irqsave(&state);
126         // Send the proper type of IPI.  I made up these numbers.
127         switch (type) {
128                 case 1:
129                         send_self_ipi(wrapper->vector);
130                         break;
131                 case 2:
132                         send_broadcast_ipi(wrapper->vector);
133                         break;
134                 case 3:
135                         send_all_others_ipi(wrapper->vector);
136                         break;
137                 case 4: // physical mode
138                         send_ipi(dest, wrapper->vector);
139                         break;
140                 case 5: // logical mode
141                         send_group_ipi(dest, wrapper->vector);
142                         break;
143                 default:
144                         panic("Invalid type for cross-core function call!");
145         }
146         // wait long enough to receive our own broadcast (PROBABLY WORKS) TODO
147         lapic_wait_to_send();
148         disable_irqsave(&state);
149         return 0;
150 }
151
152 // Wrapper functions.  Add more as they are needed.
153 int smp_call_function_self(isr_t handler, void *data,
154                            handler_wrapper_t **wait_wrapper)
155 {
156         return smp_call_function(1, 0, handler, data, wait_wrapper);
157 }
158
159 int smp_call_function_all(isr_t handler, void *data,
160                           handler_wrapper_t **wait_wrapper)
161 {
162         return smp_call_function(2, 0, handler, data, wait_wrapper);
163 }
164
165 int smp_call_function_single(uint32_t dest, isr_t handler, void *data,
166                              handler_wrapper_t **wait_wrapper)
167 {
168         return smp_call_function(4, dest, handler, data, wait_wrapper);
169 }
170
171 // If you want to wait, pass the address of a pointer up above, then call
172 // this to do the actual waiting.  Be somewhat careful about uninitialized 
173 // or old wrapper pointers.
174 int smp_call_wait(handler_wrapper_t* wrapper)
175 {
176         if (wrapper) {
177                 waiton_checklist(wrapper->cpu_list);
178                 return 0;
179         } else {
180                 warn("Attempting to wait on null wrapper!  Check your return values!");
181                 return -EFAIL;
182         }
183 }
184