1 /* $NetBSD: cpu.c,v 1.68 2012/01/18 19:17:02 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.68 2012/01/18 19:17:02 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 #include <sys/kmem.h> 46 #include <sys/kernel.h> 47 #include <sys/mount.h> 48 49 #include <dev/cons.h> 50 51 #include <machine/cpu.h> 52 #include <machine/mainbus.h> 53 #include <machine/pcb.h> 54 #include <machine/machdep.h> 55 #include <machine/thunk.h> 56 57 #include <uvm/uvm_extern.h> 58 #include <uvm/uvm_page.h> 59 60 #if __GNUC_PREREQ__(4,4) 61 #define cpu_unreachable() __builtin_unreachable() 62 #else 63 #define cpu_unreachable() do { thunk_abort(); } while (0) 64 #endif 65 66 static int cpu_match(device_t, cfdata_t, void *); 67 static void cpu_attach(device_t, device_t, void *); 68 69 struct cpu_info cpu_info_primary = { 70 .ci_dev = 0, 71 .ci_self = &cpu_info_primary, 72 .ci_idepth = -1, 73 .ci_curlwp = &lwp0, 74 }; 75 76 char cpu_model[48] = "virtual processor"; 77 78 typedef struct cpu_softc { 79 device_t sc_dev; 80 struct cpu_info *sc_ci; 81 82 ucontext_t sc_ucp; 83 uint8_t sc_ucp_stack[PAGE_SIZE]; 84 } cpu_softc_t; 85 86 87 /* statics */ 88 static struct pcb lwp0pcb; 89 static void *um_msgbuf; 90 91 92 /* attachment */ 93 CFATTACH_DECL_NEW(cpu, sizeof(cpu_softc_t), cpu_match, cpu_attach, NULL, NULL); 94 95 static int 96 cpu_match(device_t parent, cfdata_t match, void *opaque) 97 { 98 struct thunkbus_attach_args *taa = opaque; 99 100 if (taa->taa_type != THUNKBUS_TYPE_CPU) 101 return 0; 102 103 return 1; 104 } 105 106 static void 107 cpu_attach(device_t parent, device_t self, void *opaque) 108 { 109 cpu_softc_t *sc = device_private(self); 110 111 aprint_naive("\n"); 112 aprint_normal("\n"); 113 114 cpu_info_primary.ci_dev = self; 115 sc->sc_dev = self; 116 sc->sc_ci = &cpu_info_primary; 117 118 thunk_getcontext(&sc->sc_ucp); 119 sc->sc_ucp.uc_stack.ss_sp = sc->sc_ucp_stack; 120 sc->sc_ucp.uc_stack.ss_size = PAGE_SIZE - sizeof(register_t); 121 sc->sc_ucp.uc_flags = _UC_STACK | _UC_CPU | _UC_SIGMASK; 122 thunk_sigaddset(&sc->sc_ucp.uc_sigmask, SIGALRM); 123 thunk_sigaddset(&sc->sc_ucp.uc_sigmask, SIGIO); 124 } 125 126 void 127 cpu_configure(void) 128 { 129 if (config_rootfound("mainbus", NULL) == NULL) 130 panic("configure: mainbus not configured"); 131 132 spl0(); 133 } 134 135 136 /* main guts */ 137 void 138 cpu_reboot(int howto, char *bootstr) 139 { 140 extern void usermode_reboot(void); 141 142 if (cold) 143 howto |= RB_HALT; 144 145 if ((howto & RB_NOSYNC) == 0) 146 vfs_shutdown(); 147 else 148 suspendsched(); 149 150 doshutdownhooks(); 151 pmf_system_shutdown(boothowto); 152 153 if ((howto & RB_POWERDOWN) == RB_POWERDOWN) 154 thunk_exit(0); 155 156 splhigh(); 157 158 if (howto & RB_DUMP) 159 thunk_abort(); 160 161 if (howto & RB_HALT) { 162 printf("\n"); 163 printf("The operating system has halted.\n"); 164 printf("Please press any key to reboot.\n\n"); 165 cnpollc(1); 166 cngetc(); 167 cnpollc(0); 168 } 169 170 printf("rebooting...\n"); 171 172 usermode_reboot(); 173 174 /* NOTREACHED */ 175 cpu_unreachable(); 176 } 177 178 void 179 cpu_need_resched(struct cpu_info *ci, int flags) 180 { 181 ci->ci_want_resched |= flags; 182 aston(ci); 183 } 184 185 void 186 cpu_need_proftick(struct lwp *l) 187 { 188 } 189 190 static 191 void 192 cpu_switchto_atomic(lwp_t *oldlwp, lwp_t *newlwp) 193 { 194 struct pcb *oldpcb = oldlwp ? lwp_getpcb(oldlwp) : NULL; 195 struct pcb *newpcb = lwp_getpcb(newlwp); 196 struct cpu_info *ci = curcpu(); 197 198 ci->ci_stash = oldlwp; 199 200 if (oldpcb) 201 oldpcb->pcb_errno = thunk_geterrno(); 202 203 thunk_seterrno(newpcb->pcb_errno); 204 205 curlwp = newlwp; 206 if (thunk_setcontext(&newpcb->pcb_ucp)) 207 panic("setcontext failed"); 208 /* not reached */ 209 } 210 211 lwp_t * 212 cpu_switchto(lwp_t *oldlwp, lwp_t *newlwp, bool returning) 213 { 214 struct pcb *oldpcb = oldlwp ? lwp_getpcb(oldlwp) : NULL; 215 struct pcb *newpcb = lwp_getpcb(newlwp); 216 struct cpu_info *ci = curcpu(); 217 cpu_softc_t *sc = device_private(ci->ci_dev); 218 219 #ifdef CPU_DEBUG 220 thunk_printf_debug("cpu_switchto [%s,pid=%d,lid=%d] -> [%s,pid=%d,lid=%d]\n", 221 oldlwp ? oldlwp->l_name : "none", 222 oldlwp ? oldlwp->l_proc->p_pid : -1, 223 oldlwp ? oldlwp->l_lid : -1, 224 newlwp ? newlwp->l_name : "none", 225 newlwp ? newlwp->l_proc->p_pid : -1, 226 newlwp ? newlwp->l_lid : -1); 227 if (oldpcb) { 228 thunk_printf_debug(" oldpcb uc_link=%p, uc_stack.ss_sp=%p, " 229 "uc_stack.ss_size=%d\n", 230 oldpcb->pcb_ucp.uc_link, 231 oldpcb->pcb_ucp.uc_stack.ss_sp, 232 (int)oldpcb->pcb_ucp.uc_stack.ss_size); 233 } 234 if (newpcb) { 235 thunk_printf_debug(" newpcb uc_link=%p, uc_stack.ss_sp=%p, " 236 "uc_stack.ss_size=%d\n", 237 newpcb->pcb_ucp.uc_link, 238 newpcb->pcb_ucp.uc_stack.ss_sp, 239 (int)newpcb->pcb_ucp.uc_stack.ss_size); 240 } 241 #endif /* !CPU_DEBUG */ 242 243 /* create atomic switcher */ 244 thunk_makecontext(&sc->sc_ucp, (void (*)(void)) cpu_switchto_atomic, 245 2, oldlwp, newlwp, NULL); 246 247 if (!oldpcb) { 248 thunk_setcontext(&sc->sc_ucp); 249 /* never returns */ 250 } else { 251 thunk_swapcontext(&oldpcb->pcb_ucp, &sc->sc_ucp); 252 /* returns here */ 253 } 254 255 #ifdef CPU_DEBUG 256 thunk_printf_debug("cpu_switchto: returning %p (was %p)\n", ci->ci_stash, oldlwp); 257 #endif 258 return ci->ci_stash; 259 } 260 261 void 262 cpu_dumpconf(void) 263 { 264 #ifdef CPU_DEBUG 265 thunk_printf_debug("cpu_dumpconf\n"); 266 #endif 267 } 268 269 void 270 cpu_signotify(struct lwp *l) 271 { 272 } 273 274 void 275 cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags) 276 { 277 struct pcb *pcb = lwp_getpcb(l); 278 ucontext_t *ucp = &pcb->pcb_userret_ucp; 279 280 #ifdef CPU_DEBUG 281 thunk_printf_debug("cpu_getmcontext\n"); 282 #endif 283 memcpy(mcp, &ucp->uc_mcontext, sizeof(mcontext_t)); 284 return; 285 } 286 287 int 288 cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags) 289 { 290 struct pcb *pcb = lwp_getpcb(l); 291 ucontext_t *ucp = &pcb->pcb_userret_ucp; 292 293 #ifdef CPU_DEBUG 294 thunk_printf_debug("cpu_setmcontext\n"); 295 #endif 296 memcpy(&ucp->uc_mcontext, mcp, sizeof(mcontext_t)); 297 return 0; 298 } 299 300 void 301 cpu_idle(void) 302 { 303 struct cpu_info *ci = curcpu(); 304 305 if (ci->ci_want_resched) 306 return; 307 308 thunk_idle(); 309 } 310 311 void 312 cpu_lwp_free(struct lwp *l, int proc) 313 { 314 #ifdef CPU_DEBUG 315 thunk_printf_debug("cpu_lwp_free (dummy)\n"); 316 #endif 317 } 318 319 void 320 cpu_lwp_free2(struct lwp *l) 321 { 322 struct pcb *pcb = lwp_getpcb(l); 323 324 #ifdef CPU_DEBUG 325 thunk_printf_debug("cpu_lwp_free2\n"); 326 #endif 327 328 if (pcb == NULL) 329 return; 330 /* XXX nothing to do? */ 331 } 332 333 static void 334 cpu_lwp_trampoline(ucontext_t *ucp, void (*func)(void *), void *arg) 335 { 336 #ifdef CPU_DEBUG 337 thunk_printf_debug("cpu_lwp_trampoline called with func %p, arg %p\n", (void *) func, arg); 338 #endif 339 /* init lwp */ 340 lwp_startup(curcpu()->ci_stash, curlwp); 341 342 /* actual jump */ 343 thunk_makecontext(ucp, (void (*)(void)) func, 1, arg, NULL, NULL); 344 thunk_setcontext(ucp); 345 } 346 347 void 348 cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize, 349 void (*func)(void *), void *arg) 350 { 351 struct pcb *pcb1 = lwp_getpcb(l1); 352 struct pcb *pcb2 = lwp_getpcb(l2); 353 354 #ifdef CPU_DEBUG 355 thunk_printf_debug("cpu_lwp_fork [%s/%p] -> [%s/%p] stack=%p stacksize=%d\n", 356 l1 ? l1->l_name : "none", l1, 357 l2 ? l2->l_name : "none", l2, 358 stack, (int)stacksize); 359 #endif 360 361 if (stack) 362 panic("%s: stack passed, can't handle\n", __func__); 363 364 /* copy the PCB and its switchframes from parent */ 365 memcpy(pcb2, pcb1, sizeof(struct pcb)); 366 367 /* refresh context */ 368 if (thunk_getcontext(&pcb2->pcb_ucp)) 369 panic("getcontext failed"); 370 371 /* recalculate the system stack top */ 372 pcb2->sys_stack_top = pcb2->sys_stack + TRAPSTACKSIZE; 373 374 /* get l2 its own stack */ 375 pcb2->pcb_ucp.uc_stack.ss_sp = pcb2->sys_stack; 376 pcb2->pcb_ucp.uc_stack.ss_size = pcb2->sys_stack_top - pcb2->sys_stack; 377 pcb2->pcb_ucp.uc_flags = _UC_STACK | _UC_CPU | _UC_SIGMASK; 378 pcb2->pcb_ucp.uc_link = &pcb2->pcb_userret_ucp; 379 thunk_makecontext(&pcb2->pcb_ucp, 380 (void (*)(void)) cpu_lwp_trampoline, 381 3, &pcb2->pcb_ucp, func, arg); 382 } 383 384 void 385 cpu_initclocks(void) 386 { 387 extern timer_t clock_timerid; 388 389 thunk_timer_start(clock_timerid, HZ); 390 } 391 392 void 393 cpu_startup(void) 394 { 395 vaddr_t minaddr, maxaddr; 396 size_t msgbufsize = 32 * 1024; 397 398 /* get ourself a message buffer */ 399 um_msgbuf = kmem_zalloc(msgbufsize, KM_SLEEP); 400 if (um_msgbuf == NULL) 401 panic("couldn't allocate msgbuf"); 402 initmsgbuf(um_msgbuf, msgbufsize); 403 404 /* allocate a submap for physio, 1Mb enough? */ 405 minaddr = 0; 406 phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, 407 1024 * 1024, 0, false, NULL); 408 409 /* say hi! */ 410 banner(); 411 412 /* init lwp0 */ 413 memset(&lwp0pcb, 0, sizeof(lwp0pcb)); 414 thunk_getcontext(&lwp0pcb.pcb_ucp); 415 lwp0pcb.pcb_ucp.uc_flags = _UC_STACK | _UC_CPU | _UC_SIGMASK; 416 uvm_lwp_setuarea(&lwp0, (vaddr_t) &lwp0pcb); 417 memcpy(&lwp0pcb.pcb_userret_ucp, &lwp0pcb.pcb_ucp, sizeof(ucontext_t)); 418 419 /* set stack top */ 420 lwp0pcb.sys_stack_top = lwp0pcb.sys_stack + TRAPSTACKSIZE; 421 } 422 423 void 424 cpu_rootconf(void) 425 { 426 extern char *usermode_root_device; 427 device_t rdev; 428 429 if (usermode_root_device != NULL) { 430 rdev = device_find_by_xname(usermode_root_device); 431 } else { 432 rdev = device_find_by_xname("ld0"); 433 if (rdev == NULL) 434 rdev = device_find_by_xname("md0"); 435 } 436 437 aprint_normal("boot device: %s\n", 438 rdev ? device_xname(rdev) : "<unknown>"); 439 setroot(rdev, 0); 440 } 441 442 bool 443 cpu_intr_p(void) 444 { 445 int idepth; 446 447 kpreempt_disable(); 448 idepth = curcpu()->ci_idepth; 449 kpreempt_enable(); 450 451 return (idepth >= 0); 452 } 453