// arch/x86/entry.S - x86 entry points (booting and traps) // // This software is copyright (c) 2006 Scott Wood. // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors or contributors be held liable for any damages // arising from the use of this software. // // Permission is hereby granted to everyone, free of charge, to use, copy, // modify, prepare derivative works of, publish, distribute, perform, // sublicense, and/or sell copies of the Software, provided that the above // copyright notice and disclaimer of warranty be included in all copies or // substantial portions of this software. #include #define ROSHARED_PTR(x) (x - roshared + 0x7fff0000) .org 0 .code32 .global _start .align 4 multiboot_hdr: .long 0x1badb002 // Multi-boot magic // Multi-boot flags: // bit 0: 4KiB-align all boot modules // bit 1: must include memory size and map // bit 2: must include video mode table // bit 16: load addresses in this header are valid // and should be used instead of the ELF header .long 0x00010003 // checksum: -(magic + flags), update if flags change .long 0xe4514ffb .long multiboot_hdr - KERNEL_START // header_addr .long 0x00200000 // load_addr .long 0 // load_end_addr: load whole file .long bss_end - KERNEL_START // bss_end_addr .long _start - KERNEL_START // entry_addr _start: cld lgdt x86_gdtr_phys + 6 - KERNEL_START ljmp $0x10, $using_our_gdt - KERNEL_START using_our_gdt: movw $0x08, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss movl %ebx, %esi // Save the multiboot pointer somewhere // it won't be clobbered by CPUID // This gives 512 bytes to Threads::Thread; if it gets larger // this needs to be updated (as well as the code at high_vaddr). movl $x86_init_stack + 3584 - KERNEL_START, %esp // Test for CPUID pushfl popl %eax movl %eax, %ebx xorl $0x00200000, %eax pushl %eax popfl pushfl popl %eax cmpl %eax, %ebx je no_pentium // Test for Page Size Extensions xorl %eax, %eax cpuid cmpl $1, %eax jb no_pentium movl $1, %eax cpuid btl $3, %edx jnc no_pentium // enable PSE movl %cr4, %eax btsl $4, %eax movl %eax, %cr4 // Load the initial page table movl $x86_init_ptbl_l2 - KERNEL_START, %eax movl %eax, %cr3 // enable paging, kernel write-protect, // and internal floating point error handling movl %cr0, %eax orl $0x80010020, %eax movl %eax, %cr0 // Set up high page tables for 0x80000000 mapping, movl $0x87, x86_init_ptbl_l2 + 0x800 - KERNEL_START ljmp $0x10, $paging_on - KERNEL_START paging_on: lgdt x86_gdtr + 6 movl %esi, x86_boot_info_phys movl $high_vaddr, %eax jmp *%eax high_vaddr: movl $x86_init_stack + 3584, %esp jmp start_kernel no_multiboot: movl $no_multiboot_str - KERNEL_START, %esi jmp do_print no_pentium: movl $no_pentium_str - KERNEL_START, %esi do_print: movl $0xb8000, %edi movb (%esi), %al xorl %ecx, %ecx 1: movb %al, (%edi, %ecx, 2) movb $14, 1(%edi, %ecx, 2) // It's not at the cursor, so use // yellow to make it stand out. incl %ecx movb (%esi, %ecx), %al testb %al, %al jnz 1b 2: jmp 2b no_pentium_str: .string "This kernel requires a Pentium-compatible CPU. Either CPUID or PSE is missing." no_multiboot_str: .string "Unrecognized bootloader; a multiboot-compliant loader is required." .macro enterkernel, reg pushl %es pushl %ds movl $8, \reg cld movl \reg, %ds movl \reg, %es .endm .macro exitkernel popl %ds popl %es .endm .macro pushvolatilesnoeax pushl %ecx pushl %edx enterkernel %ecx .endm .macro pushvolatiles pushl %eax pushvolatilesnoeax .endm .macro popvolatiles exitkernel popl %edx popl %ecx popl %eax .endm // Non-volatile registers must be pushed if the handler will // need to access all of the interrupted code's registers, // such as when producing an error dump. Does not include // edi, as that is usually swapped with the error code. .macro pushall pushl %esi pushl %ebp pushl %ebx pushl %edx pushl %ecx pushl %eax enterkernel %esi .endm .macro pushallnoerr pushl %edi pushall .endm .macro popall exitkernel popl %eax popl %ecx popl %edx popl %ebx popl %ebp popl %esi popl %edi .endm .global x86_diverr x86_diverr: pushallnoerr pushl %esp call x86_do_diverr addl $4, %esp popall iret .global x86_debug x86_debug: pushallnoerr pushl %esp call x86_do_debug addl $4, %esp popall iret .global x86_breakpoint x86_breakpoint: pushallnoerr pushl %esp call x86_do_breakpoint addl $4, %esp popall iret .global x86_invalid_insn x86_invalid_insn: pushallnoerr pushl %esp call x86_do_invalid_insn addl $4, %esp popall iret .global x86_gpf x86_gpf: xchgl %edi, (%esp) // get error code pushall movl %esp, %eax pushl %edi pushl %eax call x86_do_gpf addl $8, %esp popall iret .global x86_page_fault x86_page_fault: xchgl %edi, (%esp) // get error code pushall movl %esp, %ecx movl %cr2, %eax pushl %edi pushl %eax pushl %ecx call x86_do_page_fault addl $12, %esp popall iret .global x86_int98_entry x86_int98_entry: pushl $0 pushl %eax enterkernel %ecx pushl %esp call x86_invoke_method addl $4, %esp exitkernel popl %eax xorl %ecx, %ecx popl %edx xorl %ebx, %ebx xorl %ebp, %ebp xorl %esi, %esi xorl %edi, %edi iret .global x86_int99_entry x86_int99_entry: pushl %edx pushl %eax enterkernel %edx pushl %esp call x86_return_from_method addl $4, %esp exitkernel popl %eax xorl %ecx, %ecx popl %edx xorl %ebx, %ebx xorl %ebp, %ebp xorl %esi, %esi xorl %edi, %edi movl $ROSHARED_PTR(x86_shared_int98_ret), (%esp) iret .global x86_irq x86_irq: pushvolatilesnoeax pushl %eax call x86_do_irq addl $4, %esp movl need_resched, %eax testl %eax, %eax jnz x86_reschedule x86_ret_irq: popvolatiles iret x86_reschedule: // The cli is to make sure interrupts don't get re-enabled in // this thread context between the schedule and the ret from // IRQ. cli call schedule jmp x86_ret_irq .section ".irqs","x" .global x86_irqs x86_irqs: .text .macro irq from,to 1: pushl %eax movl $\from, %eax jmp x86_irq .section ".irqs","x" .long 1b .text .if \to-\from irq (\from+1),\to .endif .endm .macro irq16 from,to irq \from,(\from+15) .if \to-\from irq16 (\from+16),\to .endif .endm irq16 0,240 .global x86_new_thread x86_new_thread: xorl %ebx, %ebx xorl %ecx, %ecx xorl %edx, %edx xorl %ebp, %ebp xorl %esi, %esi xorl %edi, %edi call sched_new_thread popl %eax call *%eax call exit_thread ud2a .global arch_new_user_thread arch_new_user_thread: movl 4(%esp), %eax // arg1: user entry movl 8(%esp), %ecx // arg2: user stack pushl $0x23 // SS pushl %ecx // ESP pushl $0x202 // EFLAGS pushl $0x2b // CS pushl %eax // EIP movl $0x23, %ebx movl %ebx, %ds movl %ebx, %es xorl %eax, %eax xorl %ecx, %ecx iret .section ".roshared","ax" roshared: x86_shared_int98: // FIXME: This must be unpreemptible by traps pushl %gs pushl %fs pushl %ebp pushl %edi pushl %esi pushl %ebx int $0x98 x86_shared_int98_ret: popl %ebx popl %esi popl %edi popl %ebp popl %fs popl %gs ret . = roshared + 0x80 x86_shared_int99: int $0x99 ret .global test_user_ptr test_user_ptr: .long ROSHARED_PTR(test_user) test_pib: .long 0 .long 0 .long ROSHARED_PTR(test_objlist) .long 1 .long 0 test_objlist: .long 0 .space 128 test_user: movl $ROSHARED_PTR(test_pib), %eax movl $0x7fff0000, %ecx call *%ecx 1: jmp 1b .section ".rwshared","a" .align 4096 // Userspace must set this to an appropriate entry point // prior to registering objects with the kernel. It should // not be modified while an object is registered (the kernel // won't mind, but there's no mechanism to wait until no more // upcalls to the old entry point are in progress). .global x86_upcall_entry x86_upcall_entry: .long 0