Hi forum,
I've been reading this for a long time, but this is my first post. My toy OS is progressing nicely... I have virtual memory and paging set up, a decent console driver, interrupts working, a keyboard driver, and a floppy controller driver working. I'm trying to implement task switching with hardware, and the whole TSS thing giving me fits. I've read the Intel manuals over and over, and I understand the concepts well enough, it's just the implementation that's giving me fits. I was wondering if someone could tell me if I'm on the right track...
I have an initial TSS set up, so the first task switch has some place to store the current state. Then I have function, we'll call it idle(), that just prints a letter to the screen. I create a new TSS, and a descriptor for it, and put the descriptor in the GDT (which already has space allocated in it for it).
The TR register is initialized with the selector for the initial "dummy" TSS. Whenever I do the task switch, by ljmp'ing to the new task's selector, bochs dies, with the message "SS NULL".
My questions:
1. What fields in the TSS do I have to initialize before the first task switch to idle()? I'm leaving the intial TSS pretty much empty, since the CPU will fill it in on the first switch, right? I have the second TSS set up with the address of the idle() func for eip, and the kernel's DS, CS, SS, ES, GS, and FS. What value should I use for esp0? (This is all in the kernel right now, no privelidge level changes). Do I just use the current value of esp? All of my segment registers are base 0, spanning the whole 4 gig address space. I have one descriptor for kernel data, and one for kernel code. (0x10 and 0x8, respectively).
2. About the TSS descriptor. It's base and limit should be set to the location in memory of the TSS, and the exact size of it, correct? Like if my TSS is pointed to be the pointer *t, the base should be t, and the size 104 (or however big it is).
Is this right?
3. The selector I ljmp to is just the index into the GDT times 8, right? For a ring 0 task with no LDT being used anyway, I mean. I'm just saying something like asm("ljmp 0x30"), where 0x30 is the selector for the idle() task's TSS descriptor.
Any help would be appreciated. I think my problem lies somewhere in the descriptor, or in my questionable gcc inline assembly skills, but I wanted to make sure I'm following the right steps, conceptually.
Also, I wanted to say that this board totally rocks and I've learned more in the last month on this board than I could have imagined. You guys are awesome. Thanks in advance!
TSS woes
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:TSS woes
just put ss0 to your data selector, just to make sure ... all the rest seems okay for me.
Also, if ds.base != 0, just make sure you have proper address (e.g. post-segmentation, pre-paging address) in descriptor.
Btw, if you could get bochs's cpu dump (e.g. what's ss content, and are you still in earlier task or already in the newer task ?)
Also, if ds.base != 0, just make sure you have proper address (e.g. post-segmentation, pre-paging address) in descriptor.
Btw, if you could get bochs's cpu dump (e.g. what's ss content, and are you still in earlier task or already in the newer task ?)
Re:TSS woes
Thanks pype. That's what I'm doing. Like I said, I think that the problem is in my descriptor for the new TSS. Either that, or my inline asm that's calling ljmp... is it just me or does gcc inline assembly suck beyond belief?
I either get one of two errors now...
If I say this:
asm("ljmp %0\n\t"::"m" (new_task->tss_selector));
bochs doesn't crash, but my OS does (by design), with a GPF. The register dump from my GPF handler says that CS is 0x8 at this point.
If I say this:
asm("ljmp $0x30\n\t")
I get the "task_switch: SS NULL" error in bochs. new_task->tss_selector is 0x30 when I printf it's value right before the call.
I realize that the first example is using an immediate value for ljmp, and the second is using a memory reference, does that make a difference? Examples of using ljmp in actual code are pretty scarce, as far as I can find on google.
The problem could also be my descriptor. I have it defined as having fields for base_low, base_high, limit_low, and limit_high (due to the framented nature of the descriptors fields. I set base_low to the address of the tss_t struct, and base_high to 0. I have limit_low set to sizeof(tss_t) and limit_high set to 0. Does that sound right? I'm using the same descriptor struct for my other GDT entries, and it works fine, as far as I can tell. I'm still investigating...
I either get one of two errors now...
If I say this:
asm("ljmp %0\n\t"::"m" (new_task->tss_selector));
bochs doesn't crash, but my OS does (by design), with a GPF. The register dump from my GPF handler says that CS is 0x8 at this point.
If I say this:
asm("ljmp $0x30\n\t")
I get the "task_switch: SS NULL" error in bochs. new_task->tss_selector is 0x30 when I printf it's value right before the call.
I realize that the first example is using an immediate value for ljmp, and the second is using a memory reference, does that make a difference? Examples of using ljmp in actual code are pretty scarce, as far as I can find on google.
The problem could also be my descriptor. I have it defined as having fields for base_low, base_high, limit_low, and limit_high (due to the framented nature of the descriptors fields. I set base_low to the address of the tss_t struct, and base_high to 0. I have limit_low set to sizeof(tss_t) and limit_high set to 0. Does that sound right? I'm using the same descriptor struct for my other GDT entries, and it works fine, as far as I can tell. I'm still investigating...
Re:TSS woes
Just checking, but make sure that the TSS is in the first 64K of memory.I set base_low to the address of the tss_t struct, and base_high to 0.
Also, Ss0 shouldn't matter, as long as Cs is a DPL0 descriptor. Well... it is never touched by the CPU during a task switch - only during a interrupt/call gate/etc. that needs to switch stacks.
If your TSS spans pages, make sure they are physically contiguous.
If you can post a copy of the bochslog file, please do so.
Good luck,
Mike
Re:TSS woes
Here's the bochs log. Does the TSS really have to be in the first 64K of memory? I hadn't read that. I don't think that's the problem though.
Re:TSS woes
No the TSS does NOT have to be in the first 64KB, the TSS has a maximum length of 64KB though. It doesn't have to physically contiguous either, segments exist on top of pages. Make sure all pages are in memory though.
According to the BOCHS log, your SS0/SS3 is NULL, you should check that it's loaded correctly before the task switch.
According to the BOCHS log, your SS0/SS3 is NULL, you should check that it's loaded correctly before the task switch.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:TSS woes
okay, if you have a debugger-enabled bochs, i suggest you put a breakpoint right on your ljmp instruction (well, i'm not 100% sure of it in GAS syntax, but shouldn't it be "jmp 0x30:0" so that the CPU knows it's a selector, not an offset, but since it says "task switch", that shouldn't matter)
From that breakpoint, use the "x /8ux <gdtr.base+0x30>" to read your descriptor entry and then "x /64ux <tssbase>" to read your target TSS content ... it looks like the "SS" field actually contains a zero ...
From that breakpoint, use the "x /8ux <gdtr.base+0x30>" to read your descriptor entry and then "x /64ux <tssbase>" to read your target TSS content ... it looks like the "SS" field actually contains a zero ...
Re:TSS woes
Thanks for the suggestion, Pype. I'm going to try that when I get home from work... we'll see what it says.