Blame view

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