Page 1 of 1
TSS, userspace and segment selectors.
Posted: Sat Nov 22, 2014 11:15 am
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).
.... . .-.. .--.
Re: TSS, userspace and segment selectors.
Posted: Sat Nov 22, 2014 4:47 pm
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
Re: TSS, userspace and segment selectors.
Posted: Sun Nov 23, 2014 2:05 am
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.
Re: TSS, userspace and segment selectors.
Posted: Sun Nov 23, 2014 6:09 am
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
Re: TSS, userspace and segment selectors.
Posted: Mon Nov 24, 2014 11:21 am
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...
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?
Re: TSS, userspace and segment selectors.
Posted: Mon Nov 24, 2014 11:31 am
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.
Re: TSS, userspace and segment selectors.
Posted: Mon Nov 24, 2014 11:50 am
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.
Re: TSS, userspace and segment selectors.
Posted: Tue Nov 25, 2014 6:54 am
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?
Re: TSS, userspace and segment selectors.
Posted: Tue Nov 25, 2014 9:20 am
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.
Re: TSS, userspace and segment selectors.
Posted: Wed Nov 26, 2014 10:01 pm
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?
). 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.
Re: TSS, userspace and segment selectors.
Posted: Thu Nov 27, 2014 2:57 am
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