TSS, userspace and segment selectors.

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
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

TSS, userspace and segment selectors.

Post by ExeTwezz »

Hi,

I've finished developing simple (Round-Robin) multitasking in the kernel, and threads can run only in the kernel mode (ring0). So, I've begun implementing switching to and from ring3. First, I've addded three segments into the GDT: ring3 code, ring3 data, 32-bit TSS. I've added a simple TSS setting up and loading function, but Bochs reboots and in the console there is:

Code: Select all

00089235313i[CPU0 ] 0x000000000010006e>> ltr ax : 0F00D8
00089235313e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
I've followed this tutorial, and changed some things because I've been confused since the limit of the TSS is different (with adding base and without). Anyway, I've got the above message in Bochs.

The code for setting the TSS descriptor in the GDT (`tss' is defined in the same file, with no `static'):

Code: Select all

// Setup the TSS segment.
void setup_tss (void)
{
    // Set the GDT entry.
    uint32_t base = (uint32_t) &tss;
    uint32_t limit = base + sizeof (tss_t); // Not sure here, it's different in the code I saw.
    set_gdt_entry (5, base, limit, 0xE9, 0x00); // TSS

    // Zero the TSS.
    memset ((uint8_t *) &tss, 0, sizeof (tss_t));

    // Fill the TSS (not the whole, only the used by the OS fields).
    tss.esp0 = 0; // It is changed *somewhere* in the IRQ0 handler or somewhere else, I don't know.
    // But actually, I change it nowhere, since the TSS isn't loaded anyway (see the above [code] tag).
    tss.ss0 = 0x10; // Data segment.
}

// Change the TSS `esp0' field.
void set_kernel_stack (uint32_t esp0)
{
    tss.esp0 = esp0;
}
I'm sure that my GDT in the C code has enough space for 6 segments (null, code, data, code3, data3, tss). I suppose that the error is in the TSS descriptor, but I don't know where, since I've filled it as in the above link).

Upd: And yeah, the `tss_t' structure is the same as in the above link. Also, I've a question about segment selectors. If the segment in the GDT is ring3, then do I need to set the "privilege level" bits to 3 (e.g. 0x1B for 0x18 ring3 code segment).

.... . .-.. .--.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: TSS, userspace and segment selectors.

Post by Brendan »

Hi,
ExeTwezz wrote:

Code: Select all

00089235313i[CPU0 ] 0x000000000010006e>> ltr ax : 0F00D8
00089235313e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
From this I'd assume that either AX contained a dodgy value (e.g not 0x0028) or the GDT entry is wrong. I don't know which value was in AX or what the GDT entry actually looks like.

Note: Loading the Task Register just tells the CPU which GDT entry contains the "currently being used" TSS descriptor. The CPU doesn't read or write anything to/from the TSS that the descriptor describes (until later).
ExeTwezz wrote: // Set the GDT entry.
uint32_t base = (uint32_t) &tss;
uint32_t limit = base + sizeof (tss_t); // Not sure here, it's different in the code I saw.
set_gdt_entry (5, base, limit, 0xE9, 0x00); // TSS
Limit should be "sizeof(tss_t) - 1". Also note that this is probably the only segment you have where the granularity bit would be clear (limit is in bytes, not pages).

If 0xE9 is the descriptor's "DPL and type" byte; then you don't want CPL=3 code to be able to use the descriptor so DPL should be 0, and I think the busy flag (bit 2) should be set (think of it as the TSS descriptor for the currently executing code, which is busy because the currently executing code is using it). This would mean that the 0xE9 should probably be 0x8B.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: TSS, userspace and segment selectors.

Post by ExeTwezz »

Hi, Brendan.

Yep, I've forgotten to write about TSS loading routine:

Code: Select all

global load_tss
load_tss:
    mov ax, 0x2B ; 6th entry (counting from 1) in the GDT with privilege level set to 3.
    ; Upd: now 0x28.
    ltr ax
Brendan wrote:Limit should be "sizeof(tss_t) - 1". Also note that this is probably the only segment you have where the granularity bit would be clear (limit is in bytes, not pages).

If 0xE9 is the descriptor's "DPL and type" byte; then you don't want CPL=3 code to be able to use the descriptor so DPL should be 0, and I think the busy flag (bit 2) should be set (think of it as the TSS descriptor for the currently executing code, which is busy because the currently executing code is using it). This would mean that the 0xE9 should probably be 0x8B.
Yes, this is the access byte. I've filled it as in http://wiki.osdev.org/Getting_to_Ring_3, because I didn't and don't know how the TSS entry differs from an usual GDT entry. And I think 0x8B is more correct than 0xE9.

Code: Select all

// Setup the TSS segment.
void setup_tss (void)
{
    // Set the GDT entry.
    uint32_t base = (uint32_t) &tss;
    uint32_t limit = sizeof (tss_t) - 1;
    set_gdt_entry (5, base, limit, 0x8B, 0x40); // TSS

    // Zero the TSS.
    memset ((uint8_t *) &tss, 0, sizeof (tss_t));

    // Fill the TSS (not the whole, only the used by the OS fields).
    tss.esp0 = 0; // It is changed in switch_thread().
    tss.ss0 = 0x10; // Data segment.
}
But it is still

Code: Select all

00089235313i[CPU0 ] 0x000000000010006e>> ltr ax : 0F00D8
00089235313e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
As for GDT setting up and loading, I set up the TSS first, then load the GDT, and then load the TSS:

Code: Select all

global kernel_ll
kernel_ll:
    cli
    extern setup_tss            ; multitasking/tss.c
    call setup_tss              ; Setup the TSS.
    extern init_gdt             ; gdt/gdt.c
    call init_gdt				   ; Load the GDT.
    call load_tss

    ; Then the C kernel main is called.
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, userspace and segment selectors.

Post by Combuster »

ExeTwezz wrote:But it is still

Code: Select all

00089235313i[CPU0 ] 0x000000000010006e>> ltr ax : 0F00D8
00089235313e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
I'm also sure that's still the last error you're getting, not the first one. You're skipping the most valuable information.

Brendan wrote:From this I'd assume that either AX contained a dodgy value (e.g not 0x0028)
ExeTwezz wrote:mov ax, 0x2B
"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 ]
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: TSS, userspace and segment selectors.

Post by ExeTwezz »

Hi, Combuster.
Combuster wrote:I'm also sure that's still the last error you're getting, not the first one. You're skipping the most valuable information.
Well... :D Thank you.

Code: Select all

00089235311e[CPU0 ] fetch_raw_descriptor: GDT: index (2f) 5 > limit (27)
00089235311e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00089235311e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00089235311i[CPU0 ] CPU is in protected mode (active)
00089235311i[CPU0 ] CS.d_b = 32 bit
00089235311i[CPU0 ] SS.d_b = 32 bit
00089235311i[CPU0 ] EFER   = 0x00000000
00089235311i[CPU0 ] | RAX=0000000000000028  RBX=0000000000010000
00089235311i[CPU0 ] | RCX=0000000000000000  RDX=0000000000000000
00089235311i[CPU0 ] | RSP=0000000000106ff8  RBP=0000000000000000
00089235311i[CPU0 ] | RSI=0000000000000000  RDI=0000000000000000
00089235311i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00089235311i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00089235311i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00089235311i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00089235311i[CPU0 ] | IOPL=0 ID vip vif ac vm RF nt of df if tf sf zf af pf cf
00089235311i[CPU0 ] | SEG selector     base    limit G D
00089235311i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00089235311i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00089235311i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00089235311i[CPU0 ] |  SS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
00089235311i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00089235311i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00089235311i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00089235311i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00089235311i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00089235311i[CPU0 ] | RIP=000000000010006e (000000000010006e)
00089235311i[CPU0 ] | CR0=0x60000011 CR2=0x0000000000000000
00089235311i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00089235311i[CPU0 ] 0x000000000010006e>> ltr ax : 0F00D8
00089235311e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00089235311i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00089235311i[CPU0 ] cpu hardware reset
From this, I've just remembered that I've forgotten to change the limit of the GDT descriptor.

OK, fixed this. Now, Bochs is saying:

Code: Select all

00089235311e[CPU0 ] LTR: doesn't point to an available TSS descriptor!
Maybe it's because I'm incorrectly loading TSS. I don't know because I set up the TSS descriptor in the 5th entry of the GDT and load 0x28 into LTR. What do you think?
Last edited by ExeTwezz on Mon Nov 24, 2014 11:32 am, edited 1 time 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:

Re: TSS, userspace and segment selectors.

Post by Combuster »

ExeTwezz wrote:

Code: Select all

00089235311e[CPU0 ] LTR: doesn't point to an available TSS descriptor!
I think it's because I set up and load GDT&TSS in incorrect sequence. Maybe no... Maybe it's because I'm incorrectly loading TSS. I don't know because I set up the TSS descriptor in GDT #5 and load 0x28 into LTR. What do you think?
I think the error message is obvious and more importantly, you didn't use a debugger to see what it actually tries to load.
"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 ]
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: TSS, userspace and segment selectors.

Post by ExeTwezz »

Combuster wrote:I think the error message is obvious and more importantly, you didn't use a debugger to see what it actually tries to load.
Well... Using Bochs debugger I saw that it really loads 0x28 into EAX and then EAX into LTR... But actually, from the error I know that "LTR: doesn't point to an available TSS descriptor!". That's all.

Okay, I've an incorrect GDT entry. I think. But why? Access byte is 0x9B and flags is 0x4. Base and limit are a pointer to the empty TSS and its (TSS's) size respectively.
Last edited by ExeTwezz on Tue Nov 25, 2014 6:55 am, edited 1 time in total.
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: TSS, userspace and segment selectors.

Post by ExeTwezz »

I was incorrectly filling in the TSS descriptor. Now it is loaded, but I get Page Fault after `iret' (err code: 101 bin). I think It is caused by empty registers (fields) in the TSS. Do I need to fill in the whole TSS?
naegelejd
Posts: 12
Joined: Thu Oct 09, 2014 5:09 pm

Re: TSS, userspace and segment selectors.

Post by naegelejd »

Add a bochs magic breakpoint just before the IRET (xchg bx, bx) then dump the stack in bochs. A page fault after IRET could easily be caused by ESP off-by-one issues because IRET expects to pop EIP first, which must be a valid address in code.

I do not fill the TSS, but I do set SS0, ESP0, CS, SS, DS, ES, FS, GS and I set IOMAP to sizeof(struct tss) which is 104 bytes in my case.
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: TSS, userspace and segment selectors.

Post by ExeTwezz »

Hi,

After a little "debugging" for a couple of days, I've concluded that page fault is caused by accessing a kernel page in user mode (silly, yeah? :D). When I'll boot to the Ubuntu, I'd like to correct the page directory of my 2 threads and see what'll be. But, as says http://wiki.osdev.org/Exceptions#Page_Fault,
the above link wrote: U 1 bit User When set, the page fault was caused while CPL = 3. This does not necessarily mean that the page fault was a privilege violation.
I shouldn't rely on this (that page fault may be caused by privilege violation), but I hope that it's true.
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, userspace and segment selectors.

Post by Combuster »

In the early stages of development, a pagefault is an error.

Later on you can use paging to implement lazy allocation of memory, copy-on-write and memory mapped files. Each of these gives a process an address without actually mapping the page, instead doing so when the page is actually used and thus generates a pagefault.

A third case - also an error - is when the TLB has cached a page with different privileges. This means that you're having race conditions or that you're not properly using INVLPG
"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 ]
Post Reply