TSS (ring 3) [SOLVED]

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
AndrewAPrice
Member
Member
Posts: 2308
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

TSS (ring 3) [SOLVED]

Post by AndrewAPrice »

I'm trying to get protection in to my micro kernel and I'm not too familiar with the GDT and TSSes.

I want a flat memory layout (from a segmentation P.O.V., not paging), and basically, I have 6 entries in my GDT:

Code: Select all

  - null descriptor
entry 0: base: 0, limit: 0, access: 0, granularity: 0
  - ring 0 code (for kernel)
entry 1: base: 0x00000000, limit: 0xFFFFFFFF, access: 0x9A, granularity: 0xCF
   - ring 0 data (for kernel)
entry 2: base: 0x00000000, limit: 0xFFFFFFFF, access: 0x92, granularity: 0xCF
   - ring 3 code (for processes)
entry 3: base: 0x00000000, limit: 0xFFFFFFFF, access: 0xFA, granularity: 0xCF
   - ring 3 data (for processes)
entry 4: base: 0x00000000, limit: 0xFFFFFFFF, access: 0xF2, granularity: 0xCF
   - tss entry:
entry 5: base: vir addr of TSS, limit: base + size of TSS, access: 0x89, granularity: 0xCF
each one of those entries is set up using:

Code: Select all

void IDTGDT::SetGDTGate(int num, size_t base, size_t limit, unsigned
				char access, unsigned char gran)
{
	gdtEntry[num].BaseLow = (base & 0xFFFF);
	gdtEntry[num].BaseMiddle = (base >> 16) & 0xFF;
	gdtEntry[num].BaseHigh = (base >> 24) & 0xFF;

	gdtEntry[num].LimitLow = (limit & 0xFFFF);
	gdtEntry[num].Granularity = ((limit >> 16) & 0x0F);

	gdtEntry[num].Granularity |= (gran & 0xF0);
	gdtEntry[num].Access = access;
}
I'm only initialising a few of the static (the name the Intel docs give them) values of my TSS, other than that my TSS is completely 0'ed out. The values I'm initialising are cr0, esp0 (located > 3GB), and the bitmap, then I load the TSS using
ltr 5<<3.

