1#include <linux/linkage.h> 2#include <linux/init.h> 3#include <asm/segment.h> 4#include <asm/page_types.h> 5 6/* 7 * The following code and data reboots the machine by switching to real 8 * mode and jumping to the BIOS reset entry point, as if the CPU has 9 * really been reset. The previous version asked the keyboard 10 * controller to pulse the CPU reset line, which is more thorough, but 11 * doesn't work with at least one type of 486 motherboard. It is easy 12 * to stop this code working; hence the copious comments. 13 * 14 * This code is called with the restart type (0 = BIOS, 1 = APM) in %eax. 15 */ 16 .section ".x86_trampoline","a" 17 .balign 16 18 .code32 19ENTRY(machine_real_restart_asm) 20r_base = . 21 /* Get our own relocated address */ 22 call 1f 231: popl %ebx 24 subl $(1b - r_base), %ebx 25 26 /* Compute the equivalent real-mode segment */ 27 movl %ebx, %ecx 28 shrl $4, %ecx 29 30 /* Patch post-real-mode segment jump */ 31 movw (dispatch_table - r_base)(%ebx,%eax,2),%ax 32 movw %ax, (101f - r_base)(%ebx) 33 movw %cx, (102f - r_base)(%ebx) 34 35 /* Set up the IDT for real mode. */ 36 lidtl (machine_real_restart_idt - r_base)(%ebx) 37 38 /* 39 * Set up a GDT from which we can load segment descriptors for real 40 * mode. The GDT is not used in real mode; it is just needed here to 41 * prepare the descriptors. 42 */ 43 lgdtl (machine_real_restart_gdt - r_base)(%ebx) 44 45 /* 46 * Load the data segment registers with 16-bit compatible values 47 */ 48 movl $16, %ecx 49 movl %ecx, %ds 50 movl %ecx, %es 51 movl %ecx, %fs 52 movl %ecx, %gs 53 movl %ecx, %ss 54 ljmpl $8, $1f - r_base 55 56/* 57 * This is 16-bit protected mode code to disable paging and the cache, 58 * switch to real mode and jump to the BIOS reset code. 59 * 60 * The instruction that switches to real mode by writing to CR0 must be 61 * followed immediately by a far jump instruction, which set CS to a 62 * valid value for real mode, and flushes the prefetch queue to avoid 63 * running instructions that have already been decoded in protected 64 * mode. 65 * 66 * Clears all the flags except ET, especially PG (paging), PE 67 * (protected-mode enable) and TS (task switch for coprocessor state 68 * save). Flushes the TLB after paging has been disabled. Sets CD and 69 * NW, to disable the cache on a 486, and invalidates the cache. This 70 * is more like the state of a 486 after reset. I don't know if 71 * something else should be done for other chips. 72 * 73 * More could be done here to set up the registers as if a CPU reset had 74 * occurred; hopefully real BIOSs don't assume much. This is not the 75 * actual BIOS entry point, anyway (that is at 0xfffffff0). 76 * 77 * Most of this work is probably excessive, but it is what is tested. 78 */ 79 .code16 801: 81 xorl %ecx, %ecx 82 movl %cr0, %eax 83 andl $0x00000011, %eax 84 orl $0x60000000, %eax 85 movl %eax, %cr0 86 movl %ecx, %cr3 87 movl %cr0, %edx 88 andl $0x60000000, %edx /* If no cache bits -> no wbinvd */ 89 jz 2f 90 wbinvd 912: 92 andb $0x10, %al 93 movl %eax, %cr0 94 .byte 0xea /* ljmpw */ 95101: .word 0 /* Offset */ 96102: .word 0 /* Segment */ 97 98bios: 99 ljmpw $0xf000, $0xfff0 100 101apm: 102 movw $0x1000, %ax 103 movw %ax, %ss 104 movw $0xf000, %sp 105 movw $0x5307, %ax 106 movw $0x0001, %bx 107 movw $0x0003, %cx 108 int $0x15 109 110END(machine_real_restart_asm) 111 112 .balign 16 113 /* These must match <asm/reboot.h */ 114dispatch_table: 115 .word bios - r_base 116 .word apm - r_base 117END(dispatch_table) 118 119 .balign 16 120machine_real_restart_idt: 121 .word 0xffff /* Length - real mode default value */ 122 .long 0 /* Base - real mode default value */ 123END(machine_real_restart_idt) 124 125 .balign 16 126ENTRY(machine_real_restart_gdt) 127 .quad 0 /* Self-pointer, filled in by PM code */ 128 .quad 0 /* 16-bit code segment, filled in by PM code */ 129 /* 130 * 16-bit data segment with the selector value 16 = 0x10 and 131 * base value 0x100; since this is consistent with real mode 132 * semantics we don't have to reload the segments once CR0.PE = 0. 133 */ 134 .quad GDT_ENTRY(0x0093, 0x100, 0xffff) 135END(machine_real_restart_gdt) 136