[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / subsystems / mvdm / dos / command.S
1 /*
2 * PROJECT: ReactOS Virtual DOS Machine
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: DOS32 command.com for NTVDM
5 * COPYRIGHT: Copyright 2015-2018 Hermes Belusca-Maito
6 */
7
8 /* INCLUDES *******************************************************************/
9
10 #include <asm.inc>
11 #include "asmxtras.inc"
12 #include <isvbop.inc>
13
14 #define NDEBUG
15
16 /* DEFINES ********************************************************************/
17
18 #define MAX_PATH 260
19 #define DOS_CMDLINE_LENGTH 127
20
21 #define DOS_VERSION HEX(0005) // MAKEWORD(5, 00)
22 #define NTDOS_VERSION HEX(3205) // MAKEWORD(5, 50)
23
24 #define SYSTEM_PSP HEX(08)
25
26 #define BOP .byte HEX(C4), HEX(C4),
27 // #define BOP_START_DOS HEX(2C)
28 #define BOP_CMD HEX(54)
29
30 /**** PSP MEMBERS ****/
31 #define PSP_VAR(x) es:[x]
32 #define PSP HEX(0000)
33 #define ParentPsp HEX(0016) // word
34 #define EnvBlock HEX(002C) // word
35 #define CmdLineLen HEX(0080) // byte
36 #define CmdLineStr HEX(0081) // byte[DOS_CMDLINE_LENGTH]
37
38 /**** DATA stored inside the stack ****/
39 #define CmdLine BaseStack // Command line for the program to be started
40 #define PgmName [CmdLine + (1 + DOS_CMDLINE_LENGTH)]
41
42
43 // WARNING! Using the VAL(x) macro doesn't work with GCC/GAS (because it inserts
44 // a spurious space in front of the parameter when the macro is expanded).
45 // So that: 'VAL(foo)' is expanded to: '\ foo', instead of '\foo' as one would expect.
46
47 /* NEXT_CMD structure */
48 STRUCT(NEXT_CMD, 2)
49 FIELD_DECL(EnvBlockSeg, word, 0)
50 FIELD_DECL(EnvBlockLen, word, 0)
51 FIELD_DECL(CurDrive , word, 0)
52 FIELD_DECL(NumDrives , word, 1)
53 FIELD_DECL(CmdLineSeg , word, 0)
54 FIELD_DECL(CmdLineOff , word, 0)
55 FIELD_DECL(Unknown0 , word, 2)
56 FIELD_DECL(ExitCode , word, 3)
57 FIELD_DECL(Unknown1 , word, 4)
58 FIELD_DECL(Unknown2 , long, 5)
59 FIELD_DECL(CodePage , word, 6)
60 FIELD_DECL(Unknown3 , word, 7)
61 FIELD_DECL(Unknown4 , word, 8)
62 FIELD_DECL(AppNameSeg , word, 0)
63 FIELD_DECL(AppNameOff , word, 0)
64 FIELD_DECL(AppNameLen , word, 0)
65 FIELD_DECL(Flags , word, 0)
66 ENDS(NEXT_CMD)
67
68 /* DOS_EXEC_PARAM_BLOCK structure */
69 STRUCT(DOS_EXEC_PARAM_BLOCK, 2)
70 FIELD_DECL(EnvSeg, word, 0) // Use parent's environment (ours)
71 FIELD_DECL(CmdLineOff, word, OFF(CmdLine))
72 FIELD_DECL(CmdLineSeg, word, 0)
73 FIELD_DECL(Fcb1Off, word, OFF(Fcb1))
74 FIELD_DECL(Fcb1Seg, word, 0)
75 FIELD_DECL(Fcb2Off, word, OFF(Fcb2))
76 FIELD_DECL(Fcb2Seg, word, 0)
77 ENDS(DOS_EXEC_PARAM_BLOCK)
78
79
80
81 /* RESIDENT CODE AND DATA *****************************************************/
82
83 .code16
84 // .org HEX(0100)
85 ASSUME CS:.text, DS:.text, ES:.text
86
87
88 /* CODE *******************************/
89
90 EntryPoint:
91 jmp Main
92 .align 2
93
94 ResidentMain:
95 /*
96 * Relocate our stack.
97 * Our local stack is big enough for holding the command line and the path
98 * to the executable to start, plus a full DOS_REGISTER_STATE and for one pusha 16-bit.
99 *
100 * FIXME: enlarge it for pushing the needed Load&Exec data.
101 */
102 cli
103 mov ax, ds
104 mov ss, ax
105 mov bp, offset BaseStack
106 lea sp, [bp + (1 + DOS_CMDLINE_LENGTH) + MAX_PATH + 255]
107 sti
108
109 /* Resize ourselves */
110 mov bx, sp // Get size in bytes...
111 // sub bx, offset PSP_VAR(PSP)
112 add bx, HEX(0F) // (for rounding to the next paragraph)
113 shr bx, 4 // ... then the number of paragraphs
114 mov ah, HEX(4A)
115 int HEX(21)
116
117 /* Check whether we need to start the 32-bit command interpreter */
118 BOP BOP_CMD, HEX(10) // Check whether we were started from a new console (SessionId != 0)
119 test al, al // and we are not reentering (32-bit process starting a 16-bit process).
120 jz Run
121 cmp word ptr OldParentPsp, SYSTEM_PSP // Check whether our parent is SYSTEM
122 je Run
123
124 #ifndef NDEBUG
125 /********************************/
126 mov dx, offset Msg1
127 mov ah, HEX(09)
128 int HEX(21)
129 /********************************/
130 #endif
131
132 BOP BOP_CMD, HEX(0A) // Start 32-bit COMSPEC
133 jnc Quit
134
135 /* Loop for new commands to run */
136 Run:
137 /* Initialize the NextCmd structure */
138 mov word ptr FIELD(NextCmd, EnvBlockSeg), ds
139 mov word ptr FIELD(NextCmd, EnvBlockLen), 0 // FIXME
140 mov word ptr FIELD(NextCmd, CmdLineSeg), ds
141 mov word ptr FIELD(NextCmd, CmdLineOff), offset CmdLine
142 mov word ptr FIELD(NextCmd, AppNameSeg), ds
143 mov word ptr FIELD(NextCmd, AppNameOff), offset PgmName
144
145 /* Wait for the next command */
146 #ifndef NDEBUG
147 /********************************/
148 mov dx, offset Msg2
149 mov ah, HEX(09)
150 int HEX(21)
151 /********************************/
152 #endif
153
154 // FIXME: Initialize memory with structure for holding CmdLine etc...
155 // mov ds, seg NextCmd
156 mov dx, offset NextCmd
157 BOP BOP_CMD, HEX(01)
158 /* Quit if we shell-out */
159 jc Quit
160
161 /* Initialize the DosLoadExec structure */
162 // mov word ptr FIELD(DosLoadExec, EnvSeg), 0
163 mov word ptr FIELD(DosLoadExec, CmdLineSeg), ds
164 mov word ptr FIELD(DosLoadExec, Fcb1Seg), ds
165 mov word ptr FIELD(DosLoadExec, Fcb2Seg), ds
166
167 /* Run the command */
168 mov ds, word ptr FIELD(NextCmd, AppNameSeg)
169 mov dx, word ptr FIELD(NextCmd, AppNameOff)
170 // mov es, seg DosLoadExec
171 mov bx, offset DosLoadExec
172 pusha // Save the registers in case stuff go wrong
173 // FIXME: Save also SS !!
174 mov ax, HEX(4B00)
175 int HEX(21)
176 popa // Restore the registers
177 // FIXME: Restore also SS !!
178
179 /* Retrieve and set its exit code. Also detect whether
180 * we need to continue or whether we need to quit. */
181 // xor ax, ax
182 // mov ah, HEX(4D)
183 mov ax, HEX(4D00)
184 int HEX(21)
185 /* Send exit code back to NTVDM */
186 mov dx, ax
187 BOP BOP_CMD, HEX(0B)
188
189 /* If we don't shell-out, go and get a new app! */
190 jc Run
191
192 mov al, HEX(00) // ERROR_SUCCESS
193
194 Quit:
195 mov bl, al // Save AL in BL
196
197 #ifndef NDEBUG
198 /********************************/
199 cmp al, HEX(0A)
200 jne XXXX
201 mov dx, offset Msg3
202 mov ah, HEX(09)
203 int HEX(21)
204 XXXX:
205 /********************************/
206 #endif
207
208 #ifndef NDEBUG
209 /* Say bye-bye */
210 // mov ds, seg QuitMsg
211 mov dx, offset QuitMsg
212 mov ah, HEX(09)
213 int HEX(21)
214 #endif
215
216 /* Restore our old parent PSP */
217 mov ax, word ptr OldParentPsp
218 mov PSP_VAR(ParentPsp), ax
219
220 mov al, bl // Restore AL from BL
221
222 Exit:
223 /* Return to caller (with possible error code in AL) */
224 mov ah, HEX(4C)
225 int HEX(21)
226 int 3
227
228 /* Does not return */
229
230 /* DATA *******************************/
231
232 #ifndef NDEBUG
233 QuitMsg:
234 .ascii "Bye bye!", CR, LF, "$"
235
236 /********************************/
237 Msg1: .ascii "Starting COMSPEC...", CR, LF, "$"
238 Msg2: .ascii "Waiting for new command...", CR, LF, "$"
239 Msg3: .ascii "Bad environment!", CR, LF, "$"
240 /********************************/
241 #endif
242
243 OldParentPsp: .word 0
244 CurrentPsp: .word 0
245
246 // BOP_CMD, HEX(01) "Get a new app to start" structure
247 VAR_STRUCT(NextCmd, NEXT_CMD)
248
249 // DOS INT 21h, AH=4Bh "Load and Execute" structure
250 VAR_STRUCT(DosLoadExec, DOS_EXEC_PARAM_BLOCK)
251
252 // Blank FCB blocks needed for DOS INT 21h, AH=4Bh
253 Fcb1:
254 .byte 0
255 .space 11, ' '
256 .space 25, 0
257 Fcb2:
258 .byte 0
259 .space 11, ' '
260 .space 25, 0
261
262 // The stack resides at the end of the resident code+data
263 // and it overwrites the transient part.
264 BaseStack:
265
266
267 /* TRANSIENT CODE AND DATA ****************************************************/
268
269 /* DATA *******************************/
270
271 #ifndef NDEBUG
272 WelcomeMsg:
273 .ascii "ReactOS DOS32 Command", CR, LF, \
274 "Copyright (C) ReactOS Team 2015" , CR, LF, "$"
275 #endif
276
277 VerErrMsg:
278 .ascii "Incorrect DOS version", CR, LF, "$"
279
280 /* CODE *******************************/
281
282 .align 2
283
284 Main:
285 /* Setup segment registers */
286 mov ax, cs // cs contains the PSP segment on entry
287 mov ds, ax
288 mov es, ax
289 // mov fs, ax
290 // mov gs, ax
291 /* Stack is set to cs:FFFE down to cs:09xx by the DOS.
292 * We will need to relocate it before we resize ourselves. */
293
294 /*
295 * Verify DOS version:
296 * - Check whether we are on DOS 5+ (subject to SETVER);
297 * - If so, check our real DOS version and see
298 * whether we are running on NTVDM (version 5.50).
299 */
300 mov ax, HEX(3000)
301 int HEX(21)
302 cmp ax, DOS_VERSION // AH:AL contains minor:major version number
303 jne VerErr
304
305 mov ax, HEX(3306)
306 int HEX(21)
307 cmp bx, NTDOS_VERSION // BH:BL contains minor:major version number
308 je Continue
309
310 VerErr:
311 /* Display wrong version error message and exit */
312 // mov ds, seg VerErrMsg
313 mov dx, offset VerErrMsg
314 mov ah, HEX(09)
315 int HEX(21)
316 jmp Exit
317
318 Continue:
319 /* Save our PSP */
320 mov word ptr CurrentPsp, cs
321 /*
322 * The DOS way:
323 *
324 * mov ah, HEX(51) // DOS 2+ internal, or HEX(62) since DOS 3+
325 * int HEX(21)
326 * mov word ptr CurrentPsp, bx
327 */
328
329 /* Save our old parent PSP */
330 mov ax, PSP_VAR(ParentPsp)
331 mov word ptr OldParentPsp, ax
332
333 /* Give us shell privileges: set our PSP as our new parent */
334 mov ax, word ptr CurrentPsp
335 mov PSP_VAR(ParentPsp), ax
336
337 #ifndef NDEBUG
338 /* Say hello */
339 // mov ds, seg WelcomeMsg
340 mov dx, offset WelcomeMsg
341 mov ah, HEX(09)
342 int HEX(21)
343 #endif
344
345 /* Jump to resident code */
346 jmp ResidentMain
347
348 .endcode16
349 END
350
351 /* EOF */