All page table entries and page directory entries > 3GB (my kernel's base address) have the user bit unset, while all below have it set.

Now, when an interrupt occurs (actually only tested with the timer, since it's the first interrupt that fires) bochs reports:

Code: Select all

00051171719i[CPU0 ] CS.d_b = 32 bit
00051171719i[CPU0 ] SS.d_b = 32 bit
00051171719i[CPU0 ] EFER   = 0x00000000
00051171719i[CPU0 ] | RAX=00000000c0109080  RBX=00000000c0104baa
00051171719i[CPU0 ] | RCX=00000000c00b8000  RDX=00000000c01003d5
00051171719i[CPU0 ] | RSP=00000000c010d03c  RBP=00000000c010d068
00051171719i[CPU0 ] | RSI=00000000c0104bb6  RDI=000000000005457d
00051171719i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00051171719i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00051171719i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00051171719i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00051171719i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df IF tf SF zf AF PF cf
00051171719i[CPU0 ] | SEG selector     base    limit G D
00051171719i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00051171719i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00051171719i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00051171719i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00051171719i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00051171719i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00051171719i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00051171719i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00051171719i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00051171719i[CPU0 ] | RIP=00000000c0100f23 (00000000c0100f23)
00051171719i[CPU0 ] | CR0=0xe0000011 CR1=0x0 CR2=0x0000000000000000
00051171719i[CPU0 ] | CR3=0x00420000 CR4=0x00000010
00051171719i[CPU0 ] >> ret  : C3
00051171719e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown
 status is 00h, resetting
Which is a GPF (at least I know it's protecting me... against myself). However, this isn't the behavior I'm looking for :(

I also have an interrupt handler for GPF, which too isn't picking it up (hence the triple fault).

What might I be doing wrong?

Also, can I make it so ESP does not change when an interrupt is called (I use software multitasking and save esi and esp manually, which I'm not sure how to get if my esp changes to tss->esp0)?
Last edited by AndrewAPrice on Thu Aug 28, 2008 8:13 am, edited 2 times in total.
My OS is Perception.
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:

Re: TSS (ring 3)

Post by Combuster »

To handle interrupts you'll need an IDT entry for that interrupt (at minumum, you'll need one for GPF, pagefault and doublefault)
The segment selectors go into the GDT

And you can do without TSS as long as you stay in kernel mode. For usermode you'll need both ss0 and esp0 set correctly
"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
AndrewAPrice
Member
Member
Posts: 2308
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: TSS (ring 3)

Post by AndrewAPrice »

My IDT is set up and faults/timer are successfully handled before I load the TSS (ltr).

I've initialised SS0 to be (0x10) which should be my second segment entry.

For now I've marked all my pages as supervisor (only bits 0 (present) and 1 (read/write) of their page directory/table entries are set), so it shouldn't be jumping in to ring 3 at all.

I was wondering, maybe my timer handler was being called in ring 3, which called protected instructions, so I've stripped my interrupt handler for my timer down to do:

Code: Select all

timer_handle:
   iret
Now the kernels should just hang in a loop, but it continues to triple fault with a GPF. But I've noticed Bochs says I'm in ring 0 (IOPL = 0) so that can't be the case.

I also noticed CR4 had bit 5 set (PSE) but I'm using 4KB pages (yet it wasn't screwing up before?) so I've unset that.

Am I required to set cr3 in the TSS? I write to cr3 manually before I initialize the GDT, and on context switches (currently not being called since my timer handler does nothing).

This is my TSS struct, I don't think I've missed anything:

Code: Select all

typedef struct {
	unsigned short	backlink, __blh;
	unsigned int	esp0;
	unsigned short	ss0, __ss0h;
	unsigned int	esp1;
	unsigned short	ss1, __ss1h;
	unsigned int	esp2;
	unsigned short	ss2, __ss2h;
	unsigned int	cr3;
	unsigned int	eip;
	unsigned int	eflags;
	unsigned int	eax, ecx, edx, ebx;
	unsigned int	esp, ebp, esi, edi;
	unsigned short	es, __esh;
	unsigned short	cs, __csh;
	unsigned short	ss, __ssh;
	unsigned short	ds, __dsh;
	unsigned short	fs, __fsh;
	unsigned short	gs, __gsh;
	unsigned short	ldt, __ldth;
	unsigned short	trace, iobitmap;
} __attribute__((packed)) TSS;
My OS is Perception.
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: TSS (ring 3)

Post by egos »

Try this:

Code: Select all

typedef struct {
	...
	unsigned short	trace, iobitmap;
	unsigned char term;
} __attribute__((packed)) TSS;

tss.iobitmap=sizeof(TSS)-1;
tss.term=0xFF;
And check the TSS descriptor. Base: linear address of TSS, limit: sizeof(TSS)-1, access rights: 0x89, granularity: 0x00!
If you have seen bad English in my words, tell me what's wrong, please.
xyzzy
Member
Member
Posts: 391
Joined: Wed Jul 25, 2007 8:45 am
Libera.chat IRC: aejsmith
Location: London, UK
Contact:

Re: TSS (ring 3)

Post by xyzzy »

Could you post the code you're using to set up the IDT? Also, after you've set up your GDT, are you reloading the segment registers?

Code: Select all

.global reload_segments
reload_segments:
        mov     $0x10, %ax
        mov     %ax, %ds
        mov     %ax, %es
        mov     %ax, %fs
        mov     %ax, %ss
        ljmp    $0x08, $1f
1:      ret
.size reload_segments, .-reload_segments
User avatar
AndrewAPrice
Member
Member
Posts: 2308
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: TSS (ring 3) [SOLVED]

Post by AndrewAPrice »

Thanks for both of your help. And I missed a few other bugs (e.g. the way my stack was set up before I called iret), but it's working great now. My servers are running exactly how they should (in usermode), and when they try to do something bad they GPF. :)
My OS is Perception.
Post Reply