/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #if defined(__lint) int silence_lint = 0; #else #include #include /* * Do a call into BIOS. This goes down to 16 bit real mode and back again. */ /* * instruction prefix to change operand size in instruction */ #define DATASZ .byte 0x66; #if defined(__amd64) #define MOVCR(x, y) movq x,%rax; movq %rax, y #define LOAD_XAX(sym) leaq sym, %rax #elif defined(__i386) #define MOVCR(x, y) movl x,%eax; movl %eax, y #define LOAD_XAX(sym) leal sym, %eax #endif .globl _start _start: #if defined(__i386) /* * Save caller registers */ movl %ebp, save_ebp movl %esp, save_esp movl %ebx, save_ebx movl %esi, save_esi movl %edi, save_edi /* get registers argument into esi */ movl 8(%esp), %esi /* put interrupt number in %bl */ movl 4(%esp), %ebx /* Switch to a low memory stack */ movl $_start, %esp /* allocate space for args on stack */ subl $18, %esp movl %esp, %edi #elif defined(__amd64) /* * Save caller registers */ movq %rbp, save_rbp movq %rsp, save_rsp movq %rbx, save_rbx movq %rsi, save_rsi movq %r12, save_r12 movq %r13, save_r13 movq %r14, save_r14 movq %r15, save_r15 /* Switch to a low memory stack */ movq $_start, %rsp /* put interrupt number in %bl */ movq %rdi, %rbx /* allocate space for args on stack */ subq $18, %rsp movq %rsp, %rdi #endif /* copy args from high memory to stack in low memory */ cld movl $18, %ecx rep movsb /* * Save system registers */ sidt save_idt sgdt save_gdt str save_tr movw %cs, save_cs movw %ds, save_ds movw %ss, save_ss movw %es, save_es movw %fs, save_fs movw %gs, save_gs MOVCR( %cr4, save_cr4) MOVCR( %cr3, save_cr3) MOVCR( %cr0, save_cr0) #if defined(__amd64) /* * save/clear the extension parts of the fs/gs base registers and cr8 */ movl $MSR_AMD_FSBASE, %ecx rdmsr movl %eax, save_fsbase movl %edx, save_fsbase + 4 xorl %eax, %eax xorl %edx, %edx wrmsr movl $MSR_AMD_GSBASE, %ecx rdmsr movl %eax, save_gsbase movl %edx, save_gsbase + 4 xorl %eax, %eax xorl %edx, %edx wrmsr movl $MSR_AMD_KGSBASE, %ecx rdmsr movl %eax, save_kgsbase movl %edx, save_kgsbase + 4 xorl %eax, %eax xorl %edx, %edx wrmsr movq %cr8, %rax movq %rax, save_cr8 #endif /* * set offsets in 16 bit ljmp instructions below */ LOAD_XAX(enter_real) movw %ax, enter_real_ljmp LOAD_XAX(enter_protected) movw %ax, enter_protected_ljmp LOAD_XAX(gdt_info) movw %ax, gdt_info_load /* * insert BIOS interrupt number into later instruction */ movb %bl, int_instr+1 jmp 1f 1: /* * zero out all the registers to make sure they're 16 bit clean */ #if defined(__amd64) xorq %r8, %r8 xorq %r9, %r9 xorq %r10, %r10 xorq %r11, %r11 xorq %r12, %r12 xorq %r13, %r13 xorq %r14, %r14 xorq %r15, %r15 #endif xorl %eax, %eax xorl %ebx, %ebx xorl %ecx, %ecx xorl %edx, %edx xorl %ebp, %ebp xorl %esi, %esi xorl %edi, %edi /* * Load our own GDT/IDT */ lgdt gdt_info lidt idt_info #if defined(__amd64) /* * Shut down 64 bit mode. First get into compatiblity mode. */ movq %rsp, %rax pushq $B32DATA_SEL pushq %rax pushf pushq $B32CODE_SEL pushq $1f iretq 1: .code32 /* * disable long mode by: * - shutting down paging (bit 31 of cr0) * - flushing the TLB * - disabling LME (long made enable) in EFER (extended feature reg) */ movl %cr0, %eax btcl $31, %eax /* disable paging */ movl %eax, %cr0 ljmp $B32CODE_SEL, $1f 1: xorl %eax, %eax movl %eax, %cr3 /* flushes TLB */ movl $MSR_AMD_EFER, %ecx /* Extended Feature Enable */ rdmsr btcl $8, %eax /* bit 8 Long Mode Enable bit */ wrmsr #endif /* * ok.. now enter 16 bit mode, so we can shut down protected mode * * We'll have to act like we're still in a 32 bit section. * So the code from this point has DATASZ in front of it to get 32 bit * operands. If DATASZ is missing the operands will be 16 bit. * * Now shut down paging and protected (ie. segmentation) modes. */ ljmp $B16CODE_SEL, $enter_16_bit enter_16_bit: /* * Make sure hidden parts of segment registers are 16 bit clean */ DATASZ movl $B16DATA_SEL, %eax movw %ax, %ss movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs DATASZ movl $0x0, %eax /* put us in real mode */ DATASZ movl %eax, %cr0 .byte 0xea /* ljmp */ enter_real_ljmp: .value 0 /* addr (16 bit) */ .value 0x0 /* value for %cs */ enter_real: /* * zero out the remaining segment registers */ DATASZ xorl %eax, %eax movw %ax, %ss movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs /* * load the arguments to the BIOS call from the stack */ popl %eax /* really executes a 16 bit pop */ popl %ebx popl %ecx popl %edx popl %esi popl %edi popl %ebp pop %es pop %ds /* * do the actual BIOS call */ sti int_instr: int $0x10 /* this int number is overwritten */ cli /* ensure interrupts remain disabled */ /* * save results of the BIOS call */ pushf push %ds push %es pushl %ebp /* still executes as 16 bit */ pushl %edi pushl %esi pushl %edx pushl %ecx pushl %ebx pushl %eax /* * Restore protected mode and 32 bit execution */ push $0 /* make sure %ds is zero before lgdt */ pop %ds .byte 0x0f, 0x01, 0x16 /* lgdt */ gdt_info_load: .value 0 /* temp GDT in currently addressible mem */ DATASZ movl $0x1, %eax DATASZ movl %eax, %cr0 .byte 0xea /* ljmp */ enter_protected_ljmp: .value 0 /* addr (still in 16 bit) */ .value B32CODE_SEL /* %cs value */ enter_protected: /* * We are now back in a 32 bit code section, fix data/stack segments */ .code32 movw $B32DATA_SEL, %ax movw %ax, %ds movw %ax, %ss /* * Re-enable paging. Note we only use 32 bit mov's to restore these * control registers. That's OK as the upper 32 bits are always zero. */ movl save_cr4, %eax movl %eax, %cr4 movl save_cr3, %eax movl %eax, %cr3 #if defined(__amd64) /* * re-enable long mode */ movl $MSR_AMD_EFER, %ecx rdmsr btsl $8, %eax wrmsr #endif movl save_cr0, %eax movl %eax, %cr0 jmp enter_paging enter_paging: #if defined(__amd64) /* * transition back to 64 bit mode */ pushl $B64CODE_SEL pushl $longmode lret longmode: .code64 #endif /* * restore caller frame pointer and segment registers */ lgdt save_gdt lidt save_idt /* * Before loading the task register we need to reset the busy bit * in its corresponding GDT selector. The busy bit is the 2nd bit in * the 5th byte of the selector. */ #if defined(__i386) movzwl save_tr, %eax addl save_gdt+2, %eax btcl $1, 5(%eax) #elif defined(__amd64) movzwq save_tr, %rax addq save_gdt+2, %rax btcl $1, 5(%rax) #endif ltr save_tr movw save_ds, %ds movw save_ss, %ss movw save_es, %es movw save_fs, %fs movw save_gs, %gs #if defined(__i386) pushl save_cs pushl $.newcs lret #elif defined(__amd64) pushq save_cs pushq $.newcs lretq #endif .newcs: #if defined(__amd64) /* * restore the hidden kernel segment base register values */ movl save_fsbase, %eax movl save_fsbase + 4, %edx movl $MSR_AMD_FSBASE, %ecx wrmsr movl save_gsbase, %eax movl save_gsbase + 4, %edx movl $MSR_AMD_GSBASE, %ecx wrmsr movl save_kgsbase, %eax movl save_kgsbase + 4, %edx movl $MSR_AMD_KGSBASE, %ecx wrmsr movq save_cr8, %rax cmpq $0, %rax je 1f movq %rax, %cr8 1: #endif /* * copy results to caller's location, then restore remaining registers */ #if defined(__i386) movl save_esp, %edi movl 8(%edi), %edi movl %esp, %esi movl $18, %ecx rep movsb movw 18(%esp), %ax andl $0xffff, %eax movl save_ebx, %ebx movl save_esi, %esi movl save_edi, %edi movl save_esp, %esp movl save_ebp, %ebp movl save_esp, %esp ret #elif defined(__amd64) movq save_rsi, %rdi movq %rsp, %rsi movq $18, %rcx rep movsb movw 18(%rsp), %ax andq $0xffff, %rax movq save_r12, %r12 movq save_r13, %r13 movq save_r14, %r14 movq save_r15, %r15 movq save_rbx, %rbx movq save_rbp, %rbp movq save_rsp, %rsp ret #endif /* * Caller's registers to restore */ .align 4 save_esi: .long 0 save_edi: .long 0 save_ebx: .long 0 save_ebp: .long 0 save_esp: .long 0 .align 8 #if defined(__amd64) save_rsi: .quad 0 save_rbx: .quad 0 save_rbp: .quad 0 save_rsp: .quad 0 save_r12: .quad 0 save_r13: .quad 0 save_r14: .quad 0 save_r15: .quad 0 save_kgsbase: .quad 0 save_gsbase: .quad 0 save_fsbase: .quad 0 save_cr8: .quad 0 #endif /* __amd64 */ save_idt: .quad 0 .quad 0 save_gdt: .quad 0 .quad 0 save_cr0: .quad 0 save_cr3: .quad 0 save_cr4: .quad 0 save_cs: .quad 0 save_ss: .value 0 save_ds: .value 0 save_es: .value 0 save_fs: .value 0 save_gs: .value 0 save_tr: .value 0 idt_info: .value 0x3ff .quad 0 /* * We need to trampoline thru a gdt we have in low memory. */ #include "../boot/boot_gdt.s" #endif /* __lint */