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