1 /* $NetBSD: cpu.c,v 1.46 2011/09/14 18:30:13 reinoud Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "opt_cpu.h" 30 #include "opt_hz.h" 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.46 2011/09/14 18:30:13 reinoud Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/conf.h> 37 #include <sys/proc.h> 38 #include <sys/systm.h> 39 #include <sys/device.h> 40 #include <sys/reboot.h> 41 #include <sys/lwp.h> 42 #include <sys/cpu.h> 43 #include <sys/mbuf.h> 44 #include <sys/msgbuf.h> 45 46 #include <dev/cons.h> 47 48 #include <machine/cpu.h> 49 #include <machine/mainbus.h> 50 #include <machine/pcb.h> 51 #include <machine/machdep.h> 52 #include <machine/thunk.h> 53 54 #include <uvm/uvm_extern.h> 55 #include <uvm/uvm_page.h> 56 57 #if __GNUC_PREREQ__(4,4) 58 #define cpu_unreachable() __builtin_unreachable() 59 #else 60 #define cpu_unreachable() do { thunk_abort(); } while (0) 61 #endif 62 63 static int cpu_match(device_t, cfdata_t, void *); 64 static void cpu_attach(device_t, device_t, void *); 65 66 struct cpu_info cpu_info_primary = { 67 .ci_dev = 0, 68 .ci_self = &cpu_info_primary, 69 .ci_idepth = -1, 70 .ci_curlwp = &lwp0, 71 }; 72 73 char cpu_model[48] = "virtual processor"; 74 75 typedef struct cpu_softc { 76 device_t sc_dev; 77 struct cpu_info *sc_ci; 78 } cpu_softc_t; 79 80 static struct pcb lwp0pcb; 81 static void *msgbuf; 82 83 CFATTACH_DECL_NEW(cpu, sizeof(cpu_softc_t), cpu_match, cpu_attach, NULL, NULL); 84 85 static int 86 cpu_match(device_t parent, cfdata_t match, void *opaque) 87 { 88 struct thunkbus_attach_args *taa = opaque; 89 90 if (taa->taa_type != THUNKBUS_TYPE_CPU) 91 return 0; 92 93 return 1; 94 } 95 96 static void 97 cpu_attach(device_t parent, device_t self, void *opaque) 98 { 99 cpu_softc_t *sc = device_private(self); 100 101 aprint_naive("\n"); 102 aprint_normal("\n"); 103 104 sc->sc_dev = self; 105 sc->sc_ci = &cpu_info_primary; 106 } 107 108 void 109 cpu_configure(void) 110 { 111 if (config_rootfound("mainbus", NULL) == NULL) 112 panic("configure: mainbus not configured"); 113 114 spl0(); 115 } 116 117 void 118 cpu_reboot(int howto, char *bootstr) 119 { 120 extern void usermode_reboot(void); 121 122 splhigh(); 123 124 if ((howto & RB_POWERDOWN) == RB_POWERDOWN) 125 thunk_exit(0); 126 127 if (howto & RB_DUMP) 128 thunk_abort(); 129 130 if (howto & RB_HALT) { 131 printf("\n"); 132 printf("The operating system has halted.\n"); 133 printf("Please press any key to reboot.\n\n"); 134 cnpollc(1); 135 cngetc(); 136 cnpollc(0); 137 } 138 139 printf("rebooting...\n"); 140 141 usermode_reboot(); 142 143 /* NOTREACHED */ 144 cpu_unreachable(); 145 } 146 147 void 148 cpu_need_resched(struct cpu_info *ci, int flags) 149 { 150 ci->ci_want_resched |= flags; 151 } 152 153 void 154 cpu_need_proftick(struct lwp *l) 155 { 156 } 157 158 lwp_t * 159 cpu_switchto(lwp_t *oldlwp, lwp_t *newlwp, bool returning) 160 { 161 struct pcb *oldpcb = oldlwp ? lwp_getpcb(oldlwp) : NULL; 162 struct pcb *newpcb = lwp_getpcb(newlwp); 163 struct cpu_info *ci = curcpu(); 164 165 #ifdef CPU_DEBUG 166 printf("cpu_switchto [%s,pid=%d,lid=%d] -> [%s,pid=%d,lid=%d]\n", 167 oldlwp ? oldlwp->l_name : "none", 168 oldlwp ? oldlwp->l_proc->p_pid : -1, 169 oldlwp ? oldlwp->l_lid : -1, 170 newlwp ? newlwp->l_name : "none", 171 newlwp ? newlwp->l_proc->p_pid : -1, 172 newlwp ? newlwp->l_lid : -1); 173 if (oldpcb) { 174 printf(" oldpcb uc_link=%p, uc_stack.ss_sp=%p, " 175 "uc_stack.ss_size=%d\n", 176 oldpcb->pcb_ucp.uc_link, 177 oldpcb->pcb_ucp.uc_stack.ss_sp, 178 (int)oldpcb->pcb_ucp.uc_stack.ss_size); 179 } 180 if (newpcb) { 181 printf(" newpcb uc_link=%p, uc_stack.ss_sp=%p, " 182 "uc_stack.ss_size=%d\n", 183 newpcb->pcb_ucp.uc_link, 184 newpcb->pcb_ucp.uc_stack.ss_sp, 185 (int)newpcb->pcb_ucp.uc_stack.ss_size); 186 } 187 #endif /* !CPU_DEBUG */ 188 189 ci->ci_stash = oldlwp; 190 191 if (oldpcb) { 192 oldpcb->pcb_errno = thunk_geterrno(); 193 thunk_seterrno(newpcb->pcb_errno); 194 curlwp = newlwp; 195 if (thunk_swapcontext(&oldpcb->pcb_ucp, &newpcb->pcb_ucp)) 196 panic("swapcontext failed"); 197 } else { 198 thunk_seterrno(newpcb->pcb_errno); 199 curlwp = newlwp; 200 if (thunk_setcontext(&newpcb->pcb_ucp)) 201 panic("setcontext failed"); 202 } 203 204 #ifdef CPU_DEBUG 205 printf("cpu_switchto: returning %p (was %p)\n", ci->ci_stash, oldlwp); 206 #endif 207 return ci->ci_stash; 208 } 209 210 void 211 cpu_dumpconf(void) 212 { 213 #ifdef CPU_DEBUG 214 printf("cpu_dumpconf\n"); 215 #endif 216 } 217 218 void 219 cpu_signotify(struct lwp *l) 220 { 221 } 222 223 void 224 cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags) 225 { 226 #ifdef CPU_DEBUG 227 printf("cpu_getmcontext\n"); 228 #endif 229 } 230 231 int 232 cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags) 233 { 234 #ifdef CPU_DEBUG 235 printf("cpu_setmcontext\n"); 236 #endif 237 return 0; 238 } 239 240 void 241 cpu_idle(void) 242 { 243 struct cpu_info *ci = curcpu(); 244 245 if (ci->ci_want_resched) 246 return; 247 248 #if notyet 249 thunk_usleep(10000); 250 #endif 251 } 252 253 void 254 cpu_lwp_free(struct lwp *l, int proc) 255 { 256 #ifdef CPU_DEBUG 257 printf("cpu_lwp_free\n"); 258 #endif 259 } 260 261 void 262 cpu_lwp_free2(struct lwp *l) 263 { 264 struct pcb *pcb = lwp_getpcb(l); 265 266 #ifdef CPU_DEBUG 267 printf("cpu_lwp_free2\n"); 268 #endif 269 270 if (pcb == NULL) 271 return; 272 273 if (pcb->pcb_needfree) { 274 free(pcb->pcb_ucp.uc_stack.ss_sp, M_TEMP); 275 pcb->pcb_ucp.uc_stack.ss_sp = NULL; 276 pcb->pcb_ucp.uc_stack.ss_size = 0; 277 278 free(pcb->pcb_syscall_ucp.uc_stack.ss_sp, M_TEMP); 279 pcb->pcb_syscall_ucp.uc_stack.ss_sp = NULL; 280 pcb->pcb_syscall_ucp.uc_stack.ss_size = 0; 281 282 pcb->pcb_needfree = false; 283 } 284 } 285 286 static void 287 cpu_lwp_trampoline(ucontext_t *ucp, void (*func)(void *), void *arg) 288 { 289 #ifdef CPU_DEBUG 290 printf("cpu_lwp_trampoline called with func %p, arg %p\n", (void *) func, arg); 291 #endif 292 /* init lwp */ 293 lwp_startup(curcpu()->ci_stash, curlwp); 294 295 /* actual jump */ 296 thunk_makecontext(ucp, (void (*)(void)) func, 1, arg, NULL, NULL); 297 thunk_setcontext(ucp); 298 } 299 300 void 301 cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize, 302 void (*func)(void *), void *arg) 303 { 304 struct pcb *pcb1 = lwp_getpcb(l1); 305 struct pcb *pcb2 = lwp_getpcb(l2); 306 void *stack_ucp, *stack_syscall_ucp; 307 308 #ifdef CPU_DEBUG 309 printf("cpu_lwp_fork [%s/%p] -> [%s/%p] stack=%p stacksize=%d\n", 310 l1 ? l1->l_name : "none", l1, 311 l2 ? l2->l_name : "none", l2, 312 stack, (int)stacksize); 313 #endif 314 315 if (stack) 316 panic("%s: stack passed, can't handle\n", __func__); 317 318 /* copy the PCB and its switchframes from parent */ 319 memcpy(pcb2, pcb1, sizeof(struct pcb)); 320 321 stacksize = 4*PAGE_SIZE; 322 stack_ucp = malloc(stacksize, M_TEMP, M_NOWAIT); 323 stack_syscall_ucp = malloc(stacksize, M_TEMP, M_NOWAIT); 324 pcb2->pcb_needfree = true; 325 326 if (thunk_getcontext(&pcb2->pcb_ucp)) 327 panic("getcontext failed"); 328 329 /* set up the ucontext for the userland switch */ 330 pcb2->pcb_ucp.uc_stack.ss_sp = stack_ucp; 331 pcb2->pcb_ucp.uc_stack.ss_size = stacksize; 332 pcb2->pcb_ucp.uc_link = &pcb2->pcb_userland_ucp; 333 pcb2->pcb_ucp.uc_flags = _UC_STACK | _UC_CPU; 334 thunk_makecontext(&pcb2->pcb_ucp, 335 (void (*)(void)) cpu_lwp_trampoline, 336 3, &pcb2->pcb_ucp, func, arg); 337 338 /* set up the ucontext for the syscall */ 339 pcb2->pcb_syscall_ucp.uc_stack.ss_sp = stack_syscall_ucp; 340 pcb2->pcb_syscall_ucp.uc_stack.ss_size = stacksize; 341 pcb2->pcb_syscall_ucp.uc_flags = _UC_CPU; 342 pcb2->pcb_syscall_ucp.uc_link = &pcb2->pcb_userland_ucp; 343 thunk_makecontext(&pcb2->pcb_syscall_ucp, (void (*)(void)) syscall, 344 0, NULL, NULL, NULL); 345 } 346 347 void 348 cpu_initclocks(void) 349 { 350 struct thunk_itimerval itimer; 351 352 itimer.it_interval.tv_sec = 0; 353 itimer.it_interval.tv_usec = 1000000 / HZ; 354 itimer.it_value = itimer.it_interval; 355 thunk_setitimer(ITIMER_REAL, &itimer, NULL); 356 } 357 358 void 359 cpu_startup(void) 360 { 361 msgbuf = thunk_malloc(PAGE_SIZE); 362 if (msgbuf == NULL) 363 panic("couldn't allocate msgbuf"); 364 initmsgbuf(msgbuf, PAGE_SIZE); 365 366 banner(); 367 368 memset(&lwp0pcb, 0, sizeof(lwp0pcb)); 369 if (thunk_getcontext(&lwp0pcb.pcb_ucp)) 370 panic("getcontext failed"); 371 uvm_lwp_setuarea(&lwp0, (vaddr_t)&lwp0pcb); 372 373 /* init trapframe (going nowhere!), maybe a panic func? */ 374 memcpy(&lwp0pcb.pcb_userland_ucp, &lwp0pcb.pcb_ucp, sizeof(ucontext_t)); 375 memcpy(&lwp0pcb.pcb_syscall_ucp, &lwp0pcb.pcb_ucp, sizeof(ucontext_t)); 376 } 377 378 void 379 cpu_rootconf(void) 380 { 381 device_t rdev; 382 383 rdev = device_find_by_xname("ld0"); 384 if (rdev == NULL) 385 rdev = device_find_by_xname("md0"); 386 387 aprint_normal("boot device: %s\n", 388 rdev ? device_xname(rdev) : "<unknown>"); 389 setroot(rdev, 0); 390 } 391 392 bool 393 cpu_intr_p(void) 394 { 395 int idepth; 396 397 kpreempt_disable(); 398 idepth = curcpu()->ci_idepth; 399 kpreempt_enable(); 400 401 return (idepth >= 0); 402 } 403