1/* This file is part of the lowest layer of the MINIX kernel. (The other part 2 * is "proc.c".) The lowest layer does process switching and message handling. 3 * Furthermore it contains the assembler startup code for Minix and the 32-bit 4 * interrupt handlers. It cooperates with the code in "start.c" to set up a 5 * good environment for main(). 6 * 7 * Kernel is entered either because of kernel-calls, ipc-calls, interrupts or 8 * exceptions. TSS is set so that the kernel stack is loaded. The user context is 9 * saved to the proc table and the handler of the event is called. Once the 10 * handler is done, switch_to_user() function is called to pick a new process, 11 * finish what needs to be done for the next process to run, sets its context 12 * and switch to userspace. 13 * 14 * For communication with the boot monitor at startup time some constant 15 * data are compiled into the beginning of the text segment. This facilitates 16 * reading the data at the start of the boot process, since only the first 17 * sector of the file needs to be read. 18 * 19 * Some data storage is also allocated at the end of this file. This data 20 * will be at the start of the data segment of the kernel and will be read 21 * and modified by the boot monitor before the kernel starts. 22 */ 23 24#include "kernel/kernel.h" /* configures the kernel */ 25 26/* sections */ 27 28#include <machine/vm.h> 29#include "kernel/kernel.h" 30#include <minix/config.h> 31#include <minix/const.h> 32#include <minix/ipcconst.h> 33#include <minix/com.h> 34#include <machine/asm.h> 35#include <machine/interrupt.h> 36#include "archconst.h" 37#include "kernel/const.h" 38#include "kernel/proc.h" 39#include "sconst.h" 40#include <machine/multiboot.h> 41 42#include "arch_proto.h" /* K_STACK_SIZE */ 43 44#ifdef CONFIG_SMP 45#include "kernel/smp.h" 46#endif 47 48/* Selected 386 tss offsets. */ 49#define TSS3_S_SP0 4 50 51IMPORT(usermapped_offset) 52IMPORT(copr_not_available_handler) 53IMPORT(params_size) 54IMPORT(params_offset) 55IMPORT(switch_to_user) 56IMPORT(multiboot_init) 57 58.text 59/*===========================================================================*/ 60/* interrupt handlers */ 61/* interrupt handlers for 386 32-bit protected mode */ 62/*===========================================================================*/ 63 64#define PIC_IRQ_HANDLER(irq) \ 65 push $irq ;\ 66 call _C_LABEL(irq_handle) /* intr_handle(irq_handlers[irq]) */ ;\ 67 add $4, %esp ; 68 69/*===========================================================================*/ 70/* hwint00 - 07 */ 71/*===========================================================================*/ 72/* Note this is a macro, it just looks like a subroutine. */ 73 74#define hwint_master(irq) \ 75 TEST_INT_IN_KERNEL(4, 0f) ;\ 76 \ 77 SAVE_PROCESS_CTX(0, KTS_INT_HARD) ;\ 78 push %ebp ;\ 79 movl $0, %ebp /* for stack trace */ ;\ 80 call _C_LABEL(context_stop) ;\ 81 add $4, %esp ;\ 82 PIC_IRQ_HANDLER(irq) ;\ 83 movb $END_OF_INT, %al ;\ 84 outb $INT_CTL /* reenable interrupts in master pic */ ;\ 85 jmp _C_LABEL(switch_to_user) ;\ 86 \ 870: \ 88 pusha ;\ 89 call _C_LABEL(context_stop_idle) ;\ 90 PIC_IRQ_HANDLER(irq) ;\ 91 movb $END_OF_INT, %al ;\ 92 outb $INT_CTL /* reenable interrupts in master pic */ ;\ 93 CLEAR_IF(10*4(%esp)) ;\ 94 popa ;\ 95 iret ; 96 97/* Each of these entry points is an expansion of the hwint_master macro */ 98ENTRY(hwint00) 99/* Interrupt routine for irq 0 (the clock). */ 100 hwint_master(0) 101 102ENTRY(hwint01) 103/* Interrupt routine for irq 1 (keyboard) */ 104 hwint_master(1) 105 106ENTRY(hwint02) 107/* Interrupt routine for irq 2 (cascade!) */ 108 hwint_master(2) 109 110ENTRY(hwint03) 111/* Interrupt routine for irq 3 (second serial) */ 112 hwint_master(3) 113 114ENTRY(hwint04) 115/* Interrupt routine for irq 4 (first serial) */ 116 hwint_master(4) 117 118ENTRY(hwint05) 119/* Interrupt routine for irq 5 (XT winchester) */ 120 hwint_master(5) 121 122ENTRY(hwint06) 123/* Interrupt routine for irq 6 (floppy) */ 124 hwint_master(6) 125 126ENTRY(hwint07) 127/* Interrupt routine for irq 7 (printer) */ 128 hwint_master(7) 129 130/*===========================================================================*/ 131/* hwint08 - 15 */ 132/*===========================================================================*/ 133/* Note this is a macro, it just looks like a subroutine. */ 134#define hwint_slave(irq) \ 135 TEST_INT_IN_KERNEL(4, 0f) ;\ 136 \ 137 SAVE_PROCESS_CTX(0, KTS_INT_HARD) ;\ 138 push %ebp ;\ 139 movl $0, %ebp /* for stack trace */ ;\ 140 call _C_LABEL(context_stop) ;\ 141 add $4, %esp ;\ 142 PIC_IRQ_HANDLER(irq) ;\ 143 movb $END_OF_INT, %al ;\ 144 outb $INT_CTL /* reenable interrupts in master pic */ ;\ 145 outb $INT2_CTL /* reenable slave 8259 */ ;\ 146 jmp _C_LABEL(switch_to_user) ;\ 147 \ 1480: \ 149 pusha ;\ 150 call _C_LABEL(context_stop_idle) ;\ 151 PIC_IRQ_HANDLER(irq) ;\ 152 movb $END_OF_INT, %al ;\ 153 outb $INT_CTL /* reenable interrupts in master pic */ ;\ 154 outb $INT2_CTL /* reenable slave 8259 */ ;\ 155 CLEAR_IF(10*4(%esp)) ;\ 156 popa ;\ 157 iret ; 158 159/* Each of these entry points is an expansion of the hwint_slave macro */ 160ENTRY(hwint08) 161/* Interrupt routine for irq 8 (realtime clock) */ 162 hwint_slave(8) 163 164ENTRY(hwint09) 165/* Interrupt routine for irq 9 (irq 2 redirected) */ 166 hwint_slave(9) 167 168ENTRY(hwint10) 169/* Interrupt routine for irq 10 */ 170 hwint_slave(10) 171 172ENTRY(hwint11) 173/* Interrupt routine for irq 11 */ 174 hwint_slave(11) 175 176ENTRY(hwint12) 177/* Interrupt routine for irq 12 */ 178 hwint_slave(12) 179 180ENTRY(hwint13) 181/* Interrupt routine for irq 13 (FPU exception) */ 182 hwint_slave(13) 183 184ENTRY(hwint14) 185/* Interrupt routine for irq 14 (AT winchester) */ 186 hwint_slave(14) 187 188ENTRY(hwint15) 189/* Interrupt routine for irq 15 */ 190 hwint_slave(15) 191 192/* differences with sysenter: 193 * - we have to find our own per-cpu stack (i.e. post-SYSCALL 194 * %esp is not configured) 195 * - we have to save the post-SYSRET %eip, provided by the cpu 196 * in %ecx 197 * - the system call parameters are passed in %ecx, so we userland 198 * code that executes SYSCALL copies %ecx to %edx. So the roles 199 * of %ecx and %edx are reversed 200 * - we can use %esi as a scratch register 201 */ 202#define ipc_entry_syscall_percpu(cpu) ;\ 203ENTRY(ipc_entry_syscall_cpu ## cpu) ;\ 204 xchg %ecx, %edx ;\ 205 mov k_percpu_stacks+4*cpu, %esi ;\ 206 mov (%esi), %ebp ;\ 207 movl $KTS_SYSCALL, P_KERN_TRAP_STYLE(%ebp) ;\ 208 xchg %esp, %esi ;\ 209 jmp syscall_sysenter_common 210 211ipc_entry_syscall_percpu(0) 212ipc_entry_syscall_percpu(1) 213ipc_entry_syscall_percpu(2) 214ipc_entry_syscall_percpu(3) 215ipc_entry_syscall_percpu(4) 216ipc_entry_syscall_percpu(5) 217ipc_entry_syscall_percpu(6) 218ipc_entry_syscall_percpu(7) 219 220ENTRY(ipc_entry_sysenter) 221 /* SYSENTER simply sets kernel segments, EIP to here, and ESP 222 * to tss->sp0 (through MSR). so no automatic context saving is done. 223 * interrupts are disabled. 224 * 225 * register usage: 226 * edi: call type (IPCVEC, KERVEC) 227 * ebx, eax, ecx: syscall params, set by userland 228 * esi, edx: esp, eip to restore, set by userland 229 * 230 * no state is automatically saved; userland does all of that. 231 */ 232 mov (%esp), %ebp /* get proc saved by arch_finish_switch_to_user */ 233 234 /* inform kernel we entered by sysenter and should 235 * therefore exit through restore_user_context_sysenter 236 */ 237 movl $KTS_SYSENTER, P_KERN_TRAP_STYLE(%ebp) 238 add usermapped_offset, %edx /* compensate for mapping difference */ 239 240syscall_sysenter_common: 241 mov %esi, SPREG(%ebp) /* esi is return esp */ 242 mov %edx, PCREG(%ebp) /* edx is return eip */ 243 244 /* save PSW */ 245 pushf 246 pop %edx 247 mov %edx, PSWREG(%ebp) 248 249 /* check for call type; do_ipc? */ 250 cmp $IPCVEC_UM, %edi 251 jz ipc_entry_common 252 253 /* check for kernel trap */ 254 cmp $KERVEC_UM, %edi 255 jz kernel_call_entry_common 256 257 /* unrecognized call number; restore user with error */ 258 movl $-1, AXREG(%ebp) 259 push %ebp 260 call restore_user_context /* restore_user_context(%ebp); */ 261 262/* 263 * IPC is only from a process to kernel 264 */ 265ENTRY(ipc_entry_softint_orig) 266 SAVE_PROCESS_CTX(0, KTS_INT_ORIG) 267 jmp ipc_entry_common 268 269ENTRY(ipc_entry_softint_um) 270 SAVE_PROCESS_CTX(0, KTS_INT_UM) 271 jmp ipc_entry_common 272 273ENTRY(ipc_entry_common) 274 /* save the pointer to the current process */ 275 push %ebp 276 277 /* 278 * pass the syscall arguments from userspace to the handler. 279 * SAVE_PROCESS_CTX() does not clobber these registers, they are still 280 * set as the userspace have set them 281 */ 282 push %ebx 283 push %eax 284 push %ecx 285 286 /* stop user process cycles */ 287 push %ebp 288 /* for stack trace */ 289 movl $0, %ebp 290 call _C_LABEL(context_stop) 291 add $4, %esp 292 293 call _C_LABEL(do_ipc) 294 295 /* restore the current process pointer and save the return value */ 296 add $3 * 4, %esp 297 pop %esi 298 mov %eax, AXREG(%esi) 299 300 jmp _C_LABEL(switch_to_user) 301 302 303/* 304 * kernel call is only from a process to kernel 305 */ 306ENTRY(kernel_call_entry_orig) 307 SAVE_PROCESS_CTX(0, KTS_INT_ORIG) 308 jmp kernel_call_entry_common 309 310ENTRY(kernel_call_entry_um) 311 SAVE_PROCESS_CTX(0, KTS_INT_UM) 312 jmp kernel_call_entry_common 313 314ENTRY(kernel_call_entry_common) 315 /* save the pointer to the current process */ 316 push %ebp 317 318 /* 319 * pass the syscall arguments from userspace to the handler. 320 * SAVE_PROCESS_CTX() does not clobber these registers, they are still 321 * set as the userspace have set them 322 */ 323 push %eax 324 325 /* stop user process cycles */ 326 push %ebp 327 /* for stack trace */ 328 movl $0, %ebp 329 call _C_LABEL(context_stop) 330 add $4, %esp 331 332 call _C_LABEL(kernel_call) 333 334 /* restore the current process pointer and save the return value */ 335 add $8, %esp 336 337 jmp _C_LABEL(switch_to_user) 338 339 340.balign 16 341/* 342 * called by the exception interrupt vectors. If the exception does not push 343 * errorcode, we assume that the vector handler pushed 0 instead. Next pushed 344 * thing is the vector number. From this point on we can continue as if every 345 * exception pushes an error code 346 */ 347exception_entry: 348 /* 349 * check if it is a nested trap by comparing the saved code segment 350 * descriptor with the kernel CS first 351 */ 352 TEST_INT_IN_KERNEL(12, exception_entry_nested) 353 354exception_entry_from_user: 355 SAVE_PROCESS_CTX(8, KTS_INT_HARD) 356 357 /* stop user process cycles */ 358 push %ebp 359 /* for stack trace clear %ebp */ 360 movl $0, %ebp 361 call _C_LABEL(context_stop) 362 add $4, %esp 363 364 /* 365 * push a pointer to the interrupt state pushed by the cpu and the 366 * vector number pushed by the vector handler just before calling 367 * exception_entry and call the exception handler. 368 */ 369 push %esp 370 push $0 /* it's not a nested exception */ 371 call _C_LABEL(exception_handler) 372 373 jmp _C_LABEL(switch_to_user) 374 375exception_entry_nested: 376 377 pusha 378 mov %esp, %eax 379 add $(8 * 4), %eax 380 push %eax 381 pushl $1 /* it's a nested exception */ 382 call _C_LABEL(exception_handler) 383 add $8, %esp 384 popa 385 386 /* clear the error code and the exception number */ 387 add $8, %esp 388 /* resume execution at the point of exception */ 389 iret 390 391ENTRY(restore_user_context_sysenter) 392 /* return to userspace using sysexit. 393 * most of the context saving the userspace process is 394 * responsible for, we just have to take care of the right EIP 395 * and ESP restoring here to resume execution, and set EAX and 396 * EBX to the saved status values. 397 */ 398 mov 4(%esp), %ebp /* retrieve proc ptr arg */ 399 movw $USER_DS_SELECTOR, %ax 400 movw %ax, %ds 401 mov PCREG(%ebp), %edx /* sysexit restores EIP using EDX */ 402 mov SPREG(%ebp), %ecx /* sysexit restores ESP using ECX */ 403 mov AXREG(%ebp), %eax /* trap return value */ 404 mov BXREG(%ebp), %ebx /* secondary return value */ 405 406 /* restore PSW */ 407 movl PSWREG(%ebp), %edi /* load desired PSW to EDI */ 408 push %edi 409 popf 410 411 sti /* enable interrupts */ 412 sysexit /* jump to EIP in user */ 413 414ENTRY(restore_user_context_syscall) 415 /* return to userspace using sysret. 416 * the procedure is very similar to sysexit; it requires 417 * manual %esp restoring, new EIP in ECX, does not require 418 * enabling interrupts, and of course sysret instead of sysexit. 419 */ 420 mov 4(%esp), %ebp /* retrieve proc ptr arg */ 421 422 /* restore PSW (before we switch to user stack!) */ 423 movl PSWREG(%ebp), %edi /* load desired PSW to EDI */ 424 push %edi 425 popf 426 427 mov PCREG(%ebp), %ecx /* sysret restores EIP using ECX */ 428 mov SPREG(%ebp), %esp /* restore ESP directly */ 429 mov AXREG(%ebp), %eax /* trap return value */ 430 mov BXREG(%ebp), %ebx /* secondary return value */ 431 432 sysret /* jump to EIP in user */ 433 434ENTRY(restore_user_context_int) 435 mov 4(%esp), %ebp /* will assume P_STACKBASE == 0 */ 436 437 /* reconstruct the stack for iret */ 438 push $USER_DS_SELECTOR /* ss */ 439 movl SPREG(%ebp), %eax 440 push %eax 441 movl PSWREG(%ebp), %eax 442 push %eax 443 push $USER_CS_SELECTOR /* cs */ 444 movl PCREG(%ebp), %eax 445 push %eax 446 447 /* Restore segments as the user should see them. */ 448 movw $USER_DS_SELECTOR, %si 449 movw %si, %ds 450 movw %si, %es 451 movw %si, %fs 452 movw %si, %gs 453 454 /* Same for general-purpose registers. */ 455 RESTORE_GP_REGS(%ebp) 456 457 movl BPREG(%ebp), %ebp 458 459 iret /* continue process */ 460 461/*===========================================================================*/ 462/* exception handlers */ 463/*===========================================================================*/ 464 465#define EXCEPTION_ERR_CODE(vector) \ 466 push $vector ;\ 467 jmp exception_entry 468 469#define EXCEPTION_NO_ERR_CODE(vector) \ 470 pushl $0 ;\ 471 EXCEPTION_ERR_CODE(vector) 472 473LABEL(divide_error) 474 EXCEPTION_NO_ERR_CODE(DIVIDE_VECTOR) 475 476LABEL(single_step_exception) 477 EXCEPTION_NO_ERR_CODE(DEBUG_VECTOR) 478 479LABEL(nmi) 480#ifndef USE_WATCHDOG 481 EXCEPTION_NO_ERR_CODE(NMI_VECTOR) 482#else 483 /* 484 * We have to be very careful as this interrupt can occur anytime. On 485 * the other hand, if it interrupts a user process, we will resume the 486 * same process which makes things a little simpler. We know that we are 487 * already on kernel stack whenever it happened and we can be 488 * conservative and save everything as we don't need to be extremely 489 * efficient as the interrupt is infrequent and some overhead is already 490 * expected. 491 */ 492 493 /* 494 * save the important registers. We don't save %cs and %ss and they are 495 * saved and restored by CPU 496 */ 497 pushw %ds 498 pushw %es 499 pushw %fs 500 pushw %gs 501 pusha 502 503 /* 504 * We cannot be sure about the state of the kernel segment register, 505 * however, we always set %ds and %es to the same as %ss 506 */ 507 mov %ss, %si 508 mov %si, %ds 509 mov %si, %es 510 511 push %esp 512 call _C_LABEL(nmi_watchdog_handler) 513 add $4, %esp 514 515 /* restore all the important registers as they were before the trap */ 516 popa 517 popw %gs 518 popw %fs 519 popw %es 520 popw %ds 521 522 iret 523#endif 524 525LABEL(breakpoint_exception) 526 EXCEPTION_NO_ERR_CODE(BREAKPOINT_VECTOR) 527 528LABEL(overflow) 529 EXCEPTION_NO_ERR_CODE(OVERFLOW_VECTOR) 530 531LABEL(bounds_check) 532 EXCEPTION_NO_ERR_CODE(BOUNDS_VECTOR) 533 534LABEL(inval_opcode) 535 EXCEPTION_NO_ERR_CODE(INVAL_OP_VECTOR) 536 537LABEL(copr_not_available) 538 TEST_INT_IN_KERNEL(4, copr_not_available_in_kernel) 539 cld /* set direction flag to a known value */ 540 SAVE_PROCESS_CTX(0, KTS_INT_HARD) 541 /* stop user process cycles */ 542 push %ebp 543 mov $0, %ebp 544 call _C_LABEL(context_stop) 545 call _C_LABEL(copr_not_available_handler) 546 /* reached upon failure only */ 547 jmp _C_LABEL(switch_to_user) 548 549copr_not_available_in_kernel: 550 pushl $0 551 pushl $COPROC_NOT_VECTOR 552 jmp exception_entry_nested 553 554LABEL(double_fault) 555 EXCEPTION_ERR_CODE(DOUBLE_FAULT_VECTOR) 556 557LABEL(copr_seg_overrun) 558 EXCEPTION_NO_ERR_CODE(COPROC_SEG_VECTOR) 559 560LABEL(inval_tss) 561 EXCEPTION_ERR_CODE(INVAL_TSS_VECTOR) 562 563LABEL(segment_not_present) 564 EXCEPTION_ERR_CODE(SEG_NOT_VECTOR) 565 566LABEL(stack_exception) 567 EXCEPTION_ERR_CODE(STACK_FAULT_VECTOR) 568 569LABEL(general_protection) 570 EXCEPTION_ERR_CODE(PROTECTION_VECTOR) 571 572LABEL(page_fault) 573 EXCEPTION_ERR_CODE(PAGE_FAULT_VECTOR) 574 575LABEL(copr_error) 576 EXCEPTION_NO_ERR_CODE(COPROC_ERR_VECTOR) 577 578LABEL(alignment_check) 579 EXCEPTION_NO_ERR_CODE(ALIGNMENT_CHECK_VECTOR) 580 581LABEL(machine_check) 582 EXCEPTION_NO_ERR_CODE(MACHINE_CHECK_VECTOR) 583 584LABEL(simd_exception) 585 EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR) 586 587/*===========================================================================*/ 588/* reload_cr3 */ 589/*===========================================================================*/ 590/* PUBLIC void reload_cr3(void); */ 591ENTRY(reload_cr3) 592 push %ebp 593 mov %esp, %ebp 594 mov %cr3, %eax 595 mov %eax, %cr3 596 pop %ebp 597 ret 598 599#ifdef CONFIG_SMP 600ENTRY(startup_ap_32) 601 /* 602 * we are in protected mode now, %cs is correct and we need to set the 603 * data descriptors before we can touch anything 604 * 605 * first load the regular, highly mapped idt, gdt 606 */ 607 608 /* 609 * use the boot stack for now. The running CPUs are already using their 610 * own stack, the rest is still waiting to be booted 611 */ 612 movw $KERN_DS_SELECTOR, %ax 613 mov %ax, %ds 614 mov %ax, %ss 615 mov $_C_LABEL(k_boot_stktop) - 4, %esp 616 617 /* load the highly mapped idt, gdt, per-cpu tss */ 618 call _C_LABEL(prot_load_selectors) 619 620 jmp _C_LABEL(smp_ap_boot) 621 hlt 622#endif 623 624/*===========================================================================*/ 625/* data */ 626/*===========================================================================*/ 627 628.data 629.short 0x526F /* this must be the first data entry (magic #) */ 630 631.bss 632k_initial_stack: 633.space K_STACK_SIZE 634LABEL(__k_unpaged_k_initial_stktop) 635 636/* 637 * the kernel stack 638 */ 639k_boot_stack: 640.space K_STACK_SIZE /* kernel stack */ /* FIXME use macro here */ 641LABEL(k_boot_stktop) /* top of kernel stack */ 642 643.balign K_STACK_SIZE 644LABEL(k_stacks_start) 645 646/* two pages for each stack, one for data, other as a sandbox */ 647.space 2 * (K_STACK_SIZE * CONFIG_MAX_CPUS) 648 649LABEL(k_stacks_end) 650 651/* top of kernel stack */ 652