1 /* system dependent functions for use inside the whole kernel. */ 2 3 #include "kernel/kernel.h" 4 5 #include <unistd.h> 6 #include <ctype.h> 7 #include <string.h> 8 #include <machine/cmos.h> 9 #include <machine/bios.h> 10 #include <machine/cpu.h> 11 #include <minix/portio.h> 12 #include <minix/cpufeature.h> 13 #include <assert.h> 14 #include <signal.h> 15 #include <machine/vm.h> 16 17 #include <minix/u64.h> 18 19 #include "archconst.h" 20 #include "arch_proto.h" 21 #include "serial.h" 22 #include "oxpcie.h" 23 #include <machine/multiboot.h> 24 25 #include "glo.h" 26 27 #ifdef USE_APIC 28 #include "apic.h" 29 #endif 30 31 #ifdef USE_ACPI 32 #include "acpi.h" 33 #endif 34 35 static int osfxsr_feature; /* FXSAVE/FXRSTOR instructions support (SSEx) */ 36 37 /* set MP and NE flags to handle FPU exceptions in native mode. */ 38 #define CR0_MP_NE 0x0022 39 /* set CR4.OSFXSR[bit 9] if FXSR is supported. */ 40 #define CR4_OSFXSR (1L<<9) 41 /* set OSXMMEXCPT[bit 10] if we provide #XM handler. */ 42 #define CR4_OSXMMEXCPT (1L<<10) 43 44 void * k_stacks; 45 46 static void ser_debug(int c); 47 static void ser_dump_vfs(void); 48 49 #ifdef CONFIG_SMP 50 static void ser_dump_proc_cpu(void); 51 #endif 52 #if !CONFIG_OXPCIE 53 static void ser_init(void); 54 #endif 55 56 void fpu_init(void) 57 { 58 unsigned short cw, sw; 59 60 fninit(); 61 sw = fnstsw(); 62 fnstcw(&cw); 63 64 if((sw & 0xff) == 0 && 65 (cw & 0x103f) == 0x3f) { 66 /* We have some sort of FPU, but don't check exact model. 67 * Set CR0_NE and CR0_MP to handle fpu exceptions 68 * in native mode. */ 69 write_cr0(read_cr0() | CR0_MP_NE); 70 get_cpulocal_var(fpu_presence) = 1; 71 if(_cpufeature(_CPUF_I386_FXSR)) { 72 u32_t cr4 = read_cr4() | CR4_OSFXSR; /* Enable FXSR. */ 73 74 /* OSXMMEXCPT if supported 75 * FXSR feature can be available without SSE 76 */ 77 if(_cpufeature(_CPUF_I386_SSE)) 78 cr4 |= CR4_OSXMMEXCPT; 79 80 write_cr4(cr4); 81 osfxsr_feature = 1; 82 } else { 83 osfxsr_feature = 0; 84 } 85 } else { 86 /* No FPU presents. */ 87 get_cpulocal_var(fpu_presence) = 0; 88 osfxsr_feature = 0; 89 return; 90 } 91 } 92 93 void save_local_fpu(struct proc *pr, int retain) 94 { 95 char *state = pr->p_seg.fpu_state; 96 97 /* Save process FPU context. If the 'retain' flag is set, keep the FPU 98 * state as is. If the flag is not set, the state is undefined upon 99 * return, and the caller is responsible for reloading a proper state. 100 */ 101 102 if(!is_fpu()) 103 return; 104 105 assert(state); 106 107 if(osfxsr_feature) { 108 fxsave(state); 109 } else { 110 fnsave(state); 111 if (retain) 112 (void) frstor(state); 113 } 114 } 115 116 void save_fpu(struct proc *pr) 117 { 118 #ifdef CONFIG_SMP 119 if (cpuid != pr->p_cpu) { 120 int stopped; 121 122 /* remember if the process was already stopped */ 123 stopped = RTS_ISSET(pr, RTS_PROC_STOP); 124 125 /* stop the remote process and force its context to be saved */ 126 smp_schedule_stop_proc_save_ctx(pr); 127 128 /* 129 * If the process wasn't stopped let the process run again. The 130 * process is kept block by the fact that the kernel cannot run 131 * on its cpu 132 */ 133 if (!stopped) 134 RTS_UNSET(pr, RTS_PROC_STOP); 135 136 return; 137 } 138 #endif 139 140 if (get_cpulocal_var(fpu_owner) == pr) { 141 disable_fpu_exception(); 142 save_local_fpu(pr, TRUE /*retain*/); 143 } 144 } 145 146 /* reserve a chunk of memory for fpu state; every one has to 147 * be FPUALIGN-aligned. 148 */ 149 static char fpu_state[NR_PROCS][FPU_XFP_SIZE] __aligned(FPUALIGN); 150 151 void arch_proc_reset(struct proc *pr) 152 { 153 char *v = NULL; 154 struct stackframe_s reg; 155 156 assert(pr->p_nr < NR_PROCS); 157 158 if(pr->p_nr >= 0) { 159 v = fpu_state[pr->p_nr]; 160 /* verify alignment */ 161 assert(!((vir_bytes)v % FPUALIGN)); 162 /* initialize state */ 163 memset(v, 0, FPU_XFP_SIZE); 164 } 165 166 /* Clear process state. */ 167 memset(®, 0, sizeof(pr->p_reg)); 168 if(iskerneln(pr->p_nr)) 169 reg.psw = INIT_TASK_PSW; 170 else 171 reg.psw = INIT_PSW; 172 173 pr->p_seg.fpu_state = v; 174 175 /* Initialize the fundamentals that are (initially) the same for all 176 * processes - the segment selectors it gets to use. 177 */ 178 pr->p_reg.cs = USER_CS_SELECTOR; 179 pr->p_reg.gs = 180 pr->p_reg.fs = 181 pr->p_reg.ss = 182 pr->p_reg.es = 183 pr->p_reg.ds = USER_DS_SELECTOR; 184 185 /* set full context and make sure it gets restored */ 186 arch_proc_setcontext(pr, ®, 0, KTS_FULLCONTEXT); 187 } 188 189 void arch_set_secondary_ipc_return(struct proc *p, u32_t val) 190 { 191 p->p_reg.bx = val; 192 } 193 194 int restore_fpu(struct proc *pr) 195 { 196 int failed; 197 char *state = pr->p_seg.fpu_state; 198 199 assert(state); 200 201 if(!proc_used_fpu(pr)) { 202 fninit(); 203 pr->p_misc_flags |= MF_FPU_INITIALIZED; 204 } else { 205 if(osfxsr_feature) { 206 failed = fxrstor(state); 207 } else { 208 failed = frstor(state); 209 } 210 211 if (failed) return EINVAL; 212 } 213 214 return OK; 215 } 216 217 void cpu_identify(void) 218 { 219 u32_t eax, ebx, ecx, edx; 220 unsigned cpu = cpuid; 221 222 eax = 0; 223 _cpuid(&eax, &ebx, &ecx, &edx); 224 225 if (ebx == INTEL_CPUID_GEN_EBX && ecx == INTEL_CPUID_GEN_ECX && 226 edx == INTEL_CPUID_GEN_EDX) { 227 cpu_info[cpu].vendor = CPU_VENDOR_INTEL; 228 } else if (ebx == AMD_CPUID_GEN_EBX && ecx == AMD_CPUID_GEN_ECX && 229 edx == AMD_CPUID_GEN_EDX) { 230 cpu_info[cpu].vendor = CPU_VENDOR_AMD; 231 } else 232 cpu_info[cpu].vendor = CPU_VENDOR_UNKNOWN; 233 234 if (eax == 0) 235 return; 236 237 eax = 1; 238 _cpuid(&eax, &ebx, &ecx, &edx); 239 240 cpu_info[cpu].family = (eax >> 8) & 0xf; 241 if (cpu_info[cpu].family == 0xf) 242 cpu_info[cpu].family += (eax >> 20) & 0xff; 243 cpu_info[cpu].model = (eax >> 4) & 0xf; 244 if (cpu_info[cpu].model == 0xf || cpu_info[cpu].model == 0x6) 245 cpu_info[cpu].model += ((eax >> 16) & 0xf) << 4 ; 246 cpu_info[cpu].stepping = eax & 0xf; 247 cpu_info[cpu].flags[0] = ecx; 248 cpu_info[cpu].flags[1] = edx; 249 } 250 251 void arch_init(void) 252 { 253 k_stacks = (void*) &k_stacks_start; 254 assert(!((vir_bytes) k_stacks % K_STACK_SIZE)); 255 256 #ifndef CONFIG_SMP 257 /* 258 * use stack 0 and cpu id 0 on a single processor machine, SMP 259 * configuration does this in smp_init() for all cpus at once 260 */ 261 tss_init(0, get_k_stack_top(0)); 262 #endif 263 264 #if !CONFIG_OXPCIE 265 ser_init(); 266 #endif 267 268 #ifdef USE_ACPI 269 acpi_init(); 270 #endif 271 272 #if defined(USE_APIC) && !defined(CONFIG_SMP) 273 if (config_no_apic) { 274 DEBUGBASIC(("APIC disabled, using legacy PIC\n")); 275 } 276 else if (!apic_single_cpu_init()) { 277 DEBUGBASIC(("APIC not present, using legacy PIC\n")); 278 } 279 #endif 280 281 /* Reserve some BIOS ranges */ 282 cut_memmap(&kinfo, BIOS_MEM_BEGIN, BIOS_MEM_END); 283 cut_memmap(&kinfo, BASE_MEM_TOP, UPPER_MEM_END); 284 } 285 286 /*===========================================================================* 287 * do_ser_debug * 288 *===========================================================================*/ 289 void do_ser_debug() 290 { 291 u8_t c, lsr; 292 293 #if CONFIG_OXPCIE 294 { 295 int oxin; 296 if((oxin = oxpcie_in()) >= 0) 297 ser_debug(oxin); 298 } 299 #endif 300 301 lsr= inb(COM1_LSR); 302 if (!(lsr & LSR_DR)) 303 return; 304 c = inb(COM1_RBR); 305 ser_debug(c); 306 } 307 308 static void ser_dump_queue_cpu(unsigned cpu) 309 { 310 int q; 311 struct proc ** rdy_head; 312 313 rdy_head = get_cpu_var(cpu, run_q_head); 314 315 for(q = 0; q < NR_SCHED_QUEUES; q++) { 316 struct proc *p; 317 if(rdy_head[q]) { 318 printf("%2d: ", q); 319 for(p = rdy_head[q]; p; p = p->p_nextready) { 320 printf("%s / %d ", p->p_name, p->p_endpoint); 321 } 322 printf("\n"); 323 } 324 } 325 } 326 327 static void ser_dump_queues(void) 328 { 329 #ifdef CONFIG_SMP 330 unsigned cpu; 331 332 printf("--- run queues ---\n"); 333 for (cpu = 0; cpu < ncpus; cpu++) { 334 printf("CPU %d :\n", cpu); 335 ser_dump_queue_cpu(cpu); 336 } 337 #else 338 ser_dump_queue_cpu(0); 339 #endif 340 } 341 342 #ifdef CONFIG_SMP 343 static void dump_bkl_usage(void) 344 { 345 unsigned cpu; 346 347 printf("--- BKL usage ---\n"); 348 for (cpu = 0; cpu < ncpus; cpu++) { 349 printf("cpu %3d kernel ticks 0x%x%08x bkl ticks 0x%x%08x succ %d tries %d\n", cpu, 350 ex64hi(kernel_ticks[cpu]), 351 ex64lo(kernel_ticks[cpu]), 352 ex64hi(bkl_ticks[cpu]), 353 ex64lo(bkl_ticks[cpu]), 354 bkl_succ[cpu], bkl_tries[cpu]); 355 } 356 } 357 358 static void reset_bkl_usage(void) 359 { 360 memset(kernel_ticks, 0, sizeof(kernel_ticks)); 361 memset(bkl_ticks, 0, sizeof(bkl_ticks)); 362 memset(bkl_tries, 0, sizeof(bkl_tries)); 363 memset(bkl_succ, 0, sizeof(bkl_succ)); 364 } 365 #endif 366 367 static void ser_debug(const int c) 368 { 369 serial_debug_active = 1; 370 371 switch(c) 372 { 373 case 'Q': 374 minix_shutdown(NULL); 375 NOT_REACHABLE; 376 #ifdef CONFIG_SMP 377 case 'B': 378 dump_bkl_usage(); 379 break; 380 case 'b': 381 reset_bkl_usage(); 382 break; 383 #endif 384 case '1': 385 ser_dump_proc(); 386 break; 387 case '2': 388 ser_dump_queues(); 389 break; 390 #ifdef CONFIG_SMP 391 case '4': 392 ser_dump_proc_cpu(); 393 break; 394 #endif 395 case '5': 396 ser_dump_vfs(); 397 break; 398 #if DEBUG_TRACE 399 #define TOGGLECASE(ch, flag) \ 400 case ch: { \ 401 if(verboseflags & flag) { \ 402 verboseflags &= ~flag; \ 403 printf("%s disabled\n", #flag); \ 404 } else { \ 405 verboseflags |= flag; \ 406 printf("%s enabled\n", #flag); \ 407 } \ 408 break; \ 409 } 410 TOGGLECASE('8', VF_SCHEDULING) 411 TOGGLECASE('9', VF_PICKPROC) 412 #endif 413 #ifdef USE_APIC 414 case 'I': 415 dump_apic_irq_state(); 416 break; 417 #endif 418 } 419 serial_debug_active = 0; 420 } 421 422 #if DEBUG_SERIAL 423 424 static void ser_dump_vfs() 425 { 426 /* Notify VFS it has to generate stack traces. Kernel can't do that as 427 * it's not aware of user space threads. 428 */ 429 mini_notify(proc_addr(KERNEL), VFS_PROC_NR); 430 } 431 432 #ifdef CONFIG_SMP 433 static void ser_dump_proc_cpu(void) 434 { 435 struct proc *pp; 436 unsigned cpu; 437 438 for (cpu = 0; cpu < ncpus; cpu++) { 439 printf("CPU %d processes : \n", cpu); 440 for (pp= BEG_USER_ADDR; pp < END_PROC_ADDR; pp++) { 441 if (isemptyp(pp) || pp->p_cpu != cpu) 442 continue; 443 print_proc(pp); 444 } 445 } 446 } 447 #endif 448 449 #endif /* DEBUG_SERIAL */ 450 451 #if SPROFILE 452 453 int arch_init_profile_clock(const u32_t freq) 454 { 455 int r; 456 /* Set CMOS timer frequency. */ 457 outb(RTC_INDEX, RTC_REG_A); 458 outb(RTC_IO, RTC_A_DV_OK | freq); 459 /* Enable CMOS timer interrupts. */ 460 outb(RTC_INDEX, RTC_REG_B); 461 r = inb(RTC_IO); 462 outb(RTC_INDEX, RTC_REG_B); 463 outb(RTC_IO, r | RTC_B_PIE); 464 /* Mandatory read of CMOS register to enable timer interrupts. */ 465 outb(RTC_INDEX, RTC_REG_C); 466 inb(RTC_IO); 467 468 return CMOS_CLOCK_IRQ; 469 } 470 471 void arch_stop_profile_clock(void) 472 { 473 int r; 474 /* Disable CMOS timer interrupts. */ 475 outb(RTC_INDEX, RTC_REG_B); 476 r = inb(RTC_IO); 477 outb(RTC_INDEX, RTC_REG_B); 478 outb(RTC_IO, r & ~RTC_B_PIE); 479 } 480 481 void arch_ack_profile_clock(void) 482 { 483 /* Mandatory read of CMOS register to re-enable timer interrupts. */ 484 outb(RTC_INDEX, RTC_REG_C); 485 inb(RTC_IO); 486 } 487 488 #endif 489 490 void arch_do_syscall(struct proc *proc) 491 { 492 /* do_ipc assumes that it's running because of the current process */ 493 assert(proc == get_cpulocal_var(proc_ptr)); 494 /* Make the system call, for real this time. */ 495 assert(proc->p_misc_flags & MF_SC_DEFER); 496 proc->p_reg.retreg = 497 do_ipc(proc->p_defer.r1, proc->p_defer.r2, proc->p_defer.r3); 498 } 499 500 struct proc * arch_finish_switch_to_user(void) 501 { 502 char * stk; 503 struct proc * p; 504 505 #ifdef CONFIG_SMP 506 stk = (char *)tss[cpuid].sp0; 507 #else 508 stk = (char *)tss[0].sp0; 509 #endif 510 /* set pointer to the process to run on the stack */ 511 p = get_cpulocal_var(proc_ptr); 512 *((reg_t *)stk) = (reg_t) p; 513 514 /* make sure IF is on in FLAGS so that interrupts won't be disabled 515 * once p's context is restored. 516 */ 517 p->p_reg.psw |= IF_MASK; 518 519 /* Set TRACEBIT state properly. */ 520 if(p->p_misc_flags & MF_STEP) 521 p->p_reg.psw |= TRACEBIT; 522 else 523 p->p_reg.psw &= ~TRACEBIT; 524 525 return p; 526 } 527 528 void arch_proc_setcontext(struct proc *p, struct stackframe_s *state, 529 int isuser, int trap_style) 530 { 531 if(isuser) { 532 /* Restore user bits of psw from sc, maintain system bits 533 * from proc. 534 */ 535 state->psw = (state->psw & X86_FLAGS_USER) | 536 (p->p_reg.psw & ~X86_FLAGS_USER); 537 } 538 539 /* someone wants to totally re-initialize process state */ 540 assert(sizeof(p->p_reg) == sizeof(*state)); 541 if(state != &p->p_reg) { 542 memcpy(&p->p_reg, state, sizeof(*state)); 543 } 544 545 /* further code is instructed to not touch the context 546 * any more 547 */ 548 p->p_misc_flags |= MF_CONTEXT_SET; 549 550 /* on x86 this requires returning using iret (KTS_INT) 551 * so that the full context is restored instead of relying on 552 * the userspace doing it (as it would do on SYSEXIT). 553 * as ESP and EIP are also reset, userspace won't try to 554 * restore bogus context after returning. 555 * 556 * if the process is not blocked, or the kernel will ignore 557 * our trap style, we needn't panic but things will probably 558 * not go well for the process (restored context will be ignored) 559 * and the situation should be debugged. 560 */ 561 if(!(p->p_rts_flags)) { 562 printf("WARNINIG: setting full context of runnable process\n"); 563 print_proc(p); 564 util_stacktrace(); 565 } 566 if(p->p_seg.p_kern_trap_style == KTS_NONE) 567 printf("WARNINIG: setting full context of out-of-kernel process\n"); 568 p->p_seg.p_kern_trap_style = trap_style; 569 } 570 571 void restore_user_context(struct proc *p) 572 { 573 int trap_style = p->p_seg.p_kern_trap_style; 574 #if 0 575 #define TYPES 10 576 static int restores[TYPES], n = 0; 577 578 if(trap_style >= 0 && trap_style < TYPES) 579 restores[trap_style]++; 580 581 if(!(n++ % 500000)) { 582 int t; 583 for(t = 0; t < TYPES; t++) 584 if(restores[t]) 585 printf("%d: %d ", t, restores[t]); 586 printf("\n"); 587 } 588 #endif 589 590 p->p_seg.p_kern_trap_style = KTS_NONE; 591 592 if(trap_style == KTS_SYSENTER) { 593 restore_user_context_sysenter(p); 594 NOT_REACHABLE; 595 } 596 597 if(trap_style == KTS_SYSCALL) { 598 restore_user_context_syscall(p); 599 NOT_REACHABLE; 600 } 601 602 switch(trap_style) { 603 case KTS_NONE: 604 panic("no entry trap style known"); 605 case KTS_INT_HARD: 606 case KTS_INT_UM: 607 case KTS_FULLCONTEXT: 608 case KTS_INT_ORIG: 609 restore_user_context_int(p); 610 NOT_REACHABLE; 611 default: 612 panic("unknown trap style recorded"); 613 NOT_REACHABLE; 614 } 615 616 NOT_REACHABLE; 617 } 618 619 void fpu_sigcontext(struct proc *pr, struct sigframe_sigcontext *fr, struct sigcontext *sc) 620 { 621 int fp_error; 622 623 if (osfxsr_feature) { 624 fp_error = sc->sc_fpu_state.xfp_regs.fp_status & 625 ~sc->sc_fpu_state.xfp_regs.fp_control; 626 } else { 627 fp_error = sc->sc_fpu_state.fpu_regs.fp_status & 628 ~sc->sc_fpu_state.fpu_regs.fp_control; 629 } 630 631 if (fp_error & 0x001) { /* Invalid op */ 632 /* 633 * swd & 0x240 == 0x040: Stack Underflow 634 * swd & 0x240 == 0x240: Stack Overflow 635 * User must clear the SF bit (0x40) if set 636 */ 637 fr->sf_code = FPE_FLTINV; 638 } else if (fp_error & 0x004) { 639 fr->sf_code = FPE_FLTDIV; /* Divide by Zero */ 640 } else if (fp_error & 0x008) { 641 fr->sf_code = FPE_FLTOVF; /* Overflow */ 642 } else if (fp_error & 0x012) { 643 fr->sf_code = FPE_FLTUND; /* Denormal, Underflow */ 644 } else if (fp_error & 0x020) { 645 fr->sf_code = FPE_FLTRES; /* Precision */ 646 } else { 647 fr->sf_code = 0; /* XXX - probably should be used for FPE_INTOVF or 648 * FPE_INTDIV */ 649 } 650 } 651 652 reg_t arch_get_sp(struct proc *p) { return p->p_reg.sp; } 653 654 #if !CONFIG_OXPCIE 655 static void ser_init(void) 656 { 657 unsigned char lcr; 658 unsigned divisor; 659 660 /* keep BIOS settings if cttybaud is not set */ 661 if (kinfo.serial_debug_baud <= 0) return; 662 663 /* set DLAB to make baud accessible */ 664 lcr = LCR_8BIT | LCR_1STOP | LCR_NPAR; 665 outb(COM1_LCR, lcr | LCR_DLAB); 666 667 /* set baud rate */ 668 divisor = UART_BASE_FREQ / kinfo.serial_debug_baud; 669 if (divisor < 1) divisor = 1; 670 if (divisor > 65535) divisor = 65535; 671 672 outb(COM1_DLL, divisor & 0xff); 673 outb(COM1_DLM, (divisor >> 8) & 0xff); 674 675 /* clear DLAB */ 676 outb(COM1_LCR, lcr); 677 } 678 #endif 679