Page 1 of 1
[resolved] TSS and system segments in Long Mode.
Posted: Thu Jun 05, 2008 6:15 am
by deste
Hi,
To create my OS, I need to manage the TSS, with descriptors systems in the GDT.
The problem is that I can't create them in C, and I get a TripleFault.
Can you tell me how can I easily create system descriptors, in Long Mode ?
Cheers.
(sorry for my English, I'm french)
Posted: Thu Jun 05, 2008 7:02 am
by AJ
You absolutely can create a GDT in C, but you will need a way of separating your code compiled for 32 and 64 bit. I prefer to go to long mode in my boot loader, so the kernel is pure 64 bit. The kernel can always manage the GDT later. If you prefer to do it another way, you will need to link 64 and 32 bit code in to the same executable - something which is probably easy, but I have never tried.
I suggest you run your code in Bochs, which will often give you some nice debug information. When playing with the GDT, a single bit out of place can give you an immediate triple fault. Really verify that the GDT entries are exactly correct, read the Intel Manuals (available for download or in hard copy) and if all else still fails, post the Bochs debug dump here and we will try to help further.
Cheers,
Adam
Posted: Thu Jun 05, 2008 7:09 am
by deste
Hi,
The GDT is created in C, and I believe that his recordings are good. The kernel is 100% in Long Mode.
I test my OS in Qemu, which gives me a dump of registers on a TripleFault.
Here is the code (inline assembly) which generates my GDT's entries :
Code: Select all
void CreateSysSegment(int index, int64 baseAddress, int16 limit, int16 flags) {
int64 addr = 0;
addr = ((int64)(index))*8; //entries measures 8 bytes.
addr += 0x30000; //The base address of GDT is 0x30000.
//inline assembly in GCC.
asm volatile (
"mov %0, %%rbx\n"
"mov %1, %%ax\n"
"mov %%ax, (%%rbx)\n"
"mov %2, %%rax\n"
"mov %%eax, 2(%%rbx)\n"
"mov %3, %%ax\n"
"mov %%ax, 5(%%rbx)\n"
"mov %2, %%rax\n"
"shl $24, %%rax\n"
"mov %%rax, 7(%%rbx)\n"
"mov $0x0, %%eax\n"
"mov %%eax, 12(%%rbx)\n"
:
: "m" (addr), "m" (limit), "m" (baseAddress), "m" (flags)
: "rbx", "rax"
);
}
EDIT: I forgot the exit code of Qemu, here it is:
Code: Select all
IN:
0x00000000008007c4: push %rbp
0x00000000008007c5: mov %rsp,%rbp
0x00000000008007c8: mov %di,0xfffffffffffffffc(%rbp)
0x00000000008007cc: shlw $0x3,0xfffffffffffffffc(%rbp)
0x00000000008007d1: movzwl 0xfffffffffffffffc(%rbp),%eax
0x00000000008007d5: ltr %ax
0x00000000008007d8: leaveq
0x00000000008007d9: retq
qemu: fatal: triple fault
RAX=0000000000000020 RBX=0000000000000000 RCX=000000000000808b RDX=0000000000000100
RSI=0000000000801580 RDI=0000000000000004 RBP=000000000000ff60 RSP=000000000000ff60
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=00000000008007d5 RFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =ffff 00000000000ffff0 0000ffff 00009300
CS =0010 0000000000000000 0fffffff 00a09a00
SS =0018 0000000000000000 0fffffff 00809300
DS =0018 0000000000000000 00000fff 00809300
FS =0000 0000000000000000 0000ffff 00009300
GS =0000 0000000000000000 0000ffff 00009300
LDT=0000 0000000000000000 0000ffff 00008000
TR =0000 0000000000000000 0000ffff 00008000
GDT= 0000000000030000 0000ffff
IDT= 0000000000000000 00000200
CR0=80000011 CR2=0000000000000080 CR3=0000000000050000 CR4=00000020
CCS=0000000000000010 CCD=0000000000000020 CCO=SHLW
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
Cheers.
Posted: Thu Jun 05, 2008 7:19 am
by AJ
Hi,
I'm not going to try to debug that assembly - someone else will be better at that, but I can tell you what the problem is. You seem to be getting a page fault exception at 0x0000000000000080, which strikes me as what happes if someone uses an array based on a null pointer.
You are triple-faulting rather than handling the page fault gracefully which means that either you have no 64 bit IDT or you are trying to load RIP or RSP with an unpaged area. I wonder if that 'iret' statement could be trying to load invalid data or the stack has become misaligned?
Cheers,
Adam
Posted: Thu Jun 05, 2008 7:28 am
by deste
Hi,
The page 0x00000080 (address 0x800000) is properly mapped, the kernel is at this address. I will verify the contents of my TSS, as it may be bad.
I can't use IDT without TSS and TSS requires a system descriptor, which is why I asked this question.
Thank you very much for your help.
Posted: Thu Jun 05, 2008 8:01 am
by AJ
Hi,
CR2 actually contains the linear address of the page fault, not the page number. This means something is trying to access linear address 0x80, not 0x800000.
Also, you can load the IDT before you have a TSS and it will work as it should do (I handle exceptions and IRQ's and I have no TSS yet). You only need a TSS once you start multitasking. Note also that long mode does not use the 'task switch in interrupt' mechanism like PMode. Instead, you can switch stack using the IST mechanism.
Cheers,
Adam
Posted: Thu Jun 05, 2008 8:06 am
by deste
Hi,
In Long Mode, interruptions in the IDT require IST, which must be in the TSS.
I recoded function, using structures. They are:
Code: Select all
typedef struct {
int16 limit;
int16 base0_15;
int8 base16_23;
int16 flags;
int8 base24_31;
} __attribute__ ((packed)) gdtenrg;
typedef struct {
int16 limit;
int16 base0_15;
int8 base16_23;
int16 flags;
int8 base24_31;
int32 base32_63;
int32 zeros;
} __attribute__ ((packed)) gdtsysenrg;
void CreateSegment(int index, int16 flags) {
gdtenrg *enrg;
enrg = (gdtenrg *) 0x30000;
enrg[index].limit = 0;
enrg[index].base0_15 = 0;
enrg[index].base16_23 = 0;
enrg[index].flags = flags;
enrg[index].base24_31 = 0;
}
void CreateSysSegment(int index, int64 baseAddress, int16 limit, int16 flags) {
gdtsysenrg *enrg;
int64 mindex = (int64) index;
enrg = (gdtsysenrg *)(0x30000+(mindex*8));
enrg->limit = limit;
enrg->base0_15 = (int16)baseAddress;
enrg->base16_23 = (int8)(baseAddress>>16);
enrg->flags = flags;
enrg->base24_31 = (int8)(baseAddress>>24);
enrg->base32_63 = (int32)(baseAddress>>32);
enrg->zeros = 0;
}
Cheers.
Posted: Thu Jun 05, 2008 8:20 am
by AJ
In Long Mode, interruptions in the IDT require IST, which must be in the TSS.
No. Note that the IST's are numbered 1-7 (not 0-7). Specifying a zero IST means that the CPU keeps the current stack if you are running in ring 0 (you
do not need a TSS), or loads RSP0 if you are running in ring 3 (for which you
do need a TSS).
Cheers,
Adam
Posted: Thu Jun 05, 2008 8:23 am
by deste
Thank you very much !
That works !
Re: TSS and system segments in Long Mode.
Posted: Mon Jul 14, 2008 10:04 pm
by Mr.Confuzed
I wouldn't say that this is resolved. I have been unsuccessful at this as well. I'll ignore it for now, because:
AJ wrote:you do not need a TSS
It would be really nice if someone would post a working example here or on the wiki.
Re: [resolved] TSS and system segments in Long Mode.
Posted: Tue Jul 15, 2008 2:43 am
by AJ
Mr.Confuzed wrote:It would be really nice if someone would post a working example here or on the wiki.
What of? Interrupts in Long Mode? A Long Mode TSS? If you just want to set up a long mode TSS, see sandpile.org for the complete layout or the Intel Manuals for a full explanation.
Sorry if I misunderstood and you want an example of something different.
Cheers,
Adam
Re: [resolved] TSS and system segments in Long Mode.
Posted: Tue Jul 15, 2008 9:24 pm
by Mr.Confuzed
Hm, sandpile.org looks useful. I'll have to remember that. Unfortunately, it didn't help today.
Yes, I was looking for an example of how to build a long mode TSS. I have been using the AMD64 manuals extensively. Let's see if you can help. My code produces a GP fault at the ltr and works perfectly when it is commented out. Bochs says, "LTR: loading with NULL selector!" I fail to see why.
Code: Select all
;Global Descriptor Table
gdt:
dq 0x0000000000000000
.code equ $ - gdt
dq 0x00af98000000ffff
.data equ $ - gdt
dq 0x008f90000000ffff
.TSS equ $ - gdt
dq 0x000089000000006a
dq 0x0000000000000000
.pointer:
dw $-gdt-1
dq gdt
Code: Select all
startLongMode:
lidt [idt.pointer]
sti
xor rcx,rcx
xor rdi,rdi
mov rax,0x000003b800000000
mov cl,13
rep stosq
mov ax,0x0068
mov [0x66],ax
mov eax,0xffffffff
mov [0x68],eax
ltr [gdt.TSS]
Thanks in advance!
Re: [resolved] TSS and system segments in Long Mode.
Posted: Wed Jul 16, 2008 2:44 am
by AJ
Ok,
I'm normally doing this stuff using C functions which nicely split the access / granularity etc... bytes of the GDT entires, but think I can see what's going on
I can't see that the "Base" of your TSS descriptor actually points to a valid TSS. It seems to be pointing to NULL.
Am I right in thinking that this should be:
? Again - I could be wrong. I'm just going to have a look through my archives and see how my test kernel did this (if I can find it!).
Cheers,
Adam
Re: [resolved] TSS and system segments in Long Mode.
Posted: Wed Jul 16, 2008 6:17 pm
by Mr.Confuzed
Argh! How could I be so stupid! Well, it turns out you're wrong. I had tried that, but in doing so, caused NASM to yell, "error: invalid combination of opcode and operands", regardless of the fact that it would seem to work in protected mode. It turns out that you have to do this...
... and this ...
... to make it work, despite that being the silliest way of doing things that I know of.
I guess my weekend wasn't wasted after all! Thank you for "passing" my brain fart, as it were.