📄 locore.s
字号:
/*- * Copyright (c) 2005, Kohsuke Ohtani * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *//* * locore.S - low level platform support */#define __ASSEMBLY__ #include <config.h>#include <param.h>#include <platform.h>#include "cpu.h"#define ENTRY(x) .global x; .align 4,0x90; x##:/* * Macro to save/restore registers * * This macro builds the trap frame by pushing registers. * If you change the push order of these macro, you must change the * trap frame structure in arch.h. In addition, the system call stub * will depend on this register format. */#define SAVE_ALL \ cld; \ pushl %es; \ pushl %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ pushl %esi; \ pushl %edx; \ pushl %ecx; \ pushl %ebx;#define RESTORE_ALL \ popl %ebx; \ popl %ecx; \ popl %edx; \ popl %esi; \ popl %edi; \ popl %ebp; \ popl %eax; \ popl %ds; \ popl %es;#define SETUP_SEG \ movl $KERNEL_DS, %edx; \ movl %edx, %ds; \ movl %edx, %es; .section ".rodata","a" ENTRY(boot_info) .long PAGE_OFFSET + BOOT_INFO .section ".text","ax"/* * Kernel start point * * The kernel assumes that the following state is already set by * the kernel loader. * - CPU is in protected mode * - Segment registers are set as 32-bit flat segment * - A20 line is enabled for 32-bit addressing * - Paging is turned off * - The boot information is loaded to address 1000h-1fffh *//* * Note: The linker will generate an address for kernel_start as 0x80010000. * But, the loader will load the kernel to 0x10000 (physical address). * So, the linear address pointer can not be used until paging is enabled. */ENTRY(kernel_start) cli /* Disable interrupt */ cld#ifdef CONFIG_MMU /* * Init page table * The physical address 0-4M is mapped to virtual address 2G. */ movl $KERNEL_PGD, %edi /* Setup kernel page directory */ xorl %eax, %eax /* Invalidate all address */ movl $0x1000, %ecx rep stosb movl $(KERNEL_PGD+0x800), %edi movl $(BOOT_PTE0+0x07), (%edi) movl $1024, %ecx /* Fill boot page table entry */ movl $(BOOT_PTE0), %edi movl $0007, %eaxfill_pte0: stosl add $0x1000, %eax loop fill_pte0 /* * Enable paging * The physical address 0-4M is mapped to virtial address 0-4M * temporary. This is needed to enable paging. */ movl $KERNEL_PGD, %edi /* Map address 0-4M */ movl $(BOOT_PTE0+0x07), (%edi) movl $KERNEL_PGD, %eax /* Set page directory pointer */ movl %eax, %cr3 movl %cr0, %eax /* Enable paging bit */ orl $0x80000000, %eax movl %eax, %cr0 jmp pipeline_flush /* Flush processor pipeline */pipeline_flush: movl $cs_reset, %eax jmp *%eaxcs_reset: movl $(KERNEL_BASE+KERNEL_PGD), %edi /* Unmap 0-4M */ movl $0, (%edi) movl %cr3, %eax movl %eax, %cr3#endif /* CONFIG_MMU */ /* * Clear kernel BSS */ xorl %eax, %eax movl $__bss, %edi movl $__end, %ecx subl %edi, %ecx rep stosb /* * Setup boot stack */ movl $(PAGE_OFFSET+BOOT_STACK), %edi movl $(BOOT_STACK_SIZE), %ecx rep stosb movl $(PAGE_OFFSET+BOOT_STACK+BOOT_STACK_SIZE), %esp movl %esp, %ebp /* * Initialize basic h/w */ call diag_init call cpu_init#ifdef CONFIG_GDB call gdb_init#endif call interrupt_init /* * Call kernel main routine */ call kernel_main /* NOTREACHED */ cli hlt/* * Common entry for all interrupts * Setup interrupt stack for outermost interrupt. * The code should be written to prevent the stack overflow * by continuous interrupt as much as it can. */ENTRY(interrupt_common) SAVE_ALL SETUP_SEG incl irq_nesting cmpl $1, irq_nesting /* Outermost interrupt ? */ jne nested_irq mov %esp, %ebp /* Save current stack */ movl $(PAGE_OFFSET+INT_STACK+0x1000), %esp /* Switch stack */ call sched_lock /* Lock scheduler */ pushl %ebp /* Push trap frame */ call interrupt_handler /* Process interrupt */ movl %ebp, %esp /* Restore original stack */ decl irq_nesting call sched_unlock /* Try to preempt */ testl $3, 0x30(%esp) /* Return to kernel mode ? */ jz interrupt_ret call exception_deliver /* Check exception */interrupt_ret: RESTORE_ALL addl $8, %esp iretnested_irq: push %esp /* Push trap frame */ call interrupt_handler /* Process interrupt */ addl $4, %esp decl irq_nesting jmp interrupt_ret/* * Macro to build interrupt entry */#define INTR_ENTRY(irq) \ENTRY(intr_##irq) \ pushl $0; \ pushl $(irq); \ jmp interrupt_commonINTR_ENTRY(0)INTR_ENTRY(1)INTR_ENTRY(2)INTR_ENTRY(3)INTR_ENTRY(4)INTR_ENTRY(5)INTR_ENTRY(6)INTR_ENTRY(7)INTR_ENTRY(8)INTR_ENTRY(9)INTR_ENTRY(10)INTR_ENTRY(11)INTR_ENTRY(12)INTR_ENTRY(13)INTR_ENTRY(14)INTR_ENTRY(15)/* * Common entry for all traps * New thread will start from trap_ret. */ENTRY(trap_common) SAVE_ALL SETUP_SEG pushl %esp call trap_handler addl $4, %esptrap_ret: RESTORE_ALL addl $8, %esp iret/* * Default trap entry */ENTRY(trap_default) pushl $0 pushl $INVALID_INT jmp trap_common/* * Macro to build trap entry * Some trap will push the error code into stack. */#define TRAP_ENTRY(id) \ENTRY(trap_##id) \ pushl $0; \ pushl $(id); \ jmp trap_common;#define TRAP_ERR_ENTRY(id) \ENTRY(trap_##id) \ pushl $(id); \ jmp trap_common;TRAP_ENTRY ( 0) /* Divide error */TRAP_ENTRY ( 1) /* Debug trap */TRAP_ENTRY ( 2) /* NMI */TRAP_ENTRY ( 3) /* Breakpoint */TRAP_ENTRY ( 4) /* Overflow */TRAP_ENTRY ( 5) /* Bounds check */TRAP_ENTRY ( 6) /* Invalid opecode */TRAP_ENTRY ( 7) /* Device not available */TRAP_ERR_ENTRY( 8) /* Double fault */TRAP_ERR_ENTRY( 9) /* Coprocessor overrun */TRAP_ERR_ENTRY(10) /* Invalid TSS */TRAP_ERR_ENTRY(11) /* Segment not present */TRAP_ERR_ENTRY(12) /* Stack bounds */TRAP_ERR_ENTRY(13) /* General Protection */TRAP_ERR_ENTRY(14) /* Page fault */TRAP_ENTRY (15) /* (reserved) */TRAP_ENTRY (16) /* Coprocessor error */TRAP_ERR_ENTRY(17) /* Alignment check */TRAP_ERR_ENTRY(18) /* Cache flush denied *//* * System call entry */ .global syscall_retENTRY(syscall_entry) pushl $0 /* Dummy for error code */ pushl $SYSCALL_INT /* Trap number */ SAVE_ALL SETUP_SEG cmpl nr_syscalls, %eax jae bad_syscall call *syscall_table(,%eax,4) /* Call function */ movl %eax, 0x18(%esp) /* Set return value to eax */ call exception_deliver /* Check exception */syscall_ret: RESTORE_ALL addl $8, %esp /* Discard err/trap no */ iretbad_syscall: movl $22, 0x18(%esp) /* Set EINVAL error to eax */ jmp syscall_ret /* * Switch register context. * Interrupts must be disabled by caller. * * syntax - void __context_switch(kern_regs *prev, kern_regs *next) * * Note: GCC assumes ebp,edi,esi registers are not changed in each routine. */ENTRY(__context_switch) movl 4(%esp), %ebx /* Point ebx to previous registers */ movl (%esp), %eax /* Get return address */ movl %eax, 0(%ebx) /* Save it as eip */ movl %edi, 4(%ebx) /* Save edi */ movl %esi, 8(%ebx) /* Save esi */ movl %ebp, 12(%ebx) /* Save ebp */ movl %esp, 16(%ebx) /* Save esp */ movl 8(%esp), %ebx /* Point ebx to next registers */ movl 4(%ebx), %edi /* Restore edi */ movl 8(%ebx), %esi /* Restore esi */ movl 12(%ebx), %ebp /* Restore ebp */ movl 16(%ebx), %esp /* Restore esp */ movl 0(%ebx), %eax /* Get eip */ movl %eax, (%esp) /* Restore it as return address */ ret/* * Copy data from user to kernel space. * Returns 0 on success, or EFAULT on page fault. * * syntax - int umem_copyin(void *uaddr, void *kaddr, size_t len) */ .global known_fault1ENTRY(umem_copyin) pushl %esi pushl %edi pushl $14 /* Set EFAULT as default return */ movl 16(%esp), %esi movl 20(%esp), %edi movl 24(%esp), %ecx movl %esi, %edx /* Check if valid user address */ addl %ecx, %edx jc umem_fault cmpl $USER_MAX, %edx jae umem_fault cldknown_fault1: /* May be fault here */ rep movsb popl %eax xorl %eax, %eax /* Set no error */ popl %edi popl %esi ret/* * Copy data to user from kernel space. * Returns 0 on success, or EFAULT on page fault. * * syntax - int umem_copyout(void *kaddr, void *uaddr, size_t len) */ .global known_fault2ENTRY(umem_copyout) pushl %esi pushl %edi pushl $14 /* Set EFAULT as default return */ movl 16(%esp), %esi movl 20(%esp), %edi movl 24(%esp), %ecx movl %edi, %edx addl %ecx, %edx jc umem_fault cmpl $USER_MAX, %edx jae umem_fault cldknown_fault2: rep movsb popl %eax xorl %eax, %eax /* Set no error */ popl %edi popl %esi ret/* * umem_strnlen - Get length of string in user space * Returns 0 on success, or EFAULT on page fault. * * syntax - int umem_strnlen(void *uaddr, size_t maxlen, size_t *len) * * Note: The returned length value does NOT include the NULL terminator. */ .global known_fault3ENTRY(umem_strnlen) pushl %esi pushl %edi pushl $14 /* Set EFAULT as default return */ movl 16(%esp), %edi movl 20(%esp), %ecx movl 24(%esp), %esi movl %edi, %edx cmpl $USER_MAX, %edx jae umem_fault movl %ecx, %edx cld xor %eax, %eaxknown_fault3: repne scasb subl %ecx, %edx decl %edx /* Adjust for terminator */ movl %edx, (%esi) popl %eax xorl %eax, %eax /* Set no error */ popl %edi popl %esi ret /* * Fault entry for user access */ENTRY(umem_fault) popl %eax /* Get return value from stack */ popl %edi popl %esi ret /* * Reset system * Use KBD reset and triple fault */ENTRY(system_reset) cli mov $(0xfe), %al /* Try to do keyboard reset */ outb %al, $(0x64) mov $(0x100000), %ecxwait_reset: loop wait_reset movl $null_idt, %eax /* Reset by triple fault */ lidt (%eax) int $3 hlt .align 4null_idt: .word 0 .long 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -