TSS Segment question and help

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

TSS Segment question and help

Post by neon »

Perhaps someone here can find what I am missing...

I am rewriting my bootloader for better stability, and management of a 32bit PE kernel and kernel level drivers.

For the most part, everything is going fine--I have paging set up, IDT, GDT. PMode, and a basic TSS that I may use for v86 mode.

I cannot seem to be able to switch into a v86 task, though...

Code: Select all

; Protected mode code:

push	dword 0x0			; real mode gs
push	dword 0x0			; real mode fs
push	dword 0x0			; real mode ds
push	dword 0x0			; real mode es
push	dword 0x9000		; real mode ss
push	dword 0xf000		; real mode esp
push	dword 100111001000000010b	; real mode eflags (Enables v8086 mode bit, ring3, interrupt enable flag)
push	dword 0x0			; real mode cs
push	dword V86_Test		; real mode eip
iretd
For a test. I set up a basic stack frame for the v86 task, and task switch using IRET.

Here is V86_Task:

Code: Select all

; rmode code:

V86_Test:
	jmp	$	
An interesting note is that, when setting up the stack frame, none of the segment registers are set. Instead, their descriptor limit values are set (??).I don't think this is supposed to happen. Please correct me if i'm wrong :)

As the segment registers are not set (No matter what I do), I get errors from Bochs, which then provokes a triple fault (Ignoring my IDT)...

Code: Select all

00001119702e[CPU  ] seg = DS
00001119702e[CPU  ] seg->selector.value = 0000

...repeated 1000's of times...

00001122367e[CPU  ] write_virtual_checks: valid bit = 0
00001122367e[CPU  ] CS: 0008
00001122367e[CPU  ] IP: 132d
00001122367e[CPU  ] seg = DS

...repeated 1000's of times...

00001122367e[CPU  ] seg->selector.value = 0000
00001122367e[CPU  ] write_virtual_checks: valid bit = 0
00001122367e[CPU  ] CS: 0008
00001122367e[CPU  ] IP: 132d
00001122367p[CPU  ] >>PANIC<< exception(): 3rd (13) exception with no resolution
00001122367i[SYS  ] Last time is 1191897309
00001122367i[CPU  ] protected mode
00001122367i[CPU  ] CS.d_b = 32 bit
00001122367i[CPU  ] SS.d_b = 32 bit
00001122367i[CPU  ] | EAX=00000010  EBX=00001350  ECX=00150010  EDX=00000fff
00001122367i[CPU  ] | ESP=00000a04  EBP=00000000  ESI=00000796  EDI=00001350
00001122367i[CPU  ] | IOPL=3 NV UP DI PL NZ NA PO NC
00001122367i[CPU  ] | SEG selector     base    limit G D
00001122367i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00001122367i[CPU  ] |  DS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001122367i[CPU  ] |  ES:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001122367i[CPU  ] |  FS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001122367i[CPU  ] |  GS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001122367i[CPU  ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00001122367i[CPU  ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00001122367i[CPU  ] | EIP=0000132d (0000132d)
00001122367i[CPU  ] | CR0=0xe0000011 CR1=0x00000000 CR2=0x00000000
00001122367i[CPU  ] | CR3=0x0009c000 CR4=0x00000000
00001122367i[     ] restoring default signal behavior
00001122367i[CTRL ] quit_sim called with exit code 1
I have debugged this like crazy through bochs. but still no go :(

I have also read up the Intel docs and searched around alot to see if I am missing something, but everything looks correct...

Here is one of the things I found while debugging, that may help.

If I set the ESP0 DWORD inside of the TSS Segment to 0, Bochs only gives me this error:

Code: Select all

00001119697p[CPU  ] >>PANIC<< exception(): 3rd (13) exception with no resolution
00001119697i[SYS  ] Last time is 1191897764
00001119697i[CPU  ] v8086 mode
00001119697i[CPU  ] CS.d_b = 32 bit
00001119697i[CPU  ] SS.d_b = 32 bit
00001119697i[CPU  ] | EAX=00000010  EBX=00001608  ECX=00150010  EDX=00000fff
00001119697i[CPU  ] | ESP=0000f000  EBP=00000000  ESI=00000796  EDI=00000005
00001119697i[CPU  ] | IOPL=3 NV UP EI PL NZ NA PO NC
00001119697i[CPU  ] | SEG selector     base    limit G D
00001119697i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00001119697i[CPU  ] |  DS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001119697i[CPU  ] |  ES:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001119697i[CPU  ] |  FS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001119697i[CPU  ] |  GS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00001119697i[CPU  ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00001119697i[CPU  ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00001119697i[CPU  ] | EIP=000007b1 (000007b1)
00001119697i[CPU  ] | CR0=0xe0000011 CR1=0x00000000 CR2=0xfffffffc
00001119697i[CPU  ] | CR3=0x0009c000 CR4=0x00000000
00001119697i[     ] restoring default signal behavior
00001119697i[CTRL ] quit_sim called with exit code 1
If it is not 0, any other value produces the previous 3 errors. This makes me wonder, because I thought ESP0 should point to the beginning of the pmode task (The caller)?

I'm tired and have been working on this for hours right now, and cannot for the life of me figure out the problem :(

If anyone would like me to post more code, I will be happy to.

Does anyone have any suggestions that I can try (That I hopefully have not already tried)?

Thanks :)
Last edited by neon on Thu Oct 11, 2007 3:32 am, edited 2 times in total.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

A few questions:

1) I see you have paging enabled... what permissions does the area of code for the v8086 task have?

2) Where in virtual memory are your kernel and v8086 task stored?

3) ESP0 holds the top of the kernel stack. Have you set it such that an interrupt will load a sensible ESP to poke the user land data to?

4) are you reloading segment registers after a return from v8086 mode?

5) after the first iretd, what is the next instruction bochs wants to execute (the timestamps between the first error and the triple fault differ), including the corresponding cpu state?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

After completely disabling paging (To take that out of the equation), and testing the frame setup for 32bit tasks, and calling v86 tasks, I have found the problem.

It seems to only output those errors when either IF=1 in EFLAGS, or an INT instruction is executed. My previous stack frame set IF, which is why it kept failing. I set that bit to 0:

Code: Select all

	push	dword 0x0			; real mode gs
	push	dword 0x0			; real mode fs
	push	dword 0x0			; real mode ds
	push	dword 0x0			; real mode es
	push	dword 0x0		; real mode ss
	push	dword 0x7FF0		; real mode esp
	push	dword 100000000000000010b	; real mode eflags (Enables v8086 mode bit, ring3, interrupt enable flag)
	push	dword 0x0			; real mode cs
	push	dword V86_Test		; real mode eip
	iretd
...Keeping in mind the task is just a 16bit label inside of the same source file, and the stack is at 0x7FF0, and that EFLAGS is set to supervisor mode, with IF=0, The above executes the task (V86_Task label) at b86 mode just fine:

Code: Select all

bits	16

V86_Test:

	jmp	$
However, because the problem only happens when IF=1, A single sti instruction will produce the same errors from my previous post.

...Also, any software interrupts (INT instruction), have the same result.

What can cause this?

Here is Bochs output when IF=0 (Not set), when executing the task:

Code: Select all

00073077000p[WGUI ] >>PANIC<< POWER button turned off.
00073077000i[SYS  ] Last time is 1191923692
00073077000i[CPU  ] v8086 mode
00073077000i[CPU  ] CS.d_b = 16 bit
00073077000i[CPU  ] SS.d_b = 16 bit
00073077000i[CPU  ] | EAX=00000010  EBX=00001601  ECX=00040010  EDX=00000fff
00073077000i[CPU  ] | ESP=00007ff0  EBP=00000000  ESI=00001774  EDI=00000005
00073077000i[CPU  ] | IOPL=0 NV UP DI PL NZ NA PO NC
00073077000i[CPU  ] | SEG selector     base    limit G D
00073077000i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00073077000i[CPU  ] |  DS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00073077000i[CPU  ] |  ES:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00073077000i[CPU  ] |  FS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00073077000i[CPU  ] |  GS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00073077000i[CPU  ] |  SS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00073077000i[CPU  ] |  CS:0000( 0001| 0|  3) 00000000 0000ffff 0 0
00073077000i[CPU  ] | EIP=00001772 (00001772)
00073077000i[CPU  ] | CR0=0x60000011 CR1=0x00000000 CR2=0x00000000
00073077000i[CPU  ] | CR3=0x00000000 CR4=0x00000000
00073077000i[     ] restoring default signal behavior
00073077000i[CTRL ] quit_sim called with exit code 1
As you can see, when IF=0, it executes just fine.

If any interrupt is called, it produces the same error as before.

The weird thing is the IDT is set up properly. (However, I did NOT remap hardware interrupts--I wonder if this can be it? this would not explain software INT's not working though, however exception handling works...I know this from experience ^^)

Either way, as the task executes at ring 0, why wouldn't Bios INT's work?

Does anyone have any suggestions?

Thanks again :)
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

v8086 tasks always run in ring 3, not ring 0. Therefore it will load ss0:esp0 from the IDT as soon as any interrupt happens.

To be ready for this, you should reserve space for a new kernel stack to be used when the processor returns to kernel space, load the end of that area into esp0, and then assume you will never be able to return to the location where you first entered v8086 mode.

The better solution would be to build a scheduler which can switch between the v8086 thread and the kernel thread. Its more involved but at least you can continue where you left off before entering v8086 mode. Useful when you want to check on the results of a bios call.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

Hm...I'm thought ring0 v86 tasks were possible...

I'll look further into it. Thanks again :)
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

I reserved space for a new v8086 stack, put that into SS:ESP prior to calling the v86 task, and got it working :)

Next up is creating the v86 monitor... I get a GPF for everything, which is what I wanted...)

Thanks again :D
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

I spoke too soon--I still have the same problem :(

I rewrote alot of the tasking code for better management, and to reduce code size. I'll post them here to see if anyone see's something odd.

The current code sets up and executes the v86 task at ring 0, it works just fine. I cannot seem to return from v86 mode though--any interrupt call triple faults, as it cannot find the IDT, which was set up in pmode (And works, as well.)

Currently, this is the bootloader trying to enable v86 mode so that it can execute bootup programs prior to the kernel.

Here is where we execute the task, and enable v8086 mode:

Code: Select all


Stage3:

	mov		ebx, msgStage4
	call	Puts32

	mov		ax, 0x10			; set data segments to data selector offset (0x10)
	mov		ds, ax
	mov		ss, ax
	mov		es, ax
	mov		fs, ax
	mov		gs, ax
	mov		esp, 0x90000			; stack begins from 90000h

	;---------------------------------------;
	;	Initialize TSS						;
	;---------------------------------------;

	mov	dword	[_TSS+TSS.m_eflags],	0x202

	mov	eax, cr3
	mov	dword	[_TSS+TSS.m_cr3],	eax
	mov	word	[_TSS+TSS.m_prevTask], 0x18
	mov	word	[_TSS+TSS.m_ss],	0x10
	mov	word	[_TSS+TSS.m_es],	0x10
	mov	word	[_TSS+TSS.m_ds],	0x10
	mov	word	[_TSS+TSS.m_fs],	0x10
	mov	word	[_TSS+TSS.m_gs],	0x10
	mov	word	[_TSS+TSS.m_cs],	0x8
	mov	dword	[_TSS+TSS.m_esp],	0x90000
	mov	word	[_TSS+TSS.m_ss0],	0x10
	mov	dword	[_TSS+TSS.m_esp0],	0x90000

	;---------------------------------------;
	;	Enable v8086 mode					;
	;---------------------------------------;

	mov		ebx, msgV86
	call	Puts32

	push	dword 0x0			; real mode gs
	push	dword 0x0			; real mode fs
	push	dword 0x0			; real mode ds
	push	dword 0x0			; real mode es
	push	dword 0x500			; real mode ss
	push	dword 0x0			; real mode esp
	push	dword 100011000000000010b	; real mode eflags (Enables v8086 mode bit, ring3, interrupt enable flag)

	push	dword 0x0			; real mode cs
	push	dword V86_Test		; real mode eip
	iretd						; task switch--call V86_Test

;--- v8086 Mode ----------------------------------------------------------

bits	16

V86_Test:

	int 3	; << Switches to pmode just fine, then triple faults as it cannot find the IDT :( :/

 jmp $
_TSS is a structure used to define the Task Switch Segment, and is set up inside of the TSS descriptor in the GDT, loaded by LTR.

Code: Select all


_TSS:
	istruc	TSS
		at	TSS.m_prevTask,		dw	0
		at	TSS.m_reserved0,	dw	0
		at	TSS.m_esp0,			dd	0
		at	TSS.m_ss0,			dw	0
		at	TSS.m_reserved1,	dw	0
		at	TSS.m_esp1,			dd	0
		at	TSS.m_ss1,			dw	0
		at	TSS.m_reserved2,	dw	0
		at	TSS.m_esp2,			dd	0
		at	TSS.m_ss2,			dw	0
		at	TSS.m_reserved3,	dw	0
		at	TSS.m_cr3,			dd	0
		at	TSS.m_eip,			dd	0
		at	TSS.m_eflags,		dd	0
		at	TSS.m_eax,			dd	0
		at	TSS.m_ecx,			dd	0
		at	TSS.m_edx,			dd	0
		at	TSS.m_ebx,			dd	0
		at	TSS.m_esp,			dd	0
		at	TSS.m_ebp,			dd	0
		at	TSS.m_esi,			dd	0
		at	TSS.m_edi,			dd	0
		at	TSS.m_es,			dw	0
		at	TSS.m_reserved4,	dw	0
		at	TSS.m_cs,			dw	0
		at	TSS.m_reserved5,	dw	0
		at	TSS.m_ss,			dw	0
		at	TSS.m_reserved6,	dw	0
		at	TSS.m_ds,			dw	0
		at	TSS.m_reserved7,	dw	0
		at	TSS.m_fs,			dw	0
		at	TSS.m_reserved8,	dw	0
		at	TSS.m_gs,			dw	0
		at	TSS.m_reserved9,	dw	0
		at	TSS.m_ldt_sel,		dw	0
		at	TSS.m_reserved10,	dw	0
		at	TSS.m_reserved11,	dw	0
		at	TSS.m_io_map,		dw	0
	iend
	
_TSS_End:
Does anyone see any problem in this code?

I can get into v86 mode just fine, as I know does not require a TSS. returning via INT, however, causes a triple fault. this makes me believe the problem is within my TSS, or the way I set up my TSS.

Does anyone see any problems with the above code? I been working on this little problem for 2 days already... :/

Thanks again :)

Code: Select all


00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862i[CPU0 ] protected mode
00002150862i[CPU0 ] CS.d_b = 32 bit
00002150862i[CPU0 ] SS.d_b = 32 bit
00002150862i[CPU0 ] | EAX=00000000  EBX=000012e6  ECX=00000010  EDX=00000fff
00002150862i[CPU0 ] | ESP=00000994  EBP=00000000  ESI=0000072e  EDI=000012e6
00002150862i[CPU0 ] | IOPL=3 id vip vif ac vm RF nt of df if tf sf zf af pf cf
00002150862i[CPU0 ] | SEG selector     base    limit G D
00002150862i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00002150862i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00002150862i[CPU0 ] |  DS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00002150862i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00002150862i[CPU0 ] |  ES:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00002150862i[CPU0 ] |  FS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00002150862i[CPU0 ] |  GS:0000( 0002| 0|  3) 00000000 0000ffff 0 0
00002150862i[CPU0 ] | EIP=000012c3 (000012c3)
00002150862i[CPU0 ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00002150862i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00002150862i[CPU0 ] >> add byte ptr ds:[eax], al : 0000
00002150862e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00002150862i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

Hm... I think I know what I may be missing... from the intel manual:
The software interrupt redirection bit map (see Figure 15-5) is a 32-byte field in the
TSS. This map is located directly below the I/O permission bit map in the TSS. Each
bit in the interrupt redirection bit map is mapped to an interrupt vector.
Is this a requirement?
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

AFAIK all interrupts that occur in real mode actually call a protected mode interrupt handler not a real mode handler. All hardware interrupts call the correct protected mode interrupt, think about it like this, would you want your VM86 task to be able to intercept your clock interrupts?

When the processor hits an int instruction it actually issues a GPF instruction and it is the job of the GPF handler to redirect the interrupt to the proper real mode interrupt.

So I guess my question is do you have an IDT set up in protected mode?
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

Thats what I originally thought, except Bochs cannot seem to find my IDT.

I have my IDT set up for software interrupts (I have not remapped the PIC yet), but it does work for exception handling in pmode. I only have the first 16 entries in the IDT set up atm.

In v8086 mode, however, it acts as if my IDT was not there... :/

I can post my IDT code, if you would like to see it.
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

Are you setting the segments in the interrupt handler?

For example your interrupt handler should do something like this:

Code: Select all

pusha
push ds, es ; etc.
mov ax, 0x10
mov ds, ax
; etc.
call handler
pop ds, es etc.
popa
iret
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

Hello,

Thanks for your reply.

All of the interrupts mapped to the IDT either displays a message and halts, or just halts the processor. For example, Here is my GPF handler:

Code: Select all

msgGPF db "*** GENERAL PROTECTION FAULT ***", 0x0A, 0

ISR_GPF:
   mov   ebx, msgGPF
   call     Puts32
   mov   ebx, msgStop
   call     Puts32  
   cli
   hlt
   iret     ; should never get here
I set it up in the IDT:

Code: Select all

   mov	eax, 13
   mov	ebx, ISR_GPF
   call	IDT_SetGate
...Which maps the interrupt handler to handle GPF's at entry 13 in the IDT.

It gets called in pmode just fine, but never in v8086 mode...

Whenever an INT n instruction is executed in v8086 mode, the processor switches back tp pmode, so the above should work, shouldn't it?
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

Yey!! I found the problem :D

Looking at the code for my GPF handler, I took the calls to Puts32 out, and left it just to hault the processor (using cli and hlt), it seems to have fixed the problem. So, my handler now looks like this:

Code: Select all

ISR_GPF: 
   cli 
   hlt 
   iret     ; should never get here
It works fine with this.

I'm not sure why Puts32 will fail to work from my handlers, although this might be because I have not set up the segments (as you posted) for it yet.

...This also explaines why my second fault handler failed to work, as that did the same... Ugh.

Thank you all so much for your help :D
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

The values in ds, es, fs, ad gs will all be the null descriptor. Any data access in the ISR will cause a GPF (IIRC but I know it will be some type of error) and then the ISR to handle that error will throw another error trying to print something to the screen and so on and on until the computer resets.
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Post by neon »

frank wrote:The values in ds, es, fs, ad gs will all be the null descriptor. Any data access in the ISR will cause a GPF (IIRC but I know it will be some type of error) and then the ISR to handle that error will throw another error trying to print something to the screen and so on and on until the computer resets.
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
00002150862e[CPU0 ] interrupt(): gate descriptor is not valid sys seg
Ah... That explaines it :)

Thank you again :D
Post Reply