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