]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/mips/mips/mp_machdep.c
Copy head to stable/8 as part of 8.0 Release cycle.
[FreeBSD/stable/8.git] / sys / mips / mips / mp_machdep.c
1 #include <sys/cdefs.h>
2 __FBSDID("$FreeBSD$");
3
4 #include "opt_kstack_pages.h"
5
6 #include <sys/param.h>
7 #include <sys/systm.h>
8 #include <sys/ktr.h>
9 #include <sys/proc.h>
10 #include <sys/cons.h>
11 #include <sys/lock.h>
12 #include <sys/malloc.h>
13 #include <sys/mutex.h>
14 #include <sys/kernel.h>
15 #include <sys/pcpu.h>
16 #include <sys/smp.h>
17 #include <sys/sysctl.h>
18 #include <sys/bus.h>
19
20 #include <vm/vm.h>
21 #include <vm/pmap.h>
22 #include <vm/vm_map.h>
23
24 #include <machine/atomic.h>
25 #include <machine/clock.h>
26 #include <machine/md_var.h>
27 #include <machine/pcb.h>
28 #include <machine/pmap.h>
29 #include <machine/smp.h>
30
31 static struct mtx ap_boot_mtx;
32 extern struct pcpu __pcpu[];
33 extern int num_tlbentries;
34 void mips_start_timer(void);
35 static volatile int aps_ready = 0;
36
37 u_int32_t boot_cpu_id;
38
39
40 void
41 cpu_mp_announce(void)
42 {
43 }
44
45 /*
46  * To implement IPIs on MIPS CPU, we use the Interrupt Line 2 ( bit 4 of cause
47  * register) and a bitmap to avoid redundant IPI interrupts. To interrupt a
48  * set of CPUs, the sender routine runs in a ' loop ' sending interrupts to
49  * all the specified CPUs. A single Mutex (smp_ipi_mtx) is used for all IPIs
50  * that spinwait for delivery. This includes the following IPIs
51  * IPI_RENDEZVOUS
52  * IPI_INVLPG
53  * IPI_INVLTLB
54  * IPI_INVLRNG
55  */
56
57 /*
58  * send an IPI to a set of cpus.
59  */
60 void
61 ipi_selected(u_int32_t cpus, u_int ipi)
62 {
63         struct pcpu *pcpu;
64         u_int cpuid, new_pending, old_pending;
65
66         CTR3(KTR_SMP, "%s: cpus: %x, ipi: %x\n", __func__, cpus, ipi);
67
68         while ((cpuid = ffs(cpus)) != 0) {
69                 cpuid--;
70                 cpus &= ~(1 << cpuid);
71                 pcpu = pcpu_find(cpuid);
72
73                 if (pcpu) {
74                         do {
75                                 old_pending = pcpu->pc_pending_ipis;
76                                 new_pending = old_pending | ipi;
77                         } while (!atomic_cmpset_int(&pcpu->pc_pending_ipis,
78                             old_pending, new_pending)); 
79
80                         if (old_pending)
81                                 continue;
82
83                         mips_ipi_send (cpuid);
84                 }
85         }
86 }
87
88 /*
89  * send an IPI to all CPUs EXCEPT myself
90  */
91 void
92 ipi_all_but_self(u_int ipi)
93 {
94
95         ipi_selected(PCPU_GET(other_cpus), ipi);
96 }
97
98 /*
99  * Handle an IPI sent to this processor.
100  */
101 intrmask_t
102 smp_handle_ipi(struct trapframe *frame)
103 {
104         cpumask_t cpumask;              /* This cpu mask */
105         u_int   ipi, ipi_bitmap;
106
107         ipi_bitmap = atomic_readandclear_int(PCPU_PTR(pending_ipis));
108         cpumask = PCPU_GET(cpumask);
109
110         CTR1(KTR_SMP, "smp_handle_ipi(), ipi_bitmap=%x", ipi_bitmap);
111         while (ipi_bitmap) {
112                 /*
113                  * Find the lowest set bit.
114                  */
115                 ipi = ipi_bitmap & ~(ipi_bitmap - 1);
116                 ipi_bitmap &= ~ipi;
117                 switch (ipi) {
118                 case IPI_INVLTLB:
119                         CTR0(KTR_SMP, "IPI_INVLTLB");
120                         break;
121
122                 case IPI_RENDEZVOUS:
123                         CTR0(KTR_SMP, "IPI_RENDEZVOUS");
124                         smp_rendezvous_action();
125                         break;
126
127                 case IPI_AST:
128                         CTR0(KTR_SMP, "IPI_AST");
129                         break;
130
131                 case IPI_STOP:
132                         CTR0(KTR_SMP, "IPI_STOP");
133                         atomic_set_int(&stopped_cpus, cpumask);
134
135                         while ((started_cpus & cpumask) == 0)
136                             ;
137                         atomic_clear_int(&started_cpus, cpumask);
138                         atomic_clear_int(&stopped_cpus, cpumask);
139                         break;
140                 }
141         }
142         return CR_INT_IPI;
143 }
144
145 void
146 cpu_mp_setmaxid(void)
147 {
148
149         mp_maxid = MAXCPU - 1;
150 }
151
152 void
153 smp_init_secondary(u_int32_t cpuid)
154 {
155
156         if (cpuid >=  MAXCPU)
157                 panic ("cpu id exceeds MAXCPU\n");
158
159         /* tlb init */
160         R4K_SetWIRED(0);
161         R4K_TLBFlush(num_tlbentries);
162         R4K_SetWIRED(VMWIRED_ENTRIES);
163         MachSetPID(0);
164
165         Mips_SyncCache();
166
167         mips_cp0_status_write(0);
168         while (!aps_ready)
169                 ;
170
171         mips_sync(); mips_sync();
172         /* Initialize curthread. */
173         KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread"));
174         PCPU_SET(curthread, PCPU_GET(idlethread));
175
176         mtx_lock_spin(&ap_boot_mtx);
177
178         smp_cpus++;
179
180         CTR1(KTR_SMP, "SMP: AP CPU #%d Launched", PCPU_GET(cpuid));
181
182         /* Build our map of 'other' CPUs. */
183         PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask));
184
185         printf("SMP: AP CPU #%d Launched!\n", PCPU_GET(cpuid));
186
187         if (smp_cpus == mp_ncpus) {
188                 smp_started = 1;
189                 smp_active = 1;
190         }
191
192         mtx_unlock_spin(&ap_boot_mtx);
193
194         while (smp_started == 0)
195                 ; /* nothing */
196         /* Enable Interrupt */
197         mips_cp0_status_write(SR_INT_ENAB);
198         /* ok, now grab sched_lock and enter the scheduler */
199         mtx_lock_spin(&sched_lock);
200
201         /*
202          * Correct spinlock nesting.  The idle thread context that we are
203          * borrowing was created so that it would start out with a single
204          * spin lock (sched_lock) held in fork_trampoline().  Since we've
205          * explicitly acquired locks in this function, the nesting count
206          * is now 2 rather than 1.  Since we are nested, calling
207          * spinlock_exit() will simply adjust the counts without allowing
208          * spin lock using code to interrupt us.
209          */
210         spinlock_exit();
211         KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
212
213         binuptime(PCPU_PTR(switchtime));
214         PCPU_SET(switchticks, ticks);
215
216         /* kick off the clock on this cpu */
217         mips_start_timer();
218         cpu_throw(NULL, choosethread());        /* doesn't return */
219
220         panic("scheduler returned us to %s", __func__);
221 }
222
223 static int
224 smp_start_secondary(int cpuid)
225 {
226         struct pcpu *pcpu;
227         void *dpcpu;
228         int i;
229
230         if (bootverbose)
231                 printf("smp_start_secondary: starting cpu %d\n", cpuid);
232
233         dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE);
234         pcpu_init(&__pcpu[cpuid], cpuid, sizeof(struct pcpu));
235         dpcpu_init(dpcpu, cpuid);
236
237         if (bootverbose)
238                 printf("smp_start_secondary: cpu %d started\n", cpuid);
239
240         return 1;
241 }
242
243 int
244 cpu_mp_probe(void)
245 {
246         int i, cpus;
247
248         /* XXX: Need to check for valid platforms here. */
249
250         boot_cpu_id = PCPU_GET(cpuid);
251         KASSERT(boot_cpu_id == 0, ("cpu_mp_probe() called on non-primary CPU"));
252         all_cpus = PCPU_GET(cpumask);
253         mp_ncpus = 1;
254
255         /* Make sure we have at least one secondary CPU. */
256         cpus = 0;
257         for (i = 0; i < MAXCPU; i++) {
258                 cpus++;
259         }
260         return (cpus);
261 }
262
263 void
264 cpu_mp_start(void)
265 {
266         int i, cpuid;
267
268         mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN);
269
270         cpuid = 1;
271         for (i = 0; i < MAXCPU; i++) {
272
273                 if (i == boot_cpu_id)
274                         continue;
275                 if (smp_start_secondary(i)) {
276                         all_cpus |= (1 << cpuid);
277                         mp_ncpus++;
278                 cpuid++;
279                 }
280         }
281         idle_mask |= CR_INT_IPI;
282         PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask));
283 }
284
285 static void
286 release_aps(void *dummy __unused)
287 {
288         if (bootverbose && mp_ncpus > 1)
289                 printf("%s: releasing secondary CPUs\n", __func__);
290         atomic_store_rel_int(&aps_ready, 1);
291
292         while (mp_ncpus > 1 && smp_started == 0)
293                 ; /* nothing */
294 }
295
296 SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL);