878ddf1f |
1 | ;******************************************************************************\r |
2 | ;*\r |
3 | ;* Copyright (c) 2006, Intel Corporation \r |
4 | ;* All rights reserved. This program and the accompanying materials \r |
5 | ;* are licensed and made available under the terms and conditions of the BSD License \r |
6 | ;* which accompanies this distribution. The full text of the license may be found at \r |
7 | ;* http://opensource.org/licenses/bsd-license.php \r |
8 | ;* \r |
9 | ;* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r |
10 | ;* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r |
11 | ;*\r |
12 | ;******************************************************************************\r |
13 | \r |
14 | .586p\r |
15 | .MODEL FLAT, C\r |
16 | \r |
17 | EXCPT32_DIVIDE_ERROR EQU 0\r |
18 | EXCPT32_DEBUG EQU 1\r |
19 | EXCPT32_NMI EQU 2\r |
20 | EXCPT32_BREAKPOINT EQU 3\r |
21 | EXCPT32_OVERFLOW EQU 4\r |
22 | EXCPT32_BOUND EQU 5\r |
23 | EXCPT32_INVALID_OPCODE EQU 6\r |
24 | EXCPT32_DOUBLE_FAULT EQU 8\r |
25 | EXCPT32_INVALID_TSS EQU 10\r |
26 | EXCPT32_SEG_NOT_PRESENT EQU 11\r |
27 | EXCPT32_STACK_FAULT EQU 12\r |
28 | EXCPT32_GP_FAULT EQU 13\r |
29 | EXCPT32_PAGE_FAULT EQU 14\r |
30 | EXCPT32_FP_ERROR EQU 16\r |
31 | EXCPT32_ALIGNMENT_CHECK EQU 17\r |
32 | EXCPT32_MACHINE_CHECK EQU 18\r |
33 | EXCPT32_SIMD EQU 19\r |
34 | \r |
35 | FXSTOR_FLAG EQU 01000000h ; bit cpuid 24 of feature flags\r |
36 | \r |
37 | ;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87,\r |
38 | ;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver\r |
39 | ;; MUST check the CPUID feature flags to see that these instructions are available\r |
40 | ;; and fail to init if they are not.\r |
41 | \r |
42 | ;; fxstor [edi]\r |
43 | FXSTOR_EDI MACRO\r |
44 | db 0fh, 0aeh, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [edi]\r |
45 | ENDM\r |
46 | \r |
47 | ;; fxrstor [esi]\r |
48 | FXRSTOR_ESI MACRO\r |
49 | db 0fh, 0aeh, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [esi]\r |
50 | ENDM\r |
51 | .DATA\r |
52 | \r |
53 | public OrigVector, InterruptEntryStub, StubSize, CommonIdtEntry, FxStorSupport\r |
54 | \r |
55 | StubSize dd InterruptEntryStubEnd - InterruptEntryStub\r |
56 | AppEsp dd 11111111h ; ?\r |
57 | DebugEsp dd 22222222h ; ?\r |
58 | ExtraPush dd 33333333h ; ?\r |
59 | ExceptData dd 44444444h ; ?\r |
60 | Eflags dd 55555555h ; ?\r |
61 | OrigVector dd 66666666h ; ?\r |
62 | \r |
63 | ;; The declarations below define the memory region that will be used for the debug stack.\r |
64 | ;; The context record will be built by pushing register values onto this stack.\r |
65 | ;; It is imparitive that alignment be carefully managed, since the FXSTOR and\r |
66 | ;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned.\r |
67 | ;;\r |
68 | ;; The stub will switch stacks from the application stack to the debuger stack\r |
69 | ;; and pushes the exception number.\r |
70 | ;;\r |
71 | ;; Then we building the context record on the stack. Since the stack grows down,\r |
72 | ;; we push the fields of the context record from the back to the front. There\r |
73 | ;; are 132 bytes of stack used prior allocating the 512 bytes of stack to be\r |
74 | ;; used as the memory buffer for the fxstor instruction. Therefore address of\r |
75 | ;; the buffer used for the FXSTOR instruction is &Eax - 132 - 512, which\r |
76 | ;; must be 16 byte aligned.\r |
77 | ;;\r |
78 | ;; We carefully locate the stack to make this happen.\r |
79 | ;;\r |
80 | ;; For reference, the context structure looks like this:\r |
81 | ;; struct {\r |
82 | ;; UINT32 ExceptionData;\r |
83 | ;; FX_SAVE_STATE FxSaveState; // 512 bytes, must be 16 byte aligned\r |
84 | ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;\r |
85 | ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;\r |
86 | ;; UINT32 Ldtr, Tr;\r |
87 | ;; UINT64 Gdtr, Idtr;\r |
88 | ;; UINT32 EFlags;\r |
89 | ;; UINT32 Eip;\r |
90 | ;; UINT32 SegGs, SegFs, SegEs, SegDs, SegCs, SegSs;\r |
91 | ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r |
92 | ;; } SYSTEM_CONTEXT_IA32; // 32 bit system context record\r |
93 | \r |
94 | \r |
95 | align 16\r |
96 | DebugStackEnd db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment\r |
97 | dd 1ffdh dup (000000000h) ;; 32K should be enough stack\r |
98 | ;; This allocation is coocked to insure \r |
99 | ;; that the the buffer for the FXSTORE instruction\r |
100 | ;; will be 16 byte aligned also.\r |
101 | ;;\r |
102 | ExceptionNumber dd ? ;; first entry will be the vector number pushed by the stub\r |
103 | \r |
104 | DebugStackBegin db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub\r |
105 | \r |
106 | .CODE\r |
107 | \r |
108 | externdef InterruptDistrubutionHub:near\r |
109 | \r |
110 | ;------------------------------------------------------------------------------\r |
111 | ; BOOLEAN\r |
112 | ; FxStorSupport (\r |
113 | ; void\r |
114 | ; )\r |
115 | ;\r |
116 | ; Abstract: Returns TRUE if FxStor instructions are supported\r |
117 | ;\r |
118 | FxStorSupport PROC C PUBLIC\r |
119 | \r |
120 | ;\r |
121 | ; cpuid corrupts ebx which must be preserved per the C calling convention\r |
122 | ;\r |
123 | push ebx\r |
124 | mov eax, 1\r |
125 | cpuid\r |
126 | mov eax, edx\r |
127 | and eax, FXSTOR_FLAG\r |
128 | shr eax, 24\r |
129 | pop ebx\r |
130 | ret\r |
131 | FxStorSupport ENDP\r |
132 | \r |
133 | \r |
134 | ;------------------------------------------------------------------------------\r |
135 | ; DESCRIPTOR *\r |
136 | ; GetIdtr (\r |
137 | ; void\r |
138 | ; )\r |
139 | ;\r |
140 | ; Abstract: Returns physical address of IDTR\r |
141 | ;\r |
142 | GetIdtr PROC C PUBLIC\r |
143 | LOCAL IdtrBuf:FWORD\r |
144 | \r |
145 | sidt IdtrBuf\r |
146 | mov eax, DWORD PTR IdtrBuf + 2\r |
147 | ret\r |
148 | GetIdtr ENDP\r |
149 | \r |
150 | \r |
151 | ;------------------------------------------------------------------------------\r |
152 | ; BOOLEAN\r |
153 | ; WriteInterruptFlag (\r |
154 | ; BOOLEAN NewState\r |
155 | ; )\r |
156 | ;\r |
157 | ; Abstract: Programs interrupt flag to the requested state and returns previous\r |
158 | ; state.\r |
159 | ;\r |
160 | WriteInterruptFlag PROC C PUBLIC State:DWORD\r |
161 | \r |
162 | pushfd\r |
163 | pop eax\r |
164 | and eax, 200h\r |
165 | shr eax, 9\r |
166 | mov ecx, State\r |
167 | .IF ecx == 0\r |
168 | cli\r |
169 | .ELSE\r |
170 | sti\r |
171 | .ENDIF\r |
172 | ret\r |
173 | \r |
174 | WriteInterruptFlag ENDP\r |
175 | \r |
176 | \r |
177 | \r |
178 | ;------------------------------------------------------------------------------\r |
179 | ; void\r |
180 | ; Vect2Desc (\r |
181 | ; DESCRIPTOR * DestDesc,\r |
182 | ; void (*Vector) (void)\r |
183 | ; )\r |
184 | ;\r |
185 | ; Abstract: Encodes an IDT descriptor with the given physical address\r |
186 | ;\r |
187 | Vect2Desc PROC C PUBLIC DestPtr:DWORD, Vector:DWORD\r |
188 | \r |
189 | mov eax, Vector\r |
190 | mov ecx, DestPtr\r |
191 | mov word ptr [ecx], ax ; write bits 15..0 of offset\r |
192 | mov word ptr [ecx+2], 20h ; SYS_CODE_SEL from GDT\r |
193 | mov word ptr [ecx+4], 0e00h OR 8000h ; type = 386 interrupt gate, present\r |
194 | shr eax, 16\r |
195 | mov word ptr [ecx+6], ax ; write bits 31..16 of offset\r |
196 | \r |
197 | ret\r |
198 | \r |
199 | Vect2Desc ENDP\r |
200 | \r |
201 | \r |
202 | \r |
203 | ;------------------------------------------------------------------------------\r |
204 | ; InterruptEntryStub\r |
205 | ;\r |
206 | ; Abstract: This code is not a function, but is a small piece of code that is\r |
207 | ; copied and fixed up once for each IDT entry that is hooked.\r |
208 | ;\r |
209 | InterruptEntryStub::\r |
210 | mov AppEsp, esp ; save stack top\r |
211 | mov esp, offset DebugStackBegin ; switch to debugger stack\r |
212 | push 0 ; push vector number - will be modified before installed\r |
213 | db 0e9h ; jump rel32\r |
214 | dd 0 ; fixed up to relative address of CommonIdtEntry\r |
215 | InterruptEntryStubEnd:\r |
216 | \r |
217 | \r |
218 | \r |
219 | ;------------------------------------------------------------------------------\r |
220 | ; CommonIdtEntry\r |
221 | ;\r |
222 | ; Abstract: This code is not a function, but is the common part for all IDT\r |
223 | ; vectors.\r |
224 | ;\r |
225 | CommonIdtEntry::\r |
226 | ;;\r |
227 | ;; At this point, the stub has saved the current application stack esp into AppEsp\r |
228 | ;; and switched stacks to the debug stack, where it pushed the vector number\r |
229 | ;;\r |
230 | ;; The application stack looks like this:\r |
231 | ;;\r |
232 | ;; ...\r |
233 | ;; (last application stack entry)\r |
234 | ;; eflags from interrupted task\r |
235 | ;; CS from interrupted task\r |
236 | ;; EIP from interrupted task\r |
237 | ;; Error code <-------------------- Only present for some exeption types\r |
238 | ;;\r |
239 | ;;\r |
240 | \r |
241 | \r |
242 | ;; The stub switched us to the debug stack and pushed the interrupt number.\r |
243 | ;;\r |
244 | ;; Next, construct the context record. It will be build on the debug stack by\r |
245 | ;; pushing the registers in the correct order so as to create the context structure\r |
246 | ;; on the debug stack. The context record must be built from the end back to the\r |
247 | ;; beginning because the stack grows down...\r |
248 | ;\r |
249 | ;; For reference, the context record looks like this:\r |
250 | ;;\r |
251 | ;; typedef\r |
252 | ;; struct {\r |
253 | ;; UINT32 ExceptionData;\r |
254 | ;; FX_SAVE_STATE FxSaveState;\r |
255 | ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;\r |
256 | ;; UINT32 Cr0, Cr2, Cr3, Cr4;\r |
257 | ;; UINT32 Ldtr, Tr;\r |
258 | ;; UINT64 Gdtr, Idtr;\r |
259 | ;; UINT32 EFlags;\r |
260 | ;; UINT32 Eip;\r |
261 | ;; UINT32 SegGs, SegFs, SegEs, SegDs, SegCs, SegSs;\r |
262 | ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r |
263 | ;; } SYSTEM_CONTEXT_IA32; // 32 bit system context record\r |
264 | \r |
265 | ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r |
266 | pushad\r |
267 | \r |
268 | ;; Save interrupt state eflags register...\r |
269 | pushfd\r |
270 | pop eax\r |
271 | mov dword ptr Eflags, eax\r |
272 | \r |
273 | ;; We need to determine if any extra data was pushed by the exception, and if so, save it\r |
274 | ;; To do this, we check the exception number pushed by the stub, and cache the\r |
275 | ;; result in a variable since we'll need this again.\r |
276 | .IF ExceptionNumber == EXCPT32_DOUBLE_FAULT\r |
277 | mov ExtraPush, 1\r |
278 | .ELSEIF ExceptionNumber == EXCPT32_INVALID_TSS\r |
279 | mov ExtraPush, 1\r |
280 | .ELSEIF ExceptionNumber == EXCPT32_SEG_NOT_PRESENT\r |
281 | mov ExtraPush, 1\r |
282 | .ELSEIF ExceptionNumber == EXCPT32_STACK_FAULT\r |
283 | mov ExtraPush, 1\r |
284 | .ELSEIF ExceptionNumber == EXCPT32_GP_FAULT\r |
285 | mov ExtraPush, 1\r |
286 | .ELSEIF ExceptionNumber == EXCPT32_PAGE_FAULT\r |
287 | mov ExtraPush, 1\r |
288 | .ELSEIF ExceptionNumber == EXCPT32_ALIGNMENT_CHECK\r |
289 | mov ExtraPush, 1\r |
290 | .ELSE\r |
291 | mov ExtraPush, 0\r |
292 | .ENDIF\r |
293 | \r |
294 | ;; If there's some extra data, save it also, and modify the saved AppEsp to effectively\r |
295 | ;; pop this value off the application's stack.\r |
296 | .IF ExtraPush == 1\r |
297 | mov eax, AppEsp\r |
298 | mov ebx, [eax]\r |
299 | mov ExceptData, ebx\r |
300 | add eax, 4\r |
301 | mov AppEsp, eax\r |
302 | .ELSE\r |
303 | mov ExceptData, 0\r |
304 | .ENDIF\r |
305 | \r |
306 | ;; The "pushad" above pushed the debug stack esp. Since what we're actually doing\r |
307 | ;; is building the context record on the debug stack, we need to save the pushed\r |
308 | ;; debug ESP, and replace it with the application's last stack entry...\r |
309 | mov eax, [esp + 12]\r |
310 | mov DebugEsp, eax\r |
311 | mov eax, AppEsp\r |
312 | add eax, 12\r |
313 | ; application stack has eflags, cs, & eip, so\r |
314 | ; last actual application stack entry is\r |
315 | ; 12 bytes into the application stack.\r |
316 | mov [esp + 12], eax\r |
317 | \r |
318 | ;; continue building context record\r |
319 | ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero\r |
320 | mov eax, ss\r |
321 | push eax\r |
322 | \r |
323 | ; CS from application is one entry back in application stack\r |
324 | mov eax, AppEsp\r |
325 | movzx eax, word ptr [eax + 4]\r |
326 | push eax\r |
327 | \r |
328 | mov eax, ds\r |
329 | push eax\r |
330 | mov eax, es\r |
331 | push eax\r |
332 | mov eax, fs\r |
333 | push eax\r |
334 | mov eax, gs\r |
335 | push eax\r |
336 | \r |
337 | ;; UINT32 Eip;\r |
338 | ; Eip from application is on top of application stack\r |
339 | mov eax, AppEsp\r |
340 | push dword ptr [eax]\r |
341 | \r |
342 | ;; UINT64 Gdtr, Idtr;\r |
343 | push 0\r |
344 | push 0\r |
345 | sidt fword ptr [esp]\r |
346 | push 0\r |
347 | push 0\r |
348 | sgdt fword ptr [esp]\r |
349 | \r |
350 | ;; UINT32 Ldtr, Tr;\r |
351 | xor eax, eax\r |
352 | str ax\r |
353 | push eax\r |
354 | sldt ax\r |
355 | push eax\r |
356 | \r |
357 | ;; UINT32 EFlags;\r |
358 | ;; Eflags from application is two entries back in application stack\r |
359 | mov eax, AppEsp\r |
360 | push dword ptr [eax + 8]\r |
361 | \r |
362 | ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;\r |
363 | ;; insure FXSAVE/FXRSTOR is enabled in CR4...\r |
364 | ;; ... while we're at it, make sure DE is also enabled...\r |
365 | mov eax, cr4\r |
366 | or eax, 208h\r |
367 | mov cr4, eax\r |
368 | push eax\r |
369 | mov eax, cr3\r |
370 | push eax\r |
371 | mov eax, cr2\r |
372 | push eax\r |
373 | push 0\r |
374 | mov eax, cr0\r |
375 | push eax\r |
376 | \r |
377 | ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;\r |
378 | mov eax, dr7\r |
379 | push eax\r |
380 | ;; clear Dr7 while executing debugger itself\r |
381 | xor eax, eax\r |
382 | mov dr7, eax\r |
383 | \r |
384 | mov eax, dr6\r |
385 | push eax\r |
386 | ;; insure all status bits in dr6 are clear...\r |
387 | xor eax, eax\r |
388 | mov dr6, eax\r |
389 | \r |
390 | mov eax, dr3\r |
391 | push eax\r |
392 | mov eax, dr2\r |
393 | push eax\r |
394 | mov eax, dr1\r |
395 | push eax\r |
396 | mov eax, dr0\r |
397 | push eax\r |
398 | \r |
399 | ;; FX_SAVE_STATE FxSaveState;\r |
400 | sub esp, 512\r |
401 | mov edi, esp\r |
402 | ; IMPORTANT!! The debug stack has been carefully constructed to\r |
403 | ; insure that esp and edi are 16 byte aligned when we get here.\r |
404 | ; They MUST be. If they are not, a GP fault will occur.\r |
405 | FXSTOR_EDI\r |
406 | \r |
407 | ;; UINT32 ExceptionData;\r |
408 | mov eax, ExceptData\r |
409 | push eax\r |
410 | \r |
411 | ; call to C code which will in turn call registered handler\r |
412 | ; pass in the vector number\r |
413 | mov eax, esp\r |
414 | push eax\r |
415 | mov eax, ExceptionNumber\r |
416 | push eax\r |
417 | call InterruptDistrubutionHub\r |
418 | add esp, 8\r |
419 | \r |
420 | ; restore context...\r |
421 | ;; UINT32 ExceptionData;\r |
422 | add esp, 4\r |
423 | \r |
424 | ;; FX_SAVE_STATE FxSaveState;\r |
425 | mov esi, esp\r |
426 | FXRSTOR_ESI\r |
427 | add esp, 512\r |
428 | \r |
429 | ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;\r |
430 | pop eax\r |
431 | mov dr0, eax\r |
432 | pop eax\r |
433 | mov dr1, eax\r |
434 | pop eax\r |
435 | mov dr2, eax\r |
436 | pop eax\r |
437 | mov dr3, eax\r |
438 | ;; skip restore of dr6. We cleared dr6 during the context save.\r |
439 | add esp, 4\r |
440 | pop eax\r |
441 | mov dr7, eax\r |
442 | \r |
443 | ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;\r |
444 | pop eax\r |
445 | mov cr0, eax\r |
446 | add esp, 4\r |
447 | pop eax\r |
448 | mov cr2, eax\r |
449 | pop eax\r |
450 | mov cr3, eax\r |
451 | pop eax\r |
452 | mov cr4, eax\r |
453 | \r |
454 | ;; UINT32 EFlags;\r |
455 | mov eax, AppEsp\r |
456 | pop dword ptr [eax + 8]\r |
457 | \r |
458 | ;; UINT16 Ldtr, Tr;\r |
459 | ;; UINT64 Gdtr, Idtr;\r |
460 | ;; Best not let anyone mess with these particular registers...\r |
461 | add esp, 24\r |
462 | \r |
463 | ;; UINT32 Eip;\r |
464 | pop dword ptr [eax]\r |
465 | \r |
466 | ;; UINT32 SegGs, SegFs, SegEs, SegDs, SegCs, SegSs;\r |
467 | ;; NOTE - modified segment registers could hang the debugger... We\r |
468 | ;; could attempt to insulate ourselves against this possibility,\r |
469 | ;; but that poses risks as well.\r |
470 | ;;\r |
471 | \r |
472 | pop gs\r |
473 | pop fs\r |
474 | pop es\r |
475 | pop ds\r |
476 | pop [eax + 4]\r |
477 | pop ss\r |
478 | \r |
479 | ;; The next stuff to restore is the general purpose registers that were pushed\r |
480 | ;; using the pushad instruction.\r |
481 | ;;\r |
482 | ;; The value of ESP as stored in the context record is the application ESP\r |
483 | ;; including the 3 entries on the application stack caused by the exception\r |
484 | ;; itself. It may have been modified by the debug agent, so we need to\r |
485 | ;; determine if we need to relocate the application stack.\r |
486 | \r |
487 | mov ebx, [esp + 12] ; move the potentially modified AppEsp into ebx\r |
488 | mov eax, AppEsp\r |
489 | add eax, 12\r |
490 | cmp ebx, eax\r |
491 | je NoAppStackMove\r |
492 | \r |
493 | mov eax, AppEsp\r |
494 | mov ecx, [eax] ; EIP\r |
495 | mov [ebx], ecx\r |
496 | \r |
497 | mov ecx, [eax + 4] ; CS\r |
498 | mov [ebx + 4], ecx\r |
499 | \r |
500 | mov ecx, [eax + 8] ; EFLAGS\r |
501 | mov [ebx + 8], ecx\r |
502 | \r |
503 | mov eax, ebx ; modify the saved AppEsp to the new AppEsp\r |
504 | mov AppEsp, eax\r |
505 | NoAppStackMove:\r |
506 | mov eax, DebugEsp ; restore the DebugEsp on the debug stack\r |
507 | ; so our popad will not cause a stack switch\r |
508 | mov [esp + 12], eax\r |
509 | \r |
510 | cmp ExceptionNumber, 068h\r |
511 | jne NoChain\r |
512 | \r |
513 | Chain:\r |
514 | \r |
515 | ;; Restore eflags so when we chain, the flags will be exactly as if we were never here.\r |
516 | ;; We gin up the stack to do an iretd so we can get ALL the flags.\r |
517 | mov eax, AppEsp\r |
518 | mov ebx, [eax + 8]\r |
519 | and ebx, NOT 300h ; special handling for IF and TF\r |
520 | push ebx\r |
521 | push cs\r |
522 | push PhonyIretd\r |
523 | iretd\r |
524 | PhonyIretd:\r |
525 | \r |
526 | ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r |
527 | popad\r |
528 | \r |
529 | ;; Switch back to application stack\r |
530 | mov esp, AppEsp\r |
531 | \r |
532 | ;; Jump to original handler\r |
533 | jmp OrigVector\r |
534 | \r |
535 | NoChain:\r |
536 | ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r |
537 | popad\r |
538 | \r |
539 | ;; Switch back to application stack\r |
540 | mov esp, AppEsp\r |
541 | \r |
542 | ;; We're outa here...\r |
543 | iretd\r |
544 | END\r |
545 | \r |
546 | \r |
547 | \r |