Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
| 3 | /* |
Martin Roth | 0949e73 | 2021-10-01 14:28:22 -0600 | [diff] [blame] | 4 | * For dropping from long mode to protected mode. |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 5 | * |
| 6 | * For reference see "AMD64 ArchitectureProgrammer's Manual Volume 2", |
| 7 | * Document 24593-Rev. 3.31-July 2019 Chapter 5.3 |
| 8 | * |
| 9 | * Clobbers: rax, rbx, rcx, rdx |
| 10 | */ |
| 11 | .code64 |
| 12 | |
| 13 | #include <cpu/x86/msr.h> |
| 14 | #include <cpu/x86/cr.h> |
Patrick Rudolph | e3dd57e | 2020-08-15 06:14:57 +0200 | [diff] [blame] | 15 | #if defined(__RAMSTAGE__) |
| 16 | #include <arch/ram_segs.h> |
| 17 | #define CODE_SEG RAM_CODE_SEG |
| 18 | #define DATA_SEG RAM_DATA_SEG |
| 19 | #else |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 20 | #include <arch/rom_segs.h> |
Patrick Rudolph | e3dd57e | 2020-08-15 06:14:57 +0200 | [diff] [blame] | 21 | #define CODE_SEG ROM_CODE_SEG |
| 22 | #define DATA_SEG ROM_DATA_SEG |
| 23 | #endif |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 24 | |
| 25 | drop_longmode: |
Patrick Rudolph | 40dc53a | 2020-11-30 19:33:06 +0100 | [diff] [blame] | 26 | #if !ENV_CACHE_AS_RAM |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 27 | /* Ensure cache is clean. */ |
| 28 | wbinvd |
Patrick Rudolph | 40dc53a | 2020-11-30 19:33:06 +0100 | [diff] [blame] | 29 | #endif |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 30 | /* Set 32-bit code segment and ss */ |
Patrick Rudolph | e3dd57e | 2020-08-15 06:14:57 +0200 | [diff] [blame] | 31 | mov $CODE_SEG, %rcx |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 32 | /* SetCodeSelector32 will drop us to protected mode on return */ |
| 33 | call SetCodeSelector32 |
| 34 | |
| 35 | /* Skip SetCodeSelector32 */ |
| 36 | .code32 |
| 37 | jmp __longmode_compatibility |
| 38 | |
| 39 | .align 8 |
| 40 | .code64 |
| 41 | SetCodeSelector32: |
| 42 | # pop the return address from stack |
| 43 | pop %rbx |
| 44 | |
| 45 | # save rsp because we need to push it after ss |
| 46 | mov %rsp, %rdx |
| 47 | |
| 48 | # use iret to jump to a 32-bit offset in a new code segment |
| 49 | # iret will pop cs:rip, flags, then ss:rsp |
Martin Roth | 0949e73 | 2021-10-01 14:28:22 -0600 | [diff] [blame] | 50 | mov %ss, %ax # need to push ss, but push ss instruction |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 51 | push %rax # not valid in x64 mode, so use ax |
| 52 | push %rdx # the rsp to load |
| 53 | pushfq # push rflags |
| 54 | push %rcx # cx is code segment selector from caller |
| 55 | push %rbx # push the IP for the next instruction |
| 56 | |
| 57 | # the iretq will behave like ret, with the new cs/ss value loaded |
| 58 | iretq |
| 59 | |
| 60 | .align 4 |
| 61 | .code32 |
| 62 | __longmode_compatibility: |
| 63 | /* Running in 32-bit compatibility mode */ |
| 64 | |
| 65 | /* Use flat data segment */ |
Patrick Rudolph | e3dd57e | 2020-08-15 06:14:57 +0200 | [diff] [blame] | 66 | movl $DATA_SEG, %eax |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 67 | movl %eax, %ds |
| 68 | movl %eax, %es |
| 69 | movl %eax, %ss |
| 70 | movl %eax, %fs |
Patrick Rudolph | 03a7952 | 2019-09-29 11:08:33 +0200 | [diff] [blame] | 71 | |
| 72 | /* Disable paging. */ |
| 73 | movl %cr0, %eax |
| 74 | andl $(~CR0_PG), %eax |
| 75 | movl %eax, %cr0 |
| 76 | |
| 77 | /* Disable long mode. */ |
| 78 | movl $(IA32_EFER), %ecx |
| 79 | rdmsr |
| 80 | andl $(~EFER_LME), %eax |
| 81 | wrmsr |
| 82 | |
| 83 | /* Disable PAE. */ |
| 84 | movl %cr4, %eax |
| 85 | andl $(~CR4_PAE), %eax |
| 86 | movl %eax, %cr4 |
| 87 | |
| 88 | /* Clear page table register */ |
| 89 | xor %eax, %eax |
| 90 | movl %eax, %cr3 |
| 91 | |
| 92 | __longmode_exit: |