1 | ;------------------------------------------------------------------------------
|
---|
2 | ; @file
|
---|
3 | ; Pen any hot-added CPU in a 16-bit, real mode HLT loop, after it leaves SMM by
|
---|
4 | ; executing the RSM instruction.
|
---|
5 | ;
|
---|
6 | ; Copyright (c) 2020, Red Hat, Inc.
|
---|
7 | ;
|
---|
8 | ; SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
9 | ;
|
---|
10 | ; The routine implemented here is stored into normal RAM, under 1MB, at the
|
---|
11 | ; beginning of a page that is allocated as EfiReservedMemoryType. On any
|
---|
12 | ; hot-added CPU, it is executed after *at least* the first RSM (i.e., after
|
---|
13 | ; SMBASE relocation).
|
---|
14 | ;
|
---|
15 | ; The first execution of this code occurs as follows:
|
---|
16 | ;
|
---|
17 | ; - The hot-added CPU is in RESET state.
|
---|
18 | ;
|
---|
19 | ; - The ACPI CPU hotplug event handler triggers a broadcast SMI, from the OS.
|
---|
20 | ;
|
---|
21 | ; - Existent CPUs (BSP and APs) enter SMM.
|
---|
22 | ;
|
---|
23 | ; - The hot-added CPU remains in RESET state, but an SMI is pending for it now.
|
---|
24 | ; (See "SYSTEM MANAGEMENT INTERRUPT (SMI)" in the Intel SDM.)
|
---|
25 | ;
|
---|
26 | ; - In SMM, pre-existent CPUs that are not elected SMM Monarch, keep themselves
|
---|
27 | ; busy with their wait loops.
|
---|
28 | ;
|
---|
29 | ; - From the root MMI handler, the SMM Monarch:
|
---|
30 | ;
|
---|
31 | ; - places this routine in the reserved page,
|
---|
32 | ;
|
---|
33 | ; - clears the "about to leave SMM" byte in SMRAM,
|
---|
34 | ;
|
---|
35 | ; - clears the last byte of the reserved page,
|
---|
36 | ;
|
---|
37 | ; - sends an INIT-SIPI-SIPI sequence to the hot-added CPU,
|
---|
38 | ;
|
---|
39 | ; - un-gates the default SMI handler by APIC ID.
|
---|
40 | ;
|
---|
41 | ; - The startup vector in the SIPI that is sent by the SMM Monarch points to
|
---|
42 | ; this code; i.e., to the reserved page. (Example: 0x9_F000.)
|
---|
43 | ;
|
---|
44 | ; - The SMM Monarch starts polling the "about to leave SMM" byte in SMRAM.
|
---|
45 | ;
|
---|
46 | ; - The hot-added CPU boots, and immediately enters SMM due to the pending SMI.
|
---|
47 | ; It starts executing the default SMI handler.
|
---|
48 | ;
|
---|
49 | ; - Importantly, the SMRAM Save State Map captures the following information,
|
---|
50 | ; when the hot-added CPU enters SMM:
|
---|
51 | ;
|
---|
52 | ; - CS selector: assumes the 16 most significant bits of the 20-bit (i.e.,
|
---|
53 | ; below 1MB) startup vector from the SIPI. (Example: 0x9F00.)
|
---|
54 | ;
|
---|
55 | ; - CS attributes: Accessed, Readable, User (S=1), CodeSegment (bit#11),
|
---|
56 | ; Present.
|
---|
57 | ;
|
---|
58 | ; - CS limit: 0xFFFF.
|
---|
59 | ;
|
---|
60 | ; - CS base: the CS selector value shifted left by 4 bits. That is, the CS
|
---|
61 | ; base equals the SIPI startup vector. (Example: 0x9_F000.)
|
---|
62 | ;
|
---|
63 | ; - IP: the least significant 4 bits from the SIPI startup vector. Because
|
---|
64 | ; the routine is page-aligned, these bits are zero (hence IP is zero).
|
---|
65 | ;
|
---|
66 | ; - ES, SS, DS, FS, GS selectors: 0.
|
---|
67 | ;
|
---|
68 | ; - ES, SS, DS, FS, GS attributes: same as the CS attributes, minus
|
---|
69 | ; CodeSegment (bit#11).
|
---|
70 | ;
|
---|
71 | ; - ES, SS, DS, FS, GS limits: 0xFFFF.
|
---|
72 | ;
|
---|
73 | ; - ES, SS, DS, FS, GS bases: 0.
|
---|
74 | ;
|
---|
75 | ; - The hot-added CPU sets its new SMBASE value in the SMRAM Save State Map.
|
---|
76 | ;
|
---|
77 | ; - The hot-added CPU sets the "about to leave SMM" byte in SMRAM, then
|
---|
78 | ; executes the RSM instruction immediately after, leaving SMM.
|
---|
79 | ;
|
---|
80 | ; - The SMM Monarch notices that the "about to leave SMM" byte in SMRAM has
|
---|
81 | ; been set, and starts polling the last byte in the reserved page.
|
---|
82 | ;
|
---|
83 | ; - The hot-added CPU jumps ("returns") to the code below (in the reserved
|
---|
84 | ; page), according to the register state listed in the SMRAM Save State Map.
|
---|
85 | ;
|
---|
86 | ; - The hot-added CPU sets the last byte of the reserved page, then halts
|
---|
87 | ; itself.
|
---|
88 | ;
|
---|
89 | ; - The SMM Monarch notices that the hot-added CPU is done with SMBASE
|
---|
90 | ; relocation.
|
---|
91 | ;
|
---|
92 | ; Note that, if the OS is malicious and sends INIT-SIPI-SIPI to the hot-added
|
---|
93 | ; CPU before allowing the ACPI CPU hotplug event handler to trigger a broadcast
|
---|
94 | ; SMI, then said broadcast SMI will yank the hot-added CPU directly into SMM,
|
---|
95 | ; without becoming pending for it (as the hot-added CPU is no longer in RESET
|
---|
96 | ; state). This is OK, because:
|
---|
97 | ;
|
---|
98 | ; - The default SMI handler copes with this, as it is gated by APIC ID. The
|
---|
99 | ; hot-added CPU won't start the actual SMBASE relocation until the SMM
|
---|
100 | ; Monarch lets it.
|
---|
101 | ;
|
---|
102 | ; - The INIT-SIPI-SIPI sequence that the SMM Monarch sends to the hot-added CPU
|
---|
103 | ; will be ignored in this sate (it won't even be latched). See "SMI HANDLER
|
---|
104 | ; EXECUTION ENVIRONMENT" in the Intel SDM: "INIT operations are inhibited
|
---|
105 | ; when the processor enters SMM".
|
---|
106 | ;
|
---|
107 | ; - When the hot-added CPU (e.g., CPU#1) executes the RSM (having relocated
|
---|
108 | ; SMBASE), it returns to the OS. The OS can use CPU#1 to attack the last byte
|
---|
109 | ; of the reserved page, while another CPU (e.g., CPU#2) is relocating SMBASE,
|
---|
110 | ; in order to trick the SMM Monarch (e.g., CPU#0) to open the APIC ID gate
|
---|
111 | ; for yet another CPU (e.g., CPU#3). However, the SMM Monarch won't look at
|
---|
112 | ; the last byte of the reserved page, until CPU#2 sets the "about to leave
|
---|
113 | ; SMM" byte in SMRAM. This leaves a very small window (just one instruction's
|
---|
114 | ; worth before the RSM) for CPU#3 to "catch up" with CPU#2, and overwrite
|
---|
115 | ; CPU#2's SMBASE with its own.
|
---|
116 | ;
|
---|
117 | ; In other words, we do not / need not prevent a malicious OS from booting the
|
---|
118 | ; hot-added CPU early; instead we provide benign OSes with a pen for hot-added
|
---|
119 | ; CPUs.
|
---|
120 | ;------------------------------------------------------------------------------
|
---|
121 |
|
---|
122 | SECTION .data
|
---|
123 | BITS 16
|
---|
124 |
|
---|
125 | GLOBAL ASM_PFX (mPostSmmPen) ; UINT8[]
|
---|
126 | GLOBAL ASM_PFX (mPostSmmPenSize) ; UINT16
|
---|
127 |
|
---|
128 | ASM_PFX (mPostSmmPen):
|
---|
129 | ;
|
---|
130 | ; Point DS at the same reserved page.
|
---|
131 | ;
|
---|
132 | mov ax, cs
|
---|
133 | mov ds, ax
|
---|
134 |
|
---|
135 | ;
|
---|
136 | ; Inform the SMM Monarch that we're done with SMBASE relocation, by setting
|
---|
137 | ; the last byte in the reserved page.
|
---|
138 | ;
|
---|
139 | mov byte [ds : word 0xFFF], 1
|
---|
140 |
|
---|
141 | ;
|
---|
142 | ; Halt now, until we get woken by another SMI, or (more likely) the OS
|
---|
143 | ; reboots us with another INIT-SIPI-SIPI.
|
---|
144 | ;
|
---|
145 | HltLoop:
|
---|
146 | cli
|
---|
147 | hlt
|
---|
148 | jmp HltLoop
|
---|
149 |
|
---|
150 | ASM_PFX (mPostSmmPenSize):
|
---|
151 | dw $ - ASM_PFX (mPostSmmPen)
|
---|