1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #include <linux/linkage.h> 3 #include <asm/desc_defs.h> 4 #include <asm/segment.h> 5 #include <asm/page_types.h> 6 #include <asm/processor-flags.h> 7 #include <asm/msr-index.h> 8 #include "realmode.h" 9 10 /* 11 * The following code and data reboots the machine by switching to real 12 * mode and jumping to the BIOS reset entry point, as if the CPU has 13 * really been reset. The previous version asked the keyboard 14 * controller to pulse the CPU reset line, which is more thorough, but 15 * doesn't work with at least one type of 486 motherboard. It is easy 16 * to stop this code working; hence the copious comments. 17 * 18 * This code is called with the restart type (0 = BIOS, 1 = APM) in 19 * the primary argument register (%eax for 32 bit, %edi for 64 bit). 20 */ 21 .section ".text32", "ax" 22 .code32 23 SYM_CODE_START(machine_real_restart_asm) 24 25 #ifdef CONFIG_X86_64 26 /* Switch to trampoline GDT as it is guaranteed < 4 GiB */ 27 movl $__KERNEL_DS, %eax 28 movl %eax, %ds 29 lgdtl pa_tr_gdt 30 31 /* Disable paging to drop us out of long mode */ 32 movl %cr0, %eax 33 andl $~X86_CR0_PG, %eax 34 movl %eax, %cr0 35 ljmpl $__KERNEL32_CS, $pa_machine_real_restart_paging_off 36 37 SYM_INNER_LABEL(machine_real_restart_paging_off, SYM_L_GLOBAL) 38 xorl %eax, %eax 39 xorl %edx, %edx 40 movl $MSR_EFER, %ecx 41 wrmsr 42 43 movl %edi, %eax 44 45 #endif /* CONFIG_X86_64 */ 46 47 /* Set up the IDT for real mode. */ 48 lidtl pa_machine_real_restart_idt 49 50 /* 51 * Set up a GDT from which we can load segment descriptors for real 52 * mode. The GDT is not used in real mode; it is just needed here to 53 * prepare the descriptors. 54 */ 55 lgdtl pa_machine_real_restart_gdt 56 57 /* 58 * Load the data segment registers with 16-bit compatible values 59 */ 60 movl $16, %ecx 61 movl %ecx, %ds 62 movl %ecx, %es 63 movl %ecx, %fs 64 movl %ecx, %gs 65 movl %ecx, %ss 66 ljmpw $8, $1f 67 SYM_CODE_END(machine_real_restart_asm) 68 69 /* 70 * This is 16-bit protected mode code to disable paging and the cache, 71 * switch to real mode and jump to the BIOS reset code. 72 * 73 * The instruction that switches to real mode by writing to CR0 must be 74 * followed immediately by a far jump instruction, which set CS to a 75 * valid value for real mode, and flushes the prefetch queue to avoid 76 * running instructions that have already been decoded in protected 77 * mode. 78 * 79 * Clears all the flags except ET, especially PG (paging), PE 80 * (protected-mode enable) and TS (task switch for coprocessor state 81 * save). Flushes the TLB after paging has been disabled. Sets CD and 82 * NW, to disable the cache on a 486, and invalidates the cache. This 83 * is more like the state of a 486 after reset. I don't know if 84 * something else should be done for other chips. 85 * 86 * More could be done here to set up the registers as if a CPU reset had 87 * occurred; hopefully real BIOSs don't assume much. This is not the 88 * actual BIOS entry point, anyway (that is at 0xfffffff0). 89 * 90 * Most of this work is probably excessive, but it is what is tested. 91 */ 92 .text 93 .code16 94 95 .balign 16 96 machine_real_restart_asm16: 97 1: 98 xorl %ecx, %ecx 99 movl %cr0, %edx 100 andl $0x00000011, %edx 101 orl $0x60000000, %edx 102 movl %edx, %cr0 103 movl %ecx, %cr3 104 movl %cr0, %edx 105 testl $0x60000000, %edx /* If no cache bits -> no wbinvd */ 106 jz 2f 107 wbinvd 108 2: 109 andb $0x10, %dl 110 movl %edx, %cr0 111 LJMPW_RM(3f) 112 3: 113 andw %ax, %ax 114 jz bios 115 116 apm: 117 movw $0x1000, %ax 118 movw %ax, %ss 119 movw $0xf000, %sp 120 movw $0x5307, %ax 121 movw $0x0001, %bx 122 movw $0x0003, %cx 123 int $0x15 124 /* This should never return... */ 125 126 bios: 127 ljmpw $0xf000, $0xfff0 128 129 .section ".rodata", "a" 130 131 .balign 16 132 SYM_DATA_START(machine_real_restart_idt) 133 .word 0xffff /* Length - real mode default value */ 134 .long 0 /* Base - real mode default value */ 135 SYM_DATA_END(machine_real_restart_idt) 136 137 .balign 16 138 SYM_DATA_START(machine_real_restart_gdt) 139 /* Self-pointer */ 140 .word 0xffff /* Length - real mode default value */ 141 .long pa_machine_real_restart_gdt 142 .word 0 143 144 /* 145 * 16-bit code segment pointing to real_mode_seg 146 * Selector value 8 147 */ 148 .word 0xffff /* Limit */ 149 .long 0x9b000000 + pa_real_mode_base 150 .word 0 151 152 /* 153 * 16-bit data segment with the selector value 16 = 0x10 and 154 * base value 0x100; since this is consistent with real mode 155 * semantics we don't have to reload the segments once CR0.PE = 0. 156 */ 157 .quad GDT_ENTRY(DESC_DATA16, 0x100, 0xffff) 158 SYM_DATA_END(machine_real_restart_gdt)
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.