From 003850b2b9d139c7079bf8b1d2b4d8a371ec792e Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 22 Mar 2026 19:29:04 +0100 Subject: [PATCH 01/62] Implemented a basic framework for testing task switching. --- src/macros_m.asm | 32 +++++++++++ src/protected_p.asm | 4 ++ src/protected_rings_p.asm | 94 ++++++++++++++++++++++++++++++++ src/protected_tssh.asm | 5 ++ src/test386.asm | 72 +++++++++++++++++++++++- src/tss_p.asm | 112 ++++++++++++++++++++++++++++++++++++++ src/x86_e.asm | 2 + 7 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 src/protected_tssh.asm diff --git a/src/macros_m.asm b/src/macros_m.asm index 54ed3b5..7647ce2 100644 --- a/src/macros_m.asm +++ b/src/macros_m.asm @@ -40,6 +40,38 @@ mov word [ebx+6], di ; OFFSET 31-16 %endmacro +; +; Initializes an task gate in system memory. +; This is the body of procedures used in 16 and 32-bit code segments. +; +; 7 0 7 0 +; ╔═══════════════════════════════╤═══════════════════════════════╗ +; +7║ UNUSED ║+6 +; ╟───┬───────┬───┬───────────────┬───────────────────────────────╢ +; +5║ P │ DPL │ 0 │ 0 1 0 1 │ UNUSED ║+4 +; ╟───┴───┴───┴───┴───┴───┴───┴───┴───────────────────────────────╢ +; +3║ SELECTOR ║+2 +; ╟───────────────────────────────┴───────────────────────────────╢ +; +1║ UNUSED ║ 0 +; ╚═══════════════════════════════╧═══════════════════════════════╝ +; 15 0 +; +; DS:EBX pointer to IDT +; EAX vector +; ESI selector +; DX DPL (use ACC_DPL_* equs) +; +%macro initIntTaskGate 0 + shl eax, 3 + add ebx, eax + mov word [ebx], 0 ; OFFSET 15-0 + mov word [ebx+2], si ; SELECTOR + or dx, ACC_TYPE_GATE_TSS | ACC_PRESENT + mov word [ebx+4], dx + shr edi, 16 + mov word [ebx+6], 0 ; OFFSET 31-16 +%endmacro + %macro initIntGate286 0 shl eax, 3 add ebx, eax diff --git a/src/protected_p.asm b/src/protected_p.asm index 9ae52e0..be74bd2 100644 --- a/src/protected_p.asm +++ b/src/protected_p.asm @@ -4,6 +4,10 @@ initIntGateProt: initIntGate ret +initIntTaskGateProt: + initIntTaskGate + ret + initDescriptorProt: initDescriptor ret diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index d6f55b8..0360fe8 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -100,6 +100,51 @@ switchToRing3FLATkernel: push dword edx ; push return EIP iretd +; +; Switches from Ring 0 to Ring 3 in flat mode +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3FLATuser: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot] + ; save ring 0 data segments, they'll be restored with switchToRing0 + mov [ebx+0x68], ax ; save DS + mov ax, es + mov [ebx+0x6A], ax ; save ES + mov ax, fs + mov [ebx+0x6C], ax ; save FS + mov ax, gs + mov [ebx+0x6E], ax ; save GS + ; set ring 0 SS:ESP + mov ax,ss + mov [ebx],ax + mov [ebx+4], esp + cli ; disable ints during switching + push dword D_SEG_PROT32FLAT|3 ; push user stack with RPL=3 + push dword ESP_R3_PROTFLAT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) + push dword CU_SEG_PROT32|3 ; push user code segment with RPL=3 + or edx,0xF0000 ; Fix flat instruction address + push dword edx ; push return EIP + iretd + + ; ; Switches from Ring 0 to Ring 3 in V86 mode ; @@ -230,6 +275,31 @@ switchToRing0: push ecx ret +; +; Switches from Ring 3 to Ring 0 (flat user mode) +; +; After calling this procedure consider all the registers and flags as trashed. +; +switchToRing0FromFlatUser: + testCPL 3 ; we must be in ring 3 + ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. + ; A placeholder for a Call Gate is already present in the GDT. + pop ecx ; read the return offset + lfs ebx, [cs:ptrGDTUprot] + mov eax, RING0_GATE + mov esi, C_SEG_PROT32 + mov edi, .ring0_fromflatuser + mov dx, ACC_DPL_3 ; the DPL needs to be 3 + call initCallGate + call RING0_GATE|3:0 ; the RPL needs to be 3, the offset will be ignored. +.ring0_fromflatuser: + add esp, 16 ; remove from stack CS:EIP+SS:ESP pushed by the CALL to RING0_GATE + ; restore ring 0 data segments saved by switchToRing3 + ; return to caller + push ecx + ret + + ; ; Test routine for call gate with parameters ; @@ -413,6 +483,30 @@ switchedToRing0V86_cleanup: pop ds ret +; +; Handles cleanup after returning from Ring 3 (Flat 32-bit mode) to Ring 0 +; +; After calling this procedure consider all the registers and flags as trashed. Assumes that the error code has already been popped. +; +switchedToRing0FromFlat_cleanup: + push ds + push ebx + lds ebx, [cs:ptrTSSprot] + push eax + ; restore ring 0 data segments, as they'll be restored with interrupts/exceptions calling us. + mov ax, [ebx+0x68] ; restore DS + mov [esp+8], ax ; Store on the stack to pop later + mov ax, [ebx+0x6A] ; restore ES + mov es, ax + mov ax, [ebx+0x6C] ; restore FS + mov fs, ax + mov ax, [ebx+0x6E] ; restore GS + mov gs, ax + pop eax + pop ebx + pop ds + ret + ; ; Switches from Ring 3 to Ring 0 (V86 mode). This is a jump target, not a call target. diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm new file mode 100644 index 0000000..2973174 --- /dev/null +++ b/src/protected_tssh.asm @@ -0,0 +1,5 @@ +; +; Kernel mode interrupt handler +; +TSS286entrypoint: + ;TODO: Implement. \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index ed5fec8..061000a 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -247,8 +247,10 @@ cpuTest: jmp initGDT ESP_R0_PROT equ 0x0000FFFF +ESP_R2_PROT equ 0x00001FFF ESP_R0_PROTFLAT equ 0xE001FFFF ESP_R3_PROT equ 0x00007FFF +ESP_R3_PROTFLAT equ 0x0001FFFF %include "protected_m.asm" @@ -276,18 +278,31 @@ initGDT: defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc CU_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT - defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000013F,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000013F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000030f,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000030f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_SEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_LDT|ACC_PRESENT defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc S_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT + defGDTDesc S_SEG_PROT32_R2, 0x00010000,0x0008ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc TSS_PROT, 0x00004000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_PROT16, 0x00004200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_DSEG_PROT, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT + defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT + defGDTDesc CU_SEG_PROT32_3, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT32GS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 @@ -330,8 +345,17 @@ ptrSSprotFLAT: ; pointer to the stack for pmode ptrTSSprot: ; pointer to the task state segment dd 0 dw TSS_DSEG_PROT +ptrTSSprot16: ; pointer to the 16-bit task state segment + dd 0 + dw TSS_DSEG_PROT16 +ptrTSSprot32Gate: ; pointer to the 32-bit task state segment gate + dd 0 + dw TSS_GSEG_PROT32 +ptrTSSprot16Gate: ; pointer to the 16-bit task state segment gate + dd 0 + dw TSS_GSEG_PROT16 addrProtIDT: ; address of pmode IDT to be used with lidt - dw 0x13F ; 16-bit limit + dw 0x14F ; 16-bit limit dd IDT_SEG_REAL << 4 ; 32-bit base address addrGDT: ; address of GDT to be used with lgdt dw GDT_SEG_LIMIT @@ -344,6 +368,12 @@ initIntGateReal: popad ret +initIntTaskGateReal: + pushad + initIntTaskGate + popad + ret + initIntGateReal286: pushad initIntGate286 @@ -414,6 +444,22 @@ initIDT: inc eax call initIntGateReal286 + ; Interrupt 28h: task switch to 286, callable from user mode. + mov esi, TSS_PROT16 + mov edi, 0 + or edi,0xFFFF0000 ;Make sure that the offset is located on a invalid 32-bit mapped address by mapping the high 16 bits, which are not to be used. + mov dx, ACC_DPL_3 + inc eax + call initIntTaskGateReal + + ; Interrupt 29h: task switch to 386, callable from user mode. + mov esi, TSS_PROT + mov edi, 0 + mov dx, ACC_DPL_3 + inc eax + call initIntTaskGateReal + + jmp initPaging initPaging: @@ -706,6 +752,11 @@ userFarFunc: ; %include "protected_inth.asm" + ; + ; 286 TSS handler + ; +%include "protected_tssh.asm" + userRetfErrorFunction: ; From user mode to kernel mode error address, which isn't allowed. @@ -860,6 +911,21 @@ userV86IretExitFuncLocationRet: bits 32 userV86ExitFuncRet: + ; Validate the TSS task switching functionality + ;First, enter a valid 32-bit protected flat mode for testing + ;Prepare 32-bit TSS ROM fields + call initTSS32 + ;Prepare 16-bit TSS ROM/RAM fields + call initTSS16 + ;Now, switch to flat user mode to start our tests + call switchToRing3FLATuser + ;Perform tests for 386 mode parts below + ;TODO: Actual tests using TSS task switching + + ;Finishes 386 mode parts tests, return to normal protected mode + call switchToRing0FromFlatUser + ;Restore the segment registers to compatible values + call switchedToRing0FromFlat_cleanup ;------------------------------------------------------------------------------- POST B diff --git a/src/tss_p.asm b/src/tss_p.asm index 3929246..f85f193 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -71,3 +71,115 @@ clearTSS: mov [es:ebp],word 0x68 ;Make sure that the I/O map base is out of range to trigger faults properly! pop ebp ret + + +; Prepare 32-bit TSS for task switching + +initTSS32: + call clearTSS + ; initialize everything to 0 + push eax + push ebp + push ds + les ebx,[cs:ptrTSSprot] + mov eax, cr3 + mov [es:ebp+0x1C],eax ;Static field: CR3! + sldt ax + mov word [es:ebp+0x60], ax ;Static field: LDTR! + ;Ring 0 SS:ESP is loaded by the ring switching function + mov eax,0xFFFFFFFF + mov [es:ebp+0x20],eax ;Initial EIP, should be overwritten by the task switch. + mov word [es:ebp+4], S_SEG_PROT32_R2 ;R2 Stack + mov [es:ebp+8], word ESP_R2_PROT + pop ds + pop ebp + pop eax + ret + +; 16-bit Task State Segment +; +; 31 23 15 7 0 +; ╔═══════════════╪═══════════════╬═══════════════╪═════════════╦═╗ +; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ LDT ║2A +; ╟───────────────┼───────────────╫───────────────┼───────────────╢ +; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ DS ║28 +; ╟───────────────┼───────────────╫───────────────┼───────────────╢ +; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ SS ║26 +; ╟───────────────┼───────────────╫───────────────┼───────────────╢ +; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ CS ║24 +; ╟───────────────┼───────────────╫───────────────┼───────────────╢ +; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ ES ║22 +; ╟───────────────┼───────────────╫───────────────┼───────────────╢ +; ║ DI ║20 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ SI ║1E +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ BP ║1C +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ SP ║1A +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ BX ║18 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ DX ║16 +; ╠═══════════════╪═══════════════╪═══════════════╪═══════════════╣ +; ║ CX ║14 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ AX ║12 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ FLAGS ║10 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ INSTRUCTION POINTER (IP) ║E +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ SS2 ║C +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ ESP2 ║A +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ SS1 ║8 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ ESP1 ║6 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ SS0 ║4 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ ESP0 ║2 +; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ║ BACK LINK TO PREVIOUS TSS ║0 +; ╚═══════════════╪═══════════════╬═══════════════╪═══════════════╝ + +initTSS16: + ; initialize everything to predefined values + push eax + push ebp + push ds + les ebx,[cs:ptrTSSprot] + xor eax, eax + mov ecx, 0x15 + cld + rep stosw ;Clear the TSS + mov [es:ebp+0],word 0 ;No back link yet! + mov ax, ss ;R0 Stack + mov word [es:ebp+2], ss + mov [es:ebp+4], word ESP_R0_PROT + mov word [es:ebp+0xA], S_SEG_PROT32_R2 ;R2 Stack + mov [es:ebp+0xC], word ESP_R2_PROT + ; User mode pointers + mov ax, CU_SEG_PROT32_3 ;Code segment + mov [es:ebp+0x24], ax + mov [es:ebp+0x10], word 2 ;FLAGS + mov [es:ebp+0x0E], TSS286entrypoint ;Code entry point + mov [es:ebp+0x26], SU_SEG_PROT16SS ;Stack segment + mov [es:ebp+0x22], SU_SEG_PROT16ES ;ES + mov [es:ebp+0x28], SU_SEG_PROT16DS ;DS + ; General purpose registers (TODO: test patterns) + mov [es:ebp+0x12],0 ;AX + mov [es:ebp+0x14],0 ;CX + mov [es:ebp+0x16],0 ;DX + mov [es:ebp+0x18],0 ;BX + mov [es:ebp+0x1A],word ESP_R3_PROT ;SP + mov [es:ebp+0x1C],0 ;BP + mov [es:ebp+0x1E],0 ;SI + mov [es:ebp+0x20],0 ;DI + pop ds + pop ebp + pop eax + ret + \ No newline at end of file diff --git a/src/x86_e.asm b/src/x86_e.asm index df93b85..0aea4c7 100644 --- a/src/x86_e.asm +++ b/src/x86_e.asm @@ -21,6 +21,7 @@ ACC_TYPE_GATE386_INT equ 0x0E00 ACC_TYPE_GATE286_INT equ 0x0600 ACC_TYPE_GATE386_CALL equ 0x0C00 ACC_TYPE_GATE286_CALL equ 0x0400 +ACC_TYPE_GATE_TSS equ 0x0500 ACC_TYPE_SEG equ 0x1000 ACC_PRESENT equ 0x8000 ACC_TYPE_CODE_R equ 0x1a00 @@ -29,6 +30,7 @@ ACC_TYPE_DATA_R equ 0x1000 ACC_TYPE_DATA_W equ 0x1200 ACC_TYPE_LDT equ 0x0200 ACC_TYPE_TSS equ 0x0900 +ACC_TYPE_TSS16 equ 0x0100 ACC_DPL_0 equ 0x0000 ACC_DPL_1 equ 0x2000 From d9ad7aaa7331b2afec2288442fa9973f25d98320 Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 22 Mar 2026 21:55:17 +0100 Subject: [PATCH 02/62] - Improved writing 16-bit TSS entries. - Renamed 16-bit TSS CS register value to match ther other 16-bit TSS entries. --- src/test386.asm | 2 +- src/tss_p.asm | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 061000a..833c984 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -295,7 +295,7 @@ initGDT: defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT - defGDTDesc CU_SEG_PROT32_3, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT diff --git a/src/tss_p.asm b/src/tss_p.asm index f85f193..8c37e6f 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -162,7 +162,7 @@ initTSS16: mov word [es:ebp+0xA], S_SEG_PROT32_R2 ;R2 Stack mov [es:ebp+0xC], word ESP_R2_PROT ; User mode pointers - mov ax, CU_SEG_PROT32_3 ;Code segment + mov ax, CU_SEG_PROT16CS ;Code segment mov [es:ebp+0x24], ax mov [es:ebp+0x10], word 2 ;FLAGS mov [es:ebp+0x0E], TSS286entrypoint ;Code entry point @@ -170,14 +170,14 @@ initTSS16: mov [es:ebp+0x22], SU_SEG_PROT16ES ;ES mov [es:ebp+0x28], SU_SEG_PROT16DS ;DS ; General purpose registers (TODO: test patterns) - mov [es:ebp+0x12],0 ;AX - mov [es:ebp+0x14],0 ;CX - mov [es:ebp+0x16],0 ;DX - mov [es:ebp+0x18],0 ;BX + mov [es:ebp+0x12],word 0 ;AX + mov [es:ebp+0x14],word 0 ;CX + mov [es:ebp+0x16],word 0 ;DX + mov [es:ebp+0x18],word 0 ;BX mov [es:ebp+0x1A],word ESP_R3_PROT ;SP - mov [es:ebp+0x1C],0 ;BP - mov [es:ebp+0x1E],0 ;SI - mov [es:ebp+0x20],0 ;DI + mov [es:ebp+0x1C],word 0 ;BP + mov [es:ebp+0x1E],word 0 ;SI + mov [es:ebp+0x20],word 0 ;DI pop ds pop ebp pop eax From 4ce8f5d03af080a9a40c61798085566c4119114f Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 22 Mar 2026 22:00:47 +0100 Subject: [PATCH 03/62] - Implemented far pointers for user mode task switching using the TSS or using a gate. --- src/test386.asm | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 833c984..943a57d 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -344,16 +344,22 @@ ptrSSprotFLAT: ; pointer to the stack for pmode dw D_SEG_PROT32FLAT ptrTSSprot: ; pointer to the task state segment dd 0 - dw TSS_DSEG_PROT + dw TSS_DSEG_PROT|3 ptrTSSprot16: ; pointer to the 16-bit task state segment dd 0 - dw TSS_DSEG_PROT16 + dw TSS_DSEG_PROT16|3 +ptrTSSprotRaw: ; pointer to the task state segment itself + dd 0 + dw TSS_PROT|3 +ptrTSSprot16Raw: ; pointer to the 16-bit task state segment itself + dd 0 + dw TSS_PROT16|3 ptrTSSprot32Gate: ; pointer to the 32-bit task state segment gate dd 0 - dw TSS_GSEG_PROT32 + dw TSS_GSEG_PROT32|3 ptrTSSprot16Gate: ; pointer to the 16-bit task state segment gate dd 0 - dw TSS_GSEG_PROT16 + dw TSS_GSEG_PROT16|3 addrProtIDT: ; address of pmode IDT to be used with lidt dw 0x14F ; 16-bit limit dd IDT_SEG_REAL << 4 ; 32-bit base address From 91535c5278ad11e62b5f9d4243156df7b90edbc4 Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 22 Mar 2026 23:16:27 +0100 Subject: [PATCH 04/62] Implemented basic 16-bit and 32-bit TSS task switching tests with flags, segment selector and general purpose register validation. --- src/protected_rings_p.asm | 8 ++-- src/protected_tssh.asm | 61 ++++++++++++++++++++++++++-- src/test386.asm | 85 +++++++++++++++++++++++++++++++++++++-- src/tss_p.asm | 18 ++++----- 4 files changed, 153 insertions(+), 19 deletions(-) diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index 0360fe8..7d5b444 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -98,7 +98,7 @@ switchToRing3FLATkernel: or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) push dword CU_SEG_PROT32|3 ; push user code segment with RPL=3 push dword edx ; push return EIP - iretd + iretd ; ; Switches from Ring 0 to Ring 3 in flat mode @@ -132,17 +132,17 @@ switchToRing3FLATuser: mov [ebx+0x6E], ax ; save GS ; set ring 0 SS:ESP mov ax,ss - mov [ebx],ax + mov [ebx+8],ax mov [ebx+4], esp cli ; disable ints during switching push dword D_SEG_PROT32FLAT|3 ; push user stack with RPL=3 push dword ESP_R3_PROTFLAT ; push user mode esp pushfd ; push eflags or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) - push dword CU_SEG_PROT32|3 ; push user code segment with RPL=3 + push dword CU_SEG_PROT32FLAT|3 ; push user code segment with RPL=3 or edx,0xF0000 ; Fix flat instruction address push dword edx ; push return EIP - iretd + iretd ; diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 2973174..0ef247c 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -1,5 +1,60 @@ ; -; Kernel mode interrupt handler +; 16-bit TSS execution path, in parallel with test386.asm tests running in 386 mode ; -TSS286entrypoint: - ;TODO: Implement. \ No newline at end of file +errorTSS16: + mov ax,SU_SEG_PROT16SS ;SS OK? + mov ss,ax ;Fixup SS + mov esp,ESP_R3_PROT ;Fixup SP + jmp error ;Error out! + +TSS286entrypoint: + jnc errorTSS16 ;eflags loaded incorrectly? + cmp esp,0x1122 ;SP loaded correctly? + jnz errorTSS16 + mov sp,ESP_R3_PROT ;Fixup SP + cmp eax,0x1234 ;EAX loaded correctly? + jnz errorTSS16 + cmp ecx,0x5678 + jnz errorTSS16 + cmp edx,0x9ABC + jnz errorTSS16 + cmp ebx,0xDEF0 + jnz errorTSS16 + cmp ebp,0x3344 + jnz errorTSS16 + cmp esi,0x5566 + jnz errorTSS16 + cmp edi,0x7788 + jnz errorTSS16 + ;General purpose registers are loaded OK. Now check the segment registers. + pushfd + pop eax ;Validate the flags + cmp eax,2 ;Flags OK? + jnz errorTSS16_1 + mov ax,ss + cmp ax,SU_SEG_PROT16SS ;SS OK? + jnz errorTSS16_1 + mov ax,cs + cmp ax,CU_SEG_PROT16CS ;CS OK? + jnz errorTSS16_1 + mov ax,ds + cmp ax,SU_SEG_PROT16DS ;DS OK? + jnz errorTSS16_1 + mov ax,es + cmp ax,SU_SEG_PROT16ES ;ES OK? + jnz errorTSS16_1 + mov ax,fs + cmp ax,0 ;FS OK? + jnz errorTSS16_1 + mov ax,gs + cmp ax,0 ;GS OK? + jnz errorTSS16_1 + clc + iret ;Return to the calling task + jmp TSS1_returned +errorTSS16_1: ;Error occurred? + jmp error +;We return here after check #1. +TSS1_returned: + jc errorTSS16_1 ;flags register not loaded correctly if carry is set + iret ;return to the calling 32-bit task \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index 943a57d..7735b5a 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -296,6 +296,7 @@ initGDT: defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT @@ -915,6 +916,12 @@ userV86IretExitFuncLocationRet: jmp userV86ExitFuncLocation jmp error bits 32 +errorInTSS32Load: + mov ax,D_SEG_PROT32FLAT|3 ;SS safe value + mov ss,ax + mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer + jmp error ;Error out! + userV86ExitFuncRet: ; Validate the TSS task switching functionality @@ -926,9 +933,81 @@ userV86ExitFuncRet: ;Now, switch to flat user mode to start our tests call switchToRing3FLATuser ;Perform tests for 386 mode parts below - ;TODO: Actual tests using TSS task switching - - ;Finishes 386 mode parts tests, return to normal protected mode + ;Loading patterns for the 386 data segments + mov ax,SU_SEG_PROT32DS ;DS + mov ds,ax + mov ax,SU_SEG_PROT32ES ;ES + mov es,ax + mov ax,SU_SEG_PROT32FS ;FS + mov ss,ax + mov ax,SU_SEG_PROT32GS ;GS + mov gs,ax + + ;Loading patterns for the 386 data segments. + mov eax,0x12347654 + mov ecx,0x5678CBA9 + mov edx,0x9ABC3333 + mov ebx,0xDEF02222 + mov esp,0x11221111 + mov ebp,0x33447777 + mov esi,0x55665555 + mov edi,0x7788AAAA + clc ;Clear carry flag for the test + ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. + int 0x28 + jc errorInTSS32Load ;Invalid flags + ;We've returned from the test task. Verify if our registers are loaded correctly. + cmp esp,0x11221111 ;Validate the stack pointer first + jnz errorInTSS32Load + mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer, so we regain stack functionality. + cmp eax,0x12347654 + jnz error + cmp ecx,0x5678CBA9 + jnz error + cmp edx,0x9ABC3333 + jnz error + cmp ebx,0xDEF02222 + jnz error + cmp ebp,0x33447777 + jnz error + cmp esi,0x55665555 + jnz error + cmp edi,0x7788AAAA + jnz error + ;Now, validate the segment registers + mov ax,ss + cmp ax,D_SEG_PROT32FLAT|3 ;SS OK? + jnz errorTSS32_1 + mov ax,cs + cmp ax,CU_SEG_PROT32FLAT|3 ;CS OK? + jnz errorTSS32_1 + mov ax,ds + cmp ax,SU_SEG_PROT32DS ;DS OK? + jnz errorTSS32_1 + mov ax,es + cmp ax,SU_SEG_PROT32ES ;ES OK? + jnz errorTSS32_1 + mov ax,fs + cmp ax,SU_SEG_PROT32FS ;FS OK? + jnz errorTSS32_1 + mov ax,gs + cmp ax,SU_SEG_PROT32GS ;GS OK? + jnz errorTSS32_1 + jmp TSStest1finished +errorTSS32_1: + mov ax,D_SEG_PROT32FLAT|3 ;SS safe value + mov ss,ax + mov esp,ESP_R3_PROTFLAT ;ESP safe value + jmp error ;Error out + +TSStest1finished: + ;Test the flags register during task switches now. + stc ;With carry flag set. + int 0x28 + jnc errorTSS32_1 + ;32-bit eflags register loaded OK. + + ;Finishes 386 mode and 286 mode TSS tests, return to normal protected mode call switchToRing0FromFlatUser ;Restore the segment registers to compatible values call switchedToRing0FromFlat_cleanup diff --git a/src/tss_p.asm b/src/tss_p.asm index 8c37e6f..de44fc3 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -164,20 +164,20 @@ initTSS16: ; User mode pointers mov ax, CU_SEG_PROT16CS ;Code segment mov [es:ebp+0x24], ax - mov [es:ebp+0x10], word 2 ;FLAGS + mov [es:ebp+0x10], word 3 ;FLAGS with carry flag set to initialize tests mov [es:ebp+0x0E], TSS286entrypoint ;Code entry point mov [es:ebp+0x26], SU_SEG_PROT16SS ;Stack segment mov [es:ebp+0x22], SU_SEG_PROT16ES ;ES mov [es:ebp+0x28], SU_SEG_PROT16DS ;DS ; General purpose registers (TODO: test patterns) - mov [es:ebp+0x12],word 0 ;AX - mov [es:ebp+0x14],word 0 ;CX - mov [es:ebp+0x16],word 0 ;DX - mov [es:ebp+0x18],word 0 ;BX - mov [es:ebp+0x1A],word ESP_R3_PROT ;SP - mov [es:ebp+0x1C],word 0 ;BP - mov [es:ebp+0x1E],word 0 ;SI - mov [es:ebp+0x20],word 0 ;DI + mov [es:ebp+0x12],word 0x1234 ;AX + mov [es:ebp+0x14],word 0x5678 ;CX + mov [es:ebp+0x16],word 0x9ABC ;DX + mov [es:ebp+0x18],word 0xDEF0 ;BX + mov [es:ebp+0x1A],word 0x1122 ;SP + mov [es:ebp+0x1C],word 0x3344 ;BP + mov [es:ebp+0x1E],word 0x5566 ;SI + mov [es:ebp+0x20],word 0x7788 ;DI pop ds pop ebp pop eax From 8bf9a61351a7871ddcf4fdbf9b48ce2ab65a0a74 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 01:25:43 +0100 Subject: [PATCH 05/62] Improved TSS data and gate segments, allowing access in user mode for validation and task switching --- src/test386.asm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 7735b5a..02a7be3 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -291,10 +291,10 @@ initGDT: defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc TSS_PROT, 0x00004000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_PROT16, 0x00004200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_DSEG_PROT, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT - defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT + defGDTDesc TSS_DSEG_PROT, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT From f2e7c0e4d4517d5f6ade16de47e98c7a27f454aa Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 01:59:45 +0100 Subject: [PATCH 06/62] Fixed TSS segment access rights. Fixed TSS user mode accessible data segments for validation. --- src/test386.asm | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 02a7be3..bf0bb7c 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -91,7 +91,7 @@ C_SEG_REAL equ 0xf000 S_SEG_REAL equ 0x1000 IDT_SEG_REAL equ 0x0040 GDT_SEG_REAL equ 0x0060 -GDT_SEG_LIMIT equ 0x30F +GDT_SEG_LIMIT equ 0x31F %assign D1_SEG_REAL TEST_BASE1 >> 4 %assign D2_SEG_REAL TEST_BASE2 >> 4 @@ -280,8 +280,8 @@ initGDT: defGDTDesc CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 - defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000030f,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000030f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_SEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_LDT|ACC_PRESENT defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT @@ -291,8 +291,8 @@ initGDT: defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc TSS_PROT, 0x00004000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_PROT16, 0x00004200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_DSEG_PROT, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_DSEG_PROT, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT @@ -304,6 +304,8 @@ initGDT: defGDTDesc SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc TSSU_DSEG_PROT32, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSSU_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 @@ -345,10 +347,16 @@ ptrSSprotFLAT: ; pointer to the stack for pmode dw D_SEG_PROT32FLAT ptrTSSprot: ; pointer to the task state segment dd 0 - dw TSS_DSEG_PROT|3 + dw TSS_DSEG_PROT ptrTSSprot16: ; pointer to the 16-bit task state segment dd 0 - dw TSS_DSEG_PROT16|3 + dw TSS_DSEG_PROT16 +ptrTSSprotUser: ; pointer to the task state segment for user mode + dd 0 + dw TSSU_DSEG_PROT32 +ptrTSSprot16User: ; pointer to the 16-bit task state segment for user mode + dd 0 + dw TSSU_DSEG_PROT16 ptrTSSprotRaw: ; pointer to the task state segment itself dd 0 dw TSS_PROT|3 From cb71e8eac11e4996e19bcc3c6976f35eca26a169 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 02:30:11 +0100 Subject: [PATCH 07/62] Fixed TSS test initialization. Fixed TSS test data segment limits. Fixed TSS base addresses --- src/test386.asm | 12 ++++++------ src/tss_p.asm | 9 +++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index bf0bb7c..07f54a3 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -289,10 +289,10 @@ initGDT: defGDTDesc S_SEG_PROT32_R2, 0x00010000,0x0008ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc TSS_PROT, 0x00004000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_PROT16, 0x00004200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_DSEG_PROT, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSS_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSS_PROT, 0x00005000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_PROT16, 0x00005200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 + defGDTDesc TSS_DSEG_PROT, 0x00005000,0x0000006F,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSS_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT @@ -304,8 +304,8 @@ initGDT: defGDTDesc SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc TSSU_DSEG_PROT32, 0x00004000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSSU_DSEG_PROT16, 0x00004200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 diff --git a/src/tss_p.asm b/src/tss_p.asm index de44fc3..7367271 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -76,12 +76,14 @@ clearTSS: ; Prepare 32-bit TSS for task switching initTSS32: + push edi call clearTSS + pop edi ; initialize everything to 0 push eax push ebp push ds - les ebx,[cs:ptrTSSprot] + les ebp,[cs:ptrTSSprot] mov eax, cr3 mov [es:ebp+0x1C],eax ;Static field: CR3! sldt ax @@ -150,11 +152,14 @@ initTSS16: push eax push ebp push ds - les ebx,[cs:ptrTSSprot] + push edi + les edi,[cs:ptrTSSprot] xor eax, eax mov ecx, 0x15 cld rep stosw ;Clear the TSS + pop edi + les ebp,[cs:ptrTSSprot] mov [es:ebp+0],word 0 ;No back link yet! mov ax, ss ;R0 Stack mov word [es:ebp+2], ss From 2dc12a5826434ee6f7b6da4e7c65308560b940eb Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 03:00:33 +0100 Subject: [PATCH 08/62] Fixed 386 task initialization. --- src/protected_rings_p.asm | 2 +- src/test386.asm | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index 7d5b444..cf6117a 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -135,7 +135,7 @@ switchToRing3FLATuser: mov [ebx+8],ax mov [ebx+4], esp cli ; disable ints during switching - push dword D_SEG_PROT32FLAT|3 ; push user stack with RPL=3 + push dword DU_SEG_PROT32FLAT|3 ; push user stack with RPL=3 push dword ESP_R3_PROTFLAT ; push user mode esp pushfd ; push eflags or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) diff --git a/src/test386.asm b/src/test386.asm index 07f54a3..ac0bd3f 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -288,6 +288,7 @@ initGDT: defGDTDesc S_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc S_SEG_PROT32_R2, 0x00010000,0x0008ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE + defGDTDesc DU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc TSS_PROT, 0x00005000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_PROT16, 0x00005200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 @@ -296,7 +297,7 @@ initGDT: defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE + defGDTDesc CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT @@ -925,7 +926,7 @@ userV86IretExitFuncLocationRet: jmp error bits 32 errorInTSS32Load: - mov ax,D_SEG_PROT32FLAT|3 ;SS safe value + mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value mov ss,ax mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer jmp error ;Error out! @@ -942,13 +943,13 @@ userV86ExitFuncRet: call switchToRing3FLATuser ;Perform tests for 386 mode parts below ;Loading patterns for the 386 data segments - mov ax,SU_SEG_PROT32DS ;DS + mov ax,SU_SEG_PROT32DS|3 ;DS mov ds,ax - mov ax,SU_SEG_PROT32ES ;ES + mov ax,SU_SEG_PROT32ES|3 ;ES mov es,ax - mov ax,SU_SEG_PROT32FS ;FS + mov ax,SU_SEG_PROT32FS|3 ;FS mov ss,ax - mov ax,SU_SEG_PROT32GS ;GS + mov ax,SU_SEG_PROT32GS|3 ;GS mov gs,ax ;Loading patterns for the 386 data segments. @@ -984,26 +985,26 @@ userV86ExitFuncRet: jnz error ;Now, validate the segment registers mov ax,ss - cmp ax,D_SEG_PROT32FLAT|3 ;SS OK? + cmp ax,DU_SEG_PROT32FLAT|3 ;SS OK? jnz errorTSS32_1 mov ax,cs cmp ax,CU_SEG_PROT32FLAT|3 ;CS OK? jnz errorTSS32_1 mov ax,ds - cmp ax,SU_SEG_PROT32DS ;DS OK? + cmp ax,SU_SEG_PROT32DS|3 ;DS OK? jnz errorTSS32_1 mov ax,es - cmp ax,SU_SEG_PROT32ES ;ES OK? + cmp ax,SU_SEG_PROT32ES|3 ;ES OK? jnz errorTSS32_1 mov ax,fs - cmp ax,SU_SEG_PROT32FS ;FS OK? + cmp ax,SU_SEG_PROT32FS|3 ;FS OK? jnz errorTSS32_1 mov ax,gs - cmp ax,SU_SEG_PROT32GS ;GS OK? + cmp ax,SU_SEG_PROT32GS|3 ;GS OK? jnz errorTSS32_1 jmp TSStest1finished errorTSS32_1: - mov ax,D_SEG_PROT32FLAT|3 ;SS safe value + mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value mov ss,ax mov esp,ESP_R3_PROTFLAT ;ESP safe value jmp error ;Error out From ff2266b700186d157ca94fd83a0b464ef11b0089 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 03:18:48 +0100 Subject: [PATCH 09/62] Fixed 386 task FS loading to properly load the correct register. --- src/test386.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test386.asm b/src/test386.asm index ac0bd3f..a3c5d4b 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -948,7 +948,7 @@ userV86ExitFuncRet: mov ax,SU_SEG_PROT32ES|3 ;ES mov es,ax mov ax,SU_SEG_PROT32FS|3 ;FS - mov ss,ax + mov fs,ax mov ax,SU_SEG_PROT32GS|3 ;GS mov gs,ax From ec5761fe83af4967dc3c36c8b6e7141761624d2d Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 03:27:46 +0100 Subject: [PATCH 10/62] Fixed 16-bit TSS initialization to write to the correct TSS. --- src/tss_p.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tss_p.asm b/src/tss_p.asm index 7367271..258067f 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -159,7 +159,7 @@ initTSS16: cld rep stosw ;Clear the TSS pop edi - les ebp,[cs:ptrTSSprot] + les ebp,[cs:ptrTSSprot16] mov [es:ebp+0],word 0 ;No back link yet! mov ax, ss ;R0 Stack mov word [es:ebp+2], ss From 9c9951b5b995444067fc99dbaec361a27e6bd587 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 03:56:28 +0100 Subject: [PATCH 11/62] - Fixed 16-bit task initialization. --- src/tss_p.asm | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/tss_p.asm b/src/tss_p.asm index 258067f..0b96c26 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -91,8 +91,8 @@ initTSS32: ;Ring 0 SS:ESP is loaded by the ring switching function mov eax,0xFFFFFFFF mov [es:ebp+0x20],eax ;Initial EIP, should be overwritten by the task switch. - mov word [es:ebp+4], S_SEG_PROT32_R2 ;R2 Stack - mov [es:ebp+8], word ESP_R2_PROT + mov word [es:ebp+0x14], S_SEG_PROT32_R2 ;R2 Stack + mov word [es:ebp+0x18], ESP_R2_PROT pop ds pop ebp pop eax @@ -155,34 +155,34 @@ initTSS16: push edi les edi,[cs:ptrTSSprot] xor eax, eax - mov ecx, 0x15 + mov ecx, 0x16 cld - rep stosw ;Clear the TSS + rep stosw ;Clear the TSS pop edi les ebp,[cs:ptrTSSprot16] - mov [es:ebp+0],word 0 ;No back link yet! - mov ax, ss ;R0 Stack + mov [es:ebp+0],word 0 ;No back link yet! + mov ax, ss ;R0 Stack mov word [es:ebp+2], ss mov [es:ebp+4], word ESP_R0_PROT - mov word [es:ebp+0xA], S_SEG_PROT32_R2 ;R2 Stack + mov word [es:ebp+0xA], S_SEG_PROT32_R2 ;R2 Stack mov [es:ebp+0xC], word ESP_R2_PROT ; User mode pointers - mov ax, CU_SEG_PROT16CS ;Code segment + mov ax, CU_SEG_PROT16CS|3 ;Code segment mov [es:ebp+0x24], ax - mov [es:ebp+0x10], word 3 ;FLAGS with carry flag set to initialize tests - mov [es:ebp+0x0E], TSS286entrypoint ;Code entry point - mov [es:ebp+0x26], SU_SEG_PROT16SS ;Stack segment - mov [es:ebp+0x22], SU_SEG_PROT16ES ;ES - mov [es:ebp+0x28], SU_SEG_PROT16DS ;DS + mov [es:ebp+0x10], word 3 ;FLAGS with carry flag set to initialize tests + mov [es:ebp+0x0E], TSS286entrypoint ;Code entry point + mov [es:ebp+0x26], word SU_SEG_PROT16SS|3 ;Stack segment + mov [es:ebp+0x22], word SU_SEG_PROT16ES|3 ;ES + mov [es:ebp+0x28], word SU_SEG_PROT16DS|3 ;DS ; General purpose registers (TODO: test patterns) - mov [es:ebp+0x12],word 0x1234 ;AX - mov [es:ebp+0x14],word 0x5678 ;CX - mov [es:ebp+0x16],word 0x9ABC ;DX - mov [es:ebp+0x18],word 0xDEF0 ;BX - mov [es:ebp+0x1A],word 0x1122 ;SP - mov [es:ebp+0x1C],word 0x3344 ;BP - mov [es:ebp+0x1E],word 0x5566 ;SI - mov [es:ebp+0x20],word 0x7788 ;DI + mov [es:ebp+0x12],word 0x1234 ;AX + mov [es:ebp+0x14],word 0x5678 ;CX + mov [es:ebp+0x16],word 0x9ABC ;DX + mov [es:ebp+0x18],word 0xDEF0 ;BX + mov [es:ebp+0x1A],word 0x1122 ;SP + mov [es:ebp+0x1C],word 0x3344 ;BP + mov [es:ebp+0x1E],word 0x5566 ;SI + mov [es:ebp+0x20],word 0x7788 ;DI pop ds pop ebp pop eax From 09ee245bdcee211027c4838eec29063a37165d91 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 04:05:48 +0100 Subject: [PATCH 12/62] Fixed the 286 task entry point. --- src/tss_p.asm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tss_p.asm b/src/tss_p.asm index 0b96c26..c828d95 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -170,7 +170,8 @@ initTSS16: mov ax, CU_SEG_PROT16CS|3 ;Code segment mov [es:ebp+0x24], ax mov [es:ebp+0x10], word 3 ;FLAGS with carry flag set to initialize tests - mov [es:ebp+0x0E], TSS286entrypoint ;Code entry point + mov ax,TSS286entrypoint + mov [es:ebp+0x0E], ax ;Code entry point mov [es:ebp+0x26], word SU_SEG_PROT16SS|3 ;Stack segment mov [es:ebp+0x22], word SU_SEG_PROT16ES|3 ;ES mov [es:ebp+0x28], word SU_SEG_PROT16DS|3 ;DS From 73ff778679ac302aefbb13e9004c694cf1d3cb74 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 04:40:47 +0100 Subject: [PATCH 13/62] Fixed 286 task validation of segment selectors. Improved 286 and 386 task flags validation. --- src/protected_tssh.asm | 22 ++++++++++++++-------- src/test386.asm | 4 ++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 0ef247c..a50823d 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -9,6 +9,10 @@ errorTSS16: TSS286entrypoint: jnc errorTSS16 ;eflags loaded incorrectly? + mov [0], eax + lahf + mov byte [4],ah ;Store the flags for checking later + mov eax,[0] cmp esp,0x1122 ;SP loaded correctly? jnz errorTSS16 mov sp,ESP_R3_PROT ;Fixup SP @@ -27,27 +31,29 @@ TSS286entrypoint: cmp edi,0x7788 jnz errorTSS16 ;General purpose registers are loaded OK. Now check the segment registers. + movzx eax,byte [4] ;Validate the flags + sahf ;Restore the original flags of the task pushfd - pop eax ;Validate the flags - cmp eax,2 ;Flags OK? + pop eax + cmp eax,0x4002 ;Flags OK? jnz errorTSS16_1 mov ax,ss - cmp ax,SU_SEG_PROT16SS ;SS OK? + cmp ax,SU_SEG_PROT16SS|3 ;SS OK? jnz errorTSS16_1 mov ax,cs - cmp ax,CU_SEG_PROT16CS ;CS OK? + cmp ax,CU_SEG_PROT16CS|3 ;CS OK? jnz errorTSS16_1 mov ax,ds - cmp ax,SU_SEG_PROT16DS ;DS OK? + cmp ax,SU_SEG_PROT16DS|3 ;DS OK? jnz errorTSS16_1 mov ax,es - cmp ax,SU_SEG_PROT16ES ;ES OK? + cmp ax,SU_SEG_PROT16ES|3 ;ES OK? jnz errorTSS16_1 mov ax,fs - cmp ax,0 ;FS OK? + cmp ax,0 ;FS OK? jnz errorTSS16_1 mov ax,gs - cmp ax,0 ;GS OK? + cmp ax,0 ;GS OK? jnz errorTSS16_1 clc iret ;Return to the calling task diff --git a/src/test386.asm b/src/test386.asm index a3c5d4b..2367a3d 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -965,6 +965,10 @@ userV86ExitFuncRet: ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. int 0x28 jc errorInTSS32Load ;Invalid flags + mov dword [0x10000],eax + lahf + mov byte [0x10004],ah + mov eax,dword [0x10000] ;We've returned from the test task. Verify if our registers are loaded correctly. cmp esp,0x11221111 ;Validate the stack pointer first jnz errorInTSS32Load From 93b2ecaf2e5d4beb806ca148e860ce37014f8360 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 05:30:37 +0100 Subject: [PATCH 14/62] Fixed setup of CR3 and related fields in the 32-bit TSS. Fixed 16-bit TSS initialization not to clear the 32-bit TSS. --- src/tss_p.asm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tss_p.asm b/src/tss_p.asm index c828d95..90dc976 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -85,14 +85,14 @@ initTSS32: push ds les ebp,[cs:ptrTSSprot] mov eax, cr3 - mov [es:ebp+0x1C],eax ;Static field: CR3! + mov dword [es:ebp+0x1C],eax ;Static field: CR3! sldt ax mov word [es:ebp+0x60], ax ;Static field: LDTR! ;Ring 0 SS:ESP is loaded by the ring switching function mov eax,0xFFFFFFFF mov [es:ebp+0x20],eax ;Initial EIP, should be overwritten by the task switch. mov word [es:ebp+0x14], S_SEG_PROT32_R2 ;R2 Stack - mov word [es:ebp+0x18], ESP_R2_PROT + mov dword [es:ebp+0x18], ESP_R2_PROT pop ds pop ebp pop eax @@ -153,7 +153,7 @@ initTSS16: push ebp push ds push edi - les edi,[cs:ptrTSSprot] + les edi,[cs:ptrTSSprot16] xor eax, eax mov ecx, 0x16 cld From d62698139d539fd747057a57ad6ef8b11c722329 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 15:27:34 +0100 Subject: [PATCH 15/62] Fixed TSS table layout and ring 2 stack pointers. --- src/tss_p.asm | 108 +++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/tss_p.asm b/src/tss_p.asm index 90dc976..f48cefc 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -84,68 +84,68 @@ initTSS32: push ebp push ds les ebp,[cs:ptrTSSprot] - mov eax, cr3 + mov eax, cr3 mov dword [es:ebp+0x1C],eax ;Static field: CR3! sldt ax mov word [es:ebp+0x60], ax ;Static field: LDTR! ;Ring 0 SS:ESP is loaded by the ring switching function mov eax,0xFFFFFFFF mov [es:ebp+0x20],eax ;Initial EIP, should be overwritten by the task switch. - mov word [es:ebp+0x14], S_SEG_PROT32_R2 ;R2 Stack - mov dword [es:ebp+0x18], ESP_R2_PROT - pop ds - pop ebp - pop eax + mov word [es:ebp+0x18], S_SEG_PROT32_R2 ;R2 Stack + mov dword [es:ebp+0x14], ESP_R2_PROT + pop ds + pop ebp + pop eax ret ; 16-bit Task State Segment ; -; 31 23 15 7 0 -; ╔═══════════════╪═══════════════╬═══════════════╪═════════════╦═╗ -; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ LDT ║2A -; ╟───────────────┼───────────────╫───────────────┼───────────────╢ -; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ DS ║28 -; ╟───────────────┼───────────────╫───────────────┼───────────────╢ -; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ SS ║26 -; ╟───────────────┼───────────────╫───────────────┼───────────────╢ -; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ CS ║24 -; ╟───────────────┼───────────────╫───────────────┼───────────────╢ -; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ ES ║22 -; ╟───────────────┼───────────────╫───────────────┼───────────────╢ -; ║ DI ║20 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ SI ║1E -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ BP ║1C -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ SP ║1A -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ BX ║18 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ DX ║16 -; ╠═══════════════╪═══════════════╪═══════════════╪═══════════════╣ -; ║ CX ║14 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ AX ║12 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; 15 7 0 +; ╔═══════════════════════════════╬═══════════════════════════════╗ +; ║ LDT ║2A +; ╟───────────────────────────────╫───────────────────────────────╢ +; ║ DS ║28 +; ╟───────────────────────────────╫───────────────────────────────╢ +; ║ SS ║26 +; ╟───────────────────────────────╫───────────────────────────────╢ +; ║ CS ║24 +; ╟───────────────────────────────╫───────────────────────────────╢ +; ║ ES ║22 +; ╟───────────────────────────────╫───────────────────────────────╢ +; ║ DI ║20 +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ SI ║1E +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ BP ║1C +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ SP ║1A +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ BX ║18 +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ DX ║16 +; ╠═══════════════─═══════════════╪═══════════════─═══════════════╣ +; ║ CX ║14 +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ AX ║12 +; ╟───────────────────────────────┼───────────────────────────────╢ ; ║ FLAGS ║10 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ INSTRUCTION POINTER (IP) ║E -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0║ SS2 ║C -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ INSTRUCTION POINTER (IP) ║E +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ SS2 ║C +; ╟───────────────────────────────┼───────────────────────────────╢ ; ║ ESP2 ║A -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ SS1 ║8 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ SS1 ║8 +; ╟───────────────────────────────┼───────────────────────────────╢ ; ║ ESP1 ║6 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ SS0 ║4 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ SS0 ║4 +; ╟───────────────────────────────┼───────────────────────────────╢ ; ║ ESP0 ║2 -; ╟───────────────┼───────────────┼───────────────┼───────────────╢ -; ║ BACK LINK TO PREVIOUS TSS ║0 -; ╚═══════════════╪═══════════════╬═══════════════╪═══════════════╝ +; ╟───────────────────────────────┼───────────────────────────────╢ +; ║ BACK LINK TO PREVIOUS TSS ║0 +; ╚═══════════════════════════════╬═══════════════════════════════╝ initTSS16: ; initialize everything to predefined values @@ -164,8 +164,8 @@ initTSS16: mov ax, ss ;R0 Stack mov word [es:ebp+2], ss mov [es:ebp+4], word ESP_R0_PROT - mov word [es:ebp+0xA], S_SEG_PROT32_R2 ;R2 Stack - mov [es:ebp+0xC], word ESP_R2_PROT + mov word [es:ebp+0xA], ESP_R2_PROT ;R2 Stack + mov word [es:ebp+0xC], S_SEG_PROT32_R2 ; User mode pointers mov ax, CU_SEG_PROT16CS|3 ;Code segment mov [es:ebp+0x24], ax @@ -184,8 +184,8 @@ initTSS16: mov [es:ebp+0x1C],word 0x3344 ;BP mov [es:ebp+0x1E],word 0x5566 ;SI mov [es:ebp+0x20],word 0x7788 ;DI - pop ds - pop ebp - pop eax + pop ds + pop ebp + pop eax ret - \ No newline at end of file + From 50fb3a38fd56c1df6d270989c4a13e56b6df75a5 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 15:31:44 +0100 Subject: [PATCH 16/62] Update comment for data segment restoration when returning to kernel mode from flat user mode. --- src/protected_rings_p.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index cf6117a..2f0fde3 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -122,7 +122,7 @@ switchToRing3FLATuser: pop edx ; read the return offset mov ax, ds lds ebx, [cs:ptrTSSprot] - ; save ring 0 data segments, they'll be restored with switchToRing0 + ; save ring 0 data segments, they'll be restored with switchedToRing0FromFlat_cleanup mov [ebx+0x68], ax ; save DS mov ax, es mov [ebx+0x6A], ax ; save ES From d8840247ad4c4b320a17505858a660659e6c0ed1 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 17:05:36 +0100 Subject: [PATCH 17/62] Match the flat user code ring 0 setup against the other ring setup code. --- src/protected_rings_p.asm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index 2f0fde3..be6c9d3 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -131,9 +131,9 @@ switchToRing3FLATuser: mov ax, gs mov [ebx+0x6E], ax ; save GS ; set ring 0 SS:ESP - mov ax,ss - mov [ebx+8],ax mov [ebx+4], esp + mov eax, ss + mov [ebx+8], eax cli ; disable ints during switching push dword DU_SEG_PROT32FLAT|3 ; push user stack with RPL=3 push dword ESP_R3_PROTFLAT ; push user mode esp From ec2811a17c83f1abb595fe96cf1bb1e36cc71ae6 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 18:22:19 +0100 Subject: [PATCH 18/62] - Fixed return from 32-bit flat user mode to 16-bit kernel mode. --- src/protected_rings_p.asm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index be6c9d3..f1392f5 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -285,7 +285,8 @@ switchToRing0FromFlatUser: ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. ; A placeholder for a Call Gate is already present in the GDT. pop ecx ; read the return offset - lfs ebx, [cs:ptrGDTUprot] + and ecx,0xffff ;Make sure it's a 16-bit code offset. + lfs ebx, [cs:ptrGDTUprot+0xF0000] mov eax, RING0_GATE mov esi, C_SEG_PROT32 mov edi, .ring0_fromflatuser From e0f21b7db28604ea8458a285c558bf15cdcf1b66 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 23 Mar 2026 19:30:16 +0100 Subject: [PATCH 19/62] Improved 16-bit TSS validation. --- src/protected_tssh.asm | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index a50823d..79ffe17 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -2,10 +2,10 @@ ; 16-bit TSS execution path, in parallel with test386.asm tests running in 386 mode ; errorTSS16: - mov ax,SU_SEG_PROT16SS ;SS OK? - mov ss,ax ;Fixup SS - mov esp,ESP_R3_PROT ;Fixup SP - jmp error ;Error out! + mov ax,SU_SEG_PROT16SS|3 ;SS OK? + mov ss,ax ;Fixup SS + mov esp,ESP_R3_PROT ;Fixup SP + jmp error ;Error out! TSS286entrypoint: jnc errorTSS16 ;eflags loaded incorrectly? @@ -34,8 +34,9 @@ TSS286entrypoint: movzx eax,byte [4] ;Validate the flags sahf ;Restore the original flags of the task pushfd + and word [esp],0x40FF ;Make sure that the flags are properly tested. pop eax - cmp eax,0x4002 ;Flags OK? + cmp eax,0x4003 ;Flags OK? jnz errorTSS16_1 mov ax,ss cmp ax,SU_SEG_PROT16SS|3 ;SS OK? From 310c575c67d15b0319592ac111ea8698f25f6e88 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 24 Mar 2026 13:02:08 +0100 Subject: [PATCH 20/62] Improved 286 and 386 task flags test. Implemented 286 and 386 task LDTR test. --- src/protected_tssh.asm | 15 ++++++++++++--- src/test386.asm | 23 ++++++++++++++--------- src/tss_p.asm | 4 +++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 79ffe17..dddb630 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -31,7 +31,7 @@ TSS286entrypoint: cmp edi,0x7788 jnz errorTSS16 ;General purpose registers are loaded OK. Now check the segment registers. - movzx eax,byte [4] ;Validate the flags + mov ah,[4] ;Validate the flags sahf ;Restore the original flags of the task pushfd and word [esp],0x40FF ;Make sure that the flags are properly tested. @@ -56,7 +56,10 @@ TSS286entrypoint: mov ax,gs cmp ax,0 ;GS OK? jnz errorTSS16_1 - clc + sldt ax + cmp ax,LDT_SEG_PROT286 ;LDT OK? + jnz errorTSS16_1 + clc ;Prepare test for us iret ;Return to the calling task jmp TSS1_returned errorTSS16_1: ;Error occurred? @@ -64,4 +67,10 @@ errorTSS16_1: ;Error occurred? ;We return here after check #1. TSS1_returned: jc errorTSS16_1 ;flags register not loaded correctly if carry is set - iret ;return to the calling 32-bit task \ No newline at end of file + clc ; Clear carry flag for set test in 32-bit task. + iret ;return to the calling 32-bit task + clc ; Clear carry flag for set test in 32-bit task. + iret ;return to the calling 32-bit task + stc ; Set carry flag for set test in 32-bit task + iret ;return to the calling 32-bit task + \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index 2367a3d..00d042f 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -282,7 +282,8 @@ initGDT: defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 - defGDTDesc LDT_SEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_LDT|ACC_PRESENT + defGDTDesc LDT_SEG_PROT, 0x00000A00,0x000005f7,ACC_TYPE_LDT|ACC_PRESENT + defGDTDesc LDT_SEG_PROT286, 0x00000FF8,0x00000000,ACC_TYPE_LDT|ACC_PRESENT defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc S_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT @@ -961,14 +962,9 @@ userV86ExitFuncRet: mov ebp,0x33447777 mov esi,0x55665555 mov edi,0x7788AAAA - clc ;Clear carry flag for the test + clc ;Clear carry flag for the 286 test and us ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. int 0x28 - jc errorInTSS32Load ;Invalid flags - mov dword [0x10000],eax - lahf - mov byte [0x10004],ah - mov eax,dword [0x10000] ;We've returned from the test task. Verify if our registers are loaded correctly. cmp esp,0x11221111 ;Validate the stack pointer first jnz errorInTSS32Load @@ -1006,6 +1002,9 @@ userV86ExitFuncRet: mov ax,gs cmp ax,SU_SEG_PROT32GS|3 ;GS OK? jnz errorTSS32_1 + sldt ax + cmp ax,LDT_SEG_PROT ;LDT OK? + jnz errorTSS16_1 jmp TSStest1finished errorTSS32_1: mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value @@ -1015,9 +1014,15 @@ errorTSS32_1: TSStest1finished: ;Test the flags register during task switches now. - stc ;With carry flag set. + stc ;With carry flag set for testing the 286 task. int 0x28 - jnc errorTSS32_1 + ;Now the 286 flags are tested. Now test ours. + stc ;With carry flag set in our task. + int 0x28 + jnc error ;Carry got set? + clc ;With carry flag cleared in our task. + int 0x28 + jc error ;Carry got cleared? ;32-bit eflags register loaded OK. ;Finishes 386 mode and 286 mode TSS tests, return to normal protected mode diff --git a/src/tss_p.asm b/src/tss_p.asm index f48cefc..7674fb1 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -175,7 +175,9 @@ initTSS16: mov [es:ebp+0x26], word SU_SEG_PROT16SS|3 ;Stack segment mov [es:ebp+0x22], word SU_SEG_PROT16ES|3 ;ES mov [es:ebp+0x28], word SU_SEG_PROT16DS|3 ;DS - ; General purpose registers (TODO: test patterns) + mov ax,LDT_SEG_PROT286 ;LDT + mov [es:ebp+0x2A],ax + ; General purpose registers (they are zero extended into 32-bits) mov [es:ebp+0x12],word 0x1234 ;AX mov [es:ebp+0x14],word 0x5678 ;CX mov [es:ebp+0x16],word 0x9ABC ;DX From eb853d9ccbb59a03929f34a2c344e8895451e469 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 24 Mar 2026 14:42:02 +0100 Subject: [PATCH 21/62] Implemented seperated 16-bit and 32-bit TSS flags testing. --- src/protected_rings_p.asm | 2 +- src/protected_tssh.asm | 56 ++++++++++++++++++++++++++---------- src/test386.asm | 60 ++++++++++++++++++++++++++++++++------- src/tss_p.asm | 2 +- src/x86_e.asm | 10 +++++++ 5 files changed, 102 insertions(+), 28 deletions(-) diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index f1392f5..7906eed 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -138,7 +138,7 @@ switchToRing3FLATuser: push dword DU_SEG_PROT32FLAT|3 ; push user stack with RPL=3 push dword ESP_R3_PROTFLAT ; push user mode esp pushfd ; push eflags - or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) + ; don't reenable interrupts in ring 3 (can't use privileged sti) for ease of testing push dword CU_SEG_PROT32FLAT|3 ; push user code segment with RPL=3 or edx,0xF0000 ; Fix flat instruction address push dword edx ; push return EIP diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index dddb630..414b6ea 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -8,11 +8,6 @@ errorTSS16: jmp error ;Error out! TSS286entrypoint: - jnc errorTSS16 ;eflags loaded incorrectly? - mov [0], eax - lahf - mov byte [4],ah ;Store the flags for checking later - mov eax,[0] cmp esp,0x1122 ;SP loaded correctly? jnz errorTSS16 mov sp,ESP_R3_PROT ;Fixup SP @@ -31,12 +26,10 @@ TSS286entrypoint: cmp edi,0x7788 jnz errorTSS16 ;General purpose registers are loaded OK. Now check the segment registers. - mov ah,[4] ;Validate the flags - sahf ;Restore the original flags of the task pushfd - and word [esp],0x40FF ;Make sure that the flags are properly tested. + and word [esp],0x4000 ;Make sure that the flags are properly tested. pop eax - cmp eax,0x4003 ;Flags OK? + cmp eax,0x4000 ;Flags OK? jnz errorTSS16_1 mov ax,ss cmp ax,SU_SEG_PROT16SS|3 ;SS OK? @@ -59,18 +52,51 @@ TSS286entrypoint: sldt ax cmp ax,LDT_SEG_PROT286 ;LDT OK? jnz errorTSS16_1 - clc ;Prepare test for us iret ;Return to the calling task jmp TSS1_returned errorTSS16_1: ;Error occurred? jmp error ;We return here after check #1. TSS1_returned: - jc errorTSS16_1 ;flags register not loaded correctly if carry is set - clc ; Clear carry flag for set test in 32-bit task. + ;Now we start our TSS flags test + pushfd + pop eax + test ax,PS_NT ;Task properly nested? + jz error + push dword (FLAGS_SET|PS_NT) + popfd ;Set the flags to test. iret ;return to the calling 32-bit task - clc ; Clear carry flag for set test in 32-bit task. + ;Now the flags should have been set. + pushfd + cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + jnz errorTSS16_1 + popfd + push dword (FLAGS_CLEARED|PS_NT) + popfd ;Clear the flags to test iret ;return to the calling 32-bit task - stc ; Set carry flag for set test in 32-bit task + pushfd + cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + popfd ;Restore the flags iret ;return to the calling 32-bit task - \ No newline at end of file + + ;Now, we switched sides to test. + ;Test the flags register during task switches now. + int 0x29 ;Start testing the 386 flags + push dword FLAGS_CLEARED + popfd ;Clear the flags to test. + int 0x29 ;Continue testing the 386 flags + push dword FLAGS_SET + popfd ;Set the flags to test. + int 0x29 ;Third stage of the flags test. + ;386 flags test completed. + + ;Now, we switch sides + jmp far [cs:ptrTSSprot32Gate] + + ;We've been far called. Return. + iretd + + ;We're the parent task again. + call far [cs:ptrTSSprot32Gate] + ;Now, we switch sides + jmp far [cs:ptrTSSprot32Gate] \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index 00d042f..5005205 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -774,7 +774,6 @@ userFarFunc: ; %include "protected_tssh.asm" - userRetfErrorFunction: ; From user mode to kernel mode error address, which isn't allowed. push C_SEG_PROT32 @@ -933,7 +932,11 @@ errorInTSS32Load: jmp error ;Error out! userV86ExitFuncRet: - + pushfd + pop eax + and eax,0xCDFF ;Clear IOPL and interrupt flag again, as it cannot be changed in user mode + push eax + popfd ; Validate the TSS task switching functionality ;First, enter a valid 32-bit protected flat mode for testing ;Prepare 32-bit TSS ROM fields @@ -1014,15 +1017,50 @@ errorTSS32_1: TSStest1finished: ;Test the flags register during task switches now. - stc ;With carry flag set for testing the 286 task. - int 0x28 - ;Now the 286 flags are tested. Now test ours. - stc ;With carry flag set in our task. - int 0x28 - jnc error ;Carry got set? - clc ;With carry flag cleared in our task. - int 0x28 - jc error ;Carry got cleared? + int 0x28 ;Start testing the 286 flags + push dword FLAGS_CLEARED + popfd ;Clear the flags to test. + int 0x28 ;Continue testing the 286 flags + push dword FLAGS_SET + popfd ;Set the flags to test. + int 0x28 ;Third stage of the flags test. + ;286 flags test completed. + + ;Now, we switch sides + jmp far [cs:ptrTSSprot16Gate+0xF0000] + + ;Now we start our TSS flags test + pushfd + pop eax + test ax,PS_NT ;Task properly nested? + jz error + push dword (FLAGS_SET|PS_NT) + popfd ;Set the flags to test. + iretd ;return to the calling 16-bit task + ;Now the flags should have been set. + pushfd + cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + jnz errorTSS16_1 + popfd + push dword (FLAGS_CLEARED|PS_NT) + popfd ;Clear the flags to test + iretd ;return to the calling 16-bit task + pushfd + cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + popfd ;Restore the flags + iretd ;return to the calling 16-bit task + + ;We're the parent task again. + ;Perform call tests now + call far [cs:ptrTSSprot16Gate+0xF0000] + ;Now, we switch sides + jmp far [cs:ptrTSSprot16Gate+0xF0000] + ;We've been far called. Return. + iretd + ;We're the parent task again. + + + ;32-bit eflags register loaded OK. ;Finishes 386 mode and 286 mode TSS tests, return to normal protected mode diff --git a/src/tss_p.asm b/src/tss_p.asm index 7674fb1..af8b20e 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -169,7 +169,7 @@ initTSS16: ; User mode pointers mov ax, CU_SEG_PROT16CS|3 ;Code segment mov [es:ebp+0x24], ax - mov [es:ebp+0x10], word 3 ;FLAGS with carry flag set to initialize tests + mov [es:ebp+0x10], word 2 ;FLAGS set to initialize tests mov ax,TSS286entrypoint mov [es:ebp+0x0E], ax ;Code entry point mov [es:ebp+0x26], word SU_SEG_PROT16SS|3 ;Stack segment diff --git a/src/x86_e.asm b/src/x86_e.asm index 0aea4c7..ac70707 100644 --- a/src/x86_e.asm +++ b/src/x86_e.asm @@ -7,6 +7,7 @@ PS_TF equ 0x0100 PS_IF equ 0x0200 PS_DF equ 0x0400 PS_OF equ 0x0800 +PS_NT equ 0x4000 PS_ARITH equ (PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF) PS_LOGIC equ (PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF) PS_MULTIPLY equ (PS_CF | PS_OF) ; only CF and OF are "defined" following MUL or IMUL @@ -17,6 +18,15 @@ PS_SHIFTS_R equ (PS_CF | PS_SF | PS_ZF | PS_PF) CR0_MSW_PE equ 0x0001 CR0_PG equ 0x80000000 ; set if paging enabled +;Flags available for load/store tests in user mode +FLAGS_AVL equ (PS_CF|PS_PF|PS_AF|PS_ZF|PS_SF|PS_DF|PS_PF) +;Flags that are always set. +FLAGS_FORCEDSET equ 2 +;Flags that are all set +FLAGS_SET equ (FLAGS_AVL|FLAGS_FORCEDSET) +;Flags that are all cleared +FLAGS_CLEARED equ FLAGS_FORCEDSET + ACC_TYPE_GATE386_INT equ 0x0E00 ACC_TYPE_GATE286_INT equ 0x0600 ACC_TYPE_GATE386_CALL equ 0x0C00 From a8bbb3bd3f48043ff8c4a466c093d5ddaf69ab02 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 24 Mar 2026 15:15:19 +0100 Subject: [PATCH 22/62] The upper 16 bits of a 16-bit TSS task switched general purpose register are always set. --- src/protected_tssh.asm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 414b6ea..b4cdf8a 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -8,22 +8,22 @@ errorTSS16: jmp error ;Error out! TSS286entrypoint: - cmp esp,0x1122 ;SP loaded correctly? + cmp esp,0xFFFF1122 ;SP loaded correctly? jnz errorTSS16 mov sp,ESP_R3_PROT ;Fixup SP - cmp eax,0x1234 ;EAX loaded correctly? + cmp eax,0xFFFF1234 ;EAX loaded correctly? jnz errorTSS16 - cmp ecx,0x5678 + cmp ecx,0xFFFF5678 jnz errorTSS16 - cmp edx,0x9ABC + cmp edx,0xFFFF9ABC jnz errorTSS16 - cmp ebx,0xDEF0 + cmp ebx,0xFFFFDEF0 jnz errorTSS16 - cmp ebp,0x3344 + cmp ebp,0xFFFF3344 jnz errorTSS16 - cmp esi,0x5566 + cmp esi,0xFFFF5566 jnz errorTSS16 - cmp edi,0x7788 + cmp edi,0xFFFF7788 jnz errorTSS16 ;General purpose registers are loaded OK. Now check the segment registers. pushfd From 5a60e24cf80ff4f4d1bab8a78f788cbfd4c7ec47 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 24 Mar 2026 15:37:07 +0100 Subject: [PATCH 23/62] Fixed 16-bit TSS limit to use a proper 16-bit stack pointer. --- src/protected_tssh.asm | 18 +++++++++--------- src/test386.asm | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index b4cdf8a..c62a5a6 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -2,10 +2,10 @@ ; 16-bit TSS execution path, in parallel with test386.asm tests running in 386 mode ; errorTSS16: - mov ax,SU_SEG_PROT16SS|3 ;SS OK? - mov ss,ax ;Fixup SS - mov esp,ESP_R3_PROT ;Fixup SP - jmp error ;Error out! + mov ax,SU_SEG_PROT16SS|3 ;SS OK? + mov ss,ax ;Fixup SS + mov esp,(ESP_R3_PROT|0xFFFF0000) ;Fixup SP + jmp error ;Error out! TSS286entrypoint: cmp esp,0xFFFF1122 ;SP loaded correctly? @@ -27,8 +27,8 @@ TSS286entrypoint: jnz errorTSS16 ;General purpose registers are loaded OK. Now check the segment registers. pushfd - and word [esp],0x4000 ;Make sure that the flags are properly tested. pop eax + and eax,0x4000 ;Make sure that the flags are properly tested. cmp eax,0x4000 ;Flags OK? jnz errorTSS16_1 mov ax,ss @@ -68,15 +68,15 @@ TSS1_returned: iret ;return to the calling 32-bit task ;Now the flags should have been set. pushfd - cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + pop eax + cmp eax,dword (FLAGS_SET|PS_NT) ;Correct? jnz errorTSS16_1 - popfd push dword (FLAGS_CLEARED|PS_NT) popfd ;Clear the flags to test iret ;return to the calling 32-bit task pushfd - cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? - popfd ;Restore the flags + pop eax + cmp eax,dword (FLAGS_SET|PS_NT) ;Correct? iret ;return to the calling 32-bit task ;Now, we switched sides to test. diff --git a/src/test386.asm b/src/test386.asm index 5005205..789bf85 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -303,7 +303,7 @@ initGDT: defGDTDesc SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT32GS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT defGDTDesc SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT From 5df97dbb9280c1d612594ecd2d8282aeebd6625f Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 24 Mar 2026 15:59:48 +0100 Subject: [PATCH 24/62] Fixed 32-bit TSS flags tests. --- src/test386.asm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 789bf85..e5a5c94 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -1040,13 +1040,14 @@ TSStest1finished: ;Now the flags should have been set. pushfd cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? - jnz errorTSS16_1 + jnz errorTSS32_1 popfd push dword (FLAGS_CLEARED|PS_NT) popfd ;Clear the flags to test iretd ;return to the calling 16-bit task pushfd - cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + cmp [esp],dword (FLAGS_CLEARED|PS_NT) ;Correct? + jnz errorTSS32_1 popfd ;Restore the flags iretd ;return to the calling 16-bit task From 4ad0dc1d453758e6532410a7d6d6b8ae3d4fe45b Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 27 Mar 2026 16:31:51 +0100 Subject: [PATCH 25/62] Moved the entire 16-bit and 32-bit TSS task switch tests to the lower 64KB of a configurable 128KB ROM. --- src/configuration.asm | 2 + src/protected_m.asm | 31 ++++ src/protected_tssh.asm | 5 + src/test386.asm | 346 ++++++++++++++++++++++++----------------- src/tss_p.asm | 2 + 5 files changed, 241 insertions(+), 145 deletions(-) diff --git a/src/configuration.asm b/src/configuration.asm index 3ec7149..09ed3a9 100644 --- a/src/configuration.asm +++ b/src/configuration.asm @@ -53,5 +53,7 @@ IBM_PS1 equ 0 ; debugging). DEBUG equ 0 +; Enable additional tests using a 128KB ROM. +ROM128 equ 0 ; == END OF CONFIGURATION ====================================================== diff --git a/src/protected_m.asm b/src/protected_m.asm index ad7120c..7f6e215 100644 --- a/src/protected_m.asm +++ b/src/protected_m.asm @@ -63,6 +63,37 @@ %assign GDTSelDesc GDTSelDesc+8 %endmacro +; +; Defines an (earlier) defined GDT descriptor in RAM, given a name (%1), base (%2), limit (%3), +; acc byte (%4), and ext nibble (%5) +; + +;First, the prototype version, used in earlier code +%macro defGDTDescPrototype 1 + %assign %1 GDTSelDesc + %assign GDTSelDesc GDTSelDesc+8 + ;The GDT is prototyped. + %define GDTprototyped +%endmacro +;Second, the implementation +%macro defGDTDescImplementation 1-5 0,0,0,0 + %ifndef GDTprototyped + %assign %1 GDTSelDesc + %assign nested 0 + %else + %assign nested 1 + %endif + lds ebx, [cs:ptrGDTreal] ; this macro is used in real mode to set up prot mode env. + mov eax, %1 + mov esi, %2 + mov edi, %3 + mov dx, %4|%5 + initDescriptor + %if nested==0 + %assign GDTSelDesc GDTSelDesc+8 + %endif +%endmacro + ; ; Defines a LDT descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5) ; diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index c62a5a6..fd77b4c 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -1,6 +1,11 @@ ; ; 16-bit TSS execution path, in parallel with test386.asm tests running in 386 mode ; + +ptrTSSprot32Gate: ; pointer to the 32-bit task state segment gate + dd 0 + dw TSS_GSEG_PROT32|3 + errorTSS16: mov ax,SU_SEG_PROT16SS|3 ;SS OK? mov ss,ax ;Fixup SS diff --git a/src/test386.asm b/src/test386.asm index e5a5c94..6ed8a17 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -97,6 +97,174 @@ GDT_SEG_LIMIT equ 0x31F ESP_REAL equ 0xffff +%if ROM128 + ; Protected mode prototype support to support GDT entry references in the remainder of the code +%include "protected_m.asm" + ; Always start with the NULL segment selector. + defGDTDescPrototype NULL + defGDTDescPrototype LDT_SEG_PROT + defGDTDescPrototype LDT_SEG_PROT286 + defGDTDescPrototype DU_SEG_PROT32FLAT + defGDTDescPrototype TSS_DSEG_PROT16 + defGDTDescPrototype TSS_PROT + defGDTDescPrototype TSS_PROT16 + defGDTDescPrototype TSS_GSEG_PROT32 + defGDTDescPrototype TSS_GSEG_PROT16 + defGDTDescPrototype CU_SEG_PROT32FLAT + defGDTDescPrototype SU_SEG_PROT32DS + defGDTDescPrototype SU_SEG_PROT32ES + defGDTDescPrototype SU_SEG_PROT32FS + defGDTDescPrototype SU_SEG_PROT32GS + defGDTDescPrototype SU_SEG_PROT16SS + defGDTDescPrototype SU_SEG_PROT16DS + defGDTDescPrototype SU_SEG_PROT16ES + defGDTDescPrototype CU_SEG_PROT16CS + defGDTDescPrototype TSSU_DSEG_PROT32 + defGDTDescPrototype TSSU_DSEG_PROT16 + +section .high_bios start=0x00000 +;Start of high BIOS + ; + ; 286 TSS handler + ; +%include "protected_tssh.asm" +BITS 32 + ; + ; 386 TSS user mode code + ; +test386TSSstart: + ;Loading patterns for the 386 data segments + mov ax,SU_SEG_PROT32DS|3 ;DS + mov ds,ax + mov ax,SU_SEG_PROT32ES|3 ;ES + mov es,ax + mov ax,SU_SEG_PROT32FS|3 ;FS + mov fs,ax + mov ax,SU_SEG_PROT32GS|3 ;GS + mov gs,ax + + ;Loading patterns for the 386 data segments. + mov eax,0x12347654 + mov ecx,0x5678CBA9 + mov edx,0x9ABC3333 + mov ebx,0xDEF02222 + mov esp,0x11221111 + mov ebp,0x33447777 + mov esi,0x55665555 + mov edi,0x7788AAAA + clc ;Clear carry flag for the 286 test and us + ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. + int 0x28 + ;We've returned from the test task. Verify if our registers are loaded correctly. + cmp esp,0x11221111 ;Validate the stack pointer first + jnz errorInTSS32Load + mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer, so we regain stack functionality. + cmp eax,0x12347654 + jnz error + cmp ecx,0x5678CBA9 + jnz error + cmp edx,0x9ABC3333 + jnz error + cmp ebx,0xDEF02222 + jnz error + cmp ebp,0x33447777 + jnz error + cmp esi,0x55665555 + jnz error + cmp edi,0x7788AAAA + jnz error + ;Now, validate the segment registers + mov ax,ss + cmp ax,DU_SEG_PROT32FLAT|3 ;SS OK? + jnz errorTSS32_1 + mov ax,cs + cmp ax,CU_SEG_PROT32FLAT|3 ;CS OK? + jnz errorTSS32_1 + mov ax,ds + cmp ax,SU_SEG_PROT32DS|3 ;DS OK? + jnz errorTSS32_1 + mov ax,es + cmp ax,SU_SEG_PROT32ES|3 ;ES OK? + jnz errorTSS32_1 + mov ax,fs + cmp ax,SU_SEG_PROT32FS|3 ;FS OK? + jnz errorTSS32_1 + mov ax,gs + cmp ax,SU_SEG_PROT32GS|3 ;GS OK? + jnz errorTSS32_1 + sldt ax + cmp ax,LDT_SEG_PROT ;LDT OK? + jnz errorTSS16_1 + jmp TSStest1finished +errorTSS32_1: + mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value + mov ss,ax + mov esp,ESP_R3_PROTFLAT ;ESP safe value + jmp error ;Error out + +TSStest1finished: + ;Test the flags register during task switches now. + int 0x28 ;Start testing the 286 flags + push dword FLAGS_CLEARED + popfd ;Clear the flags to test. + int 0x28 ;Continue testing the 286 flags + push dword FLAGS_SET + popfd ;Set the flags to test. + int 0x28 ;Third stage of the flags test. + ;286 flags test completed. + + ;Now, we switch sides + jmp far [cs:ptrTSSprot16Gate+0xF0000] + + ;Now we start our TSS flags test + pushfd + pop eax + test ax,PS_NT ;Task properly nested? + jz error + push dword (FLAGS_SET|PS_NT) + popfd ;Set the flags to test. + iretd ;return to the calling 16-bit task + ;Now the flags should have been set. + pushfd + cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? + jnz errorTSS32_1 + popfd + push dword (FLAGS_CLEARED|PS_NT) + popfd ;Clear the flags to test + iretd ;return to the calling 16-bit task + pushfd + cmp [esp],dword (FLAGS_CLEARED|PS_NT) ;Correct? + jnz errorTSS32_1 + popfd ;Restore the flags + iretd ;return to the calling 16-bit task + + ;We're the parent task again. + ;Perform call tests now + call far [cs:ptrTSSprot16Gate+0xF0000] + ;Now, we switch sides + jmp far [cs:ptrTSSprot16Gate+0xF0000] + ;We've been far called. Return. + iretd + ;We're the parent task again. + + + + ;32-bit eflags register loaded OK. + pushfd + push cs + call nextlowbios + nextlowbios: + mov dword [esp],test386TSSend+0xF0000 ;Return to the F0000 segment in flat protected mode + retfd ;Actually return + +;End of high BIOS + ;Pad to 64KB + times 0x10000-($-$$) nop + ;Restart counting for the upper 64KB block +section .low_bios start=0x10000 +;Start of low BIOS + BITS 16 +%endif header: db COPYRIGHT @@ -252,8 +420,9 @@ ESP_R0_PROTFLAT equ 0xE001FFFF ESP_R3_PROT equ 0x00007FFF ESP_R3_PROTFLAT equ 0x0001FFFF +%if !ROM128 %include "protected_m.asm" - +%endif ;;; support for ROM based GDT (currently unused) romGDT: @@ -271,8 +440,28 @@ ptrIDTreal: ; pointer to the pmode IDT for real mode code dw IDT_SEG_REAL initGDT: - ; the first descriptor in the GDT is always a dud (the null selector) - defGDTDesc NULL + ;Implementation is automatically used for earlier defined cases if needed. Keep those at the start of the table. + ; the first descriptor in the GDT is always a dud (the null selector). + defGDTDescImplementation NULL + defGDTDescImplementation LDT_SEG_PROT, 0x00000A00,0x000005f7,ACC_TYPE_LDT|ACC_PRESENT + defGDTDescImplementation LDT_SEG_PROT286, 0x00000FF8,0x00000000,ACC_TYPE_LDT|ACC_PRESENT + defGDTDescImplementation DU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE + defGDTDescImplementation TSS_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDescImplementation TSS_PROT, 0x00005000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation TSS_PROT16, 0x00005200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE + defGDTDescImplementation SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32GS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT + defGDTDescImplementation SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation CU_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDescImplementation TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE @@ -282,32 +471,13 @@ initGDT: defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 - defGDTDesc LDT_SEG_PROT, 0x00000A00,0x000005f7,ACC_TYPE_LDT|ACC_PRESENT - defGDTDesc LDT_SEG_PROT286, 0x00000FF8,0x00000000,ACC_TYPE_LDT|ACC_PRESENT defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc S_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc S_SEG_PROT32_R2, 0x00010000,0x0008ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE - defGDTDesc DU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc TSS_PROT, 0x00005000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_PROT16, 0x00005200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 defGDTDesc TSS_DSEG_PROT, 0x00005000,0x0000006F,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSS_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 - defGDTDesc TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 - defGDTDesc CU_SEG_PROT16CS, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE - defGDTDesc SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc SU_SEG_PROT32GS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT - defGDTDesc SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 @@ -365,9 +535,6 @@ ptrTSSprotRaw: ; pointer to the task state segment itself ptrTSSprot16Raw: ; pointer to the 16-bit task state segment itself dd 0 dw TSS_PROT16|3 -ptrTSSprot32Gate: ; pointer to the 32-bit task state segment gate - dd 0 - dw TSS_GSEG_PROT32|3 ptrTSSprot16Gate: ; pointer to the 16-bit task state segment gate dd 0 dw TSS_GSEG_PROT16|3 @@ -769,11 +936,6 @@ userFarFunc: ; %include "protected_inth.asm" - ; - ; 286 TSS handler - ; -%include "protected_tssh.asm" - userRetfErrorFunction: ; From user mode to kernel mode error address, which isn't allowed. push C_SEG_PROT32 @@ -946,123 +1108,17 @@ userV86ExitFuncRet: ;Now, switch to flat user mode to start our tests call switchToRing3FLATuser ;Perform tests for 386 mode parts below - ;Loading patterns for the 386 data segments - mov ax,SU_SEG_PROT32DS|3 ;DS - mov ds,ax - mov ax,SU_SEG_PROT32ES|3 ;ES - mov es,ax - mov ax,SU_SEG_PROT32FS|3 ;FS - mov fs,ax - mov ax,SU_SEG_PROT32GS|3 ;GS - mov gs,ax - - ;Loading patterns for the 386 data segments. - mov eax,0x12347654 - mov ecx,0x5678CBA9 - mov edx,0x9ABC3333 - mov ebx,0xDEF02222 - mov esp,0x11221111 - mov ebp,0x33447777 - mov esi,0x55665555 - mov edi,0x7788AAAA - clc ;Clear carry flag for the 286 test and us - ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. - int 0x28 - ;We've returned from the test task. Verify if our registers are loaded correctly. - cmp esp,0x11221111 ;Validate the stack pointer first - jnz errorInTSS32Load - mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer, so we regain stack functionality. - cmp eax,0x12347654 - jnz error - cmp ecx,0x5678CBA9 - jnz error - cmp edx,0x9ABC3333 - jnz error - cmp ebx,0xDEF02222 - jnz error - cmp ebp,0x33447777 - jnz error - cmp esi,0x55665555 - jnz error - cmp edi,0x7788AAAA - jnz error - ;Now, validate the segment registers - mov ax,ss - cmp ax,DU_SEG_PROT32FLAT|3 ;SS OK? - jnz errorTSS32_1 - mov ax,cs - cmp ax,CU_SEG_PROT32FLAT|3 ;CS OK? - jnz errorTSS32_1 - mov ax,ds - cmp ax,SU_SEG_PROT32DS|3 ;DS OK? - jnz errorTSS32_1 - mov ax,es - cmp ax,SU_SEG_PROT32ES|3 ;ES OK? - jnz errorTSS32_1 - mov ax,fs - cmp ax,SU_SEG_PROT32FS|3 ;FS OK? - jnz errorTSS32_1 - mov ax,gs - cmp ax,SU_SEG_PROT32GS|3 ;GS OK? - jnz errorTSS32_1 - sldt ax - cmp ax,LDT_SEG_PROT ;LDT OK? - jnz errorTSS16_1 - jmp TSStest1finished -errorTSS32_1: - mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value - mov ss,ax - mov esp,ESP_R3_PROTFLAT ;ESP safe value - jmp error ;Error out -TSStest1finished: - ;Test the flags register during task switches now. - int 0x28 ;Start testing the 286 flags - push dword FLAGS_CLEARED - popfd ;Clear the flags to test. - int 0x28 ;Continue testing the 286 flags - push dword FLAGS_SET - popfd ;Set the flags to test. - int 0x28 ;Third stage of the flags test. - ;286 flags test completed. - - ;Now, we switch sides - jmp far [cs:ptrTSSprot16Gate+0xF0000] - - ;Now we start our TSS flags test - pushfd - pop eax - test ax,PS_NT ;Task properly nested? - jz error - push dword (FLAGS_SET|PS_NT) - popfd ;Set the flags to test. - iretd ;return to the calling 16-bit task - ;Now the flags should have been set. - pushfd - cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? - jnz errorTSS32_1 - popfd - push dword (FLAGS_CLEARED|PS_NT) - popfd ;Clear the flags to test - iretd ;return to the calling 16-bit task + %if ROM128 + ;Switch to segment E0000 in flat mode for more advanced tests. pushfd - cmp [esp],dword (FLAGS_CLEARED|PS_NT) ;Correct? - jnz errorTSS32_1 - popfd ;Restore the flags - iretd ;return to the calling 16-bit task - - ;We're the parent task again. - ;Perform call tests now - call far [cs:ptrTSSprot16Gate+0xF0000] - ;Now, we switch sides - jmp far [cs:ptrTSSprot16Gate+0xF0000] - ;We've been far called. Return. - iretd - ;We're the parent task again. - - - - ;32-bit eflags register loaded OK. + push cs + call nexthighbios + nexthighbios: + mov dword [esp],test386TSSstart+0xE0000 ;Return to the E0000 segment in flat protected mode + retfd ;Actually return + test386TSSend: + %endif ;Finishes 386 mode and 286 mode TSS tests, return to normal protected mode call switchToRing0FromFlatUser diff --git a/src/tss_p.asm b/src/tss_p.asm index af8b20e..1af562b 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -148,6 +148,7 @@ initTSS32: ; ╚═══════════════════════════════╬═══════════════════════════════╝ initTSS16: + %if ROM128 ; initialize everything to predefined values push eax push ebp @@ -189,5 +190,6 @@ initTSS16: pop ds pop ebp pop eax + %endif ret From 582bbb6b397d1cff0bc11081a687ac533bb194d2 Mon Sep 17 00:00:00 2001 From: superfury Date: Thu, 9 Apr 2026 19:38:00 +0200 Subject: [PATCH 26/62] - Fixed 286 TSS to be properly 32-bit as specified in the GDT. --- src/protected_tssh.asm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index fd77b4c..095ca0f 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -6,6 +6,7 @@ ptrTSSprot32Gate: ; pointer to the 32-bit task state segment gate dd 0 dw TSS_GSEG_PROT32|3 +BITS 32 errorTSS16: mov ax,SU_SEG_PROT16SS|3 ;SS OK? mov ss,ax ;Fixup SS From 721a6d8e23c36df1680b278f046d3a55d9cc3778 Mon Sep 17 00:00:00 2001 From: superfury Date: Thu, 9 Apr 2026 19:59:47 +0200 Subject: [PATCH 27/62] Fixed the system BIOS area to use proper offsets. Renamed the low BIOS area and high bios area to system BIOS area and system BIOS extensions area. --- src/test386.asm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 6ed8a17..b1e4086 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -122,7 +122,7 @@ ESP_REAL equ 0xffff defGDTDescPrototype TSSU_DSEG_PROT32 defGDTDescPrototype TSSU_DSEG_PROT16 -section .high_bios start=0x00000 +section .system_bios_extensions_area start=0x00000 ;Start of high BIOS ; ; 286 TSS handler @@ -261,7 +261,7 @@ TSStest1finished: ;Pad to 64KB times 0x10000-($-$$) nop ;Restart counting for the upper 64KB block -section .low_bios start=0x10000 +section .system_bios_area start=0x10000 vstart=0 ;Start of low BIOS BITS 16 %endif From 8e5781a39812f6e78aa969681c6d5141f824f659 Mon Sep 17 00:00:00 2001 From: superfury Date: Thu, 9 Apr 2026 20:58:16 +0200 Subject: [PATCH 28/62] Implemented 32-bit flat protected mode TSS helper functions to validate the busy bit, backlink field and NT flag of a either a specified TSS or the EFLAGS register. --- src/protected_tsshelpers.asm | 114 +++++++++++++++++++++++++++++++++++ src/test386.asm | 4 ++ 2 files changed, 118 insertions(+) create mode 100644 src/protected_tsshelpers.asm diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm new file mode 100644 index 0000000..a99d505 --- /dev/null +++ b/src/protected_tsshelpers.asm @@ -0,0 +1,114 @@ +; +; 32-bit far call instructions to be called from both 286 and 386 tasks to validate the state of the current or other TSS. +; + +;Define some far call pointers to execute in the proper mode no matter what TSS we're in. +ptrTSSprot32validatebusy: ; pointer to the 32-bit task state segment gate + dd validateTSSbusy+0xE0000 + dw CU_SEG_PROT32FLAT|3 + +ptrTSSprot32validatebacklink: ; pointer to the 32-bit task state segment gate + dd validateTSSbacklink+0xE0000 + dw CU_SEG_PROT32FLAT|3 + +ptrTSSprot32validateNT: ; pointer to the 32-bit task state segment gate + dd validateTSSNT+0xE0000 + dw CU_SEG_PROT32FLAT|3 + +BITS 32 + +;Call below functions using a 32-bit far call. + +; ValidateTSSbusy: Validate the busy bit of a TSS +; Parameters: +; EAX: lower half: TSS descriptor to validate. Bit 16=expected B-bit +validateTSSbusy: + pushfd + push eax + or ax,3 ;Make sure it's in user mode + lar ax,ax ;Load the B-bit of the TSS + jnz LARerror + test al,2 ;Check the B-bit + jnz TSSisBusy + ;TSS is idle + shr eax,0x10 ;Get the busy bit that's expected. + jz errorTSSbusy ;If incorrect, error out + pop eax ;Success, return. + popfd + retfd +LARerror: + jmp error ;Goto error + +TSSisBusy: + shr eax,0x10 ;Get the busy bit that's expected. + jnz errorTSSbusy ;If incorrect, error out + pop eax ;Success, return. + retfd + +errorTSSbusy: ;Error in the TSS while checking the busy bit + pop eax ;Restore + jmp error + +; ValidateTSSbacklink: Validate the backlink field of a TSS +; Parameters: +; EAX: lower half: TSS data descriptor to validate. upper half: expected backlink field. +validateTSSbacklink: + pushfd + push ds + push eax + or eax,3 ;Make sure we're in user mode + mov ds,eax ;Load the TSS user-mode descriptor specified into DS + shr eax,0x10 ;Get the expected value + cmp ax,word [0] ;Validate the backlink field + jnz errorTSSbacklink + ;Clean up and return. + pop eax + pop ds + popfd + retfd +errorTSSbacklink: ;An error occurred during validating the TSS backlink field? + jmp error + +; ValidateTSSNT: Validate the NT flag of a TSS +; Parameters: +; EAX: lower half: TSS data descriptor to check. Zeroed for current TSS (EFLAGS register). bit 16: expected NT field, bit 17: 32-bit TSS. +validateTSSNT: + pushfd + cmp ax,0 ;Active TSS to validate? + jz validateCurrentTSSNT + ;Validate specified TSS + push ds ;Save + push eax ;Save + push ebx ;Save + mov ds,ax ;Load the TSS + shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS) + test eax,0x8000 ;32-bit TSS? + jnz validate32bitTSS + mov bx,[0x10] ;Load flags register + and ebx,0xFFFF ;Mask off unused bits + jmp commonvalidateTSSNTbit ;Common code again + validate32bitTSS: + mov ebx,[0x24] ;Load eflags register + commonvalidateTSSNTbit: ;All NT flag checks end up here. EBX is loaded with the EFLAGS register of the requested task. + and ebx,PS_NT ;Mask off the NT bit + and eax,PS_NT ;Mask off the NT bit + cmp eax,ebx ;Check if the NT flag matches as we expect. + jnz errorTSSNT ;TSS NT is containing an error + ;Clean up and return. + pop ebx + pop eax + pop ds + popfd + retfd +errorTSSNT: ;An error occurred during validating the NT bit? + jmp error + + +validateCurrentTSSNT: ;Selector 0 specified. + push ds ;Save + push eax ;Save + push ebx ;Save + pushfd ;Push the flags + pop ebx ;Load it into EBX + shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS (unused)) + jmp commonvalidateTSSNTbit ;Common validation point \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index b1e4086..327aefd 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -132,6 +132,10 @@ BITS 32 ; ; 386 TSS user mode code ; +%include "protected_tsshelpers.asm" + + + test386TSSstart: ;Loading patterns for the 386 data segments mov ax,SU_SEG_PROT32DS|3 ;DS From 14ae4a5e22209891ba3aa9a4726bfd4e5def9d69 Mon Sep 17 00:00:00 2001 From: superfury Date: Thu, 9 Apr 2026 21:04:49 +0200 Subject: [PATCH 29/62] Simplfiied loading the EFLAGS register into EBX for checking the NT bit. --- src/protected_tsshelpers.asm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index a99d505..ea91a66 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -108,7 +108,6 @@ validateCurrentTSSNT: ;Selector 0 specified. push ds ;Save push eax ;Save push ebx ;Save - pushfd ;Push the flags - pop ebx ;Load it into EBX + mov ebx,[esp+0xC] ;Load the EFLAGS register into EBX. shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS (unused)) jmp commonvalidateTSSNTbit ;Common validation point \ No newline at end of file From db6601b198581c65ef41aa116f4f791b0d9e9a74 Mon Sep 17 00:00:00 2001 From: superfury Date: Thu, 9 Apr 2026 21:20:40 +0200 Subject: [PATCH 30/62] Fixed TSS helper functions error location. --- src/protected_tsshelpers.asm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index ea91a66..f4a7945 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -37,7 +37,7 @@ validateTSSbusy: popfd retfd LARerror: - jmp error ;Goto error + jmp error+0xF0000 ;Goto error TSSisBusy: shr eax,0x10 ;Get the busy bit that's expected. @@ -47,7 +47,7 @@ TSSisBusy: errorTSSbusy: ;Error in the TSS while checking the busy bit pop eax ;Restore - jmp error + jmp error+0xF0000 ; ValidateTSSbacklink: Validate the backlink field of a TSS ; Parameters: @@ -67,7 +67,7 @@ validateTSSbacklink: popfd retfd errorTSSbacklink: ;An error occurred during validating the TSS backlink field? - jmp error + jmp error+0xF0000 ; ValidateTSSNT: Validate the NT flag of a TSS ; Parameters: @@ -101,7 +101,7 @@ validateTSSNT: popfd retfd errorTSSNT: ;An error occurred during validating the NT bit? - jmp error + jmp error+0xF0000 validateCurrentTSSNT: ;Selector 0 specified. From 4302f8d8dbd75f55d401e4e7738fb0e8e5aaaeb2 Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 10 Apr 2026 12:22:36 +0200 Subject: [PATCH 31/62] Implemented a function for setting the TSS backlink field. Implemented macros for easy TSS bits and backlink validation and setting. --- src/protected_tss_m.asm | 67 ++++++++++++++++++++++++++++++++++++ src/protected_tsshelpers.asm | 25 ++++++++++++-- src/test386.asm | 2 ++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/protected_tss_m.asm diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm new file mode 100644 index 0000000..11caccb --- /dev/null +++ b/src/protected_tss_m.asm @@ -0,0 +1,67 @@ +; ValidateTSSbusy: Validate the busy bit of a TSS +; Parameters: +; %1 TSS descriptor to validate. +; %2 expected B-bit +%macro validateTSSbusy286 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validatebusy] +%endmacro +%macro validateTSSbusy386 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validatebusy+0xE0000] +%endmacro + + +; ValidateTSSbacklink: Validate the backlink of a TSS +; Parameters: +; %1 TSS data descriptor to validate. +; %2 expected backlink +%macro validateTSSbacklink286 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validatebacklink] +%endmacro +%macro validateTSSbacklink386 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validatebacklink+0xE0000] +%endmacro + +; Setbacklink: Validate the backlink of a TSS +; Parameters: +; %1 TSS data descriptor to validate. +; %2 backlink to set +%macro setTSSbacklink286 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32setbacklink] +%endmacro +%macro setTSSbacklink386 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32setbacklink+0xE0000] +%endmacro + +; validateTSSNT: Validate the NT bit of a TSS +; Parameters: +; %1 TSS data descriptor to validate. 0 for current task. +; %2 backlink bit to validate +%macro validateTSSNT286 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validateNT] +%endmacro +%macro validateTSSNT386 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validateNT+0xE0000] +%endmacro + +; setNTflag: Set the NT bit of the current TSS +; Parameters: +; %1 backlink bit to set +%macro setNTflag 1 + push eax + pushfd + pop eax + and ax,NOT PS_NT + or ax,(%1<<14) + push eax + popfd + pop eax +%endmacro + diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index f4a7945..c00480f 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -11,6 +11,10 @@ ptrTSSprot32validatebacklink: ; pointer to the 32-bit task state segment gate dd validateTSSbacklink+0xE0000 dw CU_SEG_PROT32FLAT|3 +ptrTSSprot32setbacklink: ; pointer to the 32-bit task state segment gate + dd setTSSbacklink+0xE0000 + dw CU_SEG_PROT32FLAT|3 + ptrTSSprot32validateNT: ; pointer to the 32-bit task state segment gate dd validateTSSNT+0xE0000 dw CU_SEG_PROT32FLAT|3 @@ -49,7 +53,7 @@ errorTSSbusy: ;Error in the TSS while checking the busy bit pop eax ;Restore jmp error+0xF0000 -; ValidateTSSbacklink: Validate the backlink field of a TSS +; validateTSSbacklink: Validate the backlink field of a TSS ; Parameters: ; EAX: lower half: TSS data descriptor to validate. upper half: expected backlink field. validateTSSbacklink: @@ -69,7 +73,24 @@ validateTSSbacklink: errorTSSbacklink: ;An error occurred during validating the TSS backlink field? jmp error+0xF0000 -; ValidateTSSNT: Validate the NT flag of a TSS +; setTSSbacklink: Clear the backlink field of a TSS +; Parameters: +; EAX: lower half: TSS data descriptor to validate. upper half: expected backlink field. +setTSSbacklink: + pushfd + push ds + push eax + or eax,3 ;Make sure we're in user mode + mov ds,eax ;Load the TSS user-mode descriptor specified into DS + shr eax,0x10 ;Get the value to set. + mov word [0], ax ;Set the backlink field + ;Clean up and return. + pop eax + pop ds + popfd + retfd + +; validateTSSNT: Validate the NT flag of a TSS ; Parameters: ; EAX: lower half: TSS data descriptor to check. Zeroed for current TSS (EFLAGS register). bit 16: expected NT field, bit 17: 32-bit TSS. validateTSSNT: diff --git a/src/test386.asm b/src/test386.asm index 327aefd..86978b2 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -124,6 +124,8 @@ ESP_REAL equ 0xffff section .system_bios_extensions_area start=0x00000 ;Start of high BIOS + ; TSS helper macros +%include "protected_tss_m.asm" ; ; 286 TSS handler ; From 30acd79764f50a3ea5d7942a21444c6d0eebb2ab Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 10 Apr 2026 13:18:36 +0200 Subject: [PATCH 32/62] Implemented 32-bit TSS and 16-bit TSS initial state validation. --- src/protected_tss_m.asm | 2 +- src/test386.asm | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm index 11caccb..fa57cfe 100644 --- a/src/protected_tss_m.asm +++ b/src/protected_tss_m.asm @@ -58,7 +58,7 @@ push eax pushfd pop eax - and ax,NOT PS_NT + and ax,0xBFFF or ax,(%1<<14) push eax popfd diff --git a/src/test386.asm b/src/test386.asm index 86978b2..6c66ddb 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -139,6 +139,17 @@ BITS 32 test386TSSstart: + ; Verify we start clean + setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + setNTflag 0 + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,0 + validateTSSNT386 TSSU_DSEG_PROT16,0 + ;Loading patterns for the 386 data segments mov ax,SU_SEG_PROT32DS|3 ;DS mov ds,ax From 3658fa6f43551a8ea70ac0f282dd47ad2f8a389b Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 10 Apr 2026 17:44:51 +0200 Subject: [PATCH 33/62] Implemented TSS task switching busy bit, nested task flag and backlink field validation. --- src/protected_tss_m.asm | 19 ++++---- src/protected_tssh.asm | 88 +++++++++++++++++++++++++++++++++++- src/protected_tsshelpers.asm | 73 ++++++++++++++++++++++++++++-- src/test386.asm | 88 +++++++++++++++++++++++++++++++++++- 4 files changed, 251 insertions(+), 17 deletions(-) diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm index fa57cfe..6c14670 100644 --- a/src/protected_tss_m.asm +++ b/src/protected_tss_m.asm @@ -53,15 +53,14 @@ ; setNTflag: Set the NT bit of the current TSS ; Parameters: -; %1 backlink bit to set -%macro setNTflag 1 - push eax - pushfd - pop eax - and ax,0xBFFF - or ax,(%1<<14) - push eax - popfd - pop eax +; %1 selector of TSS to use. 0 for current task FLAGS register. +; %2 NT bit to set +%macro setNTflag286 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validateNT] +%endmacro +%macro setNTflag386 2 + mov eax,(%1 | (%2<<16)) + o32 call far [cs:ptrTSSprot32validateNT+0xE0000] %endmacro diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 095ca0f..533184b 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -104,5 +104,91 @@ TSS1_returned: ;We're the parent task again. call far [cs:ptrTSSprot32Gate] + setNTflag286 0,0 ;Clear the NT flag for our bits and back-link tests. ;Now, we switch sides - jmp far [cs:ptrTSSprot32Gate] \ No newline at end of file + jmp far [cs:ptrTSSprot32Gate] + ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSSU_DSEG_PROT32,1 + validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSNT286 TSSU_DSEG_PROT32,0 + validateTSSNT286 TSSU_DSEG_PROT16,0 ;Not written yet, so left at 0. + validateTSSNT286 0,1 ;Set currently in register only. + iretd ;Return to caller. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSSU_DSEG_PROT32,1 + validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSNT286 TSSU_DSEG_PROT32,1 ;Set to 1 in source task. + validateTSSNT286 TSSU_DSEG_PROT16,0 ;Left at 0. + validateTSSNT286 0,1 ;Set currently in register only. + iretd ;Return to caller. + ;Now testing JMP from 32-bit task to 16-bit task, NT going from set to cleared in the source task. 16-bit task keeps it cleared. + ;TSS test 3: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSSU_DSEG_PROT32,0 + validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT286 TSSU_DSEG_PROT32,1 ;Set to 1 in source task. + validateTSSNT286 TSSU_DSEG_PROT16,0 ;Left at 0. + validateTSSNT286 0,0 ;Cleared currently in register only. + jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. + ;TSS test 3.2 Same as before, but NT in 386 is cleared. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSSU_DSEG_PROT32,0 + validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT286 TSSU_DSEG_PROT32,0 ;Set to 0 in source task. + validateTSSNT286 TSSU_DSEG_PROT16,1 ;Left at 1. + validateTSSNT286 0,0 ;Cleared currently in register only. + setNTflag286 0,1 ;Set NT flag to test. + jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. + + ;Now, we have switched sides to check CALL from 16-bit to 32-bit. + + ;Step 1: validate 32-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. + ;First, setup initial task state. + and esp,0xFFFF ;Safe ESP usage! + setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + setNTflag286 0,0 + validateTSSbusy286 TSSU_DSEG_PROT32,1 + validateTSSbusy286 TSSU_DSEG_PROT16,0 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT286 TSSU_DSEG_PROT32,0 + validateTSSNT286 TSSU_DSEG_PROT16,0 + validateTSSNT286 0,0 + call far [cs:ptrTSSprot32Gate] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). + ;Now, setup the second test for call, this time with NT set. + ;First, validate IRET did it's job correctly. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSSU_DSEG_PROT32,1 + validateTSSbusy286 TSSU_DSEG_PROT16,0 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSNT286 TSSU_DSEG_PROT32,0 + validateTSSNT286 TSSU_DSEG_PROT16,0 + ;Reset state for detection. + setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + ;Now, with NT bit set. + setNTflag286 0,1 + call far [cs:ptrTSSprot32Gate] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). + ;Now, validate IRET did it's job correctly. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSSU_DSEG_PROT32,1 + validateTSSbusy286 TSSU_DSEG_PROT16,0 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSNT286 TSSU_DSEG_PROT32,1 + validateTSSNT286 TSSU_DSEG_PROT16,0 + ;Reset state for detection. + setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + + ;Finally, return to the 386 task to finish up these TSS tests and continue running other tests. + jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. \ No newline at end of file diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index c00480f..33943db 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -3,22 +3,26 @@ ; ;Define some far call pointers to execute in the proper mode no matter what TSS we're in. -ptrTSSprot32validatebusy: ; pointer to the 32-bit task state segment gate +ptrTSSprot32validatebusy: ; pointer to the 32-bit task state segment function dd validateTSSbusy+0xE0000 dw CU_SEG_PROT32FLAT|3 -ptrTSSprot32validatebacklink: ; pointer to the 32-bit task state segment gate +ptrTSSprot32validatebacklink: ; pointer to the 32-bit task state segment function dd validateTSSbacklink+0xE0000 dw CU_SEG_PROT32FLAT|3 -ptrTSSprot32setbacklink: ; pointer to the 32-bit task state segment gate +ptrTSSprot32setbacklink: ; pointer to the 32-bit task state segment function dd setTSSbacklink+0xE0000 dw CU_SEG_PROT32FLAT|3 -ptrTSSprot32validateNT: ; pointer to the 32-bit task state segment gate +ptrTSSprot32validateNT: ; pointer to the 32-bit task state segment function dd validateTSSNT+0xE0000 dw CU_SEG_PROT32FLAT|3 +ptrTSSprot32setNT: ; pointer to the 32-bit task state segment function + dd setTSSNT+0xE0000 + dw CU_SEG_PROT32FLAT|3 + BITS 32 ;Call below functions using a 32-bit far call. @@ -131,4 +135,63 @@ validateCurrentTSSNT: ;Selector 0 specified. push ebx ;Save mov ebx,[esp+0xC] ;Load the EFLAGS register into EBX. shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS (unused)) - jmp commonvalidateTSSNTbit ;Common validation point \ No newline at end of file + jmp commonvalidateTSSNTbit ;Common validation point + +; setTSSNT: Set the NT flag of a TSS +; Parameters: +; EAX: lower half: TSS data descriptor to check. Zeroed for current TSS (EFLAGS register). bit 16: NT to set, bit 17: 32-bit TSS. +setTSSNT: + push ecx ;Type determination + mov ecx,0 ;Default: type TSS. + pushfd + cmp ax,0 ;Active TSS to validate? + jz validateCurrentTSSNT + ;Validate specified TSS + push ds ;Save + push eax ;Save + push ebx ;Save + mov ds,ax ;Load the TSS + shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS) + test eax,0x8000 ;32-bit TSS? + jnz get32bitTSSNT + mov bx,[0x10] ;Load flags register + and ebx,0xFFFF ;Mask off unused bits + jmp commonsetTSSNTbit ;Common code again + get32bitTSSNT: + mov ebx,[0x24] ;Load eflags register + commonsetTSSNTbit: ;All NT flag checks end up here. EBX is loaded with the EFLAGS register of the requested task. + and bx,0xBFFF ;Mask off the NT bit + and eax,PS_NT ;Mask off the NT bit + or bx,ax ;Set the NT bit only. + cmp ecx,1 ;Type EFLAGS? + jnz finishTSStypeEFLAGS + ;Type TSS. + test eax,0x8000 ;32-bit TSS? + jnz set32bitsTSSNT + ;16-bit TSS to write. + mov [0x10], bx ;Set the FLAGS register. + jmp commonfinishTSSNTbit + set32bitsTSSNT: + ;32-bits TSS to write. + mov [0x24], bx ;Set the FLAGS register. + commonfinishTSSNTbit: + ;Clean up and return. + pop ebx + pop eax + pop ds + popfd + pop ecx + retfd +finishTSStypeEFLAGS: + mov [esp+0xC], bx ;Update on the stack instead. + jmp commonfinishTSSNTbit + +setCurrentTSSNT: ;Selector 0 specified. + mov ecx,1 ;Type is requested to be EFLAGS instead. + push ds ;Save + push eax ;Save + push ebx ;Save + mov ebx,[esp+0xC] ;Load the EFLAGS register into EBX. + shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS (unused)) + jmp commonvalidateTSSNTbit ;Common validation point + diff --git a/src/test386.asm b/src/test386.asm index 6c66ddb..2c1a2bb 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -142,7 +142,7 @@ test386TSSstart: ; Verify we start clean setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - setNTflag 0 + setNTflag386 0,0 validateTSSbusy386 TSSU_DSEG_PROT32,1 validateTSSbusy386 TSSU_DSEG_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -264,6 +264,92 @@ TSStest1finished: iretd ;We're the parent task again. + ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) + + ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. + ;First, setup initial task state. + setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + setNTflag386 0,0 + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,0 + validateTSSNT386 TSSU_DSEG_PROT16,0 + validateTSSNT386 0,0 + call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). + ;Now, setup the second test for call, this time with NT set. + ;First, validate IRET did it's job correctly. + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSNT386 TSSU_DSEG_PROT32,0 + validateTSSNT386 TSSU_DSEG_PROT16,0 + ;Reset state for detection. + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + ;Now, with NT bit set. + setNTflag386 0,1 + call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). + ;Now, validate IRET did it's job correctly. + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSNT386 TSSU_DSEG_PROT32,1 + validateTSSNT386 TSSU_DSEG_PROT16,0 + ;Reset state for detection. + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. + ;Now, test JMP instruction task switches. + setNTflag386 0,1 ;This is going to be kept in the 32-bit TSS. + setNTflag386 TSSU_DSEG_PROT32,0 ;This is going to be set. + setNTflag386 TSSU_DSEG_PROT16,1 ;This is going to be cleared. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1 + validateTSSNT386 TSSU_DSEG_PROT16,0 + validateTSSNT386 0,0 ;The JMP instruction to our TSS cleared us. + setNTflag386 0,0 ;This is going to be kept in the 32-bit TSS. + setNTflag386 TSSU_DSEG_PROT32,1 ;This is going to be cleared. + setNTflag386 TSSU_DSEG_PROT16,1 ;This is going to be cleared. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,0 ;Set to 0 in destination task. + validateTSSNT286 TSSU_DSEG_PROT16,1 ;Left at 1. + validateTSSNT286 0,0 ;Cleared currently in register only. + + ;Now, switch sides to verify CALL from 16-bit to 32-bit. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + + ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy386 TSSU_DSEG_PROT16,1 + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSNT386 TSSU_DSEG_PROT16,0 + validateTSSNT386 TSSU_DSEG_PROT32,0 ;Not written yet, so left at 0. + validateTSSNT386 0,1 ;Set currently in register only. + iretd ;Return to caller. + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy386 TSSU_DSEG_PROT16,1 + validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSNT386 TSSU_DSEG_PROT16,1 ;Set to 1 in source task. + validateTSSNT386 TSSU_DSEG_PROT32,0 ;Left at 0. + validateTSSNT386 0,1 ;Set currently in register only. + iretd ;Return to caller. + + ;32-bit eflags register loaded OK. From cb4942125bdc0e6bda29136ad806c72d0f23120e Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 10 Apr 2026 17:45:18 +0200 Subject: [PATCH 34/62] Fixed TSS busy bit error condition. --- src/protected_tsshelpers.asm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index 33943db..cf3ce26 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -40,7 +40,7 @@ validateTSSbusy: jnz TSSisBusy ;TSS is idle shr eax,0x10 ;Get the busy bit that's expected. - jz errorTSSbusy ;If incorrect, error out + jnz errorTSSbusy ;If incorrect, error out pop eax ;Success, return. popfd retfd @@ -49,7 +49,7 @@ LARerror: TSSisBusy: shr eax,0x10 ;Get the busy bit that's expected. - jnz errorTSSbusy ;If incorrect, error out + jz errorTSSbusy ;If incorrect, error out pop eax ;Success, return. retfd From fb36819118211372ad0bddc9fa297a8e8e759189 Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 10 Apr 2026 17:58:00 +0200 Subject: [PATCH 35/62] Fixed TSS data segments for user mode to be properly accessible from user mode. --- src/test386.asm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index 2c1a2bb..1edd0d5 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -563,8 +563,8 @@ initGDT: defGDTDescImplementation SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation CU_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDescImplementation TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDescImplementation TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE From f64f018ef61f28c57dbbba2623f6f9585a2a2051 Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 10 Apr 2026 19:41:07 +0200 Subject: [PATCH 36/62] Fixed TSS busy bit detection and validation. Optimized TSS busy bit detection code. Fixed TSS error handler location. Fixed TSS busy check selectors. --- src/protected_tssh.asm | 29 ++++++++++++++-------------- src/protected_tsshelpers.asm | 37 ++++++++++++++++++------------------ src/test386.asm | 35 +++++++++++++++++----------------- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 533184b..9289d1c 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -104,13 +104,14 @@ TSS1_returned: ;We're the parent task again. call far [cs:ptrTSSprot32Gate] + and esp,0xFFFF ;Safe ESP usage! setNTflag286 0,0 ;Clear the NT flag for our bits and back-link tests. ;Now, we switch sides jmp far [cs:ptrTSSprot32Gate] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSSU_DSEG_PROT32,1 - validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbusy286 TSS_PROT,1 + validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 validateTSSNT286 TSSU_DSEG_PROT32,0 @@ -118,8 +119,8 @@ TSS1_returned: validateTSSNT286 0,1 ;Set currently in register only. iretd ;Return to caller. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSSU_DSEG_PROT32,1 - validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbusy286 TSS_PROT,1 + validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 validateTSSNT286 TSSU_DSEG_PROT32,1 ;Set to 1 in source task. @@ -129,8 +130,8 @@ TSS1_returned: ;Now testing JMP from 32-bit task to 16-bit task, NT going from set to cleared in the source task. 16-bit task keeps it cleared. ;TSS test 3: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSSU_DSEG_PROT32,0 - validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbusy286 TSS_PROT,0 + validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,1 ;Set to 1 in source task. @@ -139,8 +140,8 @@ TSS1_returned: jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. ;TSS test 3.2 Same as before, but NT in 386 is cleared. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSSU_DSEG_PROT32,0 - validateTSSbusy286 TSSU_DSEG_PROT16,1 + validateTSSbusy286 TSS_PROT,0 + validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,0 ;Set to 0 in source task. @@ -157,8 +158,8 @@ TSS1_returned: setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD setNTflag286 0,0 - validateTSSbusy286 TSSU_DSEG_PROT32,1 - validateTSSbusy286 TSSU_DSEG_PROT16,0 + validateTSSbusy286 TSS_PROT,1 + validateTSSbusy286 TSS_PROT16,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,0 @@ -168,8 +169,8 @@ TSS1_returned: ;Now, setup the second test for call, this time with NT set. ;First, validate IRET did it's job correctly. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSSU_DSEG_PROT32,1 - validateTSSbusy286 TSSU_DSEG_PROT16,0 + validateTSSbusy286 TSS_PROT,1 + validateTSSbusy286 TSS_PROT16,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 validateTSSNT286 TSSU_DSEG_PROT32,0 @@ -181,8 +182,8 @@ TSS1_returned: call far [cs:ptrTSSprot32Gate] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, validate IRET did it's job correctly. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSSU_DSEG_PROT32,1 - validateTSSbusy286 TSSU_DSEG_PROT16,0 + validateTSSbusy286 TSS_PROT,1 + validateTSSbusy286 TSS_PROT16,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 validateTSSNT286 TSSU_DSEG_PROT32,1 diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index cf3ce26..2c62bca 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -23,6 +23,10 @@ ptrTSSprot32setNT: ; pointer to the 32-bit task state segment function dd setTSSNT+0xE0000 dw CU_SEG_PROT32FLAT|3 +TSShelpererrorptr: ;An error occurred while validating task state segment information + dd error + dw CU_SEG_PROT32 + BITS 32 ;Call below functions using a 32-bit far call. @@ -33,29 +37,26 @@ BITS 32 validateTSSbusy: pushfd push eax + push ebx + mov ebx,eax ;Copy of the bit or ax,3 ;Make sure it's in user mode lar ax,ax ;Load the B-bit of the TSS - jnz LARerror - test al,2 ;Check the B-bit - jnz TSSisBusy + jnz errorTSSbusy ;Error executing LAR ;TSS is idle - shr eax,0x10 ;Get the busy bit that's expected. + shr ebx,7 ;Get the busy bit that's expected to match positions. + and ah,2 ;Get the bit to inspect + and bh,2 ;Get the bit to inspect + cmp ah,bh ;Compare the bits to their expected results jnz errorTSSbusy ;If incorrect, error out + pop ebx pop eax ;Success, return. popfd retfd -LARerror: - jmp error+0xF0000 ;Goto error - -TSSisBusy: - shr eax,0x10 ;Get the busy bit that's expected. - jz errorTSSbusy ;If incorrect, error out - pop eax ;Success, return. - retfd - -errorTSSbusy: ;Error in the TSS while checking the busy bit - pop eax ;Restore - jmp error+0xF0000 +errorTSSbusy: ;Error in the TSS while checking the expected busy bit + pop ebx ;Restore + pop eax + popfd + jmp far [TSShelpererrorptr+0xE0000] ; validateTSSbacklink: Validate the backlink field of a TSS ; Parameters: @@ -75,7 +76,7 @@ validateTSSbacklink: popfd retfd errorTSSbacklink: ;An error occurred during validating the TSS backlink field? - jmp error+0xF0000 + jmp far [TSShelpererrorptr+0xE0000] ; setTSSbacklink: Clear the backlink field of a TSS ; Parameters: @@ -126,7 +127,7 @@ validateTSSNT: popfd retfd errorTSSNT: ;An error occurred during validating the NT bit? - jmp error+0xF0000 + jmp far [TSShelpererrorptr+0xE0000] validateCurrentTSSNT: ;Selector 0 specified. diff --git a/src/test386.asm b/src/test386.asm index 1edd0d5..ce70459 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -121,6 +121,7 @@ ESP_REAL equ 0xffff defGDTDescPrototype CU_SEG_PROT16CS defGDTDescPrototype TSSU_DSEG_PROT32 defGDTDescPrototype TSSU_DSEG_PROT16 + defGDTDescPrototype CU_SEG_PROT32 section .system_bios_extensions_area start=0x00000 ;Start of high BIOS @@ -143,8 +144,8 @@ test386TSSstart: setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD setNTflag386 0,0 - validateTSSbusy386 TSSU_DSEG_PROT32,1 - validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT32,0 @@ -271,8 +272,8 @@ TSStest1finished: setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD setNTflag386 0,0 - validateTSSbusy386 TSSU_DSEG_PROT32,1 - validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT32,0 @@ -281,8 +282,8 @@ TSStest1finished: call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, setup the second test for call, this time with NT set. ;First, validate IRET did it's job correctly. - validateTSSbusy386 TSSU_DSEG_PROT32,1 - validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 validateTSSNT386 TSSU_DSEG_PROT32,0 @@ -293,8 +294,8 @@ TSStest1finished: setNTflag386 0,1 call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, validate IRET did it's job correctly. - validateTSSbusy386 TSSU_DSEG_PROT32,1 - validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 validateTSSNT386 TSSU_DSEG_PROT32,1 @@ -307,8 +308,8 @@ TSStest1finished: setNTflag386 TSSU_DSEG_PROT32,0 ;This is going to be set. setNTflag386 TSSU_DSEG_PROT16,1 ;This is going to be cleared. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). - validateTSSbusy386 TSSU_DSEG_PROT32,1 - validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT32,1 @@ -318,8 +319,8 @@ TSStest1finished: setNTflag386 TSSU_DSEG_PROT32,1 ;This is going to be cleared. setNTflag386 TSSU_DSEG_PROT16,1 ;This is going to be cleared. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). - validateTSSbusy386 TSSU_DSEG_PROT32,1 - validateTSSbusy386 TSSU_DSEG_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT32,0 ;Set to 0 in destination task. @@ -331,8 +332,8 @@ TSStest1finished: ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy386 TSSU_DSEG_PROT16,1 - validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSS_PROT16,1 + validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 validateTSSNT386 TSSU_DSEG_PROT16,0 @@ -340,8 +341,8 @@ TSStest1finished: validateTSSNT386 0,1 ;Set currently in register only. iretd ;Return to caller. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy386 TSSU_DSEG_PROT16,1 - validateTSSbusy386 TSSU_DSEG_PROT32,1 + validateTSSbusy386 TSS_PROT16,1 + validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 validateTSSNT386 TSSU_DSEG_PROT16,1 ;Set to 1 in source task. @@ -565,10 +566,10 @@ initGDT: defGDTDescImplementation CU_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation CU_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE - defGDTDesc CU_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 From 76a10cfdbf6aaacbcbce6f40ae3b41990460d33a Mon Sep 17 00:00:00 2001 From: superfury Date: Sat, 11 Apr 2026 00:24:06 +0200 Subject: [PATCH 37/62] - Properly include the TSS size when setting and validating nested task flags. --- src/protected_tss_m.asm | 22 +++++++------- src/protected_tssh.asm | 66 ++++++++++++++++++++--------------------- src/test386.asm | 62 +++++++++++++++++++------------------- 3 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm index 6c14670..600cd82 100644 --- a/src/protected_tss_m.asm +++ b/src/protected_tss_m.asm @@ -41,26 +41,28 @@ ; validateTSSNT: Validate the NT bit of a TSS ; Parameters: ; %1 TSS data descriptor to validate. 0 for current task. -; %2 backlink bit to validate -%macro validateTSSNT286 2 - mov eax,(%1 | (%2<<16)) +; %2 TSS size (0 for 16-bit, 1 for 32-bit) +; %3 NT bit to validate +%macro validateTSSNT286 3 + mov eax,(%1 | ((%2|(%3<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT] %endmacro -%macro validateTSSNT386 2 - mov eax,(%1 | (%2<<16)) +%macro validateTSSNT386 3 + mov eax,(%1 | ((%2|(%3<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT+0xE0000] %endmacro ; setNTflag: Set the NT bit of the current TSS ; Parameters: ; %1 selector of TSS to use. 0 for current task FLAGS register. -; %2 NT bit to set -%macro setNTflag286 2 - mov eax,(%1 | (%2<<16)) +; %2 TSS size (0 for 16-bit, 1 for 32-bit) +; %3 NT bit to set +%macro setNTflag286 3 + mov eax,(%1 | ((%2|(%3<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT] %endmacro -%macro setNTflag386 2 - mov eax,(%1 | (%2<<16)) +%macro setNTflag386 3 + mov eax,(%1 | ((%2|(%3<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT+0xE0000] %endmacro diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 9289d1c..08579c5 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -105,7 +105,7 @@ TSS1_returned: ;We're the parent task again. call far [cs:ptrTSSprot32Gate] and esp,0xFFFF ;Safe ESP usage! - setNTflag286 0,0 ;Clear the NT flag for our bits and back-link tests. + setNTflag286 0,0,0 ;Clear the NT flag for our bits and back-link tests. ;Now, we switch sides jmp far [cs:ptrTSSprot32Gate] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. @@ -113,19 +113,19 @@ TSS1_returned: validateTSSbusy286 TSS_PROT,1 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 - validateTSSNT286 TSSU_DSEG_PROT32,0 - validateTSSNT286 TSSU_DSEG_PROT16,0 ;Not written yet, so left at 0. - validateTSSNT286 0,1 ;Set currently in register only. + validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT + validateTSSNT286 TSSU_DSEG_PROT32,1,0 + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Not written yet, so left at 0. + validateTSSNT286 0,0,1 ;Set currently in register only. iretd ;Return to caller. and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT,1 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 - validateTSSNT286 TSSU_DSEG_PROT32,1 ;Set to 1 in source task. - validateTSSNT286 TSSU_DSEG_PROT16,0 ;Left at 0. - validateTSSNT286 0,1 ;Set currently in register only. + validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT + validateTSSNT286 TSSU_DSEG_PROT32,1,1 ;Set to 1 in source task. + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Left at 0. + validateTSSNT286 0,0,1 ;Set currently in register only. iretd ;Return to caller. ;Now testing JMP from 32-bit task to 16-bit task, NT going from set to cleared in the source task. 16-bit task keeps it cleared. ;TSS test 3: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). @@ -134,9 +134,9 @@ TSS1_returned: validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT286 TSSU_DSEG_PROT32,1 ;Set to 1 in source task. - validateTSSNT286 TSSU_DSEG_PROT16,0 ;Left at 0. - validateTSSNT286 0,0 ;Cleared currently in register only. + validateTSSNT286 TSSU_DSEG_PROT32,1,1 ;Set to 1 in source task. + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Left at 0. + validateTSSNT286 0,0,0 ;Cleared currently in register only. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. ;TSS test 3.2 Same as before, but NT in 386 is cleared. and esp,0xFFFF ;Safe ESP usage! @@ -144,10 +144,10 @@ TSS1_returned: validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT286 TSSU_DSEG_PROT32,0 ;Set to 0 in source task. - validateTSSNT286 TSSU_DSEG_PROT16,1 ;Left at 1. - validateTSSNT286 0,0 ;Cleared currently in register only. - setNTflag286 0,1 ;Set NT flag to test. + validateTSSNT286 TSSU_DSEG_PROT32,1,0 ;Set to 0 in source task. + validateTSSNT286 TSSU_DSEG_PROT16,0,1 ;Left at 1. + validateTSSNT286 0,0,0 ;Cleared currently in register only. + setNTflag286 0,0,1 ;Set NT flag to test. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. ;Now, we have switched sides to check CALL from 16-bit to 32-bit. @@ -157,37 +157,37 @@ TSS1_returned: and esp,0xFFFF ;Safe ESP usage! setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD - setNTflag286 0,0 - validateTSSbusy286 TSS_PROT,1 - validateTSSbusy286 TSS_PROT16,0 + setNTflag286 0,0,0 + validateTSSbusy286 TSS_PROT16,1 + validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT286 TSSU_DSEG_PROT32,0 - validateTSSNT286 TSSU_DSEG_PROT16,0 - validateTSSNT286 0,0 + validateTSSNT286 TSSU_DSEG_PROT32,1,0 + validateTSSNT286 TSSU_DSEG_PROT16,0,0 + validateTSSNT286 0,0,0 call far [cs:ptrTSSprot32Gate] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, setup the second test for call, this time with NT set. ;First, validate IRET did it's job correctly. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSS_PROT,1 - validateTSSbusy286 TSS_PROT16,0 - validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 - validateTSSNT286 TSSU_DSEG_PROT32,0 - validateTSSNT286 TSSU_DSEG_PROT16,0 + validateTSSbusy286 TSS_PROT16,1 + validateTSSbusy286 TSS_PROT,0 + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSNT286 TSSU_DSEG_PROT32,1,0 + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD ;Now, with NT bit set. - setNTflag286 0,1 + setNTflag286 0,0,1 call far [cs:ptrTSSprot32Gate] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, validate IRET did it's job correctly. and esp,0xFFFF ;Safe ESP usage! - validateTSSbusy286 TSS_PROT,1 - validateTSSbusy286 TSS_PROT16,0 + validateTSSbusy286 TSS_PROT16,1 + validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 - validateTSSNT286 TSSU_DSEG_PROT32,1 - validateTSSNT286 TSSU_DSEG_PROT16,0 + validateTSSNT286 TSSU_DSEG_PROT32,1,1 + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD diff --git a/src/test386.asm b/src/test386.asm index ce70459..ff0034a 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -143,13 +143,13 @@ test386TSSstart: ; Verify we start clean setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - setNTflag386 0,0 + setNTflag386 0,1,0 validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,0 - validateTSSNT386 TSSU_DSEG_PROT16,0 + validateTSSNT386 TSSU_DSEG_PROT32,1,0 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Loading patterns for the 386 data segments mov ax,SU_SEG_PROT32DS|3 ;DS @@ -271,14 +271,14 @@ TSStest1finished: ;First, setup initial task state. setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - setNTflag386 0,0 + setNTflag386 0,1,0 validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,0 - validateTSSNT386 TSSU_DSEG_PROT16,0 - validateTSSNT386 0,0 + validateTSSNT386 TSSU_DSEG_PROT32,1,0 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 0,1,0 call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, setup the second test for call, this time with NT set. ;First, validate IRET did it's job correctly. @@ -286,68 +286,66 @@ TSStest1finished: validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 - validateTSSNT386 TSSU_DSEG_PROT32,0 - validateTSSNT386 TSSU_DSEG_PROT16,0 + validateTSSNT386 TSSU_DSEG_PROT32,1,0 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD ;Now, with NT bit set. - setNTflag386 0,1 + setNTflag386 0,1,1 call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, validate IRET did it's job correctly. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 - validateTSSNT386 TSSU_DSEG_PROT32,1 - validateTSSNT386 TSSU_DSEG_PROT16,0 + validateTSSNT386 TSSU_DSEG_PROT32,1,1 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. ;Now, test JMP instruction task switches. - setNTflag386 0,1 ;This is going to be kept in the 32-bit TSS. - setNTflag386 TSSU_DSEG_PROT32,0 ;This is going to be set. - setNTflag386 TSSU_DSEG_PROT16,1 ;This is going to be cleared. + setNTflag386 0,1,1 ;This is going to be kept in the 32-bit TSS. + setNTflag386 TSSU_DSEG_PROT32,1,0 ;This is going to be set. + setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be cleared. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1 - validateTSSNT386 TSSU_DSEG_PROT16,0 - validateTSSNT386 0,0 ;The JMP instruction to our TSS cleared us. - setNTflag386 0,0 ;This is going to be kept in the 32-bit TSS. - setNTflag386 TSSU_DSEG_PROT32,1 ;This is going to be cleared. - setNTflag386 TSSU_DSEG_PROT16,1 ;This is going to be cleared. + validateTSSNT386 TSSU_DSEG_PROT32,1,1 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 0,1,0 ;The JMP instruction to our TSS cleared us. + setNTflag386 0,1,0 ;This is going to be kept in the 32-bit TSS. + setNTflag386 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. + setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be cleared. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,0 ;Set to 0 in destination task. - validateTSSNT286 TSSU_DSEG_PROT16,1 ;Left at 1. - validateTSSNT286 0,0 ;Cleared currently in register only. + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Set to 0 in destination task. + validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Left at 1. + validateTSSNT386 0,1,0 ;Cleared currently in register only. ;Now, switch sides to verify CALL from 16-bit to 32-bit. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. - and esp,0xFFFF ;Safe ESP usage! validateTSSbusy386 TSS_PROT16,1 validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 - validateTSSNT386 TSSU_DSEG_PROT16,0 - validateTSSNT386 TSSU_DSEG_PROT32,0 ;Not written yet, so left at 0. - validateTSSNT386 0,1 ;Set currently in register only. + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Not written yet, so left at 0. + validateTSSNT386 0,1,1 ;Set currently in register only. iretd ;Return to caller. - and esp,0xFFFF ;Safe ESP usage! validateTSSbusy386 TSS_PROT16,1 validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 - validateTSSNT386 TSSU_DSEG_PROT16,1 ;Set to 1 in source task. - validateTSSNT386 TSSU_DSEG_PROT32,0 ;Left at 0. - validateTSSNT386 0,1 ;Set currently in register only. + validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Left at 0. + validateTSSNT386 0,1,1 ;Set currently in register only. iretd ;Return to caller. From 49d233f2c5bbb45b6e6b2e2f94b8f8dcf36b8f9a Mon Sep 17 00:00:00 2001 From: superfury Date: Sat, 11 Apr 2026 00:29:44 +0200 Subject: [PATCH 38/62] Fixed NT flag validation and TSS size bit positions. --- src/protected_tss_m.asm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm index 600cd82..2dc4440 100644 --- a/src/protected_tss_m.asm +++ b/src/protected_tss_m.asm @@ -44,11 +44,11 @@ ; %2 TSS size (0 for 16-bit, 1 for 32-bit) ; %3 NT bit to validate %macro validateTSSNT286 3 - mov eax,(%1 | ((%2|(%3<<1))<<16)) + mov eax,(%1 | ((%3|(%2<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT] %endmacro %macro validateTSSNT386 3 - mov eax,(%1 | ((%2|(%3<<1))<<16)) + mov eax,(%1 | ((%3|(%2<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT+0xE0000] %endmacro @@ -58,11 +58,11 @@ ; %2 TSS size (0 for 16-bit, 1 for 32-bit) ; %3 NT bit to set %macro setNTflag286 3 - mov eax,(%1 | ((%2|(%3<<1))<<16)) + mov eax,(%1 | ((%3|(%2<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT] %endmacro %macro setNTflag386 3 - mov eax,(%1 | ((%2|(%3<<1))<<16)) + mov eax,(%1 | ((%3|(%2<<1))<<16)) o32 call far [cs:ptrTSSprot32validateNT+0xE0000] %endmacro From 18b5eb2a062fb067d08c5dd3683be2ed74087149 Mon Sep 17 00:00:00 2001 From: superfury Date: Sat, 11 Apr 2026 12:38:29 +0200 Subject: [PATCH 39/62] Fixed the TSS error pointer to be correctly addressed. --- src/protected_tsshelpers.asm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index 2c62bca..01afae0 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -56,7 +56,7 @@ errorTSSbusy: ;Error in the TSS while checking the expected busy bit pop ebx ;Restore pop eax popfd - jmp far [TSShelpererrorptr+0xE0000] + jmp far [cs:TSShelpererrorptr+0xE0000] ; validateTSSbacklink: Validate the backlink field of a TSS ; Parameters: @@ -76,7 +76,7 @@ validateTSSbacklink: popfd retfd errorTSSbacklink: ;An error occurred during validating the TSS backlink field? - jmp far [TSShelpererrorptr+0xE0000] + jmp far [cs:TSShelpererrorptr+0xE0000] ; setTSSbacklink: Clear the backlink field of a TSS ; Parameters: @@ -127,7 +127,7 @@ validateTSSNT: popfd retfd errorTSSNT: ;An error occurred during validating the NT bit? - jmp far [TSShelpererrorptr+0xE0000] + jmp far [cs:TSShelpererrorptr+0xE0000] validateCurrentTSSNT: ;Selector 0 specified. From 08cc75f7b685ef3e5acb4c1f919beb64094dc1b6 Mon Sep 17 00:00:00 2001 From: superfury Date: Sat, 11 Apr 2026 17:31:08 +0200 Subject: [PATCH 40/62] Fixed TSS helper functions. Reduced basic TSS testing, as it's handled in detail at a later point in the code. --- src/protected_tss_m.asm | 4 ++-- src/protected_tssh.asm | 43 +--------------------------------- src/protected_tsshelpers.asm | 18 +++++++-------- src/test386.asm | 45 ++---------------------------------- 4 files changed, 14 insertions(+), 96 deletions(-) diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm index 2dc4440..c553531 100644 --- a/src/protected_tss_m.asm +++ b/src/protected_tss_m.asm @@ -59,10 +59,10 @@ ; %3 NT bit to set %macro setNTflag286 3 mov eax,(%1 | ((%3|(%2<<1))<<16)) - o32 call far [cs:ptrTSSprot32validateNT] + o32 call far [cs:ptrTSSprot32setNT] %endmacro %macro setNTflag386 3 mov eax,(%1 | ((%3|(%2<<1))<<16)) - o32 call far [cs:ptrTSSprot32validateNT+0xE0000] + o32 call far [cs:ptrTSSprot32setNT+0xE0000] %endmacro diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 08579c5..7e55bed 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -64,50 +64,9 @@ errorTSS16_1: ;Error occurred? jmp error ;We return here after check #1. TSS1_returned: - ;Now we start our TSS flags test - pushfd - pop eax - test ax,PS_NT ;Task properly nested? - jz error - push dword (FLAGS_SET|PS_NT) - popfd ;Set the flags to test. - iret ;return to the calling 32-bit task - ;Now the flags should have been set. - pushfd - pop eax - cmp eax,dword (FLAGS_SET|PS_NT) ;Correct? - jnz errorTSS16_1 - push dword (FLAGS_CLEARED|PS_NT) - popfd ;Clear the flags to test - iret ;return to the calling 32-bit task - pushfd - pop eax - cmp eax,dword (FLAGS_SET|PS_NT) ;Correct? - iret ;return to the calling 32-bit task - - ;Now, we switched sides to test. - ;Test the flags register during task switches now. - int 0x29 ;Start testing the 386 flags - push dword FLAGS_CLEARED - popfd ;Clear the flags to test. - int 0x29 ;Continue testing the 386 flags - push dword FLAGS_SET - popfd ;Set the flags to test. - int 0x29 ;Third stage of the flags test. - ;386 flags test completed. - - ;Now, we switch sides + ;Now, we switch sides back, validating basic JMP-based task switches. jmp far [cs:ptrTSSprot32Gate] - ;We've been far called. Return. - iretd - - ;We're the parent task again. - call far [cs:ptrTSSprot32Gate] - and esp,0xFFFF ;Safe ESP usage! - setNTflag286 0,0,0 ;Clear the NT flag for our bits and back-link tests. - ;Now, we switch sides - jmp far [cs:ptrTSSprot32Gate] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT,1 diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index 01afae0..62cbcd9 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -143,10 +143,10 @@ validateCurrentTSSNT: ;Selector 0 specified. ; EAX: lower half: TSS data descriptor to check. Zeroed for current TSS (EFLAGS register). bit 16: NT to set, bit 17: 32-bit TSS. setTSSNT: push ecx ;Type determination - mov ecx,0 ;Default: type TSS. pushfd cmp ax,0 ;Active TSS to validate? - jz validateCurrentTSSNT + jz setCurrentTSSNT + mov ecx,0 ;Default: type TSS. ;Validate specified TSS push ds ;Save push eax ;Save @@ -164,18 +164,18 @@ setTSSNT: and bx,0xBFFF ;Mask off the NT bit and eax,PS_NT ;Mask off the NT bit or bx,ax ;Set the NT bit only. - cmp ecx,1 ;Type EFLAGS? - jnz finishTSStypeEFLAGS + cmp ecx,0 ;Type TSS? + jnz finishTSSsetEFLAGS ;Type TSS. test eax,0x8000 ;32-bit TSS? jnz set32bitsTSSNT ;16-bit TSS to write. mov [0x10], bx ;Set the FLAGS register. - jmp commonfinishTSSNTbit + jmp commonfinishTSSsetNTbit set32bitsTSSNT: ;32-bits TSS to write. mov [0x24], bx ;Set the FLAGS register. - commonfinishTSSNTbit: + commonfinishTSSsetNTbit: ;Clean up and return. pop ebx pop eax @@ -183,9 +183,9 @@ setTSSNT: popfd pop ecx retfd -finishTSStypeEFLAGS: +finishTSSsetEFLAGS: mov [esp+0xC], bx ;Update on the stack instead. - jmp commonfinishTSSNTbit + jmp commonfinishTSSsetNTbit setCurrentTSSNT: ;Selector 0 specified. mov ecx,1 ;Type is requested to be EFLAGS instead. @@ -194,5 +194,5 @@ setCurrentTSSNT: ;Selector 0 specified. push ebx ;Save mov ebx,[esp+0xC] ;Load the EFLAGS register into EBX. shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS (unused)) - jmp commonvalidateTSSNTbit ;Common validation point + jmp commonsetTSSNTbit ;Common validation point diff --git a/src/test386.asm b/src/test386.asm index ff0034a..ef7cf27 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -221,49 +221,8 @@ errorTSS32_1: jmp error ;Error out TSStest1finished: - ;Test the flags register during task switches now. - int 0x28 ;Start testing the 286 flags - push dword FLAGS_CLEARED - popfd ;Clear the flags to test. - int 0x28 ;Continue testing the 286 flags - push dword FLAGS_SET - popfd ;Set the flags to test. - int 0x28 ;Third stage of the flags test. - ;286 flags test completed. - - ;Now, we switch sides - jmp far [cs:ptrTSSprot16Gate+0xF0000] - - ;Now we start our TSS flags test - pushfd - pop eax - test ax,PS_NT ;Task properly nested? - jz error - push dword (FLAGS_SET|PS_NT) - popfd ;Set the flags to test. - iretd ;return to the calling 16-bit task - ;Now the flags should have been set. - pushfd - cmp [esp],dword (FLAGS_SET|PS_NT) ;Correct? - jnz errorTSS32_1 - popfd - push dword (FLAGS_CLEARED|PS_NT) - popfd ;Clear the flags to test - iretd ;return to the calling 16-bit task - pushfd - cmp [esp],dword (FLAGS_CLEARED|PS_NT) ;Correct? - jnz errorTSS32_1 - popfd ;Restore the flags - iretd ;return to the calling 16-bit task - - ;We're the parent task again. - ;Perform call tests now - call far [cs:ptrTSSprot16Gate+0xF0000] - ;Now, we switch sides - jmp far [cs:ptrTSSprot16Gate+0xF0000] - ;We've been far called. Return. - iretd - ;We're the parent task again. + ;Now, we switch sides, to basic test JMP-based task switches + jmp far [cs:ptrTSSprot16Gate] ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) From 8ac9728be501ca30fe83302bae87f97ab0956ff2 Mon Sep 17 00:00:00 2001 From: superfury Date: Sat, 11 Apr 2026 18:36:11 +0200 Subject: [PATCH 41/62] - Fixed various TSS validation issues. --- src/protected_tssh.asm | 10 +++++----- src/test386.asm | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 7e55bed..d8837ad 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -94,7 +94,7 @@ TSS1_returned: validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,1,1 ;Set to 1 in source task. - validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Left at 0. + validateTSSNT286 TSSU_DSEG_PROT16,0,1 ;Left at 1. validateTSSNT286 0,0,0 ;Cleared currently in register only. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. ;TSS test 3.2 Same as before, but NT in 386 is cleared. @@ -135,7 +135,7 @@ TSS1_returned: validateTSSNT286 TSSU_DSEG_PROT32,1,0 validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. - setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD ;Now, with NT bit set. setNTflag286 0,0,1 call far [cs:ptrTSSprot32Gate] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). @@ -143,12 +143,12 @@ TSS1_returned: and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 - validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink286 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT32,TSS_PROT16 validateTSSNT286 TSSU_DSEG_PROT32,1,1 validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. - setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD ;Finally, return to the 386 task to finish up these TSS tests and continue running other tests. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index ef7cf27..0040130 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -222,7 +222,7 @@ errorTSS32_1: TSStest1finished: ;Now, we switch sides, to basic test JMP-based task switches - jmp far [cs:ptrTSSprot16Gate] + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) @@ -244,7 +244,7 @@ TSStest1finished: validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT validateTSSNT386 TSSU_DSEG_PROT32,1,0 validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. @@ -256,7 +256,7 @@ TSStest1finished: validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT16 + validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT validateTSSNT386 TSSU_DSEG_PROT32,1,1 validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. From cff8b9918f28b532c3bb0811de11c31fbed1920a Mon Sep 17 00:00:00 2001 From: superfury Date: Sat, 11 Apr 2026 22:38:08 +0200 Subject: [PATCH 42/62] Improved the 16-bit and 32-bit TSS validation checks. Fixed issues with the nested task setup helper function. --- src/protected_tssh.asm | 42 ++++++++++++++++++++++++++++++------ src/protected_tsshelpers.asm | 11 +++++++--- src/test386.asm | 42 ++++++++++++++++++++++++++++-------- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index d8837ad..0ead552 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -67,6 +67,7 @@ TSS1_returned: ;Now, we switch sides back, validating basic JMP-based task switches. jmp far [cs:ptrTSSprot32Gate] + ;Start of the 16-bit side of the 32-bit to 16-bit TSS tests. ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT,1 @@ -111,7 +112,8 @@ TSS1_returned: ;Now, we have switched sides to check CALL from 16-bit to 32-bit. - ;Step 1: validate 32-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. + ;Start of the 16-bit to 32-bit TSS tests. + ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. ;First, setup initial task state. and esp,0xFFFF ;Safe ESP usage! setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD @@ -122,7 +124,7 @@ TSS1_returned: validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,1,0 - validateTSSNT286 TSSU_DSEG_PROT16,0,0 + validateTSSNT286 TSSU_DSEG_PROT16,0,1 validateTSSNT286 0,0,0 call far [cs:ptrTSSprot32Gate] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, setup the second test for call, this time with NT set. @@ -130,12 +132,12 @@ TSS1_returned: and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 - validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,1,0 validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Reset state for detection. - setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD ;Now, with NT bit set. setNTflag286 0,0,1 call far [cs:ptrTSSprot32Gate] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). @@ -143,12 +145,38 @@ TSS1_returned: and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 - validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT32,TSS_PROT16 - validateTSSNT286 TSSU_DSEG_PROT32,1,1 - validateTSSNT286 TSSU_DSEG_PROT16,0,0 + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT286 TSSU_DSEG_PROT32,1,0 + validateTSSNT286 TSSU_DSEG_PROT16,0,1 ;Reset state for detection. setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. + ;Now, test JMP instruction task switches. + setNTflag286 0,0,1 ;This is going to be kept in the 32-bit TSS. + setNTflag286 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. + setNTflag286 TSSU_DSEG_PROT16,0,0 ;This is going to be set. + jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSS_PROT16,1 + validateTSSbusy286 TSS_PROT,0 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT286 TSSU_DSEG_PROT32,1,0 + validateTSSNT286 TSSU_DSEG_PROT16,0,1 + validateTSSNT286 0,0,0 ;The JMP instruction to our TSS cleared us. + setNTflag286 0,0,0 ;This is going to be kept in the 32-bit TSS. + setNTflag286 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. + setNTflag286 TSSU_DSEG_PROT16,0,1 ;This is going to be cleared. + jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + and esp,0xFFFF ;Safe ESP usage! + validateTSSbusy286 TSS_PROT16,1 + validateTSSbusy286 TSS_PROT,0 + validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Set to 0 in destination task. + validateTSSNT286 TSSU_DSEG_PROT32,1,1 ;Left at 1. + validateTSSNT286 0,0,0 ;Cleared currently in register only. ;Finally, return to the 386 task to finish up these TSS tests and continue running other tests. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. \ No newline at end of file diff --git a/src/protected_tsshelpers.asm b/src/protected_tsshelpers.asm index 62cbcd9..02f3878 100644 --- a/src/protected_tsshelpers.asm +++ b/src/protected_tsshelpers.asm @@ -151,8 +151,10 @@ setTSSNT: push ds ;Save push eax ;Save push ebx ;Save + push edx ;Save mov ds,ax ;Load the TSS shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS) + mov edx,eax ;Backup test eax,0x8000 ;32-bit TSS? jnz get32bitTSSNT mov bx,[0x10] ;Load flags register @@ -167,7 +169,7 @@ setTSSNT: cmp ecx,0 ;Type TSS? jnz finishTSSsetEFLAGS ;Type TSS. - test eax,0x8000 ;32-bit TSS? + test edx,0x8000 ;32-bit TSS? jnz set32bitsTSSNT ;16-bit TSS to write. mov [0x10], bx ;Set the FLAGS register. @@ -177,6 +179,7 @@ setTSSNT: mov [0x24], bx ;Set the FLAGS register. commonfinishTSSsetNTbit: ;Clean up and return. + pop edx pop ebx pop eax pop ds @@ -184,7 +187,7 @@ setTSSNT: pop ecx retfd finishTSSsetEFLAGS: - mov [esp+0xC], bx ;Update on the stack instead. + mov [esp+0x10], bx ;Update on the stack instead. jmp commonfinishTSSsetNTbit setCurrentTSSNT: ;Selector 0 specified. @@ -192,7 +195,9 @@ setCurrentTSSNT: ;Selector 0 specified. push ds ;Save push eax ;Save push ebx ;Save - mov ebx,[esp+0xC] ;Load the EFLAGS register into EBX. + push edx ;Save + mov ebx,[esp+0x10] ;Load the EFLAGS register into EBX. shr eax,2 ;Move the data to validate into bits 14(expected NT bit) and 15(32-bit TSS (unused)) + mov edx,eax ;Backup jmp commonsetTSSNTbit ;Common validation point diff --git a/src/test386.asm b/src/test386.asm index 0040130..ed5cee2 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -226,6 +226,7 @@ TSStest1finished: ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) + ;Start of the 32-bit to 16-bit TSS tests. ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. ;First, setup initial task state. setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -286,29 +287,52 @@ TSStest1finished: validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Left at 1. validateTSSNT386 0,1,0 ;Cleared currently in register only. - ;Now, switch sides to verify CALL from 16-bit to 32-bit. + ;End of the 32-bit to 16-bit TSS tests. Now, switch sides to verify CALL from 16-bit to 32-bit. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + ;Start of the 32-bit side of the 16-bit to 32-bit TSS tests. ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. - validateTSSbusy386 TSS_PROT16,1 validateTSSbusy386 TSS_PROT,1 - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSbusy386 TSS_PROT16,1 validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 - validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Not written yet, so left at 0. - validateTSSNT386 0,1,1 ;Set currently in register only. + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 0,0,1 ;Set currently in register only. iretd ;Return to caller. - validateTSSbusy386 TSS_PROT16,1 validateTSSbusy386 TSS_PROT,1 - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSbusy386 TSS_PROT16,1 validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Left at 0. - validateTSSNT386 0,1,1 ;Set currently in register only. + validateTSSNT386 0,0,1 ;Set currently in register only. iretd ;Return to caller. + ;Now testing JMP from 32-bit task to 32-bit task, NT going from set to cleared in the source task. 32-bit task keeps it cleared. + ;TSS test 3: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSSbusy386 TSS_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Left at 1. + validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. + validateTSSNT386 0,0,0 ;Cleared currently in register only. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 32-bit task. + ;TSS test 3.2 Same as before, but NT in 286 is cleared. + validateTSSbusy386 TSS_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Left at 1. + validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Set to 0 in source task. + validateTSSNT386 0,0,0 ;Cleared currently in register only. + setNTflag386 0,1,1 ;Set NT flag to test. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. + ;Now, we have switched sides to finish the tests. + ;We're the parent task again. - + setNTflag386 0,1,1 ;Set NT flag to test. ;32-bit eflags register loaded OK. pushfd From 406e293db2dbe473259c8f97baab11af374e6d3f Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 12 Apr 2026 13:03:38 +0200 Subject: [PATCH 43/62] Fixed cleanup of the 386 task NT flag when finishing the TSS tests. --- src/test386.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test386.asm b/src/test386.asm index ed5cee2..35af715 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -332,7 +332,7 @@ TSStest1finished: ;Now, we have switched sides to finish the tests. ;We're the parent task again. - setNTflag386 0,1,1 ;Set NT flag to test. + setNTflag386 0,1,0 ;Clear NT flag to finish. ;32-bit eflags register loaded OK. pushfd From 6862f0212af5288c39cc05629039e72307f59bde Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 12 Apr 2026 16:01:20 +0200 Subject: [PATCH 44/62] Fixed 80386/80486 JMP causing task switch to properly affect the nested task flag, fixing errata in the 80386 programmer's reference manual. --- src/protected_tssh.asm | 28 ++++++++++++++-------------- src/test386.asm | 30 ++++++++++++++++-------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 0ead552..79637e6 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -87,16 +87,16 @@ TSS1_returned: validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Left at 0. validateTSSNT286 0,0,1 ;Set currently in register only. iretd ;Return to caller. - ;Now testing JMP from 32-bit task to 16-bit task, NT going from set to cleared in the source task. 16-bit task keeps it cleared. - ;TSS test 3: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + ;Now testing JMP from 32-bit task to 16-bit task, NT in 286 task kept as-is. 16-bit task keeps it cleared. + ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT,0 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,1,1 ;Set to 1 in source task. - validateTSSNT286 TSSU_DSEG_PROT16,0,1 ;Left at 1. - validateTSSNT286 0,0,0 ;Cleared currently in register only. + validateTSSNT286 TSSU_DSEG_PROT16,0,0 ;Loaded as 0. + validateTSSNT286 0,0,0 ;Set in destination task from the TSS. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. ;TSS test 3.2 Same as before, but NT in 386 is cleared. and esp,0xFFFF ;Safe ESP usage! @@ -105,8 +105,8 @@ TSS1_returned: validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD validateTSSNT286 TSSU_DSEG_PROT32,1,0 ;Set to 0 in source task. - validateTSSNT286 TSSU_DSEG_PROT16,0,1 ;Left at 1. - validateTSSNT286 0,0,0 ;Cleared currently in register only. + validateTSSNT286 TSSU_DSEG_PROT16,0,1 ;Loaded as 1. + validateTSSNT286 0,0,1 ;Set in destination task from the TSS. setNTflag286 0,0,1 ;Set NT flag to test. jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. @@ -154,21 +154,21 @@ TSS1_returned: ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. ;Now, test JMP instruction task switches. setNTflag286 0,0,1 ;This is going to be kept in the 32-bit TSS. - setNTflag286 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. - setNTflag286 TSSU_DSEG_PROT16,0,0 ;This is going to be set. - jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + setNTflag286 TSSU_DSEG_PROT16,0,0 ;This is going to be set to 1. + setNTflag286 TSSU_DSEG_PROT32,1,0 ;This is going to be loaded. + jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT286 TSSU_DSEG_PROT32,1,0 validateTSSNT286 TSSU_DSEG_PROT16,0,1 - validateTSSNT286 0,0,0 ;The JMP instruction to our TSS cleared us. - setNTflag286 0,0,0 ;This is going to be kept in the 32-bit TSS. - setNTflag286 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. + validateTSSNT286 TSSU_DSEG_PROT32,1,0 + validateTSSNT286 0,0,1 ;The JMP instruction to our TSS kept us. + setNTflag286 0,0,0 ;This is going to be kept in the 16-bit TSS. setNTflag286 TSSU_DSEG_PROT16,0,1 ;This is going to be cleared. - jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + setNTflag286 TSSU_DSEG_PROT32,1,1 ;This is going to be loaded. + jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 diff --git a/src/test386.asm b/src/test386.asm index 35af715..a23a5f6 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -225,6 +225,7 @@ TSStest1finished: jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) + ;80386 and 80486 programmer's reference manuals mention that the NT flag after a JMP instruction to another task is cleared. It is in fact, unaffected on 80386/80486 processors, due to a misprint of the manuals. ;Start of the 32-bit to 16-bit TSS tests. ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. @@ -265,20 +266,20 @@ TSStest1finished: ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. ;Now, test JMP instruction task switches. setNTflag386 0,1,1 ;This is going to be kept in the 32-bit TSS. - setNTflag386 TSSU_DSEG_PROT32,1,0 ;This is going to be set. - setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be cleared. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + setNTflag386 TSSU_DSEG_PROT32,1,0 ;This is going to be set to 1. + setNTflag386 TSSU_DSEG_PROT16,0,0 ;This is going to be loaded. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD validateTSSNT386 TSSU_DSEG_PROT32,1,1 validateTSSNT386 TSSU_DSEG_PROT16,0,0 - validateTSSNT386 0,1,0 ;The JMP instruction to our TSS cleared us. + validateTSSNT386 0,1,1 ;The JMP instruction to our TSS cleared us. setNTflag386 0,1,0 ;This is going to be kept in the 32-bit TSS. setNTflag386 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. - setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be cleared. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be loaded. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -288,7 +289,7 @@ TSStest1finished: validateTSSNT386 0,1,0 ;Cleared currently in register only. ;End of the 32-bit to 16-bit TSS tests. Now, switch sides to verify CALL from 16-bit to 32-bit. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Switch sides ;Start of the 32-bit side of the 16-bit to 32-bit TSS tests. ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. @@ -309,26 +310,27 @@ TSStest1finished: validateTSSNT386 0,0,1 ;Set currently in register only. iretd ;Return to caller. ;Now testing JMP from 32-bit task to 32-bit task, NT going from set to cleared in the source task. 32-bit task keeps it cleared. - ;TSS test 3: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 286 task cleared, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + ;TSS test 3: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + ;TSS test 3.1: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). validateTSSbusy386 TSS_PROT16,0 validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Left at 1. validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. - validateTSSNT386 0,0,0 ;Cleared currently in register only. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 32-bit task. + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Loaded as 0. + validateTSSNT386 0,1,0 ;Set in destination task from the TSS. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. ;TSS test 3.2 Same as before, but NT in 286 is cleared. validateTSSbusy386 TSS_PROT16,0 validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Left at 1. validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Set to 0 in source task. - validateTSSNT386 0,0,0 ;Cleared currently in register only. + validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Loaded as 1. + validateTSSNT386 0,1,1 ;Set in destination task from the TSS. setNTflag386 0,1,1 ;Set NT flag to test. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. - + ;Now, we have switched sides to finish the tests. ;We're the parent task again. From cb9647c376e8dc47fabe1b22c36957b3c3309ba1 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 14 Apr 2026 01:13:19 +0200 Subject: [PATCH 45/62] Implemented ring 2 stack validation when validating the TSS task switch functionality for both 16-bit and 32-bit TSS. Improved initial and final stack when switching to and from flat user mode. Implemented Task-Switched flag validation when executing TSS task switches. --- src/protected_m.asm | 27 +++++++++ src/protected_rings_p.asm | 13 ----- src/protected_tss_m.asm | 4 ++ src/protected_tssh.asm | 16 ++++- src/protected_tssinth.asm | 119 ++++++++++++++++++++++++++++++++++++++ src/test386.asm | 101 +++++++++++++++++++++++++++----- src/tss_p.asm | 4 +- 7 files changed, 253 insertions(+), 31 deletions(-) create mode 100644 src/protected_tssinth.asm diff --git a/src/protected_m.asm b/src/protected_m.asm index 7f6e215..141b922 100644 --- a/src/protected_m.asm +++ b/src/protected_m.asm @@ -1,3 +1,30 @@ +; +; Tests the Current Privilege Level value +; +; %1 the value (0-3) to compare to; jumps to error if not equal. +; +%macro testCPL_E 2 + push eax + mov ax, cs + and ax, 3 + cmp ax, %1 + jne %2 + pop eax +%endmacro + + +; +; Tests the Current Privilege Level value +; +; %1 the value (0-3) to compare to; jumps to error if not equal. +; +%macro testCPL 1 + mov ax, cs + and ax, 3 + cmp ax, %1 + jne error +%endmacro + ; ; Advances the base address of data segments used by tests, D1_SEG_PROT and ; D2_SEG_PROT. diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index 7906eed..181aafc 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -1,16 +1,3 @@ -; -; Tests the Current Privilege Level value -; -; %1 the value (0-3) to compare to; jumps to error if not equal. -; -%macro testCPL 1 - mov ax, cs - and ax, 3 - cmp ax, %1 - jne error -%endmacro - - ; ; Switches from Ring 0 to Ring 3 ; diff --git a/src/protected_tss_m.asm b/src/protected_tss_m.asm index c553531..9ecdcce 100644 --- a/src/protected_tss_m.asm +++ b/src/protected_tss_m.asm @@ -66,3 +66,7 @@ o32 call far [cs:ptrTSSprot32setNT+0xE0000] %endmacro +%macro validateTSandClear 1 + mov eax,%1 + int 0x2C ;Validate and clear TS bit. +%endmacro diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 79637e6..053faab 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -16,7 +16,7 @@ errorTSS16: TSS286entrypoint: cmp esp,0xFFFF1122 ;SP loaded correctly? jnz errorTSS16 - mov sp,ESP_R3_PROT ;Fixup SP + mov esp,ESP_R3_PROT ;Fixup SP cmp eax,0xFFFF1234 ;EAX loaded correctly? jnz errorTSS16 cmp ecx,0xFFFF5678 @@ -58,18 +58,24 @@ TSS286entrypoint: sldt ax cmp ax,LDT_SEG_PROT286 ;LDT OK? jnz errorTSS16_1 + int 0x2B ;Validate 286 interrupts to Ring 2 + kernelModeInterruptR2Return286: + validateTSandClear 1 ;TS is expected to be set by the task switch. iret ;Return to the calling task jmp TSS1_returned errorTSS16_1: ;Error occurred? jmp error ;We return here after check #1. TSS1_returned: + and esp,0xFFFF ;Safe ESP usage + validateTSandClear 1 ;TS is expected to be set by the task switch. ;Now, we switch sides back, validating basic JMP-based task switches. jmp far [cs:ptrTSSprot32Gate] ;Start of the 16-bit side of the 32-bit to 16-bit TSS tests. ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT,1 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD @@ -79,6 +85,7 @@ TSS1_returned: validateTSSNT286 0,0,1 ;Set currently in register only. iretd ;Return to caller. and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT,1 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD @@ -90,6 +97,7 @@ TSS1_returned: ;Now testing JMP from 32-bit task to 16-bit task, NT in 286 task kept as-is. 16-bit task keeps it cleared. ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT in 286 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT,0 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD @@ -100,6 +108,7 @@ TSS1_returned: jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. ;TSS test 3.2 Same as before, but NT in 386 is cleared. and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT,0 validateTSSbusy286 TSS_PROT16,1 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD @@ -116,6 +125,7 @@ TSS1_returned: ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. ;First, setup initial task state. and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. setTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink286 TSSU_DSEG_PROT16,0xDEAD setNTflag286 0,0,0 @@ -130,6 +140,7 @@ TSS1_returned: ;Now, setup the second test for call, this time with NT set. ;First, validate IRET did it's job correctly. and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,TSS_PROT16 @@ -143,6 +154,7 @@ TSS1_returned: call far [cs:ptrTSSprot32Gate] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, validate IRET did it's job correctly. and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,TSS_PROT16 @@ -158,6 +170,7 @@ TSS1_returned: setNTflag286 TSSU_DSEG_PROT32,1,0 ;This is going to be loaded. jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD @@ -170,6 +183,7 @@ TSS1_returned: setNTflag286 TSSU_DSEG_PROT32,1,1 ;This is going to be loaded. jmp far [cs:ptrTSSprot32Gate] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). and esp,0xFFFF ;Safe ESP usage! + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy286 TSS_PROT16,1 validateTSSbusy286 TSS_PROT,0 validateTSSbacklink286 TSSU_DSEG_PROT32,0xDEAD diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm new file mode 100644 index 0000000..a9abe5d --- /dev/null +++ b/src/protected_tssinth.asm @@ -0,0 +1,119 @@ +BITS 32 + +ptrTSSprot_R2: ; pointer to the task state segment + dd 0 + dw TSSU_DSEG_PROT32|3 +ptrTSSprot16_R2: ; pointer to the 16-bit task state segment + dd 0 + dw TSSU_DSEG_PROT16|3 +ptrTSSerrorR0_2: ; pointer to the error condition, from ring 0 or ring 2 + dd error + dw CU_SEG_PROT32|3 + + +errorCPLn: + jmp far [cs:ptrTSSerrorR0_2] ;JMP to the error condition. +; +; Kernel mode interrupt handler (ring 2) for 32-bit TSS +; +kernelInterrupt_R2_386: + testCPL_E 2,errorCPLn ; Elevates to CPL 2 + push ebx + push ecx + push ds + lds ebx, [cs:ptrTSSprot_R2] ; Get the TSS + mov ecx, [ebx+0x14] ; Get TSS ESP2 + sub ecx, 0x14+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed + cmp esp, ecx ; Did the stack decrease correctly? + jne errorCPLn + mov cx, ss + push edx + mov dx, word [ebx+0x18] + or dx, 2 ;Privilege level 2 + cmp cx, dx ; Did the stack pointer load correctly? + pop edx + jne errorCPLn + pop ds + pop ecx + mov bx, cs + cmp bx, C_SEG_PROT32_R2|2 ; Did we end up in kernel mode correctly? + jne errorCPLn + pop ebx + push ebx + mov ebx, kernelModeInterruptR2Return + or ebx, 0xE0000 + cmp dword [esp+0x04], ebx + pop ebx + jne errorCPLn ; Invalid return address + cmp dword [esp+0x04], CU_SEG_PROT32FLAT|3 + jne errorCPLn ; Invalid return code segment + ; Ignore eflags + cmp dword [esp+0x0C], ESP_R3_PROTFLAT + jne errorCPLn ; Invalid return ESP + cmp dword [esp+0x10], DU_SEG_PROT32FLAT|3 + jne errorCPLn ; Invalid user stack segment + iret ; Simply return to user mode + +; +; Kernel mode interrupt handler (ring 2) for 16-bit TSS +; +kernelInterrupt_R2_286: + testCPL_E 2,errorCPLn ; Elevates to CPL 2 + push ebx + push ecx + push ds + lds ebx, [cs:ptrTSSprot16_R2] ; Get the TSS + mov cx, [ebx+0xA] ; Get TSS ESP2 + and ecx,0xFFFF + sub ecx, 0x14+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed + cmp esp, ecx ; Did the stack decrease correctly? + jne errorCPLn + mov cx, ss + push edx + mov dx, word [ebx+0xC] + or dx, 2 ;Privilege level 2 + cmp cx, dx ; Did the stack pointer load correctly? + pop edx + jne errorCPLn + pop ds + pop ecx + mov bx, cs + cmp bx, C_SEG_PROT32_R2|2 ; Did we end up in kernel mode correctly? + jne errorCPLn + pop ebx + cmp dword [esp+0x00], kernelModeInterruptR2Return286 + jne errorCPLn ; Invalid return address + cmp dword [esp+0x04], CU_SEG_PROT16CS|3 + jne errorCPLn ; Invalid return code segment + ; Ignore eflags + cmp dword [esp+0x0C], ESP_R3_PROT + jne errorCPLn ; Invalid return ESP + cmp dword [esp+0x10], SU_SEG_PROT16SS|3 + jne errorCPLn ; Invalid user stack segment + iret ; Simply return to user mode + +; +; Kernel mode interrupt handler to validate a 32-bit TSS +; +kernelInterrupt_validateAndClearTS: + testCPL_E 0,errorCPLn ; Elevates to CPL 0 + push eax + push ebx + smsw eax ;Load CR0, alternative way + mov ebx, cr0 ;Load CR0, normal way + cmp ebx, eax ;CR0 loaded correctly? + jne errorCPLn ;Error out if not. + mov eax, [esp+4] ;Requested EAX restored. + shl eax, 3 ;Move tested bit to bit 3 (TS) + and eax, 0x08 ;Bit to test + and ebx, 0x08 ;Bit to test + cmp ebx, eax ;As expected? + jne errorCPLn ;Invalid TS bit + clts ;Clear the TS bit + smsw ebx ;Load CR0 + and ebx,0x08 ;Bit to test + jnz errorCPLn ;TS bit not properly cleared? + pop ebx + pop eax + iret ; Simply return to user mode + diff --git a/src/test386.asm b/src/test386.asm index a23a5f6..8d38ad3 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -105,6 +105,7 @@ ESP_REAL equ 0xffff defGDTDescPrototype LDT_SEG_PROT defGDTDescPrototype LDT_SEG_PROT286 defGDTDescPrototype DU_SEG_PROT32FLAT + defGDTDescPrototype TSS_DSEG_PROT defGDTDescPrototype TSS_DSEG_PROT16 defGDTDescPrototype TSS_PROT defGDTDescPrototype TSS_PROT16 @@ -122,11 +123,18 @@ ESP_REAL equ 0xffff defGDTDescPrototype TSSU_DSEG_PROT32 defGDTDescPrototype TSSU_DSEG_PROT16 defGDTDescPrototype CU_SEG_PROT32 + defGDTDescPrototype C_SEG_PROT32_R2 + defGDTDescPrototype S_SEG_PROT32_R2 + defGDTDescPrototype C_SEG_PROT16CS section .system_bios_extensions_area start=0x00000 ;Start of high BIOS ; TSS helper macros %include "protected_tss_m.asm" + ; + ; TSS interrupt handlers + ; +%include "protected_tssinth.asm" ; ; 286 TSS handler ; @@ -151,6 +159,24 @@ test386TSSstart: validateTSSNT386 TSSU_DSEG_PROT32,1,0 validateTSSNT386 TSSU_DSEG_PROT16,0,0 + int 0x2A ;Validate 386 interrupts to Ring 2 + kernelModeInterruptR2Return: + validateTSandClear 0 ;Expect no TS bit set yet. + ;Transfer the ring 0 stack from the 32-bit TSS to the 16-bit TSS to be able to call it and validate the TS flag. + push ds + push ebx + push ecx + lds ebx,[cs:ptrTSSprot_R2+0xE0000] ;Get the 32-bit TSS + mov cx,[ebx+4] ;Get ring 0 ESP + shl ecx,16 ;Move SP high + mov cx,[ebx+8] ;Get ring 0 SS + lds ebx,[cs:ptrTSSprot16_R2+0xE0000] ;Get the 16-bit TSS + mov [ebx+4],cx ;Set ring 0 SS + shr ecx,16 ;Move SP low + mov [ebx+2],cx ;Set ring 0 SP + pop ecx + pop ebx + pop ds ;Loading patterns for the 386 data segments mov ax,SU_SEG_PROT32DS|3 ;DS mov ds,ax @@ -213,6 +239,7 @@ test386TSSstart: sldt ax cmp ax,LDT_SEG_PROT ;LDT OK? jnz errorTSS16_1 + validateTSandClear 1 ;TS is expected to be set by the task switch. jmp TSStest1finished errorTSS32_1: mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value @@ -230,6 +257,7 @@ TSStest1finished: ;Start of the 32-bit to 16-bit TSS tests. ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. ;First, setup initial task state. + validateTSandClear 1 ;TS is expected to be set by the task switch. setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD setNTflag386 0,1,0 @@ -243,6 +271,7 @@ TSStest1finished: call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, setup the second test for call, this time with NT set. ;First, validate IRET did it's job correctly. + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -255,6 +284,7 @@ TSStest1finished: setNTflag386 0,1,1 call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). ;Now, validate IRET did it's job correctly. + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -269,6 +299,7 @@ TSStest1finished: setNTflag386 TSSU_DSEG_PROT32,1,0 ;This is going to be set to 1. setNTflag386 TSSU_DSEG_PROT16,0,0 ;This is going to be loaded. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -280,6 +311,7 @@ TSStest1finished: setNTflag386 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be loaded. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,0 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -293,6 +325,7 @@ TSStest1finished: ;Start of the 32-bit side of the 16-bit to 32-bit TSS tests. ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,1 validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 @@ -301,6 +334,7 @@ TSStest1finished: validateTSSNT386 TSSU_DSEG_PROT16,0,0 validateTSSNT386 0,0,1 ;Set currently in register only. iretd ;Return to caller. + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT,1 validateTSSbusy386 TSS_PROT16,1 validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 @@ -312,6 +346,7 @@ TSStest1finished: ;Now testing JMP from 32-bit task to 32-bit task, NT going from set to cleared in the source task. 32-bit task keeps it cleared. ;TSS test 3: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). ;TSS test 3.1: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT16,0 validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -321,6 +356,7 @@ TSStest1finished: validateTSSNT386 0,1,0 ;Set in destination task from the TSS. jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. ;TSS test 3.2 Same as before, but NT in 286 is cleared. + validateTSandClear 1 ;TS is expected to be set by the task switch. validateTSSbusy386 TSS_PROT16,0 validateTSSbusy386 TSS_PROT,1 validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -334,10 +370,10 @@ TSStest1finished: ;Now, we have switched sides to finish the tests. ;We're the parent task again. + validateTSandClear 1 ;TS is expected to be set by the task switch. setNTflag386 0,1,0 ;Clear NT flag to finish. ;32-bit eflags register loaded OK. - pushfd push cs call nextlowbios nextlowbios: @@ -502,7 +538,7 @@ cpuTest: jmp initGDT ESP_R0_PROT equ 0x0000FFFF -ESP_R2_PROT equ 0x00001FFF +ESP_R2_PROT equ 0x0000EFFE ESP_R0_PROTFLAT equ 0xE001FFFF ESP_R3_PROT equ 0x00007FFF ESP_R3_PROTFLAT equ 0x0001FFFF @@ -533,23 +569,27 @@ initGDT: defGDTDescImplementation LDT_SEG_PROT, 0x00000A00,0x000005f7,ACC_TYPE_LDT|ACC_PRESENT defGDTDescImplementation LDT_SEG_PROT286, 0x00000FF8,0x00000000,ACC_TYPE_LDT|ACC_PRESENT defGDTDescImplementation DU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE + defGDTDescImplementation TSS_DSEG_PROT, 0x00005000,0x0000006F,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDescImplementation TSS_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDescImplementation TSS_PROT, 0x00005000,0x00000067,ACC_TYPE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSS_PROT16, 0x00005200,0x0000002C,ACC_TYPE_TSS16|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE - defGDTDescImplementation SU_SEG_PROT32DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT32ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT32FS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT32GS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT16SS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT - defGDTDescImplementation SU_SEG_PROT16DS, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT16ES, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32DS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32ES, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32FS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32GS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT16SS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT + defGDTDescImplementation SU_SEG_PROT16DS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT16ES, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation CU_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation CU_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation C_SEG_PROT32_R2, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_2,EXT_32BIT + defGDTDescImplementation S_SEG_PROT32_R2, 0x00010000,0x0000ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT + defGDTDescImplementation C_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE @@ -560,11 +600,9 @@ initGDT: defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc S_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT - defGDTDesc S_SEG_PROT32_R2, 0x00010000,0x0008ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT + defGDTDesc S_SEG_PROT32, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE - defGDTDesc SU_SEG_PROT32, 0x00010000,0x0008ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDesc TSS_DSEG_PROT, 0x00005000,0x0000006F,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc SU_SEG_PROT32, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 @@ -626,7 +664,7 @@ ptrTSSprot16Gate: ; pointer to the 16-bit task state segment gate dd 0 dw TSS_GSEG_PROT16|3 addrProtIDT: ; address of pmode IDT to be used with lidt - dw 0x14F ; 16-bit limit + dw 0x167 ; 16-bit limit dd IDT_SEG_REAL << 4 ; 32-bit base address addrGDT: ; address of GDT to be used with lgdt dw GDT_SEG_LIMIT @@ -726,10 +764,44 @@ initIDT: ; Interrupt 29h: task switch to 386, callable from user mode. mov esi, TSS_PROT mov edi, 0 + or edi,0xFFFF0000 ;Make sure that the offset is located on a invalid 32-bit mapped address by mapping the high 16 bits, which are not to be used. mov dx, ACC_DPL_3 inc eax call initIntTaskGateReal + ; Interrupt 2Ah: validate ring 2 from 386 TSS. + mov esi, C_SEG_PROT32_R2 + %if ROM128 + mov edi, kernelInterrupt_R2_386 + %else + mov edi, kernelInterrupt ;Unused, placeholder + %endif + mov dx, ACC_DPL_3 + inc eax + call initIntGateReal + + ; Interrupt 2Bh: validate ring 2 from 286 TSS. + mov esi, C_SEG_PROT32_R2 + %if ROM128 + mov edi, kernelInterrupt_R2_286 + %else + mov edi, kernelInterrupt ;Unused, placeholder + %endif + mov dx, ACC_DPL_3 + inc eax + call initIntGateReal + + ; Interrupt 2Ch: validate and clear TS bit. + mov esi, C_SEG_PROT16CS + %if ROM128 + mov edi, kernelInterrupt_validateAndClearTS + %else + mov edi, kernelInterrupt ;Unused, placeholder + %endif + mov dx, ACC_DPL_3 + inc eax + call initIntGateReal + jmp initPaging @@ -1198,7 +1270,6 @@ userV86ExitFuncRet: %if ROM128 ;Switch to segment E0000 in flat mode for more advanced tests. - pushfd push cs call nexthighbios nexthighbios: diff --git a/src/tss_p.asm b/src/tss_p.asm index 1af562b..db62da0 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -91,7 +91,7 @@ initTSS32: ;Ring 0 SS:ESP is loaded by the ring switching function mov eax,0xFFFFFFFF mov [es:ebp+0x20],eax ;Initial EIP, should be overwritten by the task switch. - mov word [es:ebp+0x18], S_SEG_PROT32_R2 ;R2 Stack + mov word [es:ebp+0x18], S_SEG_PROT32_R2|2 ;R2 Stack mov dword [es:ebp+0x14], ESP_R2_PROT pop ds pop ebp @@ -166,7 +166,7 @@ initTSS16: mov word [es:ebp+2], ss mov [es:ebp+4], word ESP_R0_PROT mov word [es:ebp+0xA], ESP_R2_PROT ;R2 Stack - mov word [es:ebp+0xC], S_SEG_PROT32_R2 + mov word [es:ebp+0xC], S_SEG_PROT32_R2|2 ; User mode pointers mov ax, CU_SEG_PROT16CS|3 ;Code segment mov [es:ebp+0x24], ax From bd683a681c82e6ed49ecd6e8954ee47d4ab3602b Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 14 Apr 2026 14:52:53 +0200 Subject: [PATCH 46/62] Implemented TSS task switch to Virtual 8086 mode tests. --- src/protected_rings_p.asm | 16 +++++----- src/protected_tssh.asm | 35 ++++++++++++++++++++- src/protected_v86_m.asm | 5 +++ src/test386.asm | 65 ++++++++++++++++++++++++++++++++------- src/x86_e.asm | 5 +++ 5 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 src/protected_v86_m.asm diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index 181aafc..ac3c13a 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -167,10 +167,10 @@ switchToRing3V86_0: mov eax, ss mov [ebx+8], eax cli ; disable ints during switching - push dword 0xF000 ; V86 mode GS default to ROM - push dword 0xF000 ; V86 mode FS default to ROM - push dword 0xF000 ; V86 mode DS default to ROM - push dword 0xF000 ; V86 mode ES default to ROM + push dword V86_GS ; V86 mode GS default to ROM + push dword V86_FS ; V86 mode FS default to ROM + push dword V86_DS ; V86 mode DS default to ROM + push dword V86_ES ; V86 mode ES default to ROM push dword 0x1000 ; push user stack with RPL=3 push dword ESP_R3_PROT ; push user mode esp pushfd ; push eflags @@ -216,10 +216,10 @@ switchToRing3V86_3: mov eax, ss mov [ebx+8], eax cli ; disable ints during switching - push dword 0xF000 ; V86 mode GS default to ROM - push dword 0xF000 ; V86 mode FS default to ROM - push dword 0xF000 ; V86 mode DS default to ROM - push dword 0xF000 ; V86 mode ES default to ROM + push dword V86_GS ; V86 mode GS default to ROM + push dword V86_FS ; V86 mode FS default to ROM + push dword V86_DS ; V86 mode DS default to ROM + push dword V86_ES ; V86 mode ES default to ROM push dword 0x1000 ; push user stack with RPL=3 push dword ESP_R3_PROT ; push user mode esp pushfd ; push eflags diff --git a/src/protected_tssh.asm b/src/protected_tssh.asm index 053faab..589cb35 100644 --- a/src/protected_tssh.asm +++ b/src/protected_tssh.asm @@ -193,4 +193,37 @@ TSS1_returned: validateTSSNT286 0,0,0 ;Cleared currently in register only. ;Finally, return to the 386 task to finish up these TSS tests and continue running other tests. - jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. \ No newline at end of file + jmp far [cs:ptrTSSprot32Gate] ;Return to the 32-bit task. + + ;We've returned here to start the 386 TSS V86 mode tests. + and esp,0xFFFF ;Safe ESP usage! + lds ebx,[cs:ptrTSSprot_R2] ;Get the 32-bit TSS + ;Now, convert the 32-bit task to V86 mode. + mov word [ds:ebx+0x26],0x2 ;Set the VM flag + or word [ds:ebx+0x24],0x3000 ;Set the IOPL to 3 to allow interrupts for validation + mov word [ds:ebx+0x48],V86_ES ;ES + mov word [ds:ebx+0x50],S_SEG_REAL ;SS + mov word [ds:ebx+0x54],V86_DS ;DS + mov word [ds:ebx+0x58],V86_FS ;FS + mov word [ds:ebx+0x5C],V86_GS ;GS + mov word [ds:ebx+0x22],0 ;Truncate EIP to 16 bits + mov word [ds:ebx+0x4C],0xE000 ;Code segment + mov word [ds:ebx+0x3A],0 ;Truncate ESP to 16 bits + + jmp far [cs:ptrTSSprot32Gate] ;Start V86 mode! + + ;We've returned from V86 mode task. Convert it back to a normal flat user mode task. + and esp,0xFFFF ;Safe ESP usage! + lds ebx,[cs:ptrTSSprot_R2] ;Get the 32-bit TSS + mov word [ds:ebx+0x26],0x0 ;Clear the VM flag + and word [ds:ebx+0x24],0xCFFF ;Set the IOPL to 0 again to restore flat user mode + mov word [ds:ebx+0x48],SU_SEG_PROT32ES|3 ;ES + mov word [ds:ebx+0x50],DU_SEG_PROT32FLAT|3 ;SS + mov word [ds:ebx+0x54],SU_SEG_PROT32DS|3 ;DS + mov word [ds:ebx+0x58],SU_SEG_PROT32FS|3 ;FS + mov word [ds:ebx+0x5C],SU_SEG_PROT32GS|3 ;GS + mov word [ds:ebx+0x22],0xE ;Expand EIP to 32 bits flat + mov word [ds:ebx+0x4C],CU_SEG_PROT32FLAT|3 ;Code segment + mov word [ds:ebx+0x3A],0x1 ;Expand ESP to 32 bits flat + iret ;End V86 mode (we were called through an interrupt), returning to flat user mode! + ;Now, the tests have been completed. diff --git a/src/protected_v86_m.asm b/src/protected_v86_m.asm new file mode 100644 index 0000000..372fb29 --- /dev/null +++ b/src/protected_v86_m.asm @@ -0,0 +1,5 @@ +;Keep RPL at 0 to detect incorrect protected mode loading. Simple 64K alignment for ease of use. +V86_DS equ 0x2010 +V86_ES equ 0x3040 +VS6_FS equ 0x5060 +V86_GS equ 0x7080 diff --git a/src/test386.asm b/src/test386.asm index 8d38ad3..baa4178 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -78,6 +78,7 @@ ; 05000-05FFF TSS ; 10000-1FFFF stack ; 20000-9FFFF tests +; 30000-3FFFF 286 task stack ; TEST_BASE equ 0x20000 @@ -145,8 +146,6 @@ BITS 32 ; %include "protected_tsshelpers.asm" - - test386TSSstart: ; Verify we start clean setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD @@ -370,6 +369,50 @@ TSStest1finished: ;Now, we have switched sides to finish the tests. ;We're the parent task again. + ;Start the V86 mode tests now. This task will be converted by the 16-bit task. + setNTflag386 0,1,0 ;Clear the NT flag to test. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task to convert us. + BITS 16 + jmp noerror + errorV86: + cli ;Simply attempt to HLT + hlt + noerror: + ;Virtual 8086 mode now, if task switching did it's job. + validateTSandClear 1 ;Validate TS bit is set properly. + push cs + pop ax + cmp ax,0xE000 ;Valid CS? + jne errorV86 + push ss + pop ax + cmp ax,S_SEG_REAL ;Valid SS? + jne errorV86 + push ds + pop ax + cmp ax,V86_DS ;Valid DS? + jne errorV86 + push es + pop ax + cmp ax,V86_ES ;Valid ES? + jne errorV86 + push fs + pop ax + cmp ax,V86_FS ;Valid FS? + jne errorV86 + push gs + pop ax + cmp ax,V86_GS ;Valid GS? + jne errorV86 + pushfd + pop eax + test eax,0x20000 ;VM bit is required to be cleared with IOPL=3? + jnz errorV86 + + int 0x28 ;Switch back to the 16-bit task to terminate V86 mode. + + ;Finish up, we're back in flat user mode again. Return to the system BIOS. + BITS 32 validateTSandClear 1 ;TS is expected to be set by the task switch. setNTflag386 0,1,0 ;Clear NT flag to finish. @@ -576,13 +619,13 @@ initGDT: defGDTDescImplementation TSS_GSEG_PROT32, TSS_PROT,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSS_GSEG_PROT16, TSS_PROT16,0x00000000,ACC_TYPE_GATE_TSS|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation CU_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT|EXT_PAGE - defGDTDescImplementation SU_SEG_PROT32DS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT32ES, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT32FS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT32GS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT16SS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT - defGDTDescImplementation SU_SEG_PROT16DS, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT - defGDTDescImplementation SU_SEG_PROT16ES, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32DS, 0x00020000,0x0006ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32ES, 0x00020000,0x0006ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32FS, 0x00020000,0x0006ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32GS, 0x00020000,0x0006ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT16SS, 0x00030000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_16BIT + defGDTDescImplementation SU_SEG_PROT16DS, 0x00020000,0x0006ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT16ES, 0x00020000,0x0006ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation CU_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDescImplementation TSSU_DSEG_PROT32, 0x00005000,0x00000067,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDescImplementation TSSU_DSEG_PROT16, 0x00005200,0x0000002C,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 @@ -600,9 +643,9 @@ initGDT: defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc S_SEG_PROT32, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT + defGDTDesc S_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE - defGDTDesc SU_SEG_PROT32, 0x00020000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDesc SU_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 diff --git a/src/x86_e.asm b/src/x86_e.asm index ac70707..7f693ac 100644 --- a/src/x86_e.asm +++ b/src/x86_e.asm @@ -81,3 +81,8 @@ EX_GP equ 13 EX_PF equ 14 EX_MF equ 15 +;Keep RPL at 0 to detect incorrect protected mode loading. Simple 64K alignment for ease of use. +V86_DS equ 0x2010 +V86_ES equ 0x3040 +V86_FS equ 0x5060 +V86_GS equ 0x7080 \ No newline at end of file From df30ab70851905d73cef2c1baff7c482ecb22618 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 14 Apr 2026 16:14:38 +0200 Subject: [PATCH 47/62] Validate that Virtual 8086 mode sets the VM flag from PL0. --- src/protected_inth.asm | 9 +++++++++ src/protected_tssinth.asm | 3 +-- src/test386.asm | 14 +++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/protected_inth.asm b/src/protected_inth.asm index ffbf4cf..b15f8cd 100644 --- a/src/protected_inth.asm +++ b/src/protected_inth.asm @@ -230,3 +230,12 @@ V86ModeExitInterrupt: add esp, 0x24 ; Clean up stack from the V86 mode. push eax ; Push the return point ret + +; +; Kernel mode interrupt handler, callable from user mode. Validates if the Virtual 8086 mode is used. +; +kernelInterrupt_validateIsV86mode: + testCPL_E 0,error ; Elevates to CPL 0 + test dword [esp+8],0x20000 ;V86 bit set? + jz error ;V86 bit not properly set? + iret diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm index a9abe5d..443cdb3 100644 --- a/src/protected_tssinth.asm +++ b/src/protected_tssinth.asm @@ -115,5 +115,4 @@ kernelInterrupt_validateAndClearTS: jnz errorCPLn ;TS bit not properly cleared? pop ebx pop eax - iret ; Simply return to user mode - + iret ; Simply return to user mode \ No newline at end of file diff --git a/src/test386.asm b/src/test386.asm index baa4178..0722438 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -380,6 +380,7 @@ TSStest1finished: noerror: ;Virtual 8086 mode now, if task switching did it's job. validateTSandClear 1 ;Validate TS bit is set properly. + int 0x2D ;Are we actually in Virtual 8086 mode (from PL0, as we can't read VM directly)? push cs pop ax cmp ax,0xE000 ;Valid CS? @@ -637,8 +638,8 @@ initGDT: defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT - defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000014F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT @@ -707,7 +708,7 @@ ptrTSSprot16Gate: ; pointer to the 16-bit task state segment gate dd 0 dw TSS_GSEG_PROT16|3 addrProtIDT: ; address of pmode IDT to be used with lidt - dw 0x167 ; 16-bit limit + dw 0x16F ; 16-bit limit dd IDT_SEG_REAL << 4 ; 32-bit base address addrGDT: ; address of GDT to be used with lgdt dw GDT_SEG_LIMIT @@ -845,6 +846,12 @@ initIDT: inc eax call initIntGateReal + ; Interrupt 2D: validate V86 mode + mov esi, C_SEG_PROT32 + mov edi, kernelInterrupt_validateIsV86mode + mov dx, ACC_DPL_3 + inc eax + call initIntGateReal jmp initPaging @@ -1278,6 +1285,7 @@ V86IOSucceedFinish: ; Validate simply exiting Virtual 8086 mode, using the interrupt call switchToRing3V86_3 bits 16 + int 0x2D ;Validate we're actually in V86 mode. jmp userV86IretRealModeFunc jmp error bits 32 From ef404693c4489e1e304bd52ff9f63e94a3652d78 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 14 Apr 2026 18:22:29 +0200 Subject: [PATCH 48/62] Verify 16-bit protected mode interrupts from Virtual 8086 mode with the system BIOS extensions area storing the validation function, being required for said validation to be performed. --- src/protected_tssinth.asm | 70 ++++++++++++++++++++++++++++++++++++++- src/test386.asm | 25 +++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm index 443cdb3..2103fa2 100644 --- a/src/protected_tssinth.asm +++ b/src/protected_tssinth.asm @@ -115,4 +115,72 @@ kernelInterrupt_validateAndClearTS: jnz errorCPLn ;TS bit not properly cleared? pop ebx pop eax - iret ; Simply return to user mode \ No newline at end of file + iret ; Simply return to user mode + +; +; Kernel mode interrupt handler, callable from user mode. Validates if the Virtual 8086 mode is used. +; +kernelInterrupt_validateV86mode16bit: + ;Validate the PL0 stack is loaded correctly. + push ax + testCPL_E 0,errorCPLn ; Elevates to CPL 0 + pop ax + push ebx + push ds + push ecx + lds ebx, [cs:ptrTSSprot_R2] ; Get the TSS + mov ecx, dword [bx+4] ; Get ESP for the kernel mode program (stored into eax) + sub ecx, 0xC+0x12 ; Where we should end up on the kernel stack, taking into account what we just pushed + cmp esp, ecx ; Did the stack decrease correctly? + jne error + mov cx, ss + mov bx, word [bx+8] ; Expected: kernel mode stack + cmp cx, bx ; Did the stack pointer load correctly? + jne error + pop ecx + mov bx, cs + cmp bx, C_SEG_PROT16CS ; Did we arrive at proper kernel code? + jne error + pop ds + pop ebx + ;Basic stack pointer verified. Now check if the data on the stack is correct, while building a new stack to return to user mode. + ;Now, we need to convert the 16-bit PL0 stack into a 32-bit one for IRET to succeed. + push eax ;Save our original AX register + and eax,0xFFFF ;Mask off upper 16 bits. + mov ax, word [esp+(0x04+0x10)] ;GS + cmp ax,V86_GS ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination GS + mov ax, word [esp+(0x08+0x0E)] ;FS + cmp ax,V86_FS ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination FS + mov ax, word [esp+(0x0C+0x0C)] ;DS + cmp ax,V86_DS ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination DS + mov ax, word [esp+(0x10+0x0A)] ;ES + cmp ax,V86_ES ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination ES + mov ax, word [esp+(0x14+0x08)] ;SS + cmp ax,0x1000 ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination SS + mov ax, word [esp+(0x18+0x06)] ;SP + cmp ax,ESP_R3_PROT ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination ESP + mov ax, word [esp+(0x1C+0x04)] ;FLAGS + push eax ;Save destination EFLAGS + mov ax, word [esp+(0x20+0x02)] ;CS + cmp ax,0xF000 ;Correct? + jne error ;Error if incorrect. + push eax ;Save destination CS + mov ax, word [esp+(0x24+0x00)] ;IP + cmp ax,userV86_16bitinterruptRET ;IP correct? + jne error ;Incorrect IP address. + push eax ;Save destination EIP + or dword [esp+0x08],0x20000 ;Set VM bit to properly return. + mov eax,[esp+0x2C] ;Load original EAX register to restore it. + iret diff --git a/src/test386.asm b/src/test386.asm index 0722438..a0151c5 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -708,7 +708,7 @@ ptrTSSprot16Gate: ; pointer to the 16-bit task state segment gate dd 0 dw TSS_GSEG_PROT16|3 addrProtIDT: ; address of pmode IDT to be used with lidt - dw 0x16F ; 16-bit limit + dw 0x177 ; 16-bit limit dd IDT_SEG_REAL << 4 ; 32-bit base address addrGDT: ; address of GDT to be used with lgdt dw GDT_SEG_LIMIT @@ -853,6 +853,18 @@ initIDT: inc eax call initIntGateReal + ; Interrupt 2E: validate V86 mode + mov esi, C_SEG_PROT16CS + %if ROM128 + mov edi, kernelInterrupt_validateV86mode16bit + %else + mov edi, kernelInterrupt ;Unused, placeholder + %endif + or edi,0xFFFF0000 ;Make sure that the offset is located on a invalid 32-bit mapped address by mapping the high 16 bits, which are not to be used. + mov dx, ACC_DPL_3 + inc eax + call initIntGateReal286 + jmp initPaging initPaging: @@ -1304,6 +1316,17 @@ errorInTSS32Load: jmp error ;Error out! userV86ExitFuncRet: + ;Now we're going to test 16-bit interrupts in Virtual 8086 mode. + %if ROM128 + call switchToRing3V86_3 + BITS 16 + int 0x2E ;Verify 16-bit interrupts in Virtual 8086 mode + userV86_16bitinterruptRET: + push dword userV86ExitFuncRet_16bitinterrupts ; Where to continue + jmp switchToRing0V86 +userV86ExitFuncRet_16bitinterrupts: + %endif + BITS 32 pushfd pop eax and eax,0xCDFF ;Clear IOPL and interrupt flag again, as it cannot be changed in user mode From 4bfe237ac37594013ef1afc29cb55a76d2bf71c8 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 14 Apr 2026 21:46:21 +0200 Subject: [PATCH 49/62] Cleaned up the unused V86 definition file. --- src/protected_v86_m.asm | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/protected_v86_m.asm diff --git a/src/protected_v86_m.asm b/src/protected_v86_m.asm deleted file mode 100644 index 372fb29..0000000 --- a/src/protected_v86_m.asm +++ /dev/null @@ -1,5 +0,0 @@ -;Keep RPL at 0 to detect incorrect protected mode loading. Simple 64K alignment for ease of use. -V86_DS equ 0x2010 -V86_ES equ 0x3040 -VS6_FS equ 0x5060 -V86_GS equ 0x7080 From 68569fbce93f48180cb8d341e8880758f44f1729 Mon Sep 17 00:00:00 2001 From: superfury Date: Wed, 15 Apr 2026 19:38:13 +0200 Subject: [PATCH 50/62] Split the POST A into POST 20, 21, 22. --- src/test386.asm | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/test386.asm b/src/test386.asm index a0151c5..a70e9aa 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -1078,10 +1078,10 @@ protTests: ;------------------------------------------------------------------------------- - POST A + POST 20 ;------------------------------------------------------------------------------- ; -; Test user mode (ring 3) switching and Virtual-8086 mode +; Test user mode (ring 3) switching ; call clearTSS mov ax, D_SEG_PROT32 @@ -1239,9 +1239,12 @@ kernelModeInterruptKernelStackReturn: kernelModeInterruptKernelStackReturnPoint: ; Back in normal 32-bit protected mode with 16-bit segment limits again - ; - ; Validate Virtual-8086 mode. - ; +;------------------------------------------------------------------------------- + POST 21 +;------------------------------------------------------------------------------- +; +; Test Virtual-8086 mode +; ; Check invalid virtual 8086 mode interrupts ; interrupt without IOPL 3 faults with #GP(0) @@ -1332,7 +1335,14 @@ userV86ExitFuncRet_16bitinterrupts: and eax,0xCDFF ;Clear IOPL and interrupt flag again, as it cannot be changed in user mode push eax popfd - ; Validate the TSS task switching functionality + +;------------------------------------------------------------------------------- + POST 22 +;------------------------------------------------------------------------------- +; +; Test the TSS task switching functionality +; + ;First, enter a valid 32-bit protected flat mode for testing ;Prepare 32-bit TSS ROM fields call initTSS32 From a42b6bf06688409608c2f916c7e96755a7177d99 Mon Sep 17 00:00:00 2001 From: superfury Date: Thu, 16 Apr 2026 12:54:16 +0200 Subject: [PATCH 51/62] Moved the BIOS expansion area into a seperate file. --- src/systembiosexpansionarea.asm | 328 +++++++++++++++++++++++++++++++ src/test386.asm | 329 +------------------------------- 2 files changed, 329 insertions(+), 328 deletions(-) create mode 100644 src/systembiosexpansionarea.asm diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm new file mode 100644 index 0000000..6d0a05f --- /dev/null +++ b/src/systembiosexpansionarea.asm @@ -0,0 +1,328 @@ + ; Protected mode prototype support to support GDT entry references in the remainder of the code +%include "protected_m.asm" + ; Always start with the NULL segment selector. + defGDTDescPrototype NULL + defGDTDescPrototype LDT_SEG_PROT + defGDTDescPrototype LDT_SEG_PROT286 + defGDTDescPrototype DU_SEG_PROT32FLAT + defGDTDescPrototype TSS_DSEG_PROT + defGDTDescPrototype TSS_DSEG_PROT16 + defGDTDescPrototype TSS_PROT + defGDTDescPrototype TSS_PROT16 + defGDTDescPrototype TSS_GSEG_PROT32 + defGDTDescPrototype TSS_GSEG_PROT16 + defGDTDescPrototype CU_SEG_PROT32FLAT + defGDTDescPrototype SU_SEG_PROT32DS + defGDTDescPrototype SU_SEG_PROT32ES + defGDTDescPrototype SU_SEG_PROT32FS + defGDTDescPrototype SU_SEG_PROT32GS + defGDTDescPrototype SU_SEG_PROT16SS + defGDTDescPrototype SU_SEG_PROT16DS + defGDTDescPrototype SU_SEG_PROT16ES + defGDTDescPrototype CU_SEG_PROT16CS + defGDTDescPrototype TSSU_DSEG_PROT32 + defGDTDescPrototype TSSU_DSEG_PROT16 + defGDTDescPrototype CU_SEG_PROT32 + defGDTDescPrototype C_SEG_PROT32_R2 + defGDTDescPrototype S_SEG_PROT32_R2 + defGDTDescPrototype C_SEG_PROT16CS + +section .system_bios_extensions_area start=0x00000 +;Start of high BIOS + ; TSS helper macros +%include "protected_tss_m.asm" + ; + ; TSS interrupt handlers + ; +%include "protected_tssinth.asm" + ; + ; 286 TSS handler + ; +%include "protected_tssh.asm" +BITS 32 + ; + ; 386 TSS user mode code + ; +%include "protected_tsshelpers.asm" + +test386TSSstart: + ; Verify we start clean + setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + setNTflag386 0,1,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,0 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + + int 0x2A ;Validate 386 interrupts to Ring 2 + kernelModeInterruptR2Return: + validateTSandClear 0 ;Expect no TS bit set yet. + ;Transfer the ring 0 stack from the 32-bit TSS to the 16-bit TSS to be able to call it and validate the TS flag. + push ds + push ebx + push ecx + lds ebx,[cs:ptrTSSprot_R2+0xE0000] ;Get the 32-bit TSS + mov cx,[ebx+4] ;Get ring 0 ESP + shl ecx,16 ;Move SP high + mov cx,[ebx+8] ;Get ring 0 SS + lds ebx,[cs:ptrTSSprot16_R2+0xE0000] ;Get the 16-bit TSS + mov [ebx+4],cx ;Set ring 0 SS + shr ecx,16 ;Move SP low + mov [ebx+2],cx ;Set ring 0 SP + pop ecx + pop ebx + pop ds + ;Loading patterns for the 386 data segments + mov ax,SU_SEG_PROT32DS|3 ;DS + mov ds,ax + mov ax,SU_SEG_PROT32ES|3 ;ES + mov es,ax + mov ax,SU_SEG_PROT32FS|3 ;FS + mov fs,ax + mov ax,SU_SEG_PROT32GS|3 ;GS + mov gs,ax + + ;Loading patterns for the 386 data segments. + mov eax,0x12347654 + mov ecx,0x5678CBA9 + mov edx,0x9ABC3333 + mov ebx,0xDEF02222 + mov esp,0x11221111 + mov ebp,0x33447777 + mov esi,0x55665555 + mov edi,0x7788AAAA + clc ;Clear carry flag for the 286 test and us + ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. + int 0x28 + ;We've returned from the test task. Verify if our registers are loaded correctly. + cmp esp,0x11221111 ;Validate the stack pointer first + jnz errorInTSS32Load + mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer, so we regain stack functionality. + cmp eax,0x12347654 + jnz error + cmp ecx,0x5678CBA9 + jnz error + cmp edx,0x9ABC3333 + jnz error + cmp ebx,0xDEF02222 + jnz error + cmp ebp,0x33447777 + jnz error + cmp esi,0x55665555 + jnz error + cmp edi,0x7788AAAA + jnz error + ;Now, validate the segment registers + mov ax,ss + cmp ax,DU_SEG_PROT32FLAT|3 ;SS OK? + jnz errorTSS32_1 + mov ax,cs + cmp ax,CU_SEG_PROT32FLAT|3 ;CS OK? + jnz errorTSS32_1 + mov ax,ds + cmp ax,SU_SEG_PROT32DS|3 ;DS OK? + jnz errorTSS32_1 + mov ax,es + cmp ax,SU_SEG_PROT32ES|3 ;ES OK? + jnz errorTSS32_1 + mov ax,fs + cmp ax,SU_SEG_PROT32FS|3 ;FS OK? + jnz errorTSS32_1 + mov ax,gs + cmp ax,SU_SEG_PROT32GS|3 ;GS OK? + jnz errorTSS32_1 + sldt ax + cmp ax,LDT_SEG_PROT ;LDT OK? + jnz errorTSS16_1 + validateTSandClear 1 ;TS is expected to be set by the task switch. + jmp TSStest1finished +errorTSS32_1: + mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value + mov ss,ax + mov esp,ESP_R3_PROTFLAT ;ESP safe value + jmp error ;Error out + +TSStest1finished: + ;Now, we switch sides, to basic test JMP-based task switches + jmp far [cs:ptrTSSprot16Gate+0xF0000] + + ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) + ;80386 and 80486 programmer's reference manuals mention that the NT flag after a JMP instruction to another task is cleared. It is in fact, unaffected on 80386/80486 processors, due to a misprint of the manuals. + + ;Start of the 32-bit to 16-bit TSS tests. + ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. + ;First, setup initial task state. + validateTSandClear 1 ;TS is expected to be set by the task switch. + setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + setNTflag386 0,1,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,0 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 0,1,0 + call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). + ;Now, setup the second test for call, this time with NT set. + ;First, validate IRET did it's job correctly. + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT + validateTSSNT386 TSSU_DSEG_PROT32,1,0 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + ;Reset state for detection. + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + ;Now, with NT bit set. + setNTflag386 0,1,1 + call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). + ;Now, validate IRET did it's job correctly. + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT + validateTSSNT386 TSSU_DSEG_PROT32,1,1 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + ;Reset state for detection. + setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. + ;Now, test JMP instruction task switches. + setNTflag386 0,1,1 ;This is going to be kept in the 32-bit TSS. + setNTflag386 TSSU_DSEG_PROT32,1,0 ;This is going to be set to 1. + setNTflag386 TSSU_DSEG_PROT16,0,0 ;This is going to be loaded. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,1 + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 0,1,1 ;The JMP instruction to our TSS cleared us. + setNTflag386 0,1,0 ;This is going to be kept in the 32-bit TSS. + setNTflag386 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. + setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be loaded. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,0 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Set to 0 in destination task. + validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Left at 1. + validateTSSNT386 0,1,0 ;Cleared currently in register only. + + ;End of the 32-bit to 16-bit TSS tests. Now, switch sides to verify CALL from 16-bit to 32-bit. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Switch sides + + ;Start of the 32-bit side of the 16-bit to 32-bit TSS tests. + ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,1 + validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Not written yet, so left at 0. + validateTSSNT386 TSSU_DSEG_PROT16,0,0 + validateTSSNT386 0,0,1 ;Set currently in register only. + iretd ;Return to caller. + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT,1 + validateTSSbusy386 TSS_PROT16,1 + validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Left at 0. + validateTSSNT386 0,0,1 ;Set currently in register only. + iretd ;Return to caller. + ;Now testing JMP from 32-bit task to 32-bit task, NT going from set to cleared in the source task. 32-bit task keeps it cleared. + ;TSS test 3: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + ;TSS test 3.1: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. + validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Loaded as 0. + validateTSSNT386 0,1,0 ;Set in destination task from the TSS. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. + ;TSS test 3.2 Same as before, but NT in 286 is cleared. + validateTSandClear 1 ;TS is expected to be set by the task switch. + validateTSSbusy386 TSS_PROT16,0 + validateTSSbusy386 TSS_PROT,1 + validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD + validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD + validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Set to 0 in source task. + validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Loaded as 1. + validateTSSNT386 0,1,1 ;Set in destination task from the TSS. + setNTflag386 0,1,1 ;Set NT flag to test. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. + + ;Now, we have switched sides to finish the tests. + ;We're the parent task again. + + ;Start the V86 mode tests now. This task will be converted by the 16-bit task. + setNTflag386 0,1,0 ;Clear the NT flag to test. + jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task to convert us. + BITS 16 + jmp noerror + errorV86: + cli ;Simply attempt to HLT + hlt + noerror: + ;Virtual 8086 mode now, if task switching did it's job. + validateTSandClear 1 ;Validate TS bit is set properly. + int 0x2D ;Are we actually in Virtual 8086 mode (from PL0, as we can't read VM directly)? + push cs + pop ax + cmp ax,0xE000 ;Valid CS? + jne errorV86 + push ss + pop ax + cmp ax,S_SEG_REAL ;Valid SS? + jne errorV86 + push ds + pop ax + cmp ax,V86_DS ;Valid DS? + jne errorV86 + push es + pop ax + cmp ax,V86_ES ;Valid ES? + jne errorV86 + push fs + pop ax + cmp ax,V86_FS ;Valid FS? + jne errorV86 + push gs + pop ax + cmp ax,V86_GS ;Valid GS? + jne errorV86 + pushfd + pop eax + test eax,0x20000 ;VM bit is required to be cleared with IOPL=3? + jnz errorV86 + + int 0x28 ;Switch back to the 16-bit task to terminate V86 mode. + + ;Finish up, we're back in flat user mode again. Return to the system BIOS. + BITS 32 + validateTSandClear 1 ;TS is expected to be set by the task switch. + setNTflag386 0,1,0 ;Clear NT flag to finish. + + ;32-bit eflags register loaded OK. + push cs + call nextlowbios + nextlowbios: + mov dword [esp],test386TSSend+0xF0000 ;Return to the F0000 segment in flat protected mode + retfd ;Actually return + +;End of high BIOS + ;Pad to 64KB + times 0x10000-($-$$) nop diff --git a/src/test386.asm b/src/test386.asm index a70e9aa..a9ce7c5 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -99,334 +99,7 @@ GDT_SEG_LIMIT equ 0x31F ESP_REAL equ 0xffff %if ROM128 - ; Protected mode prototype support to support GDT entry references in the remainder of the code -%include "protected_m.asm" - ; Always start with the NULL segment selector. - defGDTDescPrototype NULL - defGDTDescPrototype LDT_SEG_PROT - defGDTDescPrototype LDT_SEG_PROT286 - defGDTDescPrototype DU_SEG_PROT32FLAT - defGDTDescPrototype TSS_DSEG_PROT - defGDTDescPrototype TSS_DSEG_PROT16 - defGDTDescPrototype TSS_PROT - defGDTDescPrototype TSS_PROT16 - defGDTDescPrototype TSS_GSEG_PROT32 - defGDTDescPrototype TSS_GSEG_PROT16 - defGDTDescPrototype CU_SEG_PROT32FLAT - defGDTDescPrototype SU_SEG_PROT32DS - defGDTDescPrototype SU_SEG_PROT32ES - defGDTDescPrototype SU_SEG_PROT32FS - defGDTDescPrototype SU_SEG_PROT32GS - defGDTDescPrototype SU_SEG_PROT16SS - defGDTDescPrototype SU_SEG_PROT16DS - defGDTDescPrototype SU_SEG_PROT16ES - defGDTDescPrototype CU_SEG_PROT16CS - defGDTDescPrototype TSSU_DSEG_PROT32 - defGDTDescPrototype TSSU_DSEG_PROT16 - defGDTDescPrototype CU_SEG_PROT32 - defGDTDescPrototype C_SEG_PROT32_R2 - defGDTDescPrototype S_SEG_PROT32_R2 - defGDTDescPrototype C_SEG_PROT16CS - -section .system_bios_extensions_area start=0x00000 -;Start of high BIOS - ; TSS helper macros -%include "protected_tss_m.asm" - ; - ; TSS interrupt handlers - ; -%include "protected_tssinth.asm" - ; - ; 286 TSS handler - ; -%include "protected_tssh.asm" -BITS 32 - ; - ; 386 TSS user mode code - ; -%include "protected_tsshelpers.asm" - -test386TSSstart: - ; Verify we start clean - setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - setNTflag386 0,1,0 - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,0 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,0 - validateTSSNT386 TSSU_DSEG_PROT16,0,0 - - int 0x2A ;Validate 386 interrupts to Ring 2 - kernelModeInterruptR2Return: - validateTSandClear 0 ;Expect no TS bit set yet. - ;Transfer the ring 0 stack from the 32-bit TSS to the 16-bit TSS to be able to call it and validate the TS flag. - push ds - push ebx - push ecx - lds ebx,[cs:ptrTSSprot_R2+0xE0000] ;Get the 32-bit TSS - mov cx,[ebx+4] ;Get ring 0 ESP - shl ecx,16 ;Move SP high - mov cx,[ebx+8] ;Get ring 0 SS - lds ebx,[cs:ptrTSSprot16_R2+0xE0000] ;Get the 16-bit TSS - mov [ebx+4],cx ;Set ring 0 SS - shr ecx,16 ;Move SP low - mov [ebx+2],cx ;Set ring 0 SP - pop ecx - pop ebx - pop ds - ;Loading patterns for the 386 data segments - mov ax,SU_SEG_PROT32DS|3 ;DS - mov ds,ax - mov ax,SU_SEG_PROT32ES|3 ;ES - mov es,ax - mov ax,SU_SEG_PROT32FS|3 ;FS - mov fs,ax - mov ax,SU_SEG_PROT32GS|3 ;GS - mov gs,ax - - ;Loading patterns for the 386 data segments. - mov eax,0x12347654 - mov ecx,0x5678CBA9 - mov edx,0x9ABC3333 - mov ebx,0xDEF02222 - mov esp,0x11221111 - mov ebp,0x33447777 - mov esi,0x55665555 - mov edi,0x7788AAAA - clc ;Clear carry flag for the 286 test and us - ;Now, all test patterns are loaded. Trigger a switch to the 16-bit task. - int 0x28 - ;We've returned from the test task. Verify if our registers are loaded correctly. - cmp esp,0x11221111 ;Validate the stack pointer first - jnz errorInTSS32Load - mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer, so we regain stack functionality. - cmp eax,0x12347654 - jnz error - cmp ecx,0x5678CBA9 - jnz error - cmp edx,0x9ABC3333 - jnz error - cmp ebx,0xDEF02222 - jnz error - cmp ebp,0x33447777 - jnz error - cmp esi,0x55665555 - jnz error - cmp edi,0x7788AAAA - jnz error - ;Now, validate the segment registers - mov ax,ss - cmp ax,DU_SEG_PROT32FLAT|3 ;SS OK? - jnz errorTSS32_1 - mov ax,cs - cmp ax,CU_SEG_PROT32FLAT|3 ;CS OK? - jnz errorTSS32_1 - mov ax,ds - cmp ax,SU_SEG_PROT32DS|3 ;DS OK? - jnz errorTSS32_1 - mov ax,es - cmp ax,SU_SEG_PROT32ES|3 ;ES OK? - jnz errorTSS32_1 - mov ax,fs - cmp ax,SU_SEG_PROT32FS|3 ;FS OK? - jnz errorTSS32_1 - mov ax,gs - cmp ax,SU_SEG_PROT32GS|3 ;GS OK? - jnz errorTSS32_1 - sldt ax - cmp ax,LDT_SEG_PROT ;LDT OK? - jnz errorTSS16_1 - validateTSandClear 1 ;TS is expected to be set by the task switch. - jmp TSStest1finished -errorTSS32_1: - mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value - mov ss,ax - mov esp,ESP_R3_PROTFLAT ;ESP safe value - jmp error ;Error out - -TSStest1finished: - ;Now, we switch sides, to basic test JMP-based task switches - jmp far [cs:ptrTSSprot16Gate+0xF0000] - - ;Since basic task switches are validated now, we can start testing the various bits related to the task switches (Busy bit of the TSS, NT bit of the FLAGS register, Back-link field in the TSS) - ;80386 and 80486 programmer's reference manuals mention that the NT flag after a JMP instruction to another task is cleared. It is in fact, unaffected on 80386/80486 processors, due to a misprint of the manuals. - - ;Start of the 32-bit to 16-bit TSS tests. - ;Step 1: validate 16-bit TSS outgoing/incoming B-bit, NT bit, Back-link during CALL. - ;First, setup initial task state. - validateTSandClear 1 ;TS is expected to be set by the task switch. - setTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - setNTflag386 0,1,0 - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,0 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,0 - validateTSSNT386 TSSU_DSEG_PROT16,0,0 - validateTSSNT386 0,1,0 - call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). - ;Now, setup the second test for call, this time with NT set. - ;First, validate IRET did it's job correctly. - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,0 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT - validateTSSNT386 TSSU_DSEG_PROT32,1,0 - validateTSSNT386 TSSU_DSEG_PROT16,0,0 - ;Reset state for detection. - setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - ;Now, with NT bit set. - setNTflag386 0,1,1 - call far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 2: CALL busy bit set in both tasks, NT set to set, back-link filled by the CALL. Outgoing NT kept as-is (currently 0). - ;Now, validate IRET did it's job correctly. - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,0 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,TSS_PROT - validateTSSNT386 TSSU_DSEG_PROT32,1,1 - validateTSSNT386 TSSU_DSEG_PROT16,0,0 - ;Reset state for detection. - setTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - ;Now, the 32-bit task to 16-bit task backlink is properly filled, NT properly updating for CALL and matching IRET. - ;Now, test JMP instruction task switches. - setNTflag386 0,1,1 ;This is going to be kept in the 32-bit TSS. - setNTflag386 TSSU_DSEG_PROT32,1,0 ;This is going to be set to 1. - setNTflag386 TSSU_DSEG_PROT16,0,0 ;This is going to be loaded. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.1: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,0 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,1 - validateTSSNT386 TSSU_DSEG_PROT16,0,0 - validateTSSNT386 0,1,1 ;The JMP instruction to our TSS cleared us. - setNTflag386 0,1,0 ;This is going to be kept in the 32-bit TSS. - setNTflag386 TSSU_DSEG_PROT32,1,1 ;This is going to be cleared. - setNTflag386 TSSU_DSEG_PROT16,0,1 ;This is going to be loaded. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;TSS test 3.2: JMP busy bit set in 16-bit task, busy bit cleared in 32-bit task, NT unaffected, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,0 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Set to 0 in destination task. - validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Left at 1. - validateTSSNT386 0,1,0 ;Cleared currently in register only. - - ;End of the 32-bit to 16-bit TSS tests. Now, switch sides to verify CALL from 16-bit to 32-bit. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Switch sides - - ;Start of the 32-bit side of the 16-bit to 32-bit TSS tests. - ;TSS test 1: CALL busy bit set in both tasks, NT cleared to set, back-link filled by the CALL. - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,1 - validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Not written yet, so left at 0. - validateTSSNT386 TSSU_DSEG_PROT16,0,0 - validateTSSNT386 0,0,1 ;Set currently in register only. - iretd ;Return to caller. - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT,1 - validateTSSbusy386 TSS_PROT16,1 - validateTSSbacklink386 TSSU_DSEG_PROT32,TSS_PROT16 - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. - validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Left at 0. - validateTSSNT386 0,0,1 ;Set currently in register only. - iretd ;Return to caller. - ;Now testing JMP from 32-bit task to 32-bit task, NT going from set to cleared in the source task. 32-bit task keeps it cleared. - ;TSS test 3: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). - ;TSS test 3.1: JMP busy bit set in 32-bit task, busy bit cleared in 16-bit task, NT in 386 task kept as-is, back-link not filled by the JMP. Outgoing NT kept as-is (currently 0). - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT16,0 - validateTSSbusy386 TSS_PROT,1 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT16,0,1 ;Set to 1 in source task. - validateTSSNT386 TSSU_DSEG_PROT32,1,0 ;Loaded as 0. - validateTSSNT386 0,1,0 ;Set in destination task from the TSS. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. - ;TSS test 3.2 Same as before, but NT in 286 is cleared. - validateTSandClear 1 ;TS is expected to be set by the task switch. - validateTSSbusy386 TSS_PROT16,0 - validateTSSbusy386 TSS_PROT,1 - validateTSSbacklink386 TSSU_DSEG_PROT32,0xDEAD - validateTSSbacklink386 TSSU_DSEG_PROT16,0xDEAD - validateTSSNT386 TSSU_DSEG_PROT16,0,0 ;Set to 0 in source task. - validateTSSNT386 TSSU_DSEG_PROT32,1,1 ;Loaded as 1. - validateTSSNT386 0,1,1 ;Set in destination task from the TSS. - setNTflag386 0,1,1 ;Set NT flag to test. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task. - - ;Now, we have switched sides to finish the tests. - ;We're the parent task again. - - ;Start the V86 mode tests now. This task will be converted by the 16-bit task. - setNTflag386 0,1,0 ;Clear the NT flag to test. - jmp far [cs:ptrTSSprot16Gate+0xF0000] ;Return to the 16-bit task to convert us. - BITS 16 - jmp noerror - errorV86: - cli ;Simply attempt to HLT - hlt - noerror: - ;Virtual 8086 mode now, if task switching did it's job. - validateTSandClear 1 ;Validate TS bit is set properly. - int 0x2D ;Are we actually in Virtual 8086 mode (from PL0, as we can't read VM directly)? - push cs - pop ax - cmp ax,0xE000 ;Valid CS? - jne errorV86 - push ss - pop ax - cmp ax,S_SEG_REAL ;Valid SS? - jne errorV86 - push ds - pop ax - cmp ax,V86_DS ;Valid DS? - jne errorV86 - push es - pop ax - cmp ax,V86_ES ;Valid ES? - jne errorV86 - push fs - pop ax - cmp ax,V86_FS ;Valid FS? - jne errorV86 - push gs - pop ax - cmp ax,V86_GS ;Valid GS? - jne errorV86 - pushfd - pop eax - test eax,0x20000 ;VM bit is required to be cleared with IOPL=3? - jnz errorV86 - - int 0x28 ;Switch back to the 16-bit task to terminate V86 mode. - - ;Finish up, we're back in flat user mode again. Return to the system BIOS. - BITS 32 - validateTSandClear 1 ;TS is expected to be set by the task switch. - setNTflag386 0,1,0 ;Clear NT flag to finish. - - ;32-bit eflags register loaded OK. - push cs - call nextlowbios - nextlowbios: - mov dword [esp],test386TSSend+0xF0000 ;Return to the F0000 segment in flat protected mode - retfd ;Actually return - -;End of high BIOS - ;Pad to 64KB - times 0x10000-($-$$) nop +%include "systembiosexpansionarea.asm" ;Restart counting for the upper 64KB block section .system_bios_area start=0x10000 vstart=0 ;Start of low BIOS From 46a25f0447a67002d4f344974d02dfe7203272fc Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 21 Apr 2026 16:56:02 +0200 Subject: [PATCH 52/62] Added tests for the IDTR and GDTR load and store instructions. --- README.md | 1 + src/systembiosexpansionarea.asm | 12 +++++ src/test386.asm | 15 ++++++ src/tests/dtr_m.asm | 86 +++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/tests/dtr_m.asm diff --git a/README.md b/README.md index c95c23d..5dbfb73 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ executed: | 0x04 | Store, move, scan, and compare string data in real mode | | 0x05 | Calls in real mode | | 0x06 | Load full pointer in real mode | +| 0x07 | Load and store GDTR and IDTR in real mode ** | | 0x08 | GDT, LDT, PDT, and PT setup, enter protected mode | | 0x09 | Stack functionality * | | 0x20 | Test user mode (ring 3) switching | diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index 6d0a05f..e129f51 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -322,6 +322,18 @@ TSStest1finished: nextlowbios: mov dword [esp],test386TSSend+0xF0000 ;Return to the F0000 segment in flat protected mode retfd ;Actually return + +%include "tests/dtr_m.asm" +;Real mode code for this test +BITS 16 +POST7entrypoint: + testDTR 0,lidt,sidt + testDTR 0,lgdt,sgdt + testDTR 1,lidt,sidt + testDTR 1,lgdt,sgdt + push word 0xF000 + push word POST7returnpoint + retf ;End of high BIOS ;Pad to 64KB diff --git a/src/test386.asm b/src/test386.asm index cfbb0ca..0403428 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -242,6 +242,21 @@ cpuTest: advTestSegReal +%if ROM128 +;------------------------------------------------------------------------------- + POST 7 +;------------------------------------------------------------------------------- +; +; Load/save GDTR/IDTR in real mode +; + ; Jump to the entry point of the test. + push word 0xE000 + push word POST7entrypoint + retf + POST7returnpoint: +%endif + + ; ============================================================================== ; Protected mode tests diff --git a/src/tests/dtr_m.asm b/src/tests/dtr_m.asm new file mode 100644 index 0000000..8ca7627 --- /dev/null +++ b/src/tests/dtr_m.asm @@ -0,0 +1,86 @@ +; +; Tests GDT/IDT loading and saving. +; Uses: EAX, EBX, ECX, EDX, Flags +; + +; testDTRpattern +; Parameters: +; %1: 0 for 16-bit load, 1 for 32-bit load +; %2: load instruction +; %3: save instruction +; %4: value to load (base) +; %5: value to load (limit) +; %6: error jump +%macro testDTRpattern 6 + ;Load input + mov dword [0],%5 + mov dword [2],%4 + ;Initialize expected output storage + mov dword [8],0xDEADDEAD + mov dword [0xC],0xDEADDEAD + mov ebx,dword [2] + %if %1==0 + ;16-bits load clears the top byte + and ebx,0xFFFFFF + %endif + mov ecx,[4] + %if %1==1 + ;Load it + o32 %2 [0] + %else + ;Load it + o16 %2 [0] + %endif + ;Save the result (full 32-bit) + o32 %3 [8] + ;Load base calculated + mov eax,dword [0xA] + ; Compare 32-bits + cmp eax,ebx + jne %6 + ;Load the limit that was loaded/stored. + mov ax,word [0x8] + ;Only 16 bits stored. + cmp ax,%5 + jne %6 + mov dword [8],0xDEADDEAD + mov dword [0xC],0xDEADDEAD + + ;Save the result (full 16-bit version). Undocumented: it stores the upper 8 bits too, just like with the 32-bits version. + o16 %3 [8] + ;Load base calculated + mov eax,dword [0xA] + ; Compare 32-bits + cmp eax,ebx + jne %6 + ;Load the limit that was loaded/stored. + mov ax,word [0x8] + ;Only 16 bits stored. + cmp ax,%5 + jne %6 +%endmacro + +; testDTR +; Parameters: +; %1: 0 for 16-bit load, 1 for 32-bit load +; %2: load instruction +; %3: save instruction + +%macro testDTR 3 + jmp %%skiperror +%%error: + push word 0xF000 + push word error + retf +%%skiperror: + testDTRpattern %1,%2,%3,0,0,%%error + testDTRpattern %1,%2,%3,0x12345678,0xAABB,%%error + testDTRpattern %1,%2,%3,0xCCDDEEFF,0x1122,%%error + testDTRpattern %1,%2,%3,0xFFFFFFFF,0xEEEE,%%error + ;Cleanup: Restore real mode tables. + mov [0],0 + mov [4],0x3FF + o16 lidt [0] + mov [4],0xFFFF + o16 lgdt [0] +%endmacro From d873fbec640f697ed92ee0d3fcf563706609a7da Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 21 Apr 2026 18:01:19 +0200 Subject: [PATCH 53/62] Added a test for the flag instructions in real mode. --- README.md | 17 +++++---- src/test386.asm | 27 ++++++++++---- src/tests/flags_m.asm | 86 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 src/tests/flags_m.asm diff --git a/README.md b/README.md index 5dbfb73..45e4bbf 100644 --- a/README.md +++ b/README.md @@ -125,14 +125,15 @@ executed: | ---- | ------------------------------------------------------------------ | | 0x00 | Real mode initialisation | | 0x01 | Conditional jumps and loops | -| 0x02 | Quick tests of unsigned 32-bit multiplication and division | -| 0x03 | Move segment registers in real mode | -| 0x04 | Store, move, scan, and compare string data in real mode | -| 0x05 | Calls in real mode | -| 0x06 | Load full pointer in real mode | -| 0x07 | Load and store GDTR and IDTR in real mode ** | -| 0x08 | GDT, LDT, PDT, and PT setup, enter protected mode | -| 0x09 | Stack functionality * | +| 0x02 | Flag instructions in real mode | +| 0x03 | Quick tests of unsigned 32-bit multiplication and division | +| 0x04 | Move segment registers in real mode | +| 0x05 | Store, move, scan, and compare string data in real mode | +| 0x06 | Calls in real mode | +| 0x07 | Load full pointer in real mode | +| 0x08 | Load and store GDTR and IDTR in real mode ** | +| 0x09 | GDT, LDT, PDT, and PT setup, enter protected mode | +| 0x0A | Stack functionality * | | 0x20 | Test user mode (ring 3) switching | | 0x21 | Test Virtual-8086 mode | | 0x22 | Test the TSS task switching functionality ** | diff --git a/src/test386.asm b/src/test386.asm index 0403428..d710c5b 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -145,6 +145,17 @@ cpuTest: testJcc 8 testJcc 16 +;------------------------------------------------------------------------------- + POST 2 +;------------------------------------------------------------------------------- +; +; Flag instructions +; +%include "tests/flags_m.asm" + testCarryFlagInstructions + testDirectionFlagInstructions + testInterruptFlagInstructions + ; ; Loops ; @@ -155,7 +166,7 @@ cpuTest: ;------------------------------------------------------------------------------- - POST 2 + POST 3 ;------------------------------------------------------------------------------- ; ; Quick tests of unsigned 32-bit multiplication and division @@ -174,7 +185,7 @@ cpuTest: %include "tests/mov_m.asm" ;------------------------------------------------------------------------------- - POST 3 + POST 4 ;------------------------------------------------------------------------------- ; ; Move segment registers in real mode @@ -191,7 +202,7 @@ cpuTest: %include "tests/string_m.asm" ;------------------------------------------------------------------------------- - POST 4 + POST 5 ;------------------------------------------------------------------------------- ; ; Test store, move, scan, and compare string data @@ -214,7 +225,7 @@ cpuTest: %include "tests/call_m.asm" ;------------------------------------------------------------------------------- - POST 5 + POST 6 ;------------------------------------------------------------------------------- ; ; Calls in real mode @@ -228,7 +239,7 @@ cpuTest: %include "tests/load_ptr_m.asm" ;------------------------------------------------------------------------------- - POST 6 + POST 7 ;------------------------------------------------------------------------------- ; ; Load full pointer in real mode @@ -244,7 +255,7 @@ cpuTest: %if ROM128 ;------------------------------------------------------------------------------- - POST 7 + POST 8 ;------------------------------------------------------------------------------- ; ; Load/save GDTR/IDTR in real mode @@ -263,7 +274,7 @@ cpuTest: ; ============================================================================== ;------------------------------------------------------------------------------- - POST 8 + POST 9 ;------------------------------------------------------------------------------- ; ; GDT, LDT, PDT, and PT setup, enter protected mode @@ -675,7 +686,7 @@ protTests: %include "tests/stack_m.asm" ;------------------------------------------------------------------------------- - POST 9 + POST A ;------------------------------------------------------------------------------- ; ; Stack functionality diff --git a/src/tests/flags_m.asm b/src/tests/flags_m.asm new file mode 100644 index 0000000..635b7ac --- /dev/null +++ b/src/tests/flags_m.asm @@ -0,0 +1,86 @@ +; +; Tests the carry flag instructions +; Needs the stack +; +%macro testCarryFlagInstructions 0 + pushf + ;Initialize flags to cleared + push word 0 + popf + ;Carry still set from pop? + jc error + cmc ;Set flag by complementing + ;Carry not complemented? + jnc error + cmc ;Clear flag by complementing + ;Carry not complemented? + jc error + stc + ;Carry not set? + jnc error + clc + ;Carry not cleared? + jc error + popf +%endmacro + +; +; Tests the direction flag instructions +; Needs the stack +; +%macro testDirectionFlagInstructions 0 + pushf + ;Initialize flags to cleared + push word 0 + popf + pushf ;Check the flags + pop ax + pushf + cmp ax,2 ;Not properly cleared? + jnz error + popf ;Restore our testing flags + std ;Set the direction flag + pushf + pushf + pop ax + cmp ax,2|PS_DF ;Direction flag properly set? + jne error + popf ;Restore flags + cld ;Clear the direction flag + pushf + pop ax + cmp ax,2 ;Direction flag properly cleared? + jne error + popf +%endmacro + +; +; Tests the interrupt flag instructions +; Needs the stack +; +%macro testInterruptFlagInstructions 0 + pushf + ;Initialize flags to cleared + push word 0 + popf + pushf ;Check the flags + pop ax + pushf + cmp ax,2 ;Not properly cleared? + jnz error + popf ;Restore our testing flags + sti ;Set the direction flag + pushf + pushf + pop ax + cmp ax,2|PS_IF ;Interrupt flag properly set? + jne error + popf ;Restore flags + cli ;Clear the direction flag + pushf + pop ax + cmp ax,2 ;Interrupt flag properly cleared? + jne error + popf +%endmacro + From 763ad6452e86ec02d0c9885a7cb674700c32f631 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 21 Apr 2026 19:38:37 +0200 Subject: [PATCH 54/62] Fixed table pointer registers cleanup after testing. --- src/tests/dtr_m.asm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/dtr_m.asm b/src/tests/dtr_m.asm index 8ca7627..c1a613e 100644 --- a/src/tests/dtr_m.asm +++ b/src/tests/dtr_m.asm @@ -78,9 +78,9 @@ testDTRpattern %1,%2,%3,0xCCDDEEFF,0x1122,%%error testDTRpattern %1,%2,%3,0xFFFFFFFF,0xEEEE,%%error ;Cleanup: Restore real mode tables. - mov [0],0 - mov [4],0x3FF + mov word [0],0x3FF + mov dword [2],0 o16 lidt [0] - mov [4],0xFFFF + mov word [0],0xFFFF o16 lgdt [0] %endmacro From bc8ef7716de998906ac23ae6c9b5530761404431 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 21 Apr 2026 19:42:43 +0200 Subject: [PATCH 55/62] Added a test for XLAT in real mode. Added a test for interrupts in real mode. Updated the POST numbers to reflect the newly added real mode tests. --- README.md | 42 +++++++++++---------- src/protected_tssinth.asm | 31 +++++++++++++++ src/systembiosexpansionarea.asm | 65 +++++++++++++++++++++++++++++++- src/test386.asm | 67 ++++++++++++++++++++++----------- 4 files changed, 161 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 45e4bbf..b4effa4 100644 --- a/README.md +++ b/README.md @@ -132,29 +132,31 @@ executed: | 0x06 | Calls in real mode | | 0x07 | Load full pointer in real mode | | 0x08 | Load and store GDTR and IDTR in real mode ** | -| 0x09 | GDT, LDT, PDT, and PT setup, enter protected mode | -| 0x0A | Stack functionality * | +| 0x09 | XLAT in real mode ** | +| 0x0A | Interrupts in real mode ** | +| 0x0B | GDT, LDT, PDT, and PT setup, enter protected mode | +| 0x0C | Stack functionality * | | 0x20 | Test user mode (ring 3) switching | | 0x21 | Test Virtual-8086 mode | | 0x22 | Test the TSS task switching functionality ** | -| 0x0B | Moving segment registers | -| 0x0C | Zero and sign-extension | -| 0x0D | 16-bit addressing modes (LEA) | -| 0x0E | 32-bit addressing modes (LEA) * | -| 0x0F | Access memory using various addressing modes | -| 0x10 | Store, move, scan, and compare string data in protected mode | -| 0x11 | Page faults and PTE bits | -| 0x12 | Other memory access faults | -| 0x13 | Bit Scan operations | -| 0x14 | Bit Test operations | -| 0x15 | Byte set on condition (SETcc) | -| 0x16 | Calls in protected mode | -| 0x17 | Adjust RPL Field of Selector (ARPL) | -| 0x18 | Check Array Index Against Bounds (BOUND) | -| 0x19 | Exchange Register/Memory with Register (XCHG) | -| 0x1A | Make Stack Frame for Procedure Parameters (ENTER) | -| 0x1B | High Level Procedure Exit (LEAVE) | -| 0x1C | Verify a Segment for Reading or Writing (VERR/VERW) | +| 0x0D | Moving segment registers | +| 0x0E | Zero and sign-extension | +| 0x0F | 16-bit addressing modes (LEA) | +| 0x10 | 32-bit addressing modes (LEA) * | +| 0x11 | Access memory using various addressing modes | +| 0x12 | Store, move, scan, and compare string data in protected mode | +| 0x13 | Page faults and PTE bits | +| 0x14 | Other memory access faults | +| 0x15 | Bit Scan operations | +| 0x16 | Bit Test operations | +| 0x17 | Byte set on condition (SETcc) | +| 0x18 | Calls in protected mode | +| 0x19 | Adjust RPL Field of Selector (ARPL) | +| 0x1A | Check Array Index Against Bounds (BOUND) | +| 0x1B | Exchange Register/Memory with Register (XCHG) | +| 0x1C | Make Stack Frame for Procedure Parameters (ENTER) | +| 0x1D | High Level Procedure Exit (LEAVE) | +| 0x1E | Verify a Segment for Reading or Writing (VERR/VERW) | | 0xE0 | Undefined behaviours and bugs (CPU family dependent) * | | 0xEE | Series of unverified tests for arithmetical and logical opcodes ***| | 0xFF | Testing completed | diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm index 2103fa2..c0e0d09 100644 --- a/src/protected_tssinth.asm +++ b/src/protected_tssinth.asm @@ -184,3 +184,34 @@ kernelInterrupt_validateV86mode16bit: or dword [esp+0x08],0x20000 ;Set VM bit to properly return. mov eax,[esp+0x2C] ;Load original EAX register to restore it. iret +; +; Kernel mode interrupt handler, callable from user mode. Validates if the Virtual 8086 mode is used. +; +BITS 16 +realmodeError: + push word 0xF000 + push word error + retf +realmodeInterrupt: + push ebx + push ecx + mov ecx, eax ; Get ESP for the interrupt program (stored into eax) + sub ecx, 0x6+0x8 ; Where we should end up on the kernel stack, taking into account what we just pushed + cmp sp, cx ; Did the stack decrease correctly? + jne realmodeError + pop ecx + mov bx, cs + cmp bx, 0xE000 ; Did we arrive at proper kernel code? + jne realmodeError + ;Basic stack pointer verified. Now check if the data on the stack is correct, while building a new stack to return to real mode. + ;Ignore flags + and esp,0xFFFF ;Real mode safety. + mov bx, word [esp+(0x4+0x02)] ;CS + cmp bx,0xE000 ;Correct? + jne error ;Error if incorrect. + mov bx, word [esp+0x4] ;IP + cmp bx,realmodeInterruptRET ;IP correct? + jne error ;Incorrect IP address. + pop ebx + iret +BITS 32 diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index e129f51..f795ac1 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -326,13 +326,74 @@ TSStest1finished: %include "tests/dtr_m.asm" ;Real mode code for this test BITS 16 -POST7entrypoint: +POST8entrypoint: testDTR 0,lidt,sidt testDTR 0,lgdt,sgdt testDTR 1,lidt,sidt testDTR 1,lgdt,sgdt + ;Return to the lower BIOS. push word 0xF000 - push word POST7returnpoint + push word POST8returnpoint + retf +XLATrealmodeError: + push word 0xF000 + push word error + retf +POST9entrypoint: + ;Test XLAT + mov cx,0x100 ;Length of the lookup table. + mov al,0x0 ;First entry + mov bx,0 ;Initialize data pointer. +fillXLATLookupTable: + neg al ;Store negated entries. + mov [bx],al ;Create the lookup table entry. + inc bx ;Next entry. + neg al ;Restore negated entry. + inc al + loop fillXLATLookupTable + mov cx,0x100 ;Length of the lookup table. + mov bx,0 ;Initialize data pointer to the start of the table. +checkXLAT: + mov ax,0x100 ;Calculate the entry we're going to check + sub ax,cx ;Create the entry number to validate. + mov dl,al ;Copy the entry number, as we're getting overwritten by the instruction. + neg dl ;Convert the input to the expected result. + xlat ;Execute the tested instruction + cmp al,dl ;Is the result as expected? + jnz XLATrealmodeError + loop checkXLAT ;Check all input values. + ;Return to the lower BIOS. + push word 0xF000 + push word POST9returnpoint + retf +POSTAentrypoint: + ;Test real mode interrupts + ;First, install the interrupt handler. + push ds + mov ax,0 + mov ds,ax ;Point to the Interrupt Vector Table. + ;Set all real mode interupt handlers to an error vector. + mov bx,0 ;Reset pointer + mov cx,0x100 ;All interrupt vectors. +nextRealModeInterruptSetError: + ;Set the interrupt to error + mov word [bx+2],0xF000 + mov word [bx],error + add bx,4 ;Next pointer + loop nextRealModeInterruptSetError + ;Set just our test interrupt vector. + mov ax,cs + mov [0x82],ax + mov ax,realmodeInterrupt + mov [0x80],ax + pop ds + ;Now, call the real mode interrupt + mov eax,esp ;Stack pointer for the interrupt to check. + int 0x20 + realmodeInterruptRET: + ;Return to the main lower BIOS. + push word 0xF000 + push word POSTAreturnpoint retf ;End of high BIOS diff --git a/src/test386.asm b/src/test386.asm index d710c5b..0e4180f 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -262,9 +262,32 @@ cpuTest: ; ; Jump to the entry point of the test. push word 0xE000 - push word POST7entrypoint + push word POST8entrypoint retf - POST7returnpoint: + POST8returnpoint: + + ; Jump to the entry point of the test. +;------------------------------------------------------------------------------- + POST 9 +;------------------------------------------------------------------------------- +; +; XLAT in real mode +; + push word 0xE000 + push word POST9entrypoint + retf + POST9returnpoint: + +;------------------------------------------------------------------------------- + POST A +;------------------------------------------------------------------------------- +; +; Interrupts in real mode +; + push word 0xE000 + push word POSTAentrypoint + retf + POSTAreturnpoint: %endif @@ -274,7 +297,7 @@ cpuTest: ; ============================================================================== ;------------------------------------------------------------------------------- - POST 9 + POST B ;------------------------------------------------------------------------------- ; ; GDT, LDT, PDT, and PT setup, enter protected mode @@ -686,7 +709,7 @@ protTests: %include "tests/stack_m.asm" ;------------------------------------------------------------------------------- - POST A + POST C ;------------------------------------------------------------------------------- ; ; Stack functionality @@ -1068,7 +1091,7 @@ userV86ExitFuncRet_16bitinterrupts: call switchedToRing0FromFlat_cleanup ;------------------------------------------------------------------------------- - POST B + POST D ;------------------------------------------------------------------------------- ; ; Test of moving segment registers in protected mode @@ -1085,7 +1108,7 @@ userV86ExitFuncRet_16bitinterrupts: ;------------------------------------------------------------------------------- - POST C + POST E ;------------------------------------------------------------------------------- ; ; Zero and sign-extension tests @@ -1173,7 +1196,7 @@ userV86ExitFuncRet_16bitinterrupts: %include "tests/lea_p.asm" postD: ;------------------------------------------------------------------------------- - POST D + POST F ;------------------------------------------------------------------------------- ; ; 16-bit addressing modes (LEA) @@ -1202,7 +1225,7 @@ postD: ;------------------------------------------------------------------------------- - POST E + POST 10 ;------------------------------------------------------------------------------- ; ; 32-bit addressing modes (LEA) @@ -1213,7 +1236,7 @@ postD: ;------------------------------------------------------------------------------- - POST F + POST 11 ;------------------------------------------------------------------------------- ; ; Access memory using various addressing modes @@ -1290,7 +1313,7 @@ postD: ;------------------------------------------------------------------------------- - POST 10 + POST 12 ;------------------------------------------------------------------------------- ; ; Store, move, scan, and compare string data in protected mode @@ -1320,7 +1343,7 @@ postD: %include "tests/paging_p.asm" post11: ;------------------------------------------------------------------------------- - POST 11 + POST 13 ;------------------------------------------------------------------------------- ; ; Verify page faults and PTE bits @@ -1372,7 +1395,7 @@ post11: ;------------------------------------------------------------------------------- - POST 12 + POST 14 ;------------------------------------------------------------------------------- ; ; Verify other memory access faults @@ -1412,7 +1435,7 @@ post11: %include "tests/bit_m.asm" ;------------------------------------------------------------------------------- - POST 13 + POST 15 ;------------------------------------------------------------------------------- ; ; Verify Bit Scan operations @@ -1422,7 +1445,7 @@ post11: ;------------------------------------------------------------------------------- - POST 14 + POST 16 ;------------------------------------------------------------------------------- ; ; Verify Bit Test operations @@ -1452,7 +1475,7 @@ post11: %include "tests/setcc_m.asm" ;------------------------------------------------------------------------------- - POST 15 + POST 17 ;------------------------------------------------------------------------------- ; ; Verify Byte set on condition (SETcc) @@ -1464,7 +1487,7 @@ post11: ;------------------------------------------------------------------------------- - POST 16 + POST 18 ;------------------------------------------------------------------------------- ; ; Calls in protected mode @@ -1477,7 +1500,7 @@ post11: ;------------------------------------------------------------------------------- - POST 17 + POST 19 ;------------------------------------------------------------------------------- ; ; Adjust RPL Field of Selector (ARPL) @@ -1528,7 +1551,7 @@ post11: ;------------------------------------------------------------------------------- - POST 18 + POST 1A ;------------------------------------------------------------------------------- ; ; Check Array Index Against Bounds (BOUND) @@ -1571,7 +1594,7 @@ BoundExcHandler: %include "tests/xchg_m.asm" post19: ;------------------------------------------------------------------------------- - POST 19 + POST 1B ;------------------------------------------------------------------------------- ; ; Exchange Register/Memory with Register (XCHG) @@ -1609,7 +1632,7 @@ post19: %include "tests/enter_m.asm" post1A: ;------------------------------------------------------------------------------- - POST 1A + POST 1C ;------------------------------------------------------------------------------- ; ; Make Stack Frame for Procedure Parameters (ENTER) @@ -1647,7 +1670,7 @@ post1A: %include "tests/leave_m.asm" post1B: ;------------------------------------------------------------------------------- - POST 1B + POST 1D ;------------------------------------------------------------------------------- ; ; High Level Procedure Exit (LEAVE) @@ -1664,7 +1687,7 @@ post1B: %include "tests/ver_p.asm" post1C: ;------------------------------------------------------------------------------- - POST 1C + POST 1E ;------------------------------------------------------------------------------- ; ; Verify a Segment for Reading or Writing (VERR/VERW) From 7917bdb0913d334c7612b609cc4647b81e85dac8 Mon Sep 17 00:00:00 2001 From: superfury Date: Wed, 22 Apr 2026 17:25:41 +0200 Subject: [PATCH 56/62] Implemented interrupt flag tests on real mode interrupts and protected mode interrupt gates clearing and properly storing of the interrupt flag on the stack and eflags register. Moved the kernel mode only interrupt handler to the system bios expansion area to create required space to compile the new code. --- src/protected_inth.asm | 33 --------------- src/protected_tssinth.asm | 73 +++++++++++++++++++++++++++------ src/systembiosexpansionarea.asm | 8 ++++ src/test386.asm | 21 +++++++--- src/x86_e.asm | 3 ++ 5 files changed, 86 insertions(+), 52 deletions(-) diff --git a/src/protected_inth.asm b/src/protected_inth.asm index b15f8cd..4f28588 100644 --- a/src/protected_inth.asm +++ b/src/protected_inth.asm @@ -1,36 +1,3 @@ -; -; Kernel mode interrupt handler -; -kernelInterrupt: - testCPL 0 ; Elevates to CPL 0 - push ebx - push ecx - push ds - lds ebx, [cs:ptrTSSprot] ; Get the TSS - mov ecx, [ebx+4] ; Get TSS ESP0 - sub ecx, 0x14+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed - cmp esp, ecx ; Did the stack decrease correctly? - jne error - mov cx, ss - cmp cx, word [ebx+8] ; Did the stack pointer load correctly? - jne error - pop ds - pop ecx - mov bx, cs - cmp bx, C_SEG_PROT32 ; Did we end up in kernel mode correctly? - jne error - pop ebx - cmp dword [esp+0x00], kernelModeInterruptReturn - jne error ; Invalid return address - cmp dword [esp+0x04], CU_SEG_PROT32|3 - jne error ; Invalid return code segment - ; Ignore eflags - cmp dword [esp+0x0C], ESP_R3_PROT - jne error ; Invalid return ESP - cmp dword [esp+0x10], SU_SEG_PROT32|3 - jne error ; Invalid user stack segment - iret ; Simply return to user mode - ; ; Kernel mode interrupt handler ; diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm index c0e0d09..732b53e 100644 --- a/src/protected_tssinth.asm +++ b/src/protected_tssinth.asm @@ -10,9 +10,50 @@ ptrTSSerrorR0_2: ; pointer to the error condition, from ring 0 or ring 2 dd error dw CU_SEG_PROT32|3 - errorCPLn: jmp far [cs:ptrTSSerrorR0_2] ;JMP to the error condition. + +; +; Kernel mode interrupt handler +; +kernelInterrupt: + testCPL 0 ; Elevates to CPL 0 + push ebx + push ecx + push ds + lds ebx, [cs:ptrTSSprot_R2] ; Get the TSS + mov ecx, [ebx+4] ; Get TSS ESP0 + sub ecx, 0x14+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed + cmp esp, ecx ; Did the stack decrease correctly? + jne errorCPLn + mov cx, ss + cmp cx, word [ebx+8] ; Did the stack pointer load correctly? + jne errorCPLn + pop ds + pop ecx + mov bx, cs + cmp bx, C_SEG_PROT16CS ; Did we end up in kernel mode correctly? + jne errorCPLn + pop ebx + cmp dword [esp+0x00], kernelModeInterruptReturn + jne errorCPLn ; Invalid return address + cmp dword [esp+0x04], CU_SEG_PROT32|3 + jne errorCPLn ; Invalid return code segment + ; Ignore most eflags + test dword [esp+0x08], PS_IF ;EFLAGS input + jz errorCPLn + cmp dword [esp+0x0C], ESP_R3_PROT + jne errorCPLn ; Invalid return ESP + cmp dword [esp+0x10], SU_SEG_PROT32|3 + jne errorCPLn ; Invalid user stack segment + push eax + pushfd + pop eax + test eax, PS_IF ;Incorrect flags. + jnz errorCPLn + pop eax + iret ; Simply return to user mode + ; ; Kernel mode interrupt handler (ring 2) for 32-bit TSS ; @@ -195,23 +236,29 @@ realmodeError: realmodeInterrupt: push ebx push ecx - mov ecx, eax ; Get ESP for the interrupt program (stored into eax) - sub ecx, 0x6+0x8 ; Where we should end up on the kernel stack, taking into account what we just pushed - cmp sp, cx ; Did the stack decrease correctly? + mov ecx, eax ; Get ESP for the interrupt program (stored into eax) + sub ecx, 0x6+0x8 ; Where we should end up on the kernel stack, taking into account what we just pushed + cmp sp, cx ; Did the stack decrease correctly? jne realmodeError pop ecx mov bx, cs - cmp bx, 0xE000 ; Did we arrive at proper kernel code? + cmp bx, 0xE000 ; Did we arrive at proper kernel code? jne realmodeError ;Basic stack pointer verified. Now check if the data on the stack is correct, while building a new stack to return to real mode. - ;Ignore flags - and esp,0xFFFF ;Real mode safety. - mov bx, word [esp+(0x4+0x02)] ;CS - cmp bx,0xE000 ;Correct? - jne error ;Error if incorrect. - mov bx, word [esp+0x4] ;IP - cmp bx,realmodeInterruptRET ;IP correct? - jne error ;Incorrect IP address. + ;Ignore most flags + test word [esp+(0x4+0x04)],PS_IF ;EFLAGS input + jz realmodeError + and esp,0xFFFF ;Real mode safety. + mov bx, word [esp+(0x4+0x02)] ;CS + cmp bx,0xE000 ;Correct? + jne error ;Error if incorrect. + mov bx, word [esp+0x4] ;IP + cmp bx,realmodeInterruptRET ;IP correct? + jne realmodeError ;Incorrect IP address. + pushfd + pop eax + test eax,PS_IF ;Incorrect flags. + jnz realmodeError pop ebx iret BITS 32 diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index f795ac1..419f2bb 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -26,6 +26,7 @@ defGDTDescPrototype C_SEG_PROT32_R2 defGDTDescPrototype S_SEG_PROT32_R2 defGDTDescPrototype C_SEG_PROT16CS + defGDTDescPrototype SU_SEG_PROT32 section .system_bios_extensions_area start=0x00000 ;Start of high BIOS @@ -375,12 +376,14 @@ POSTAentrypoint: ;Set all real mode interupt handlers to an error vector. mov bx,0 ;Reset pointer mov cx,0x100 ;All interrupt vectors. + jmp skipinterruptsclearing nextRealModeInterruptSetError: ;Set the interrupt to error mov word [bx+2],0xF000 mov word [bx],error add bx,4 ;Next pointer loop nextRealModeInterruptSetError + skipinterruptsclearing: ;Set just our test interrupt vector. mov ax,cs mov [0x82],ax @@ -388,9 +391,14 @@ nextRealModeInterruptSetError: mov [0x80],ax pop ds ;Now, call the real mode interrupt + pushfd ;Save interrupts + pushfd ;Modify flags + or word [esp],PS_IF ;Enable interrupts and flags bits to test + popfd mov eax,esp ;Stack pointer for the interrupt to check. int 0x20 realmodeInterruptRET: + popfd ;Restore interrupts ;Return to the main lower BIOS. push word 0xF000 push word POSTAreturnpoint diff --git a/src/test386.asm b/src/test386.asm index 0e4180f..1a1b7e3 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -357,6 +357,7 @@ initGDT: defGDTDescImplementation C_SEG_PROT32_R2, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_2,EXT_32BIT defGDTDescImplementation S_SEG_PROT32_R2, 0x00010000,0x0000ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDescImplementation C_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT + defGDTDescImplementation SU_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE @@ -369,7 +370,6 @@ initGDT: defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc S_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE - defGDTDesc SU_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 @@ -471,8 +471,13 @@ initIDT: ; Install some interrupt handlers for user mode tests ; Interrupt 20h: non-conforming kernel interrupt, callable from user mode - mov esi, C_SEG_PROT32 + mov esi, C_SEG_PROT16CS + %if ROM128 mov edi, kernelInterrupt + %else + ;Not enough room to test. Enter an invalid address. + mov edi, 0xFFFFFFFF + %endif mov dx, ACC_DPL_3 mov eax, 0x20 call initIntGateReal @@ -541,7 +546,7 @@ initIDT: %if ROM128 mov edi, kernelInterrupt_R2_386 %else - mov edi, kernelInterrupt ;Unused, placeholder + mov edi, 0xFFFFFFFF ;Unused, placeholder %endif mov dx, ACC_DPL_3 inc eax @@ -552,7 +557,7 @@ initIDT: %if ROM128 mov edi, kernelInterrupt_R2_286 %else - mov edi, kernelInterrupt ;Unused, placeholder + mov edi, 0xFFFFFFFF ;Unused, placeholder %endif mov dx, ACC_DPL_3 inc eax @@ -563,7 +568,7 @@ initIDT: %if ROM128 mov edi, kernelInterrupt_validateAndClearTS %else - mov edi, kernelInterrupt ;Unused, placeholder + mov edi, 0xFFFFFFFF ;Unused, placeholder %endif mov dx, ACC_DPL_3 inc eax @@ -581,7 +586,7 @@ initIDT: %if ROM128 mov edi, kernelInterrupt_validateV86mode16bit %else - mov edi, kernelInterrupt ;Unused, placeholder + mov edi, 0xFFFFFFFF ;Unused, placeholder %endif or edi,0xFFFF0000 ;Make sure that the offset is located on a invalid 32-bit mapped address by mapping the high 16 bits, which are not to be used. mov dx, ACC_DPL_3 @@ -855,10 +860,14 @@ protTests: protModeFaultTest EX_GP, 0x118|0x2, int 0x23 ; We should have the intial user mode stack setup right now for the below checks to validate. + call switchToRing0 ; back to kernel mode (ring 0) again + call switchToRing3 ; back to user mode (ring 3) again. Thus setting the Interrupt flag for our test. ; Interrupt from user mode to kernel mode using a 32-bit interrupt gate + %if ROM128 int 0x20 kernelModeInterruptReturn: + %endif ; Interrupt from user mode to kernel using a 16-bit interrupt gate int 0x27 diff --git a/src/x86_e.asm b/src/x86_e.asm index 7f693ac..0a79daa 100644 --- a/src/x86_e.asm +++ b/src/x86_e.asm @@ -8,6 +8,9 @@ PS_IF equ 0x0200 PS_DF equ 0x0400 PS_OF equ 0x0800 PS_NT equ 0x4000 +PS_RF equ 0x10000 +PS_VM equ 0x20000 + PS_ARITH equ (PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF) PS_LOGIC equ (PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF) PS_MULTIPLY equ (PS_CF | PS_OF) ; only CF and OF are "defined" following MUL or IMUL From 089aa754d3b9529a95c29bf2f3237bfa24031a48 Mon Sep 17 00:00:00 2001 From: superfury Date: Wed, 22 Apr 2026 20:08:45 +0200 Subject: [PATCH 57/62] Compressed the POST 8/9/A tests by moving the tests into the system BIOS expansion area with less returns to the system BIOS area. --- src/systembiosexpansionarea.asm | 44 +++++++++++++++++++++------------ src/test386.asm | 31 +---------------------- 2 files changed, 29 insertions(+), 46 deletions(-) diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index 419f2bb..7aae245 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -325,22 +325,31 @@ TSStest1finished: retfd ;Actually return %include "tests/dtr_m.asm" -;Real mode code for this test +;Real mode code for these tests BITS 16 -POST8entrypoint: - testDTR 0,lidt,sidt - testDTR 0,lgdt,sgdt - testDTR 1,lidt,sidt - testDTR 1,lgdt,sgdt - ;Return to the lower BIOS. - push word 0xF000 - push word POST8returnpoint - retf XLATrealmodeError: push word 0xF000 push word error retf -POST9entrypoint: +POST8_9_Aentrypoint: +;------------------------------------------------------------------------------- + POST 8 +;------------------------------------------------------------------------------- +; +; Load/save GDTR/IDTR in real mode +; + testDTR 0,lidt,sidt + testDTR 0,lgdt,sgdt + testDTR 1,lidt,sidt + testDTR 1,lgdt,sgdt + + +;------------------------------------------------------------------------------- + POST 9 +;------------------------------------------------------------------------------- +; +; XLAT in real mode +; ;Test XLAT mov cx,0x100 ;Length of the lookup table. mov al,0x0 ;First entry @@ -363,11 +372,14 @@ checkXLAT: cmp al,dl ;Is the result as expected? jnz XLATrealmodeError loop checkXLAT ;Check all input values. - ;Return to the lower BIOS. - push word 0xF000 - push word POST9returnpoint - retf -POSTAentrypoint: + +;------------------------------------------------------------------------------- + POST A +;------------------------------------------------------------------------------- +; +; Interrupts in real mode +; + ;Test real mode interrupts ;First, install the interrupt handler. push ds diff --git a/src/test386.asm b/src/test386.asm index 1a1b7e3..de657cb 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -254,38 +254,9 @@ cpuTest: advTestSegReal %if ROM128 -;------------------------------------------------------------------------------- - POST 8 -;------------------------------------------------------------------------------- -; -; Load/save GDTR/IDTR in real mode -; - ; Jump to the entry point of the test. - push word 0xE000 - push word POST8entrypoint - retf - POST8returnpoint: - ; Jump to the entry point of the test. -;------------------------------------------------------------------------------- - POST 9 -;------------------------------------------------------------------------------- -; -; XLAT in real mode -; - push word 0xE000 - push word POST9entrypoint - retf - POST9returnpoint: - -;------------------------------------------------------------------------------- - POST A -;------------------------------------------------------------------------------- -; -; Interrupts in real mode -; push word 0xE000 - push word POSTAentrypoint + push word POST8_9_Aentrypoint retf POSTAreturnpoint: %endif From c80da842da6ca1228ee8bca3b0cd2eff5ddd7f1f Mon Sep 17 00:00:00 2001 From: superfury Date: Wed, 22 Apr 2026 20:49:27 +0200 Subject: [PATCH 58/62] Reverted the POST codes back to before the real mode tests were added. Modified the newly added POST numbers to fill the unused range instead. Moved the loop tests back to POST 1 to match the documentation. --- README.md | 58 +++++++++++++-------------- src/systembiosexpansionarea.asm | 6 +-- src/test386.asm | 69 ++++++++++++++++----------------- 3 files changed, 66 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index b4effa4..f29ed62 100644 --- a/README.md +++ b/README.md @@ -125,38 +125,38 @@ executed: | ---- | ------------------------------------------------------------------ | | 0x00 | Real mode initialisation | | 0x01 | Conditional jumps and loops | -| 0x02 | Flag instructions in real mode | -| 0x03 | Quick tests of unsigned 32-bit multiplication and division | -| 0x04 | Move segment registers in real mode | -| 0x05 | Store, move, scan, and compare string data in real mode | -| 0x06 | Calls in real mode | -| 0x07 | Load full pointer in real mode | -| 0x08 | Load and store GDTR and IDTR in real mode ** | -| 0x09 | XLAT in real mode ** | -| 0x0A | Interrupts in real mode ** | -| 0x0B | GDT, LDT, PDT, and PT setup, enter protected mode | -| 0x0C | Stack functionality * | +| 0x1D | Flag instructions in real mode | +| 0x02 | Quick tests of unsigned 32-bit multiplication and division | +| 0x03 | Move segment registers in real mode | +| 0x04 | Store, move, scan, and compare string data in real mode | +| 0x05 | Calls in real mode | +| 0x06 | Load full pointer in real mode | +| 0x1E | Load and store GDTR and IDTR in real mode ** | +| 0x1F | XLAT in real mode ** | +| 0x07 | Interrupts in real mode ** | +| 0x08 | GDT, LDT, PDT, and PT setup, enter protected mode | +| 0x09 | Stack functionality * | | 0x20 | Test user mode (ring 3) switching | | 0x21 | Test Virtual-8086 mode | | 0x22 | Test the TSS task switching functionality ** | -| 0x0D | Moving segment registers | -| 0x0E | Zero and sign-extension | -| 0x0F | 16-bit addressing modes (LEA) | -| 0x10 | 32-bit addressing modes (LEA) * | -| 0x11 | Access memory using various addressing modes | -| 0x12 | Store, move, scan, and compare string data in protected mode | -| 0x13 | Page faults and PTE bits | -| 0x14 | Other memory access faults | -| 0x15 | Bit Scan operations | -| 0x16 | Bit Test operations | -| 0x17 | Byte set on condition (SETcc) | -| 0x18 | Calls in protected mode | -| 0x19 | Adjust RPL Field of Selector (ARPL) | -| 0x1A | Check Array Index Against Bounds (BOUND) | -| 0x1B | Exchange Register/Memory with Register (XCHG) | -| 0x1C | Make Stack Frame for Procedure Parameters (ENTER) | -| 0x1D | High Level Procedure Exit (LEAVE) | -| 0x1E | Verify a Segment for Reading or Writing (VERR/VERW) | +| 0x0B | Moving segment registers | +| 0x0C | Zero and sign-extension | +| 0x0D | 16-bit addressing modes (LEA) | +| 0x0E | 32-bit addressing modes (LEA) * | +| 0x0F | Access memory using various addressing modes | +| 0x10 | Store, move, scan, and compare string data in protected mode | +| 0x11 | Page faults and PTE bits | +| 0x12 | Other memory access faults | +| 0x13 | Bit Scan operations | +| 0x14 | Bit Test operations | +| 0x15 | Byte set on condition (SETcc) | +| 0x16 | Calls in protected mode | +| 0x17 | Adjust RPL Field of Selector (ARPL) | +| 0x18 | Check Array Index Against Bounds (BOUND) | +| 0x19 | Exchange Register/Memory with Register (XCHG) | +| 0x1A | Make Stack Frame for Procedure Parameters (ENTER) | +| 0x1B | High Level Procedure Exit (LEAVE) | +| 0x1C | Verify a Segment for Reading or Writing (VERR/VERW) | | 0xE0 | Undefined behaviours and bugs (CPU family dependent) * | | 0xEE | Series of unverified tests for arithmetical and logical opcodes ***| | 0xFF | Testing completed | diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index 7aae245..13a3dfa 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -333,7 +333,7 @@ XLATrealmodeError: retf POST8_9_Aentrypoint: ;------------------------------------------------------------------------------- - POST 8 + POST 1E ;------------------------------------------------------------------------------- ; ; Load/save GDTR/IDTR in real mode @@ -345,7 +345,7 @@ POST8_9_Aentrypoint: ;------------------------------------------------------------------------------- - POST 9 + POST 1F ;------------------------------------------------------------------------------- ; ; XLAT in real mode @@ -374,7 +374,7 @@ checkXLAT: loop checkXLAT ;Check all input values. ;------------------------------------------------------------------------------- - POST A + POST 7 ;------------------------------------------------------------------------------- ; ; Interrupts in real mode diff --git a/src/test386.asm b/src/test386.asm index de657cb..350edea 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -145,8 +145,16 @@ cpuTest: testJcc 8 testJcc 16 +; +; Loops +; +%include "tests/loop_m.asm" + testLoop + testLoopZ + testLoopNZ + ;------------------------------------------------------------------------------- - POST 2 + POST 1D ;------------------------------------------------------------------------------- ; ; Flag instructions @@ -156,17 +164,8 @@ cpuTest: testDirectionFlagInstructions testInterruptFlagInstructions -; -; Loops -; -%include "tests/loop_m.asm" - testLoop - testLoopZ - testLoopNZ - - ;------------------------------------------------------------------------------- - POST 3 + POST 2 ;------------------------------------------------------------------------------- ; ; Quick tests of unsigned 32-bit multiplication and division @@ -185,7 +184,7 @@ cpuTest: %include "tests/mov_m.asm" ;------------------------------------------------------------------------------- - POST 4 + POST 3 ;------------------------------------------------------------------------------- ; ; Move segment registers in real mode @@ -202,7 +201,7 @@ cpuTest: %include "tests/string_m.asm" ;------------------------------------------------------------------------------- - POST 5 + POST 4 ;------------------------------------------------------------------------------- ; ; Test store, move, scan, and compare string data @@ -225,7 +224,7 @@ cpuTest: %include "tests/call_m.asm" ;------------------------------------------------------------------------------- - POST 6 + POST 5 ;------------------------------------------------------------------------------- ; ; Calls in real mode @@ -239,7 +238,7 @@ cpuTest: %include "tests/load_ptr_m.asm" ;------------------------------------------------------------------------------- - POST 7 + POST 6 ;------------------------------------------------------------------------------- ; ; Load full pointer in real mode @@ -268,7 +267,7 @@ cpuTest: ; ============================================================================== ;------------------------------------------------------------------------------- - POST B + POST 8 ;------------------------------------------------------------------------------- ; ; GDT, LDT, PDT, and PT setup, enter protected mode @@ -685,7 +684,7 @@ protTests: %include "tests/stack_m.asm" ;------------------------------------------------------------------------------- - POST C + POST 9 ;------------------------------------------------------------------------------- ; ; Stack functionality @@ -1071,7 +1070,7 @@ userV86ExitFuncRet_16bitinterrupts: call switchedToRing0FromFlat_cleanup ;------------------------------------------------------------------------------- - POST D + POST B ;------------------------------------------------------------------------------- ; ; Test of moving segment registers in protected mode @@ -1088,7 +1087,7 @@ userV86ExitFuncRet_16bitinterrupts: ;------------------------------------------------------------------------------- - POST E + POST C ;------------------------------------------------------------------------------- ; ; Zero and sign-extension tests @@ -1176,7 +1175,7 @@ userV86ExitFuncRet_16bitinterrupts: %include "tests/lea_p.asm" postD: ;------------------------------------------------------------------------------- - POST F + POST D ;------------------------------------------------------------------------------- ; ; 16-bit addressing modes (LEA) @@ -1205,7 +1204,7 @@ postD: ;------------------------------------------------------------------------------- - POST 10 + POST E ;------------------------------------------------------------------------------- ; ; 32-bit addressing modes (LEA) @@ -1216,7 +1215,7 @@ postD: ;------------------------------------------------------------------------------- - POST 11 + POST F ;------------------------------------------------------------------------------- ; ; Access memory using various addressing modes @@ -1293,7 +1292,7 @@ postD: ;------------------------------------------------------------------------------- - POST 12 + POST 10 ;------------------------------------------------------------------------------- ; ; Store, move, scan, and compare string data in protected mode @@ -1323,7 +1322,7 @@ postD: %include "tests/paging_p.asm" post11: ;------------------------------------------------------------------------------- - POST 13 + POST 11 ;------------------------------------------------------------------------------- ; ; Verify page faults and PTE bits @@ -1375,7 +1374,7 @@ post11: ;------------------------------------------------------------------------------- - POST 14 + POST 12 ;------------------------------------------------------------------------------- ; ; Verify other memory access faults @@ -1415,7 +1414,7 @@ post11: %include "tests/bit_m.asm" ;------------------------------------------------------------------------------- - POST 15 + POST 13 ;------------------------------------------------------------------------------- ; ; Verify Bit Scan operations @@ -1425,7 +1424,7 @@ post11: ;------------------------------------------------------------------------------- - POST 16 + POST 14 ;------------------------------------------------------------------------------- ; ; Verify Bit Test operations @@ -1455,7 +1454,7 @@ post11: %include "tests/setcc_m.asm" ;------------------------------------------------------------------------------- - POST 17 + POST 15 ;------------------------------------------------------------------------------- ; ; Verify Byte set on condition (SETcc) @@ -1467,7 +1466,7 @@ post11: ;------------------------------------------------------------------------------- - POST 18 + POST 16 ;------------------------------------------------------------------------------- ; ; Calls in protected mode @@ -1480,7 +1479,7 @@ post11: ;------------------------------------------------------------------------------- - POST 19 + POST 17 ;------------------------------------------------------------------------------- ; ; Adjust RPL Field of Selector (ARPL) @@ -1531,7 +1530,7 @@ post11: ;------------------------------------------------------------------------------- - POST 1A + POST 18 ;------------------------------------------------------------------------------- ; ; Check Array Index Against Bounds (BOUND) @@ -1574,7 +1573,7 @@ BoundExcHandler: %include "tests/xchg_m.asm" post19: ;------------------------------------------------------------------------------- - POST 1B + POST 19 ;------------------------------------------------------------------------------- ; ; Exchange Register/Memory with Register (XCHG) @@ -1612,7 +1611,7 @@ post19: %include "tests/enter_m.asm" post1A: ;------------------------------------------------------------------------------- - POST 1C + POST 1A ;------------------------------------------------------------------------------- ; ; Make Stack Frame for Procedure Parameters (ENTER) @@ -1650,7 +1649,7 @@ post1A: %include "tests/leave_m.asm" post1B: ;------------------------------------------------------------------------------- - POST 1D + POST 1B ;------------------------------------------------------------------------------- ; ; High Level Procedure Exit (LEAVE) @@ -1667,7 +1666,7 @@ post1B: %include "tests/ver_p.asm" post1C: ;------------------------------------------------------------------------------- - POST 1E + POST 1C ;------------------------------------------------------------------------------- ; ; Verify a Segment for Reading or Writing (VERR/VERW) From 34f32b507bf62a707cc1beb454bf679ce42af93f Mon Sep 17 00:00:00 2001 From: superfury Date: Sun, 24 May 2026 15:49:06 +0200 Subject: [PATCH 59/62] - Added a test for a far return with immediate to a more privileged privilege level. --- src/test386.asm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test386.asm b/src/test386.asm index 350edea..79b2d32 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -865,6 +865,12 @@ userRetfErrorFunction: push error userRetfErrorLocation: retf +userRetfImmErrorFunction: + ; From user mode to kernel mode error address, with immediate, which isn't allowed. + push C_SEG_PROT32 + push error +userRetfImmErrorLocation: + retf 1 userV86ExitFuncLocation: bits 16 push userV86ExitFuncRet ; Where to continue @@ -917,6 +923,7 @@ ring0_2TestEndLocation: testUserFault EX_GP, C_SEG_PROT32, jmp C_SEG_PROT32|3:0 ; Basic jump from user mode to kernel mode testUserFault EX_GP, C_SEG_PROT32, call C_SEG_PROT32|3:0 ; Basic call from user mode to kernel mode testUserFaultEx EX_GP, C_SEG_PROT32, userRetfErrorLocation, jmp userRetfErrorFunction ; Far return from user mode to kernel mode + testUserFaultEx EX_GP, C_SEG_PROT32, userRetfImmErrorLocation, jmp userRetfImmErrorFunction ; Far return from user mode to kernel mode ; Test kernel mode only interrupt mov eax, esp ; Save the stack pointer for us to check, as the interrupt doesn't have a comparison. From b116ce29f922a2887705574c6c932a215d66ed69 Mon Sep 17 00:00:00 2001 From: superfury Date: Mon, 1 Jun 2026 20:44:02 +0200 Subject: [PATCH 60/62] - Moved the POST 20 and 21 protected mode tests to the lower 64K ROM. --- src/protected_inth.asm | 25 +-- src/protected_m.asm | 190 +++++++++++++++++- src/protected_rings_p.asm | 129 ++++++++++++- src/protected_tssinth.asm | 2 +- src/systembiosexpansionarea.asm | 285 ++++++++++++++++++++++++++- src/test386.asm | 331 +++++--------------------------- src/tss_p.asm | 4 + 7 files changed, 664 insertions(+), 302 deletions(-) diff --git a/src/protected_inth.asm b/src/protected_inth.asm index 4f28588..b198a21 100644 --- a/src/protected_inth.asm +++ b/src/protected_inth.asm @@ -17,12 +17,12 @@ kernelInterrupt286: pop ds pop ecx mov bx, cs - cmp bx, C_SEG_PROT32 ; Did we end up in kernel mode correctly? + cmp bx, C_SEG_PROT32low ; Did we end up in kernel mode correctly? jne error pop ebx cmp word [esp+0x00], kernelModeInterruptReturn286 jne error ; Invalid return address - cmp word [esp+0x02], CU_SEG_PROT32|3 + cmp word [esp+0x02], CU_SEG_PROT32low|3 jne error ; Invalid return code segment ; Ignore eflags cmp word [esp+0x06], ESP_R3_PROT @@ -57,7 +57,7 @@ kernelInterruptRestoreKernelStack: pop ebx cmp dword [esp+0x00], kernelModeInterruptKernelStackReturn jne error ; Invalid return address - cmp dword [esp+0x04], CU_SEG_PROT32|3 + cmp dword [esp+0x04], CU_SEG_PROT32low|3 jne error ; Invalid return code segment ; Ignore eflags cmp dword [esp+0x0C], ESP_R3_PROT @@ -88,13 +88,13 @@ kernelOnlyInterrupt: jne error pop ecx mov bx, cs - cmp bx, C_SEG_PROT32 ; Did we arrive at proper kernel code? + cmp bx, C_SEG_PROT32low ; Did we arrive at proper kernel code? jne error pop ds pop ebx cmp dword [esp+0x00], kernelModeOnlyInterruptReturn jne error ; Invalid return address - cmp dword [esp+0x04],C_SEG_PROT32 + cmp dword [esp+0x04],C_SEG_PROT32low jne error ; Invalid return code segment ; Ignore eflags iret ; Simply return to kernel mode @@ -117,12 +117,12 @@ kernelConformingInterrupt: jne error pop ecx mov bx, cs - cmp bx, CC_SEG_PROT32|3 ; Did we arrive at proper conforming code? + cmp bx, CC_SEG_PROT32low|3 ; Did we arrive at proper conforming code? jne error pop ebx cmp dword [esp+0x00], kernelConformingInterruptReturn jne error ; Invalid return address - cmp dword [esp+0x04], CU_SEG_PROT32|3 + cmp dword [esp+0x04], CU_SEG_PROT32low|3 jne error ; Invalid return code segment ; Ignore eflags iret ; Simply return to user mode, stays at CPL 3 @@ -149,13 +149,13 @@ kernelOnlyConformingInterrupt: jne error pop ecx mov bx, cs - cmp bx, CC_SEG_PROT32 ; Did we arrive at proper conforming kernel code? + cmp bx, CC_SEG_PROT32low ; Did we arrive at proper conforming kernel code? jne error pop ds pop ebx cmp dword [esp+0x00], kernelOnlyConformingInterruptReturn jne error ; Invalid return address - cmp dword [esp+0x04],C_SEG_PROT32 + cmp dword [esp+0x04],C_SEG_PROT32low jne error ; Invalid return code segment ; Ignore eflags iret ; Simply return to kernel mode @@ -178,12 +178,12 @@ userModeInterrupt: jne error pop ecx mov bx, cs - cmp bx, CU_SEG_PROT32|3 ; Did we arrive at proper user mode code? + cmp bx, CU_SEG_PROT32low|3 ; Did we arrive at proper user mode code? jne error pop ebx cmp dword [esp+0x00], userInterruptReturn jne error ; Invalid return address - cmp dword [esp+0x04],CU_SEG_PROT32|3 + cmp dword [esp+0x04],CU_SEG_PROT32low|3 jne error ; Invalid return code segment ; Ignore eflags iret ; Simply return to caller, stays as user mode. @@ -206,3 +206,6 @@ kernelInterrupt_validateIsV86mode: test dword [esp+8],0x20000 ;V86 bit set? jz error ;V86 bit not properly set? iret + +DefaultExcHandlerLow: + jmp C_SEG_PROT32:DefaultExcHandler \ No newline at end of file diff --git a/src/protected_m.asm b/src/protected_m.asm index 141b922..5e4afd1 100644 --- a/src/protected_m.asm +++ b/src/protected_m.asm @@ -136,6 +136,37 @@ %assign LDTSelDesc LDTSelDesc+8 %endmacro +; +; Defines a LDT descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5) +; +%assign LDTSelDesc 4 +%macro defLDTDescPrototype 1 + %assign %1 LDTSelDesc + %assign LDTSelDesc LDTSelDesc+8 + ;The LDT is prototyped. + %define LDTprototyped +%endmacro + +;Second, the implementation +%macro defLDTDescImplementation 1-5 0,0,0,0 + %ifndef LDTprototyped + %assign %1 LDTSelDesc + %assign nested 0 + %else + %assign nested 1 + %endif + lds ebx, [cs:ptrLDTprot] ; this macro is used in prot mode to set up prot mode env. + mov eax, %1 + mov esi, %2 + mov edi, %3 + mov dx, %4|%5 + initDescriptor + %if nested==0 + %assign LDTSelDesc LDTSelDesc+8 + %endif +%endmacro + + ; ; Updates a LDT descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5) ; @@ -257,6 +288,53 @@ popad %endmacro +; +; Set a int gate on the IDT in protected mode +; +; %1: vector +; %2: offset +; %3: DPL, use ACC_DPL_* equs (optional) +; +; the stack must be initialized +; +%macro setProtModeIntGateLow 2-3 -1 + pushad + pushf + mov ax, ds ; save ds + push ax + mov eax, %1 + mov edi, %2 + %if %3 != -1 + mov dx, %3 + %else + mov dx, cs + and dx, 7 + shl dx, 13 + %endif + cmp dx, ACC_DPL_0 + jne %%dpl3 +%%dpl0: + mov esi, C_SEG_PROT32low + jmp %%cont +%%dpl3: + mov esi, CU_SEG_PROT32low +%%cont: + mov cx, cs + test cx, 7 + jnz %%ring3 +%%ring0: + lds ebx, [cs:ptrIDTprot] + jmp %%call +%%ring3: + lds ebx, [cs:ptrIDTUprot] +%%call: + call initIntGateProt + pop ax + mov ds, ax ; restore ds + popf + popad +%endmacro + ; ; Tests a fault ; @@ -276,6 +354,16 @@ setProtModeIntGate %1, DefaultExcHandler, ACC_DPL_0 %endmacro +%macro protModeFaultTestLow 3+ + setProtModeIntGate %1, %%continue +%%test: + %3 + jmp error +%%continue: + protModeExcCheckLow %1, %2, %%test + setProtModeIntGate %1, DefaultExcHandlerLow, ACC_DPL_0 +%endmacro + ; %1: vector ; %2: expected error code ; %3: the provilege level the test code will run in @@ -303,6 +391,33 @@ setProtModeIntGate %1, DefaultExcHandler, ACC_DPL_0 %endmacro +; %1: vector +; %2: expected error code +; %3: the provilege level the test code will run in +; %4: the expected value of pushed EIP (specify if %5 is a call, otherwise use -1) +; %5: fault causing code (can be a call to procedure) +; +; The fault handler is executed in ring 0. The caller must reset the data segments. +; +%macro protModeFaultTestExLow 5+ + setProtModeIntGate %1, %%continue, ACC_DPL_0 +%%test: + %5 + jmp error +%%continue: + %if %3 = 0 + %assign expectedCS C_SEG_PROT32low + %else + %assign expectedCS CU_SEG_PROT32low|3 + %endif + %if %4 = -1 + protModeExcCheckLow %1, %2, %%test, expectedCS + %else + protModeExcCheckLow %1, %2, %4, expectedCS + %endif + setProtModeIntGate %1, DefaultExcHandlerLow, ACC_DPL_0 +%endmacro + ; %1: vector ; %2: expected error code ; %3: the provilege level the test code will run in @@ -318,9 +433,9 @@ jmp error %%continue: %if %3 = 0 - %assign expectedCS C_SEG_PROT32 + %assign expectedCS C_SEG_PROT32low %else - %assign expectedCS 0xF000 + %assign expectedCS 0xE000 %endif %if %4 = -1 protModeExcCheckV86 %1, %2, %%test, expectedCS @@ -328,7 +443,7 @@ protModeExcCheckV86 %1, %2, %4, expectedCS %endif call switchedToRing0V86_cleanup ;Cleanup the user-mode stack and restore segment registers for kernel mode - setProtModeIntGate %1, DefaultExcHandler, ACC_DPL_0 + setProtModeIntGate %1, DefaultExcHandlerLow, ACC_DPL_0 %endmacro ; Tests an user-mode exception @@ -351,6 +466,22 @@ jmp %%startinglabel testCPL 0 %endmacro +%macro testUserFaultLow 3+ +jmp %%startinglabel +%%usercodelabel: + call switchToRing3low + mov ax, DU_SEG_PROT|3 + mov ss, ax + mov esp, 0x1004 +%%instructionlabel: + %3 + jmp error +%%startinglabel: + loadProtModeStack + protModeFaultTestExLow %1, %2, 3, %%instructionlabel, call %%usercodelabel + testCPL 0 +%endmacro + ; Tests an user-mode exception (Virtual 8086 mode) under IOPL 0 ;%1: exception number ;%2: fault error code @@ -414,6 +545,26 @@ jmp %%startinglabel testCPL 0 %endmacro +%macro testUserFaultExLow 4+ +jmp %%startinglabel +%%usercodelabel: + call switchToRing3low + mov ax, DU_SEG_PROT|3 + mov ss, ax + mov esp, 0x1004 +%%instructionlabel: + %4 + jmp error +%%startinglabel: + loadProtModeStack + %if %3 = -1 + protModeFaultTestExLow %1, %2, 3, %%instructionlabel, call %%usercodelabel + %else + protModeFaultTestExLow %1, %2, 3, %3, call %%usercodelabel + %endif + testCPL 0 +%endmacro + ; Tests an user-mode exception with custom fault point (Virtual 8086 mode) under IOPL 0 ;%1: exception number ;%2: fault error code @@ -499,6 +650,35 @@ jmp %%startinglabel add esp, 12+exc_errcode %endmacro +%macro protModeExcCheckLow 3-4 -1 + %if %1 == 8 || (%1 > 10 && %1 <= 14) + %assign exc_errcode 4 + cmp [ss:esp], dword %2 + jne error + %else + %assign exc_errcode 0 + %endif + %if %4 != -1 + cmp [ss:esp+exc_errcode+4], dword %4 + jne error + %else + mov bx, cs + test bx, 7 + jnz %%ring3 + %%ring0: + cmp [ss:esp+exc_errcode+4], dword C_SEG_PROT32low + jne error + jmp %%continue + %%ring3: + cmp [ss:esp+exc_errcode+4], dword CU_SEG_PROT32low|3 + jne error + %%continue: + %endif + cmp [ss:esp+exc_errcode], dword %3 + jne error + add esp, 12+exc_errcode +%endmacro + ; ; Checks exception result and restores the previous handler ; @@ -542,11 +722,11 @@ jmp %%startinglabel test bx, 7 jnz %%ring3 %%ring0: - cmp [ss:esp+exc_errcode+4], dword 0xF000 + cmp [ss:esp+exc_errcode+4], dword 0xE000 jne error jmp %%continue %%ring3: - cmp [ss:esp+exc_errcode+4], dword 0xF000 + cmp [ss:esp+exc_errcode+4], dword 0xE000 jne error %%continue: %endif diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index ac3c13a..1ddca40 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -41,6 +41,49 @@ switchToRing3: push dword edx ; push return EIP iretd +; +; Switches from Ring 0 to Ring 3 +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3low: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot] + ; save ring 0 data segments, they'll be restored with switchToRing0 + mov [ebx+0x54], ax ; save DS + mov ax, es + mov [ebx+0x48], ax ; save ES + mov ax, fs + mov [ebx+0x58], ax ; save FS + mov ax, gs + mov [ebx+0x5C], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + mov eax, ss + mov [ebx+8], eax + cli ; disable ints during switching + push dword SU_SEG_PROT32|3 ; push user stack with RPL=3 + push dword ESP_R3_PROT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) + push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 + push dword edx ; push return EIP + iretd + ; ; Switches from Ring 0 to Ring 3, to return to ring 0 in flat mode later ; @@ -83,11 +126,56 @@ switchToRing3FLATkernel: push dword ESP_R3_PROT ; push user mode esp pushfd ; push eflags or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) - push dword CU_SEG_PROT32|3 ; push user code segment with RPL=3 + push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 push dword edx ; push return EIP iretd ; +; Switches from Ring 0 to Ring 3, to return to ring 0 in flat mode later +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3FLATkernelLow: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot] + ; save ring 0 data segments, they'll be restored with switchToRing0 + mov [ebx+0x54], ax ; save DS + mov ax, es + mov [ebx+0x48], ax ; save ES + mov ax, fs + mov [ebx+0x58], ax ; save FS + mov ax, gs + mov [ebx+0x5C], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + pushfd + or dword [ebx+4], 0xE0010000 ; make sure we return to a proper ring0 stack + popfd + mov eax, D_SEG_PROT32FLAT + mov [ebx+8], eax + cli ; disable ints during switching + push dword SU_SEG_PROT32|3 ; push user stack with RPL=3 + push dword ESP_R3_PROT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) + push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 + push dword edx ; push return EIP + iretd +; ; Switches from Ring 0 to Ring 3 in flat mode ; ; After calling this procedure consider all the registers and flags as trashed. @@ -176,7 +264,7 @@ switchToRing3V86_0: pushfd ; push eflags or dword [ss:esp], 0x20200 ; reenable interrupts in ring 3 (can't use privileged sti), V8086 flag set, IOPL 0 and dword [ss:esp], 0xF0FFF ; setup IOPL 0 properly - push dword 0xF000 ; push user code segment with RPL=3 + push dword 0xE000 ; push user code segment with RPL=3 push dword edx ; push return EIP iretd @@ -224,7 +312,7 @@ switchToRing3V86_3: push dword ESP_R3_PROT ; push user mode esp pushfd ; push eflags or dword [ss:esp], 0x23200 ; reenable interrupts in ring 3 (can't use privileged sti), V8086 flag set, IOPL 3 - push dword 0xF000 ; push user code segment with RPL=3 + push dword 0xE000 ; push user code segment with RPL=3 push dword edx ; push return EIP iretd @@ -262,6 +350,39 @@ switchToRing0: push ecx ret +; +; Switches from Ring 3 to Ring 0 (Non-V86 mode) +; +; After calling this procedure consider all the registers and flags as trashed. +; +switchToRing0low: + testCPL 3 ; we must be in ring 3 + ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. + ; A placeholder for a Call Gate is already present in the GDT. + pop ecx ; read the return offset + lfs ebx, [cs:ptrGDTUprot] + mov eax, RING0_GATE + mov esi, C_SEG_PROT32low + mov edi, .ring0 + mov dx, ACC_DPL_3 ; the DPL needs to be 3 + call initCallGate + call RING0_GATE|3:0 ; the RPL needs to be 3, the offset will be ignored. +.ring0: + add esp, 16 ; remove from stack CS:EIP+SS:ESP pushed by the CALL to RING0_GATE + ; restore ring 0 data segments saved by switchToRing3 + lds ebx, [cs:ptrTSSprot] + mov ax, [ebx+0x48] ; restore ES + mov es, ax + mov ax, [ebx+0x58] ; restore FS + mov fs, ax + mov ax, [ebx+0x5C] ; restore GS + mov gs, ax + mov ax, [ebx+0x54] ; restore DS + mov ds, ax + ; return to caller + push ecx + ret + ; ; Switches from Ring 3 to Ring 0 (flat user mode) ; @@ -275,7 +396,7 @@ switchToRing0FromFlatUser: and ecx,0xffff ;Make sure it's a 16-bit code offset. lfs ebx, [cs:ptrGDTUprot+0xF0000] mov eax, RING0_GATE - mov esi, C_SEG_PROT32 + mov esi, C_SEG_PROT32low mov edi, .ring0_fromflatuser mov dx, ACC_DPL_3 ; the DPL needs to be 3 call initCallGate diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm index 732b53e..2a9ab7a 100644 --- a/src/protected_tssinth.asm +++ b/src/protected_tssinth.asm @@ -261,4 +261,4 @@ realmodeInterrupt: jnz realmodeError pop ebx iret -BITS 32 +BITS 32 \ No newline at end of file diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index 13a3dfa..d0e0e3c 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -27,7 +27,15 @@ defGDTDescPrototype S_SEG_PROT32_R2 defGDTDescPrototype C_SEG_PROT16CS defGDTDescPrototype SU_SEG_PROT32 - + defGDTDescPrototype C_SEG_PROT32low + defGDTDescPrototype CC_SEG_PROT32low + defGDTDescPrototype CU_SEG_PROT32low + defGDTDescPrototype C_SEG_PROT32 + defGDTDescPrototype S_SEG_PROT32 + defGDTDescPrototype C_SEG_PROT32FLAT +;LDT entries + defLDTDescPrototype D_SEG_PROT32 + defLDTDescPrototype DU_SEG_PROT section .system_bios_extensions_area start=0x00000 ;Start of high BIOS ; TSS helper macros @@ -41,6 +49,10 @@ section .system_bios_extensions_area start=0x00000 ; %include "protected_tssh.asm" BITS 32 + ; + ; Interrupt handlers (installed during POST 8) + ; +%include "protected_inth.asm" ; ; 386 TSS user mode code ; @@ -415,7 +427,278 @@ nextRealModeInterruptSetError: push word 0xF000 push word POSTAreturnpoint retf + +test386POST20start: +;------------------------------------------------------------------------------- + POST 20 +;------------------------------------------------------------------------------- +; +; Test user mode (ring 3) switching +; + call far C_SEG_PROT32:clearTSSfar + mov ax, D_SEG_PROT32 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + pushfd + + ; Test I/O port access permissions + or word [esp], 0x3000 ; Make I/O ports available on user mode + popfd ; Make I/O ports availble now + call switchToRing3low ; Switch to user mode (ring 3) + in al, 0x64 ; Read some I/O port freely + call switchToRing0low ; Switch back to kernel mode (ring 0) + ; CS must be C_SEG_PROT32low|0 (CPL=0) + mov ax, cs + cmp ax, C_SEG_PROT32low + jne error + pushfd + and word [esp], 0xFFF ; Block ports on user mode again + popfd + call switchToRing3low ; back to user mode (ring 3) again + ; CS must be CU_SEG_PROT32low|3 (CPL=3) + mov ax, cs + cmp ax, CU_SEG_PROT32low|3 + jne error + ; data segments must be NULL + mov ax, ds + cmp ax, 0 + jne error + mov ax, es + cmp ax, 0 + jne error + mov ax, fs + cmp ax, 0 + jne error + mov ax, gs + cmp ax, 0 + jne error + + ; test privileged instructions in user mode (ring 3) + protModeFaultTestLow EX_GP, 0, cli + protModeFaultTestLow EX_GP, 0, hlt + protModeFaultTestLow EX_GP, 0, in al,0x64 + + ; test invalid interrupt call + protModeFaultTestLow EX_GP, 0x118|0x2, int 0x23 + + ; We should have the intial user mode stack setup right now for the below checks to validate. + call switchToRing0low ; back to kernel mode (ring 0) again + call switchToRing3low ; back to user mode (ring 3) again. Thus setting the Interrupt flag for our test. + + ; Interrupt from user mode to kernel mode using a 32-bit interrupt gate + %if ROM128 + int 0x20 +kernelModeInterruptReturn: + %endif + ; Interrupt from user mode to kernel using a 16-bit interrupt gate + int 0x27 +kernelModeInterruptReturn286: + ; Interrupt from user mode to kernel conforming + int 0x21 +kernelConformingInterruptReturn: + ; Interrupt from user mode to user mode + int 0x22 +userInterruptReturn: + call CU_SEG_PROT32low|3:userFarFunc ; User-mode far call test + jmp CU_SEG_PROT32low|3:userJmpFunc ; User-mode far jump test + +userFarFunc: + retf ; Simply return to the caller on the same privilege level + +userRetfErrorFunction: + ; From user mode to kernel mode error address, which isn't allowed. + push C_SEG_PROT32low + push error +userRetfErrorLocation: + retf +userRetfImmErrorFunction: + ; From user mode to kernel mode error address, with immediate, which isn't allowed. + push C_SEG_PROT32low + push error +userRetfImmErrorLocation: + retf 1 +userV86ExitFuncLocation: + bits 16 + push userV86ExitFuncRet ; Where to continue + jmp switchToRing0V86 + bits 32 +userV86IretInterruptRet: + bits 16 + push dword userV86IretExitFuncLocationRet + jmp switchToRing0V86 + bits 32 +userV86IretRealModeFunc: + bits 16 + ; Perform some pushf(d)/popf(d) with IOPL 3 test + pushf ; This doubles as a pushf IOPL 3 test + popf ; This doubles as a popf IOPL 3 test + pushfd ; This doubles as a pushfd IOPL3 test + popfd ; This doubles as a popfd IOPL3 test + pushf ; Real pushf we need for parameters now. + push cs + push word userV86IretInterruptRet + ; Also, STI/CLI are allowed in this case, perform the test here. + sti + cli + iret + bits 32 +userV86IretErrorFuncLocation: + bits 16 + push cs + push error +userV86IretErrorFuncLocationInstruction: + iret + jmp error +userV86IOInstruction0: + in al, 0x64 + jmp error + bits 32 + +userJmpFunc: + call switchToRing0low ; switch back to kernel mode (ring 0) + ; CS must be C_SEG_PROT32low|0 (CPL=0) + mov ax, cs + cmp ax, C_SEG_PROT32low + jne error + + ; Test call gates now + jmp testCallGateWithParameters ; Test call gate with parameters +ring0_2TestEndLocation: + + ; Perform some user mode exception tests + testUserFaultLow EX_GP, C_SEG_PROT32low, jmp C_SEG_PROT32low|3:0 ; Basic jump from user mode to kernel mode + testUserFaultLow EX_GP, C_SEG_PROT32low, call C_SEG_PROT32low|3:0 ; Basic call from user mode to kernel mode + testUserFaultExLow EX_GP, C_SEG_PROT32low, userRetfErrorLocation, jmp userRetfErrorFunction ; Far return from user mode to kernel mode + testUserFaultExLow EX_GP, C_SEG_PROT32low, userRetfImmErrorLocation, jmp userRetfImmErrorFunction ; Far return from user mode to kernel mode + + ; Test kernel mode only interrupt + mov eax, esp ; Save the stack pointer for us to check, as the interrupt doesn't have a comparison. + int 0x23 +kernelModeOnlyInterruptReturn: + + ; Interrupt from kernel mode to user mode is forbidden, so test for that. + protModeFaultTestLow EX_GP, CU_SEG_PROT32low, int 0x22 + + ; Interrupt from kernel mode to kernel mode (conforming) + mov eax, esp ; Save the stack pointer for us to check, as the interrupt doesn't have a comparison. + int 0x24 +kernelOnlyConformingInterruptReturn: + + ; Test user to kernel stack switch using different address spaces + ; Interrupt from user mode to kernel mode (flat address space) + call switchToRing3FLATkernel ;Switch to ring 3 with flat kernel stack prepared + int 0x26 +kernelModeInterruptKernelStackReturn: + push kernelModeInterruptKernelStackReturnPoint + call switchToRing0low + kernelModeInterruptKernelStackReturnPoint: + ; Back in normal 32-bit protected mode with 16-bit segment limits again + +;------------------------------------------------------------------------------- + POST 21 +;------------------------------------------------------------------------------- +; +; Test Virtual-8086 mode +; + + ; Check invalid virtual 8086 mode interrupts + ; interrupt without IOPL 3 faults with #GP(0) + testUserV86_0_Fault EX_GP, 0, int 0x22 + ; CLI/STI without IOPL 3 faults with #GP(0) + testUserV86_0_Fault EX_GP, 0, cli + testUserV86_0_Fault EX_GP, 0, sti + ; pushf(d) isn't allowed with IOPL 0 + testUserV86_0_Fault EX_GP, 0, pushf + testUserV86_0_Fault EX_GP, 0, pushfd + ; popf(d) isn't allowed with IOPL 0 + testUserV86_0_Fault EX_GP, 0, popf + testUserV86_0_Fault EX_GP, 0, popfd + ; port i/o isn't allowed with IOPL 0 and TSS I/O map set or out of range + testUserV86_0_FaultEx EX_GP, 0, userV86IOInstruction0, call userV86IOInstruction0 + + ; Manipulate TSS to allow port I/O for a bit. + push es + push ebp + les ebp, [cs:ptrTSSprot_R2] ; Load our TSS + mov word [es:ebp+0x66], 0 ; Make it available temporarily + mov word [es:ebp+0xC], 0 ; Make the port available + pop ebp + pop es + call switchToRing3V86_3 ; Switch to v86 mode to test + bits 16 + in al, 0x64 ; Some valid I/O port to use that is harmless. + push dword V86IOSucceedFinish ; Return to kernel mode + jmp switchToRing0V86 + bits 32 +V86IOSucceedFinish: + push es + push ebp + les ebp, [cs:ptrTSSprot_R2] ; Load our TSS + mov word [es:ebp+0x66], 0x68 ; Make the port unavailable again + pop ebp + pop es + + ; If we reach here, the return to kernel mode was successful. + + ; iret without IOPL 3 faults with #GP(0) + testUserV86_0_FaultEx EX_GP, 0, userV86IretErrorFuncLocationInstruction, call userV86IretErrorFuncLocation + ; interrupt with IOPL 3 to non-V86 privilege level 0 faults with #GP(usermodesegment) + testUserV86_3_Fault EX_GP, CU_SEG_PROT32low, int 0x22 + ; interrupt with IOPL 3 to non-V86 monitor privilege level 0 faults with #GP(kernel conforming segment) + testUserV86_3_Fault EX_GP, CC_SEG_PROT32low, int 0x21 + ; HLT is privileged and raises #GP(0) no matter what IOPL is used + testUserV86_3_Fault EX_GP, 0, hlt + testUserV86_0_Fault EX_GP, 0, hlt + + ; iret with IOPL 3 proceeds as in real mode + + ; Validate simply exiting Virtual 8086 mode, using the interrupt + call switchToRing3V86_3 + bits 16 + int 0x2D ;Validate we're actually in V86 mode. + jmp userV86IretRealModeFunc + jmp error + bits 32 +userV86IretExitFuncLocationRet: + + ; Validate simply exiting Virtual 8086 mode, using the interrupt + call switchToRing3V86_3 + bits 16 + jmp userV86ExitFuncLocation + jmp error + bits 32 +errorInTSS32Load: + mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value + mov ss,ax + mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer + jmp error ;Error out! + +userV86ExitFuncRet: + ;Now we're going to test 16-bit interrupts in Virtual 8086 mode. + %if ROM128 + call switchToRing3V86_3 + BITS 16 + int 0x2E ;Verify 16-bit interrupts in Virtual 8086 mode + userV86_16bitinterruptRET: + push dword userV86ExitFuncRet_16bitinterrupts ; Where to continue + jmp switchToRing0V86 +userV86ExitFuncRet_16bitinterrupts: + %endif + BITS 32 + pushfd + pop eax + and eax,0xCDFF ;Clear IOPL and interrupt flag again, as it cannot be changed in user mode + push eax + popfd + +;Return to the high BIOS to continue testing. + push C_SEG_PROT32 + push test386POST21end + retf + ;End of high BIOS ;Pad to 64KB times 0x10000-($-$$) nop diff --git a/src/test386.asm b/src/test386.asm index 79b2d32..f70417f 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -328,9 +328,13 @@ initGDT: defGDTDescImplementation S_SEG_PROT32_R2, 0x00010000,0x0000ffff,ACC_DPL_2|ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDescImplementation C_SEG_PROT16CS, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDescImplementation SU_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation C_SEG_PROT32low, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT + defGDTDescImplementation CC_SEG_PROT32low, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT + defGDTDescImplementation CU_SEG_PROT32low, 0x000e0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT|ACC_DPL_3,EXT_32BIT + defGDTDescImplementation C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT + defGDTDescImplementation S_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT + defGDTDescImplementation C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT - defGDTDesc C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT - defGDTDesc C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 @@ -338,7 +342,6 @@ initGDT: defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc S_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 @@ -452,44 +455,72 @@ initIDT: mov eax, 0x20 call initIntGateReal ; Interrupt 21h: conforming kernel interrupt, callable from user mode - mov esi, CC_SEG_PROT32 + mov esi, CC_SEG_PROT32low + %if ROM128 mov edi, kernelConformingInterrupt + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif inc eax call initIntGateReal ; Interrupt 22h: user mode only interrupt - mov esi, CU_SEG_PROT32 + mov esi, CU_SEG_PROT32low + %if ROM128 mov edi, userModeInterrupt + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif inc eax call initIntGateReal ; Interrupt 23h: kernel mode only interrupt - mov esi, C_SEG_PROT32 + mov esi, C_SEG_PROT32low + %if ROM128 mov edi, kernelOnlyInterrupt + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif mov dx, ACC_DPL_0 inc eax call initIntGateReal ; Interrupt 24h: kernel mode only conforming interrupt - mov esi, CC_SEG_PROT32 + mov esi, CC_SEG_PROT32low + %if ROM128 mov edi, kernelOnlyConformingInterrupt + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif mov dx, ACC_DPL_0 inc eax call initIntGateReal ; Interrupt 25h: kernel mode interrupt, callable from user mode. Switches out of V86 mode - mov esi, C_SEG_PROT32 + mov esi, C_SEG_PROT32low + %if ROM128 mov edi, V86ModeExitInterrupt + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif mov dx, ACC_DPL_3 inc eax call initIntGateReal ; Interrupt 26h: non-conforming kernel interrupt, flat memory, callable from user mode. Restores kernel stack to 32-bit mov esi, C_SEG_PROT32FLAT + %if ROM128 mov edi, kernelInterruptRestoreKernelStack - or edi,0xE00F0000 ;Make sure that the offset is located on a valid 32-bit mapped address by mapping the high 16 bits. + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif + or edi,0xE00E0000 ;Make sure that the offset is located on a valid 32-bit mapped address by mapping the high 16 bits. mov dx, ACC_DPL_3 inc eax call initIntGateReal ; Interrupt 27h: non-conforming 286 kernel interrupt, callable from user mode. - mov esi, C_SEG_PROT32 + mov esi, C_SEG_PROT32low + %if ROM128 mov edi, kernelInterrupt286 + %else + mov edi, 0xFFFFFFFF ;Unused, placeholder + %endif or edi,0xFFFF0000 ;Make sure that the offset is located on a invalid 32-bit mapped address by mapping the high 16 bits, which are not to be used. mov dx, ACC_DPL_3 inc eax @@ -545,8 +576,10 @@ initIDT: call initIntGateReal ; Interrupt 2D: validate V86 mode - mov esi, C_SEG_PROT32 + mov esi, C_SEG_PROT32low + %if ROM128 mov edi, kernelInterrupt_validateIsV86mode + %endif mov dx, ACC_DPL_3 inc eax call initIntGateReal @@ -656,9 +689,9 @@ toProt32: %include "protected_p.asm" initLDT: + defLDTDescImplementation D_SEG_PROT32, TEST_BASE, 0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT + defLDTDescImplementation DU_SEG_PROT, TEST_BASE, 0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defLDTDesc D_SEG_PROT16, TEST_BASE, 0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT - defLDTDesc D_SEG_PROT32, TEST_BASE, 0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT - defLDTDesc DU_SEG_PROT, TEST_BASE, 0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defLDTDesc D1_SEG_PROT, TEST_BASE1,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT defLDTDesc D2_SEG_PROT, TEST_BASE2,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT defLDTDesc DC_SEG_PROT32, TEST_BASE1,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT @@ -775,275 +808,13 @@ protTests: advTestSegProt -;------------------------------------------------------------------------------- - POST 20 -;------------------------------------------------------------------------------- -; -; Test user mode (ring 3) switching -; - call clearTSS - mov ax, D_SEG_PROT32 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - pushfd - - ; Test I/O port access permissions - or word [esp], 0x3000 ; Make I/O ports available on user mode - popfd ; Make I/O ports availble now - call switchToRing3 ; Switch to user mode (ring 3) - in al, 0x64 ; Read some I/O port freely - call switchToRing0 ; Switch back to kernel mode (ring 0) - ; CS must be C_SEG_PROT32|0 (CPL=0) - mov ax, cs - cmp ax, C_SEG_PROT32 - jne error - pushfd - and word [esp], 0xFFF ; Block ports on user mode again - popfd - call switchToRing3 ; back to user mode (ring 3) again - ; CS must be CU_SEG_PROT32|3 (CPL=3) - mov ax, cs - cmp ax, CU_SEG_PROT32|3 - jne error - ; data segments must be NULL - mov ax, ds - cmp ax, 0 - jne error - mov ax, es - cmp ax, 0 - jne error - mov ax, fs - cmp ax, 0 - jne error - mov ax, gs - cmp ax, 0 - jne error - - ; test privileged instructions in user mode (ring 3) - protModeFaultTest EX_GP, 0, cli - protModeFaultTest EX_GP, 0, hlt - protModeFaultTest EX_GP, 0, in al,0x64 - - ; test invalid interrupt call - protModeFaultTest EX_GP, 0x118|0x2, int 0x23 - - ; We should have the intial user mode stack setup right now for the below checks to validate. - call switchToRing0 ; back to kernel mode (ring 0) again - call switchToRing3 ; back to user mode (ring 3) again. Thus setting the Interrupt flag for our test. - - ; Interrupt from user mode to kernel mode using a 32-bit interrupt gate - %if ROM128 - int 0x20 -kernelModeInterruptReturn: - %endif - ; Interrupt from user mode to kernel using a 16-bit interrupt gate - int 0x27 - -kernelModeInterruptReturn286: - ; Interrupt from user mode to kernel conforming - int 0x21 -kernelConformingInterruptReturn: - ; Interrupt from user mode to user mode - int 0x22 -userInterruptReturn: - call CU_SEG_PROT32|3:userFarFunc ; User-mode far call test - jmp CU_SEG_PROT32|3:userJmpFunc ; User-mode far jump test - -userFarFunc: - retf ; Simply return to the caller on the same privilege level - - ; - ; Interrupt handlers (installed during POST 8) - ; -%include "protected_inth.asm" - -userRetfErrorFunction: - ; From user mode to kernel mode error address, which isn't allowed. - push C_SEG_PROT32 - push error -userRetfErrorLocation: - retf -userRetfImmErrorFunction: - ; From user mode to kernel mode error address, with immediate, which isn't allowed. - push C_SEG_PROT32 - push error -userRetfImmErrorLocation: - retf 1 -userV86ExitFuncLocation: - bits 16 - push userV86ExitFuncRet ; Where to continue - jmp switchToRing0V86 - bits 32 -userV86IretInterruptRet: - bits 16 - push dword userV86IretExitFuncLocationRet - jmp switchToRing0V86 - bits 32 -userV86IretRealModeFunc: - bits 16 - ; Perform some pushf(d)/popf(d) with IOPL 3 test - pushf ; This doubles as a pushf IOPL 3 test - popf ; This doubles as a popf IOPL 3 test - pushfd ; This doubles as a pushfd IOPL3 test - popfd ; This doubles as a popfd IOPL3 test - pushf ; Real pushf we need for parameters now. - push cs - push word userV86IretInterruptRet - ; Also, STI/CLI are allowed in this case, perform the test here. - sti - cli - iret - bits 32 -userV86IretErrorFuncLocation: - bits 16 - push cs - push error -userV86IretErrorFuncLocationInstruction: - iret - jmp error -userV86IOInstruction0: - in al, 0x64 - jmp error - bits 32 - -userJmpFunc: - call switchToRing0 ; switch back to kernel mode (ring 0) - ; CS must be C_SEG_PROT32|0 (CPL=0) - mov ax, cs - cmp ax, C_SEG_PROT32 - jne error - - ; Test call gates now - jmp testCallGateWithParameters ; Test call gate with parameters -ring0_2TestEndLocation: - - ; Perform some user mode exception tests - testUserFault EX_GP, C_SEG_PROT32, jmp C_SEG_PROT32|3:0 ; Basic jump from user mode to kernel mode - testUserFault EX_GP, C_SEG_PROT32, call C_SEG_PROT32|3:0 ; Basic call from user mode to kernel mode - testUserFaultEx EX_GP, C_SEG_PROT32, userRetfErrorLocation, jmp userRetfErrorFunction ; Far return from user mode to kernel mode - testUserFaultEx EX_GP, C_SEG_PROT32, userRetfImmErrorLocation, jmp userRetfImmErrorFunction ; Far return from user mode to kernel mode - - ; Test kernel mode only interrupt - mov eax, esp ; Save the stack pointer for us to check, as the interrupt doesn't have a comparison. - int 0x23 -kernelModeOnlyInterruptReturn: - - ; Interrupt from kernel mode to user mode is forbidden, so test for that. - protModeFaultTest EX_GP, CU_SEG_PROT32, int 0x22 - - ; Interrupt from kernel mode to kernel mode (conforming) - mov eax, esp ; Save the stack pointer for us to check, as the interrupt doesn't have a comparison. - int 0x24 -kernelOnlyConformingInterruptReturn: - - ; Test user to kernel stack switch using different address spaces - ; Interrupt from user mode to kernel mode (flat address space) - call switchToRing3FLATkernel ;Switch to ring 3 with flat kernel stack prepared - int 0x26 -kernelModeInterruptKernelStackReturn: - push kernelModeInterruptKernelStackReturnPoint - call switchToRing0 - kernelModeInterruptKernelStackReturnPoint: - ; Back in normal 32-bit protected mode with 16-bit segment limits again - -;------------------------------------------------------------------------------- - POST 21 -;------------------------------------------------------------------------------- -; -; Test Virtual-8086 mode -; - - ; Check invalid virtual 8086 mode interrupts - ; interrupt without IOPL 3 faults with #GP(0) - testUserV86_0_Fault EX_GP, 0, int 0x22 - ; CLI/STI without IOPL 3 faults with #GP(0) - testUserV86_0_Fault EX_GP, 0, cli - testUserV86_0_Fault EX_GP, 0, sti - ; pushf(d) isn't allowed with IOPL 0 - testUserV86_0_Fault EX_GP, 0, pushf - testUserV86_0_Fault EX_GP, 0, pushfd - ; popf(d) isn't allowed with IOPL 0 - testUserV86_0_Fault EX_GP, 0, popf - testUserV86_0_Fault EX_GP, 0, popfd - ; port i/o isn't allowed with IOPL 0 and TSS I/O map set or out of range - testUserV86_0_FaultEx EX_GP, 0, userV86IOInstruction0, call userV86IOInstruction0 - - ; Manipulate TSS to allow port I/O for a bit. - push es - push ebp - les ebp, [cs:ptrTSSprot] ; Load our TSS - mov word [es:ebp+0x66], 0 ; Make it available temporarily - mov word [es:ebp+0xC], 0 ; Make the port available - pop ebp - pop es - call switchToRing3V86_3 ; Switch to v86 mode to test - bits 16 - in al, 0x64 ; Some valid I/O port to use that is harmless. - push dword V86IOSucceedFinish ; Return to kernel mode - jmp switchToRing0V86 - bits 32 -V86IOSucceedFinish: - push es - push ebp - les ebp, [cs:ptrTSSprot] ; Load our TSS - mov word [es:ebp+0x66], 0x68 ; Make the port unavailable again - pop ebp - pop es - - ; If we reach here, the return to kernel mode was successful. - - ; iret without IOPL 3 faults with #GP(0) - testUserV86_0_FaultEx EX_GP, 0, userV86IretErrorFuncLocationInstruction, call userV86IretErrorFuncLocation - ; interrupt with IOPL 3 to non-V86 privilege level 0 faults with #GP(usermodesegment) - testUserV86_3_Fault EX_GP, CU_SEG_PROT32, int 0x22 - ; interrupt with IOPL 3 to non-V86 monitor privilege level 0 faults with #GP(kernel conforming segment) - testUserV86_3_Fault EX_GP, CC_SEG_PROT32, int 0x21 - ; HLT is privileged and raises #GP(0) no matter what IOPL is used - testUserV86_3_Fault EX_GP, 0, hlt - testUserV86_0_Fault EX_GP, 0, hlt - - ; iret with IOPL 3 proceeds as in real mode - - ; Validate simply exiting Virtual 8086 mode, using the interrupt - call switchToRing3V86_3 - bits 16 - int 0x2D ;Validate we're actually in V86 mode. - jmp userV86IretRealModeFunc - jmp error - bits 32 -userV86IretExitFuncLocationRet: - - ; Validate simply exiting Virtual 8086 mode, using the interrupt - call switchToRing3V86_3 - bits 16 - jmp userV86ExitFuncLocation - jmp error - bits 32 -errorInTSS32Load: - mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value - mov ss,ax - mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer - jmp error ;Error out! - -userV86ExitFuncRet: - ;Now we're going to test 16-bit interrupts in Virtual 8086 mode. + ;Run POST 20 from the lower ROM, if enabled. %if ROM128 - call switchToRing3V86_3 - BITS 16 - int 0x2E ;Verify 16-bit interrupts in Virtual 8086 mode - userV86_16bitinterruptRET: - push dword userV86ExitFuncRet_16bitinterrupts ; Where to continue - jmp switchToRing0V86 -userV86ExitFuncRet_16bitinterrupts: + push C_SEG_PROT32low + push test386POST20start + retf ;Jump to the entry point of the test. + test386POST21end: %endif - BITS 32 - pushfd - pop eax - and eax,0xCDFF ;Clear IOPL and interrupt flag again, as it cannot be changed in user mode - push eax - popfd ;------------------------------------------------------------------------------- POST 22 diff --git a/src/tss_p.asm b/src/tss_p.asm index db62da0..bc9e128 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -72,6 +72,10 @@ clearTSS: pop ebp ret +;Far call version of clearTSS +clearTSSfar: + call clearTSS + retf ; Prepare 32-bit TSS for task switching From 1237a1e689a68a2b4b046cb3ce5b6c3c32c8f686 Mon Sep 17 00:00:00 2001 From: superfury Date: Tue, 2 Jun 2026 00:54:32 +0200 Subject: [PATCH 61/62] - Moved the protected mode tests properly into the low 64K of the 128K ROM. --- src/protected_inth.asm | 6 +- src/protected_m.asm | 35 +-- src/protected_p.asm | 12 + src/protected_rings_p.asm | 420 +---------------------------- src/protected_rings_tssp.asm | 462 ++++++++++++++++++++++++++++++++ src/protected_tssinth.asm | 42 ++- src/systembiosexpansionarea.asm | 42 +-- src/test386.asm | 15 +- 8 files changed, 561 insertions(+), 473 deletions(-) create mode 100644 src/protected_rings_tssp.asm diff --git a/src/protected_inth.asm b/src/protected_inth.asm index b198a21..453d53e 100644 --- a/src/protected_inth.asm +++ b/src/protected_inth.asm @@ -6,7 +6,7 @@ kernelInterrupt286: push ebx push ecx push ds - lds ebx, [cs:ptrTSSprot] ; Get the TSS + lds ebx, [cs:ptrTSSprot_R0] ; Get the TSS mov ecx, [ebx+4] ; Get TSS ESP0 sub ecx, 0x0A+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed cmp esp, ecx ; Did the stack decrease correctly? @@ -77,7 +77,7 @@ kernelOnlyInterrupt: push ebx push ds push ecx - lds ebx, [cs:ptrTSSprot] ; Get the TSS + lds ebx, [cs:ptrTSSprot_R0] ; Get the TSS mov ecx, eax ; Get ESP for the kernel mode program (stored into eax) sub ecx, 0xC+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed cmp esp, ecx ; Did the stack decrease correctly? @@ -138,7 +138,7 @@ kernelOnlyConformingInterrupt: push ebx push ds push ecx - lds ebx, [cs:ptrTSSprot] ; Get the TSS + lds ebx, [cs:ptrTSSprot_R0] ; Get the TSS mov ecx, eax ; Get ESP for the kernel mode program (stored into eax) sub ecx, 0xC+0xC ; Where we should end up on the kernel stack, taking into account what we just pushed cmp esp, ecx ; Did the stack decrease correctly? diff --git a/src/protected_m.asm b/src/protected_m.asm index 5e4afd1..975b416 100644 --- a/src/protected_m.asm +++ b/src/protected_m.asm @@ -240,6 +240,10 @@ lss esp, [cs:ptrSSprot] %endmacro +%macro loadProtModeStackLow 0 + lss esp, [cs:ptrSSprot_R0] +%endmacro + ; ; Set a int gate on the IDT in protected mode @@ -323,12 +327,13 @@ test cx, 7 jnz %%ring3 %%ring0: - lds ebx, [cs:ptrIDTprot] + lds ebx, [cs:ptrIDTprot_R0] + call far C_SEG_PROT32:initIntGateProtFar jmp %%call %%ring3: - lds ebx, [cs:ptrIDTUprot] + lds ebx, [cs:ptrIDTUprot_R3] + call far CU_SEG_PROT32|3:initIntGateProtFar %%call: - call initIntGateProt pop ax mov ds, ax ; restore ds popf @@ -355,13 +360,13 @@ %endmacro %macro protModeFaultTestLow 3+ - setProtModeIntGate %1, %%continue + setProtModeIntGateLow %1, %%continue %%test: %3 jmp error %%continue: protModeExcCheckLow %1, %2, %%test - setProtModeIntGate %1, DefaultExcHandlerLow, ACC_DPL_0 + setProtModeIntGateLow %1, DefaultExcHandlerLow, ACC_DPL_0 %endmacro ; %1: vector @@ -400,7 +405,7 @@ ; The fault handler is executed in ring 0. The caller must reset the data segments. ; %macro protModeFaultTestExLow 5+ - setProtModeIntGate %1, %%continue, ACC_DPL_0 + setProtModeIntGateLow %1, %%continue, ACC_DPL_0 %%test: %5 jmp error @@ -415,7 +420,7 @@ %else protModeExcCheckLow %1, %2, %4, expectedCS %endif - setProtModeIntGate %1, DefaultExcHandlerLow, ACC_DPL_0 + setProtModeIntGateLow %1, DefaultExcHandlerLow, ACC_DPL_0 %endmacro ; %1: vector @@ -427,7 +432,7 @@ ; The fault handler is executed in ring 0. The caller must reset the data segments. ; %macro protModeFaultTestExV86 5+ - setProtModeIntGate %1, %%continue, ACC_DPL_0 + setProtModeIntGateLow %1, %%continue, ACC_DPL_0 %%test: %5 jmp error @@ -443,7 +448,7 @@ protModeExcCheckV86 %1, %2, %4, expectedCS %endif call switchedToRing0V86_cleanup ;Cleanup the user-mode stack and restore segment registers for kernel mode - setProtModeIntGate %1, DefaultExcHandlerLow, ACC_DPL_0 + setProtModeIntGateLow %1, DefaultExcHandlerLow, ACC_DPL_0 %endmacro ; Tests an user-mode exception @@ -477,7 +482,7 @@ jmp %%startinglabel %3 jmp error %%startinglabel: - loadProtModeStack + loadProtModeStackLow protModeFaultTestExLow %1, %2, 3, %%instructionlabel, call %%usercodelabel testCPL 0 %endmacro @@ -496,7 +501,7 @@ jmp %%startinglabel jmp error bits 32 %%startinglabel: - loadProtModeStack + loadProtModeStackLow protModeFaultTestExV86 %1, %2, 3, %%instructionlabel, call %%usercodelabel testCPL 0 %endmacro @@ -515,7 +520,7 @@ jmp %%startinglabel jmp error bits 32 %%startinglabel: - loadProtModeStack + loadProtModeStackLow protModeFaultTestExV86 %1, %2, 3, %%instructionlabel, call %%usercodelabel testCPL 0 %endmacro @@ -556,7 +561,7 @@ jmp %%startinglabel %4 jmp error %%startinglabel: - loadProtModeStack + loadProtModeStackLow %if %3 = -1 protModeFaultTestExLow %1, %2, 3, %%instructionlabel, call %%usercodelabel %else @@ -580,7 +585,7 @@ jmp %%startinglabel jmp error bits 32 %%startinglabel: - loadProtModeStack + loadProtModeStackLow %if %3 = -1 protModeFaultTestExV86 %1, %2, 3, %%instructionlabel, call %%usercodelabel %else @@ -604,7 +609,7 @@ jmp %%startinglabel jmp error bits 32 %%startinglabel: - loadProtModeStack + loadProtModeStackLow %if %3 = -1 protModeFaultTestExV86 %1, %2, 3, %%instructionlabel, call %%usercodelabel %else diff --git a/src/protected_p.asm b/src/protected_p.asm index be74bd2..0934f7f 100644 --- a/src/protected_p.asm +++ b/src/protected_p.asm @@ -4,6 +4,10 @@ initIntGateProt: initIntGate ret +initIntGateProtFar: + call initIntGate + retf + initIntTaskGateProt: initIntTaskGate ret @@ -45,6 +49,10 @@ initCallGate: mov word [fs:ebx+6], di ; DESTINATION OFFSET 31-16 ret +initCallGateFar: + call initCallGate + retf + ; ; Defines a Call Gate in GDT ; @@ -76,3 +84,7 @@ initCallGate286: mov word [fs:ebx+4], dx ; ACC byte | WORD COUNT 4-0 mov word [fs:ebx+6], 0xFFFF ; DESTINATION OFFSET 31-16 (isn't used. Load invalid linear address value to validate it) ret + +initCallGate286Far: + call initCallGate286 + retf diff --git a/src/protected_rings_p.asm b/src/protected_rings_p.asm index 1ddca40..7f3b9af 100644 --- a/src/protected_rings_p.asm +++ b/src/protected_rings_p.asm @@ -41,49 +41,6 @@ switchToRing3: push dword edx ; push return EIP iretd -; -; Switches from Ring 0 to Ring 3 -; -; After calling this procedure consider all the registers and flags as trashed. -; Also, the stack will be different, so saving the CPU state there will be pointless. -; -switchToRing3low: - ; In order to swich to user mode (ring 3) we need to execute an IRET with these - ; values on the stack: - ; - the instruction to continue execution at - the value of EIP. - ; - the code segment selector to change to. - ; - the value of the EFLAGS register to load. - ; - the stack pointer to load. - ; - the stack segment selector to change to. - ; We also need: - ; - a 32bit code descriptor in GDT with DPL 3 - ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) - ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 - testCPL 0 ; we must be in ring 0 - pop edx ; read the return offset - mov ax, ds - lds ebx, [cs:ptrTSSprot] - ; save ring 0 data segments, they'll be restored with switchToRing0 - mov [ebx+0x54], ax ; save DS - mov ax, es - mov [ebx+0x48], ax ; save ES - mov ax, fs - mov [ebx+0x58], ax ; save FS - mov ax, gs - mov [ebx+0x5C], ax ; save GS - ; set ring 0 SS:ESP - mov [ebx+4], esp - mov eax, ss - mov [ebx+8], eax - cli ; disable ints during switching - push dword SU_SEG_PROT32|3 ; push user stack with RPL=3 - push dword ESP_R3_PROT ; push user mode esp - pushfd ; push eflags - or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) - push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 - push dword edx ; push return EIP - iretd - ; ; Switches from Ring 0 to Ring 3, to return to ring 0 in flat mode later ; @@ -131,51 +88,6 @@ switchToRing3FLATkernel: iretd ; -; Switches from Ring 0 to Ring 3, to return to ring 0 in flat mode later -; -; After calling this procedure consider all the registers and flags as trashed. -; Also, the stack will be different, so saving the CPU state there will be pointless. -; -switchToRing3FLATkernelLow: - ; In order to swich to user mode (ring 3) we need to execute an IRET with these - ; values on the stack: - ; - the instruction to continue execution at - the value of EIP. - ; - the code segment selector to change to. - ; - the value of the EFLAGS register to load. - ; - the stack pointer to load. - ; - the stack segment selector to change to. - ; We also need: - ; - a 32bit code descriptor in GDT with DPL 3 - ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) - ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 - testCPL 0 ; we must be in ring 0 - pop edx ; read the return offset - mov ax, ds - lds ebx, [cs:ptrTSSprot] - ; save ring 0 data segments, they'll be restored with switchToRing0 - mov [ebx+0x54], ax ; save DS - mov ax, es - mov [ebx+0x48], ax ; save ES - mov ax, fs - mov [ebx+0x58], ax ; save FS - mov ax, gs - mov [ebx+0x5C], ax ; save GS - ; set ring 0 SS:ESP - mov [ebx+4], esp - pushfd - or dword [ebx+4], 0xE0010000 ; make sure we return to a proper ring0 stack - popfd - mov eax, D_SEG_PROT32FLAT - mov [ebx+8], eax - cli ; disable ints during switching - push dword SU_SEG_PROT32|3 ; push user stack with RPL=3 - push dword ESP_R3_PROT ; push user mode esp - pushfd ; push eflags - or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) - push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 - push dword edx ; push return EIP - iretd -; ; Switches from Ring 0 to Ring 3 in flat mode ; ; After calling this procedure consider all the registers and flags as trashed. @@ -219,104 +131,6 @@ switchToRing3FLATuser: push dword edx ; push return EIP iretd - -; -; Switches from Ring 0 to Ring 3 in V86 mode -; -; After calling this procedure consider all the registers and flags as trashed. -; Also, the stack will be different, so saving the CPU state there will be pointless. -; -switchToRing3V86_0: - ; In order to swich to user mode (ring 3) we need to execute an IRET with these - ; values on the stack: - ; - the instruction to continue execution at - the value of EIP. - ; - the code segment selector to change to. - ; - the value of the EFLAGS register to load. - ; - the stack pointer to load. - ; - the stack segment selector to change to. - ; We also need: - ; - a 32bit code descriptor in GDT with DPL 3 - ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) - ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 - testCPL 0 ; we must be in ring 0 - pop edx ; read the return offset - mov ax, ds - lds ebx, [cs:ptrTSSprot] - ; save ring 0 data segments, they'll be restored with interrupts/exceptions - mov [ebx+0x54], ax ; save DS - mov ax, es - mov [ebx+0x48], ax ; save ES - mov ax, fs - mov [ebx+0x58], ax ; save FS - mov ax, gs - mov [ebx+0x5C], ax ; save GS - ; set ring 0 SS:ESP - mov [ebx+4], esp - mov eax, ss - mov [ebx+8], eax - cli ; disable ints during switching - push dword V86_GS ; V86 mode GS default to ROM - push dword V86_FS ; V86 mode FS default to ROM - push dword V86_DS ; V86 mode DS default to ROM - push dword V86_ES ; V86 mode ES default to ROM - push dword 0x1000 ; push user stack with RPL=3 - push dword ESP_R3_PROT ; push user mode esp - pushfd ; push eflags - or dword [ss:esp], 0x20200 ; reenable interrupts in ring 3 (can't use privileged sti), V8086 flag set, IOPL 0 - and dword [ss:esp], 0xF0FFF ; setup IOPL 0 properly - push dword 0xE000 ; push user code segment with RPL=3 - push dword edx ; push return EIP - iretd - - -; -; Switches from Ring 0 to Ring 3 in V86 mode -; -; After calling this procedure consider all the registers and flags as trashed. -; Also, the stack will be different, so saving the CPU state there will be pointless. -; -switchToRing3V86_3: - ; In order to swich to user mode (ring 3) we need to execute an IRET with these - ; values on the stack: - ; - the instruction to continue execution at - the value of EIP. - ; - the code segment selector to change to. - ; - the value of the EFLAGS register to load. - ; - the stack pointer to load. - ; - the stack segment selector to change to. - ; We also need: - ; - a 32bit code descriptor in GDT with DPL 3 - ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) - ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 - testCPL 0 ; we must be in ring 0 - pop edx ; read the return offset - mov ax, ds - lds ebx, [cs:ptrTSSprot] - ; save ring 0 data segments, they'll be restored with interrupts/exceptions - mov [ebx+0x54], ax ; save DS - mov ax, es - mov [ebx+0x48], ax ; save ES - mov ax, fs - mov [ebx+0x58], ax ; save FS - mov ax, gs - mov [ebx+0x5C], ax ; save GS - ; set ring 0 SS:ESP - mov [ebx+4], esp - mov eax, ss - mov [ebx+8], eax - cli ; disable ints during switching - push dword V86_GS ; V86 mode GS default to ROM - push dword V86_FS ; V86 mode FS default to ROM - push dword V86_DS ; V86 mode DS default to ROM - push dword V86_ES ; V86 mode ES default to ROM - push dword 0x1000 ; push user stack with RPL=3 - push dword ESP_R3_PROT ; push user mode esp - pushfd ; push eflags - or dword [ss:esp], 0x23200 ; reenable interrupts in ring 3 (can't use privileged sti), V8086 flag set, IOPL 3 - push dword 0xE000 ; push user code segment with RPL=3 - push dword edx ; push return EIP - iretd - - ; ; Switches from Ring 3 to Ring 0 (Non-V86 mode) ; @@ -350,39 +164,6 @@ switchToRing0: push ecx ret -; -; Switches from Ring 3 to Ring 0 (Non-V86 mode) -; -; After calling this procedure consider all the registers and flags as trashed. -; -switchToRing0low: - testCPL 3 ; we must be in ring 3 - ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. - ; A placeholder for a Call Gate is already present in the GDT. - pop ecx ; read the return offset - lfs ebx, [cs:ptrGDTUprot] - mov eax, RING0_GATE - mov esi, C_SEG_PROT32low - mov edi, .ring0 - mov dx, ACC_DPL_3 ; the DPL needs to be 3 - call initCallGate - call RING0_GATE|3:0 ; the RPL needs to be 3, the offset will be ignored. -.ring0: - add esp, 16 ; remove from stack CS:EIP+SS:ESP pushed by the CALL to RING0_GATE - ; restore ring 0 data segments saved by switchToRing3 - lds ebx, [cs:ptrTSSprot] - mov ax, [ebx+0x48] ; restore ES - mov es, ax - mov ax, [ebx+0x58] ; restore FS - mov fs, ax - mov ax, [ebx+0x5C] ; restore GS - mov gs, ax - mov ax, [ebx+0x54] ; restore DS - mov ds, ax - ; return to caller - push ecx - ret - ; ; Switches from Ring 3 to Ring 0 (flat user mode) ; @@ -396,7 +177,7 @@ switchToRing0FromFlatUser: and ecx,0xffff ;Make sure it's a 16-bit code offset. lfs ebx, [cs:ptrGDTUprot+0xF0000] mov eax, RING0_GATE - mov esi, C_SEG_PROT32low + mov esi, C_SEG_PROT32 mov edi, .ring0_fromflatuser mov dx, ACC_DPL_3 ; the DPL needs to be 3 call initCallGate @@ -408,190 +189,6 @@ switchToRing0FromFlatUser: push ecx ret - -; -; Test routine for call gate with parameters -; -testCallGateWithParameters: - push dword switchToRing0_2 ; Where to start - jmp switchToRing3 ; Switch to ring 3 to start the test - - -; -; Switches from Ring 3 to Ring 0 (Non-V86 mode) and calls back into kernel mode with parameters -; -; After calling this procedure consider all the registers and flags as trashed. -; -switchToRing0_2: - testCPL 3 ; we must be in ring 3 - ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. - ; A placeholder for a Call Gate is already present in the GDT. - mov ecx, ring0_2TestEndLocation ; read the return offset - ;Setup a 32-bit call gate - lfs ebx, [cs:ptrGDTUprot] - mov eax, RING0_GATE2 - mov esi, C_SEG_PROT32 - mov edi, .ring0_2 - mov dx, ACC_DPL_3|0xA ; the DPL needs to be 3 - call initCallGate - - push ecx ;Save caller - - ; Create a stack of 32-bit test values to transfer using the call gate - push dword 0x12347654 - push dword 0x5678CBA9 - push dword 0x9ABC3333 - push dword 0xDEF02222 - push dword 0x11221111 - push dword 0x33447777 - push dword 0x55665555 - push dword 0x7788AAAA - push dword 0x99AA4444 - push dword 0xBBCCFFFF - - ; 32-bit call gate - call RING0_GATE2|3:0 ; the RPL needs to be 3, the offset will be ignored. - - ;Setup a 16-bit call gate - lfs ebx, [cs:ptrGDTUprot] - mov eax, RING0_GATE2 - mov esi, C_SEG_PROT32 - mov edi, .ring0_3 - mov dx, ACC_DPL_3|0xA ; the DPL needs to be 3 - call initCallGate286 - - ; Create a stack of 16-bit test values to transfer using the call gate - push word 0x1234 - push word 0x5678 - push word 0x9ABC - push word 0xDEF0 - push word 0x1122 - push word 0x3344 - push word 0x5566 - push word 0x7788 - push word 0x99AA - push word 0xBBCC - - ; 16-bit call gate - call RING0_GATE2|3:0 ; the RPL needs to be 3, the offset will be ignored. - - pop ecx ; Restore caller - call RING0_GATE|3:0 ; the RPL needs to be 3, the offset will be ignored. - - ; This time we return to the caller proper -.ring0_2: ;32-bit call gate entry point - push ds - push ebp - push ebx - lds ebp, [cs:ptrTSSprot] - mov ebx, [ds:ebp+4] ; Get the base - sub ebx, 0x10+0xC+40 ; Where we should end up in the kernel stack now - cmp esp, ebx ; Wrong kernel stack? - jnz error - mov ebx, [ds:ebp+8] ; Get the stack - mov bp, ss - cmp bx, bp - jnz error ; Invalid kernel stack - pop ebx - pop ebp - pop ds - push eax - mov ax, cs - cmp ax, C_SEG_PROT32 - jnz error - pop eax - ; Validate the parameters on the kernel stack - cmp [esp+0x2C], dword 0x12347654 - jne error - cmp [esp+0x28], dword 0x5678CBA9 - jne error - cmp [esp+0x24], dword 0x9ABC3333 - jne error - cmp [esp+0x20], dword 0xDEF02222 - jne error - cmp [esp+0x1C], dword 0x11221111 - jne error - cmp [esp+0x18], dword 0x33447777 - jne error - cmp [esp+0x14], dword 0x55665555 - jne error - cmp [esp+0x10], dword 0x7788AAAA - jne error - cmp [esp+0x0C], dword 0x99AA4444 - jne error - cmp [esp+0x08], dword 0xBBCCFFFF - jne error - retf 40 ; Return to the user mode code, discarding the parameters from the stack. -.ring0_3: ;16-bit call gate entry point - push ds - push ebp - push ebx - lds ebp, [cs:ptrTSSprot] - mov ebx, [ds:ebp+4] ; Get the base - sub ebx, 0x8+0xC+20 ; Where we should end up in the kernel stack now - cmp esp, ebx ; Wrong kernel stack? - jnz error - mov ebx, [ds:ebp+8] ; Get the stack - mov bp, ss - cmp bx, bp - jnz error ; Invalid kernel stack - pop ebx - pop ebp - pop ds - push eax - mov ax, cs - cmp ax, C_SEG_PROT32 - jnz error - pop eax - ; Validate the parameters on the kernel stack - cmp [esp+0x16], word 0x1234 - jne error - cmp [esp+0x14], word 0x5678 - jne error - cmp [esp+0x12], word 0x9ABC - jne error - cmp [esp+0x10], word 0xDEF0 - jne error - cmp [esp+0x0E], word 0x1122 - jne error - cmp [esp+0x0C], word 0x3344 - jne error - cmp [esp+0x0A], word 0x5566 - jne error - cmp [esp+0x08], word 0x7788 - jne error - cmp [esp+0x06], word 0x99AA - jne error - cmp [esp+0x04], word 0xBBCC - jne error - o16 retf 20 ; Return to the user mode code, discarding the parameters from the stack. - - - -; -; Handles cleanup after returning from Ring 3 (Virtual 8086 mode) to Ring 0 -; -; After calling this procedure consider all the registers and flags as trashed. Assumes that the error code has already been popped. -; -switchedToRing0V86_cleanup: - push ds - push ebx - lds ebx, [cs:ptrTSSprot] - push eax - ; restore ring 0 data segments, as they'll be restored with interrupts/exceptions calling us. - mov ax, [ebx+0x54] ; restore DS - mov [esp+8], ax ; Store on the stack to pop later - mov ax, [ebx+0x48] ; restore ES - mov es, ax - mov ax, [ebx+0x58] ; restore FS - mov fs, ax - mov ax, [ebx+0x5C] ; restore GS - mov gs, ax - pop eax - pop ebx - pop ds - ret - ; ; Handles cleanup after returning from Ring 3 (Flat 32-bit mode) to Ring 0 ; @@ -614,17 +211,4 @@ switchedToRing0FromFlat_cleanup: pop eax pop ebx pop ds - ret - - -; -; Switches from Ring 3 to Ring 0 (V86 mode). This is a jump target, not a call target. -; -; After calling this procedure consider all the registers and flags as trashed. -; -switchToRing0V86: - bits 16 - pop eax ; read the return offset - int 0x25 - bits 32 - + ret \ No newline at end of file diff --git a/src/protected_rings_tssp.asm b/src/protected_rings_tssp.asm new file mode 100644 index 0000000..6dfb845 --- /dev/null +++ b/src/protected_rings_tssp.asm @@ -0,0 +1,462 @@ +; +; Switches from Ring 0 to Ring 3 +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3low: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot_R0] + ; save ring 0 data segments, they'll be restored with switchToRing0 + mov [ebx+0x54], ax ; save DS + mov ax, es + mov [ebx+0x48], ax ; save ES + mov ax, fs + mov [ebx+0x58], ax ; save FS + mov ax, gs + mov [ebx+0x5C], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + mov eax, ss + mov [ebx+8], eax + cli ; disable ints during switching + push dword SU_SEG_PROT32|3 ; push user stack with RPL=3 + push dword ESP_R3_PROT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) + push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 + push dword edx ; push return EIP + iretd + +; +; Switches from Ring 0 to Ring 3, to return to ring 0 in flat mode later +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3FLATkernelLow: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot_R0] + ; save ring 0 data segments, they'll be restored with switchToRing0 + mov [ebx+0x54], ax ; save DS + mov ax, es + mov [ebx+0x48], ax ; save ES + mov ax, fs + mov [ebx+0x58], ax ; save FS + mov ax, gs + mov [ebx+0x5C], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + pushfd + or dword [ebx+4], 0xE0010000 ; make sure we return to a proper ring0 stack + popfd + mov eax, D_SEG_PROT32FLAT + mov [ebx+8], eax + cli ; disable ints during switching + push dword SU_SEG_PROT32|3 ; push user stack with RPL=3 + push dword ESP_R3_PROT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x200 ; reenable interrupts in ring 3 (can't use privileged sti) + push dword CU_SEG_PROT32low|3 ; push user code segment with RPL=3 + push dword edx ; push return EIP + iretd +; +; Switches from Ring 0 to Ring 3 in flat mode +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3FLATuserLow: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot_R0] + ; save ring 0 data segments, they'll be restored with switchedToRing0FromFlat_cleanup + mov [ebx+0x68], ax ; save DS + mov ax, es + mov [ebx+0x6A], ax ; save ES + mov ax, fs + mov [ebx+0x6C], ax ; save FS + mov ax, gs + mov [ebx+0x6E], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + mov eax, ss + mov [ebx+8], eax + cli ; disable ints during switching + push dword DU_SEG_PROT32FLAT|3 ; push user stack with RPL=3 + push dword ESP_R3_PROTFLAT ; push user mode esp + pushfd ; push eflags + ; don't reenable interrupts in ring 3 (can't use privileged sti) for ease of testing + push dword CU_SEG_PROT32FLAT|3 ; push user code segment with RPL=3 + or edx,0xE0000 ; Fix flat instruction address + push dword edx ; push return EIP + iretd + + +; +; Switches from Ring 0 to Ring 3 in V86 mode +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3V86_0: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot_R0] + ; save ring 0 data segments, they'll be restored with interrupts/exceptions + mov [ebx+0x54], ax ; save DS + mov ax, es + mov [ebx+0x48], ax ; save ES + mov ax, fs + mov [ebx+0x58], ax ; save FS + mov ax, gs + mov [ebx+0x5C], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + mov eax, ss + mov [ebx+8], eax + cli ; disable ints during switching + push dword V86_GS ; V86 mode GS default to ROM + push dword V86_FS ; V86 mode FS default to ROM + push dword V86_DS ; V86 mode DS default to ROM + push dword V86_ES ; V86 mode ES default to ROM + push dword 0x1000 ; push user stack with RPL=3 + push dword ESP_R3_PROT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x20200 ; reenable interrupts in ring 3 (can't use privileged sti), V8086 flag set, IOPL 0 + and dword [ss:esp], 0xF0FFF ; setup IOPL 0 properly + push dword 0xE000 ; push user code segment with RPL=3 + push dword edx ; push return EIP + iretd + + +; +; Switches from Ring 0 to Ring 3 in V86 mode +; +; After calling this procedure consider all the registers and flags as trashed. +; Also, the stack will be different, so saving the CPU state there will be pointless. +; +switchToRing3V86_3: + ; In order to swich to user mode (ring 3) we need to execute an IRET with these + ; values on the stack: + ; - the instruction to continue execution at - the value of EIP. + ; - the code segment selector to change to. + ; - the value of the EFLAGS register to load. + ; - the stack pointer to load. + ; - the stack segment selector to change to. + ; We also need: + ; - a 32bit code descriptor in GDT with DPL 3 + ; - a 32bit data descriptor in GDT with DPL 3 (for the new stack) + ; - to put the ring 0 stack in TSS.SS0 and TSS.ESP0 + testCPL 0 ; we must be in ring 0 + pop edx ; read the return offset + mov ax, ds + lds ebx, [cs:ptrTSSprot_R0] + ; save ring 0 data segments, they'll be restored with interrupts/exceptions + mov [ebx+0x54], ax ; save DS + mov ax, es + mov [ebx+0x48], ax ; save ES + mov ax, fs + mov [ebx+0x58], ax ; save FS + mov ax, gs + mov [ebx+0x5C], ax ; save GS + ; set ring 0 SS:ESP + mov [ebx+4], esp + mov eax, ss + mov [ebx+8], eax + cli ; disable ints during switching + push dword V86_GS ; V86 mode GS default to ROM + push dword V86_FS ; V86 mode FS default to ROM + push dword V86_DS ; V86 mode DS default to ROM + push dword V86_ES ; V86 mode ES default to ROM + push dword 0x1000 ; push user stack with RPL=3 + push dword ESP_R3_PROT ; push user mode esp + pushfd ; push eflags + or dword [ss:esp], 0x23200 ; reenable interrupts in ring 3 (can't use privileged sti), V8086 flag set, IOPL 3 + push dword 0xE000 ; push user code segment with RPL=3 + push dword edx ; push return EIP + iretd + + +; +; Switches from Ring 3 to Ring 0 (Non-V86 mode) +; +; After calling this procedure consider all the registers and flags as trashed. +; +switchToRing0low: + testCPL 3 ; we must be in ring 3 + ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. + ; A placeholder for a Call Gate is already present in the GDT. + pop ecx ; read the return offset + lfs ebx, [cs:ptrGDTUprot_R2] + mov eax, RING0_GATE + mov esi, C_SEG_PROT32low + mov edi, .ring0 + mov dx, ACC_DPL_3 ; the DPL needs to be 3 + call CU_SEG_PROT32:initCallGateFar + call RING0_GATE|3:0 ; the RPL needs to be 3, the offset will be ignored. +.ring0: + add esp, 16 ; remove from stack CS:EIP+SS:ESP pushed by the CALL to RING0_GATE + ; restore ring 0 data segments saved by switchToRing3 + lds ebx, [cs:ptrTSSprot_R2] + mov ax, [ebx+0x48] ; restore ES + mov es, ax + mov ax, [ebx+0x58] ; restore FS + mov fs, ax + mov ax, [ebx+0x5C] ; restore GS + mov gs, ax + mov ax, [ebx+0x54] ; restore DS + mov ds, ax + ; return to caller + push ecx + ret + +; +; Test routine for call gate with parameters +; +testCallGateWithParameters: + push dword switchToRing0_2 ; Where to start + jmp switchToRing3low ; Switch to ring 3 to start the test + +errorTSS: + ; If we end up here, the test failed. Loop forever. + cli + hlt + +; +; Switches from Ring 3 to Ring 0 (Non-V86 mode) and calls back into kernel mode with parameters +; +; After calling this procedure consider all the registers and flags as trashed. +; +switchToRing0_2: + testCPL 3 ; we must be in ring 3 + ; In order to swich to kernel mode (ring 0) we'll use a Call Gate. + ; A placeholder for a Call Gate is already present in the GDT. + mov ecx, ring0_2TestEndLocation ; read the return offset + ;Setup a 32-bit call gate + lfs ebx, [cs:ptrGDTUprot_R2] + mov eax, RING0_GATE2 + mov esi, C_SEG_PROT32low + mov edi, .ring0_2 + mov dx, ACC_DPL_3|0xA ; the DPL needs to be 3 + call CU_SEG_PROT32:initCallGateFar + + push ecx ;Save caller + + ; Create a stack of 32-bit test values to transfer using the call gate + push dword 0x12347654 + push dword 0x5678CBA9 + push dword 0x9ABC3333 + push dword 0xDEF02222 + push dword 0x11221111 + push dword 0x33447777 + push dword 0x55665555 + push dword 0x7788AAAA + push dword 0x99AA4444 + push dword 0xBBCCFFFF + + ; 32-bit call gate + call RING0_GATE2|3:0 ; the RPL needs to be 3, the offset will be ignored. + + ;Setup a 16-bit call gate + lfs ebx, [cs:ptrGDTUprot_R2] + mov eax, RING0_GATE2 + mov esi, C_SEG_PROT32low + mov edi, .ring0_3 + mov dx, ACC_DPL_3|0xA ; the DPL needs to be 3 + call CU_SEG_PROT32:initCallGate286Far + + ; Create a stack of 16-bit test values to transfer using the call gate + push word 0x1234 + push word 0x5678 + push word 0x9ABC + push word 0xDEF0 + push word 0x1122 + push word 0x3344 + push word 0x5566 + push word 0x7788 + push word 0x99AA + push word 0xBBCC + + ; 16-bit call gate + call RING0_GATE2|3:0 ; the RPL needs to be 3, the offset will be ignored. + + pop ecx ; Restore caller + call RING0_GATE|3:0 ; the RPL needs to be 3, the offset will be ignored. + + ; This time we return to the caller proper +.ring0_2: ;32-bit call gate entry point + push ds + push ebp + push ebx + lds ebp, [cs:ptrTSSprot_R2] + mov ebx, [ds:ebp+4] ; Get the base + sub ebx, 0x10+0xC+40 ; Where we should end up in the kernel stack now + cmp esp, ebx ; Wrong kernel stack? + jnz errorTSS + mov ebx, [ds:ebp+8] ; Get the stack + mov bp, ss + cmp bx, bp + jnz errorTSS ; Invalid kernel stack + pop ebx + pop ebp + pop ds + push eax + mov ax, cs + cmp ax, C_SEG_PROT32low + jnz errorTSS + pop eax + ; Validate the parameters on the kernel stack + cmp [esp+0x2C], dword 0x12347654 + jne errorTSS + cmp [esp+0x28], dword 0x5678CBA9 + jne errorTSS + cmp [esp+0x24], dword 0x9ABC3333 + jne errorTSS + cmp [esp+0x20], dword 0xDEF02222 + jne errorTSS + cmp [esp+0x1C], dword 0x11221111 + jne errorTSS + cmp [esp+0x18], dword 0x33447777 + jne errorTSS + cmp [esp+0x14], dword 0x55665555 + jne errorTSS + cmp [esp+0x10], dword 0x7788AAAA + jne errorTSS + cmp [esp+0x0C], dword 0x99AA4444 + jne errorTSS + cmp [esp+0x08], dword 0xBBCCFFFF + jne errorTSS + retf 40 ; Return to the user mode code, discarding the parameters from the stack. +.ring0_3: ;16-bit call gate entry point + push ds + push ebp + push ebx + lds ebp, [cs:ptrTSSprot_R2] + mov ebx, [ds:ebp+4] ; Get the base + sub ebx, 0x8+0xC+20 ; Where we should end up in the kernel stack now + cmp esp, ebx ; Wrong kernel stack? + jnz errorTSS + mov ebx, [ds:ebp+8] ; Get the stack + mov bp, ss + cmp bx, bp + jnz errorTSS ; Invalid kernel stack + pop ebx + pop ebp + pop ds + push eax + mov ax, cs + cmp ax, C_SEG_PROT32low + jnz errorTSS + pop eax + ; Validate the parameters on the kernel stack + cmp [esp+0x16], word 0x1234 + jne errorTSS + cmp [esp+0x14], word 0x5678 + jne errorTSS + cmp [esp+0x12], word 0x9ABC + jne errorTSS + cmp [esp+0x10], word 0xDEF0 + jne errorTSS + cmp [esp+0x0E], word 0x1122 + jne errorTSS + cmp [esp+0x0C], word 0x3344 + jne errorTSS + cmp [esp+0x0A], word 0x5566 + jne errorTSS + cmp [esp+0x08], word 0x7788 + jne errorTSS + cmp [esp+0x06], word 0x99AA + jne errorTSS + cmp [esp+0x04], word 0xBBCC + jne errorTSS + o16 retf 20 ; Return to the user mode code, discarding the parameters from the stack. + + + +; +; Handles cleanup after returning from Ring 3 (Virtual 8086 mode) to Ring 0 +; +; After calling this procedure consider all the registers and flags as trashed. Assumes that the error code has already been popped. +; +switchedToRing0V86_cleanup: + push ds + push ebx + lds ebx, [cs:ptrTSSprot_R2] + push eax + ; restore ring 0 data segments, as they'll be restored with interrupts/exceptions calling us. + mov ax, [ebx+0x54] ; restore DS + mov [esp+8], ax ; Store on the stack to pop later + mov ax, [ebx+0x48] ; restore ES + mov es, ax + mov ax, [ebx+0x58] ; restore FS + mov fs, ax + mov ax, [ebx+0x5C] ; restore GS + mov gs, ax + pop eax + pop ebx + pop ds + ret + + +; +; Switches from Ring 3 to Ring 0 (V86 mode). This is a jump target, not a call target. +; +; After calling this procedure consider all the registers and flags as trashed. +; +switchToRing0V86: + bits 16 + pop eax ; read the return offset + int 0x25 + bits 32 + diff --git a/src/protected_tssinth.asm b/src/protected_tssinth.asm index 2a9ab7a..cdcc5f3 100644 --- a/src/protected_tssinth.asm +++ b/src/protected_tssinth.asm @@ -3,13 +3,27 @@ BITS 32 ptrTSSprot_R2: ; pointer to the task state segment dd 0 dw TSSU_DSEG_PROT32|3 +ptrTSSprot_R0: ; pointer to the task state segment + dd 0 + dw TSS_DSEG_PROT ptrTSSprot16_R2: ; pointer to the 16-bit task state segment dd 0 dw TSSU_DSEG_PROT16|3 ptrTSSerrorR0_2: ; pointer to the error condition, from ring 0 or ring 2 dd error dw CU_SEG_PROT32|3 - +ptrGDTUprot_R2: ; pointer to the GDT for pmode (user mode data segment) + dd 0 + dw GDTU_DSEG_PROT|3 +ptrIDTprot_R0: ; pointer to the IDT for pmode + dd 0 ; 32-bit offset + dw IDT_SEG_PROT ; 16-bit segment selector +ptrIDTUprot_R3: ; pointer to the IDT for pmode + dd 0 ; 32-bit offset + dw IDTU_SEG_PROT ; 16-bit segment selector +ptrSSprot_R0: ; pointer to the stack for pmode + dd ESP_R0_PROT + dw S_SEG_PROT32 errorCPLn: jmp far [cs:ptrTSSerrorR0_2] ;JMP to the error condition. @@ -37,7 +51,7 @@ kernelInterrupt: pop ebx cmp dword [esp+0x00], kernelModeInterruptReturn jne errorCPLn ; Invalid return address - cmp dword [esp+0x04], CU_SEG_PROT32|3 + cmp dword [esp+0x04], CU_SEG_PROT32low|3 jne errorCPLn ; Invalid return code segment ; Ignore most eflags test dword [esp+0x08], PS_IF ;EFLAGS input @@ -173,15 +187,15 @@ kernelInterrupt_validateV86mode16bit: mov ecx, dword [bx+4] ; Get ESP for the kernel mode program (stored into eax) sub ecx, 0xC+0x12 ; Where we should end up on the kernel stack, taking into account what we just pushed cmp esp, ecx ; Did the stack decrease correctly? - jne error + jne errorCPLn mov cx, ss mov bx, word [bx+8] ; Expected: kernel mode stack cmp cx, bx ; Did the stack pointer load correctly? - jne error + jne errorCPLn pop ecx mov bx, cs cmp bx, C_SEG_PROT16CS ; Did we arrive at proper kernel code? - jne error + jne errorCPLn pop ds pop ebx ;Basic stack pointer verified. Now check if the data on the stack is correct, while building a new stack to return to user mode. @@ -190,37 +204,37 @@ kernelInterrupt_validateV86mode16bit: and eax,0xFFFF ;Mask off upper 16 bits. mov ax, word [esp+(0x04+0x10)] ;GS cmp ax,V86_GS ;Correct? - jne error ;Error if incorrect. + jne errorCPLn ;Error if incorrect. push eax ;Save destination GS mov ax, word [esp+(0x08+0x0E)] ;FS cmp ax,V86_FS ;Correct? - jne error ;Error if incorrect. + jne errorCPLn ;Error if incorrect. push eax ;Save destination FS mov ax, word [esp+(0x0C+0x0C)] ;DS cmp ax,V86_DS ;Correct? - jne error ;Error if incorrect. + jne errorCPLn ;Error if incorrect. push eax ;Save destination DS mov ax, word [esp+(0x10+0x0A)] ;ES cmp ax,V86_ES ;Correct? - jne error ;Error if incorrect. + jne errorCPLn ;Error if incorrect. push eax ;Save destination ES mov ax, word [esp+(0x14+0x08)] ;SS cmp ax,0x1000 ;Correct? - jne error ;Error if incorrect. + jne errorCPLn ;Error if incorrect. push eax ;Save destination SS mov ax, word [esp+(0x18+0x06)] ;SP cmp ax,ESP_R3_PROT ;Correct? - jne error ;Error if incorrect. + jne errorCPLn ;Error if incorrect. push eax ;Save destination ESP mov ax, word [esp+(0x1C+0x04)] ;FLAGS push eax ;Save destination EFLAGS mov ax, word [esp+(0x20+0x02)] ;CS - cmp ax,0xF000 ;Correct? - jne error ;Error if incorrect. + cmp ax,0xE000 ;Correct? + jne errorCPLn ;Error if incorrect. push eax ;Save destination CS mov ax, word [esp+(0x24+0x00)] ;IP cmp ax,userV86_16bitinterruptRET ;IP correct? - jne error ;Incorrect IP address. + jne errorCPLn ;Incorrect IP address. push eax ;Save destination EIP or dword [esp+0x08],0x20000 ;Set VM bit to properly return. mov eax,[esp+0x2C] ;Load original EAX register to restore it. diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index d0e0e3c..375c8e9 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -33,6 +33,13 @@ defGDTDescPrototype C_SEG_PROT32 defGDTDescPrototype S_SEG_PROT32 defGDTDescPrototype C_SEG_PROT32FLAT + defGDTDescPrototype D_SEG_PROT32FLAT + defGDTDescPrototype GDTU_DSEG_PROT + defGDTDescPrototype IDT_SEG_PROT + defGDTDescPrototype IDTU_SEG_PROT + defGDTDescPrototype CC_SEG_PROT32 + defGDTDescPrototype RING0_GATE ; placeholder for a call gate used to switch to ring 0 + defGDTDescPrototype RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 ;LDT entries defLDTDescPrototype D_SEG_PROT32 defLDTDescPrototype DU_SEG_PROT @@ -49,6 +56,7 @@ section .system_bios_extensions_area start=0x00000 ; %include "protected_tssh.asm" BITS 32 +%include "protected_rings_tssp.asm" ; ; Interrupt handlers (installed during POST 8) ; @@ -429,6 +437,7 @@ nextRealModeInterruptSetError: retf test386POST20start: +BITS 32 ;------------------------------------------------------------------------------- POST 20 ;------------------------------------------------------------------------------- @@ -452,7 +461,7 @@ test386POST20start: ; CS must be C_SEG_PROT32low|0 (CPL=0) mov ax, cs cmp ax, C_SEG_PROT32low - jne error + jne errorTSS pushfd and word [esp], 0xFFF ; Block ports on user mode again popfd @@ -460,20 +469,20 @@ test386POST20start: ; CS must be CU_SEG_PROT32low|3 (CPL=3) mov ax, cs cmp ax, CU_SEG_PROT32low|3 - jne error + jne errorTSS ; data segments must be NULL mov ax, ds cmp ax, 0 - jne error + jne errorTSS mov ax, es cmp ax, 0 - jne error + jne errorTSS mov ax, fs cmp ax, 0 - jne error + jne errorTSS mov ax, gs cmp ax, 0 - jne error + jne errorTSS ; test privileged instructions in user mode (ring 3) protModeFaultTestLow EX_GP, 0, cli @@ -511,13 +520,13 @@ userFarFunc: userRetfErrorFunction: ; From user mode to kernel mode error address, which isn't allowed. push C_SEG_PROT32low - push error + push errorTSS userRetfErrorLocation: retf userRetfImmErrorFunction: ; From user mode to kernel mode error address, with immediate, which isn't allowed. push C_SEG_PROT32low - push error + push errorTSS userRetfImmErrorLocation: retf 1 userV86ExitFuncLocation: @@ -548,13 +557,13 @@ userV86IretRealModeFunc: userV86IretErrorFuncLocation: bits 16 push cs - push error + push errorTSS userV86IretErrorFuncLocationInstruction: iret - jmp error + jmp errorTSS userV86IOInstruction0: in al, 0x64 - jmp error + jmp errorTSS bits 32 userJmpFunc: @@ -562,7 +571,7 @@ userJmpFunc: ; CS must be C_SEG_PROT32low|0 (CPL=0) mov ax, cs cmp ax, C_SEG_PROT32low - jne error + jne errorTSS ; Test call gates now jmp testCallGateWithParameters ; Test call gate with parameters @@ -589,7 +598,7 @@ kernelOnlyConformingInterruptReturn: ; Test user to kernel stack switch using different address spaces ; Interrupt from user mode to kernel mode (flat address space) - call switchToRing3FLATkernel ;Switch to ring 3 with flat kernel stack prepared + call switchToRing3FLATkernelLow ;Switch to ring 3 with flat kernel stack prepared int 0x26 kernelModeInterruptKernelStackReturn: push kernelModeInterruptKernelStackReturnPoint @@ -660,7 +669,7 @@ V86IOSucceedFinish: bits 16 int 0x2D ;Validate we're actually in V86 mode. jmp userV86IretRealModeFunc - jmp error + jmp errorTSS bits 32 userV86IretExitFuncLocationRet: @@ -668,13 +677,13 @@ userV86IretExitFuncLocationRet: call switchToRing3V86_3 bits 16 jmp userV86ExitFuncLocation - jmp error + jmp errorTSS bits 32 errorInTSS32Load: mov ax,DU_SEG_PROT32FLAT|3 ;SS safe value mov ss,ax mov esp,ESP_R3_PROTFLAT ;Restore our stack pointer - jmp error ;Error out! + jmp errorTSS ;Error out! userV86ExitFuncRet: ;Now we're going to test 16-bit interrupts in Virtual 8086 mode. @@ -698,6 +707,7 @@ userV86ExitFuncRet_16bitinterrupts: push C_SEG_PROT32 push test386POST21end retf +BITS 16 ;End of high BIOS ;Pad to 64KB diff --git a/src/test386.asm b/src/test386.asm index f70417f..45d1352 100644 --- a/src/test386.asm +++ b/src/test386.asm @@ -334,18 +334,18 @@ initGDT: defGDTDescImplementation C_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT defGDTDescImplementation S_SEG_PROT32, 0x00010000,0x0007ffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT defGDTDescImplementation C_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_CODE_R|ACC_PRESENT,EXT_32BIT|EXT_PAGE + defGDTDescImplementation D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE + defGDTDescImplementation GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation IDT_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT + defGDTDescImplementation IDTU_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 + defGDTDescImplementation CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT defGDTDesc C_SEG_PROT16, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_PRESENT - defGDTDesc CC_SEG_PROT32, 0x000f0000,0x0000ffff,ACC_TYPE_CODE_R|ACC_TYPE_CONFORMING|ACC_PRESENT|EXT_32BIT - defGDTDesc IDT_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc IDTU_SEG_PROT, 0x00000400,0x0000016F,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc GDT_DSEG_PROT, 0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc GDTU_DSEG_PROT,0x00000600,0x0000031f,ACC_TYPE_DATA_W|ACC_PRESENT|ACC_DPL_3 defGDTDesc LDT_DSEG_PROT, 0x00000A00,0x000005ff,ACC_TYPE_DATA_W|ACC_PRESENT defGDTDesc PG_SEG_PROT, 0x00001000,0x00003fff,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc D_SEG_PROT32FLAT, 0x00000000,0x000fffff,ACC_TYPE_DATA_W|ACC_PRESENT,EXT_32BIT|EXT_PAGE defGDTDesc FLAT_SEG_PROT, 0x00000000,0xffffffff,ACC_TYPE_DATA_W|ACC_PRESENT - defGDTDesc RING0_GATE ; placeholder for a call gate used to switch to ring 0 - defGDTDesc RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 + defGDTDescImplementation RING0_GATE ; placeholder for a call gate used to switch to ring 0 + defGDTDescImplementation RING0_GATE2 ; placeholder for a second call gate used to switch to ring 0 jmp initIDT @@ -810,6 +810,7 @@ protTests: ;Run POST 20 from the lower ROM, if enabled. %if ROM128 + POST 20 push C_SEG_PROT32low push test386POST20start retf ;Jump to the entry point of the test. From d0c744c9445c01824191ddad98c2a27b7083443b Mon Sep 17 00:00:00 2001 From: superfury Date: Fri, 26 Jun 2026 20:47:57 +0200 Subject: [PATCH 62/62] Fixed interrupt gate and call gate far calls when compiled with the latest nasm. --- src/protected_m.asm | 4 ++-- src/protected_p.asm | 2 +- src/systembiosexpansionarea.asm | 2 +- src/tss_p.asm | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/protected_m.asm b/src/protected_m.asm index 975b416..9421fac 100644 --- a/src/protected_m.asm +++ b/src/protected_m.asm @@ -328,11 +328,11 @@ jnz %%ring3 %%ring0: lds ebx, [cs:ptrIDTprot_R0] - call far C_SEG_PROT32:initIntGateProtFar + o32 call C_SEG_PROT32:initIntGateProtFar jmp %%call %%ring3: lds ebx, [cs:ptrIDTUprot_R3] - call far CU_SEG_PROT32|3:initIntGateProtFar + o32 call CU_SEG_PROT32|3:initIntGateProtFar %%call: pop ax mov ds, ax ; restore ds diff --git a/src/protected_p.asm b/src/protected_p.asm index 0934f7f..f88ca36 100644 --- a/src/protected_p.asm +++ b/src/protected_p.asm @@ -6,7 +6,7 @@ initIntGateProt: initIntGateProtFar: call initIntGate - retf + o32 retf initIntTaskGateProt: initIntTaskGate diff --git a/src/systembiosexpansionarea.asm b/src/systembiosexpansionarea.asm index 375c8e9..2e25df5 100644 --- a/src/systembiosexpansionarea.asm +++ b/src/systembiosexpansionarea.asm @@ -444,7 +444,7 @@ BITS 32 ; ; Test user mode (ring 3) switching ; - call far C_SEG_PROT32:clearTSSfar + o32 call C_SEG_PROT32:clearTSSfar mov ax, D_SEG_PROT32 mov ds, ax mov es, ax diff --git a/src/tss_p.asm b/src/tss_p.asm index bc9e128..8d7edef 100644 --- a/src/tss_p.asm +++ b/src/tss_p.asm @@ -75,7 +75,7 @@ clearTSS: ;Far call version of clearTSS clearTSSfar: call clearTSS - retf + o32 retf ; Prepare 32-bit TSS for task switching