OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Apr 18, 2024 11:48 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 5 posts ] 
Author Message
 Post subject: General Protection Fault with TSS
PostPosted: Tue May 28, 2019 1:54 pm 
Offline

Joined: Tue Jan 01, 2019 4:10 pm
Posts: 16
Location: Normandy, France
Hello,

I'm currently trying to implement a TSS for multitasking on my kernel. Here is my code:

Code:
static tss_entry_t tss_entry;

__attribute__((cold))
static void tss_init(void)
{
   const uint32_t base = (uint32_t) &tss_entry;
   const uint64_t limit = sizeof(tss_entry_t);
   const uint8_t flags = 0x40;
   const uint8_t access = 0x86;

   void *tss_gdt = tss_gdt_entry();
   bzero(tss_gdt, sizeof(uint64_t));
   *((uint16_t *) (tss_gdt)) = limit & 0xffff;
   *((uint16_t *) (tss_gdt + 2)) = base & 0xffff;
   *((uint8_t *) (tss_gdt + 4)) = (base >> 16) & 0xff;
   *((uint8_t *) (tss_gdt + 5)) = access;
   *((uint8_t *) (tss_gdt + 6)) = ((limit >> 16) & 0xf) | flags;
   *((uint8_t *) (tss_gdt + 7)) = (base >> 24) & 0xff;

   bzero(&tss_entry, sizeof(tss_entry_t));
   tss_entry.ss0 = 0x10;
   asm volatile("mov %%esp, %0" : "=a"(tss_entry.esp0));

   tss_flush();
}


Code:
__attribute__((packed))
struct tss_entry
{
   uint32_t prev_tss;
   uint32_t esp0;
   uint32_t ss0;
   uint32_t esp1;
   uint32_t ss1;
   uint32_t esp2;
   uint32_t ss2;
   uint32_t cr3;
   uint32_t eip;
   uint32_t eflags;
   uint32_t eax;
   uint32_t ecx;
   uint32_t edx;
   uint32_t ebx;
   uint32_t esp;
   uint32_t ebp;
   uint32_t esi;
   uint32_t edi;
   uint32_t es;
   uint32_t cs;
   uint32_t ss;
   uint32_t ds;
   uint32_t fs;
   uint32_t gs;
   uint32_t ldt;
   uint16_t trap;
   uint16_t iomap_base;
};

typedef struct tss_entry tss_entry_t;


Code:
.global tss_gdt_entry
.global tss_flush

tss_gdt_entry:
   mov gdt_tss, %eax

   ret

tss_flush:
   mov TSS_OFFSET, %ax
   ltr %ax

   ret


Basically, I'm getting a General Protection Fault when the kernel reaches the ltr instruction in tss_flush.
Is my GDT entry wrong or could the problem come from somewhere else?

Note: I already loaded my GDT when calling this code

Thanks in advance :)

_________________
Student at School 42 Paris (FR: https://42.fr/)


Top
 Profile  
 
 Post subject: Re: General Protection Fault with TSS
PostPosted: Tue May 28, 2019 6:52 pm 
Offline
Member
Member

Joined: Fri Jun 28, 2013 1:48 am
Posts: 65
The type field of your TSS descriptor should be 0x9, not 0x6.

Change
Code:
const uint8_t access = 0x86;

to
Code:
const uint8_t access = 0x89;

_________________
Reinventing the Wheel, code: https://github.com/songziming/wheel


Top
 Profile  
 
 Post subject: Re: General Protection Fault with TSS
PostPosted: Wed May 29, 2019 3:30 am 
Offline
Member
Member

Joined: Tue Mar 04, 2014 5:27 am
Posts: 1108
Crumble14 wrote:
Code:
static tss_entry_t tss_entry;

__attribute__((cold))
static void tss_init(void)
{
   const uint32_t base = (uint32_t) &tss_entry;
   const uint64_t limit = sizeof(tss_entry_t);


Limit should be size minus one.

And there's no need for uint64_t.

Crumble14 wrote:
Code:
   const uint8_t flags = 0x40;


Can't be 0x40. Most likely should be 0x10 or 0.

Crumble14 wrote:
Code:
   const uint8_t access = 0x86;


As already noted, it must be 0x89.

Crumble14 wrote:
Code:
   void *tss_gdt = tss_gdt_entry();
   bzero(tss_gdt, sizeof(uint64_t));
   *((uint16_t *) (tss_gdt)) = limit & 0xffff;


Why don't you use a proper C structure here?

Crumble14 wrote:
Code:
   *((uint16_t *) (tss_gdt + 2)) = base & 0xffff;


Adding anything to a pointer to void should not compile in proper C. I bet you're using the compiler in a rather permissive mode and this may be hiding bugs.

Crumble14 wrote:
Code:
   *((uint8_t *) (tss_gdt + 4)) = (base >> 16) & 0xff;
   *((uint8_t *) (tss_gdt + 5)) = access;
   *((uint8_t *) (tss_gdt + 6)) = ((limit >> 16) & 0xf) | flags;
   *((uint8_t *) (tss_gdt + 7)) = (base >> 24) & 0xff;

   bzero(&tss_entry, sizeof(tss_entry_t));
   tss_entry.ss0 = 0x10;
   asm volatile("mov %%esp, %0" : "=a"(tss_entry.esp0));


The SS#:ESP# pairs get loaded into SS:ESP when there's a privilege transition. In most/simplest cases it's when switching from ring/level 3 to ring/level 0 upon a hardware or software interrupt/exception, in which case SS:ESP gets loaded with SS0:ESP0 before the interrupt/exception handler starts. SS0:ESP0 should point to the memory used as stack for interrupt/exception handlers.
When already running in ring/level 0, there's no ring/level "-1" to switch to (and no SS"-1":ESP"-1") and the current SS:ESP continues to be used (and thus you may overflow your kernel stack).

IOW, those two lines are unneeded (if you intend to stay in ring/level 0) or wrong (if you intend to use e.g. ring/level 3).

Also, if you find yourself manipulating the stack in inline assembly, you must be doing something wrong. Reason: you don't know how the compiler generates code for stack management, what ESP and EBP point to. You don't know not because you didn't look at the disassembly. You don't know because there's no guarantee for the stack to have any particular layout in the middle of C code (ditto beginning/end). If you need to mess with the current stack, write a full assembly routine in a separate file (you'll normally do it for your entry points to interrupt/exception handlers and things similar to setjmp()/longjmp()).

Crumble14 wrote:
Code:
   tss_flush();
}


Code:
__attribute__((packed))
struct tss_entry
{
   uint32_t prev_tss;
   uint32_t esp0;
   uint32_t ss0;
   uint32_t esp1;
   uint32_t ss1;
   uint32_t esp2;
   uint32_t ss2;
   uint32_t cr3;
   uint32_t eip;
   uint32_t eflags;
   uint32_t eax;
   uint32_t ecx;
   uint32_t edx;
   uint32_t ebx;
   uint32_t esp;
   uint32_t ebp;
   uint32_t esi;
   uint32_t edi;
   uint32_t es;
   uint32_t cs;
   uint32_t ss;
   uint32_t ds;
   uint32_t fs;
   uint32_t gs;
   uint32_t ldt;
   uint16_t trap;
   uint16_t iomap_base;
};

typedef struct tss_entry tss_entry_t;


I'd rather not make the reserved fields part of the non-reserved ones. Set those reserved fields to 0 and don't access them afterwards. You're packing the structure anyway, so, you should be able to use more uint16_t's safely. Your structure should still retain at least a 4 byte alignment since it has 32-bit values in it.

Crumble14 wrote:
Code:
.global tss_gdt_entry
.global tss_flush

tss_gdt_entry:
   mov gdt_tss, %eax

   ret


If gdt_tss is the label at the beginning of a structure, you probably want this:
Code:
   movl $gdt_tss, %eax


Crumble14 wrote:
Code:
tss_flush:
   mov TSS_OFFSET, %ax
   ltr %ax

   ret


Likewise, if TSS_OFFSET is the TSS selector (or the offset of the TSS descriptor from the beginning of the TSS), as it should be, you want this instead:
Code:
   movw $TSS_OFFSET, %ax


If there's no $, the operand is considered to be in memory and so the mov instruction will read or write memory. If there is, the operand is just that number/address, no memory read/write will be involved.

EDIT: corrected the 0x40 part.


Top
 Profile  
 
 Post subject: Re: General Protection Fault with TSS
PostPosted: Wed May 29, 2019 4:07 am 
Offline

Joined: Tue Jan 01, 2019 4:10 pm
Posts: 16
Location: Normandy, France
alexfru wrote:
0x40 will overwrite bits 19...16 of the segment limit below... What did you want to get with this 0x40?


The value of 0x40 in binary is 0100 0000, so doing a binary OR like I do just writes on the high nibble.

alexfru wrote:
Why don't you use a proper C structure here?


Didn't thought about it at the moment, it's a good idea, thanks ^^

alexfru wrote:
Adding anything to a pointer to void should not compile in proper C. I bet you're using the compiler in a rather permissive mode and this may be hiding bugs.


Well, I'm compiling with -Wall -Wextra -Werror and I'm not getting any errors. Should I have more flags?


Anyway, I fixed everything you pointed out and it seems to work now. Thank you very much :)

_________________
Student at School 42 Paris (FR: https://42.fr/)


Top
 Profile  
 
 Post subject: Re: General Protection Fault with TSS
PostPosted: Wed May 29, 2019 4:36 am 
Offline
Member
Member

Joined: Tue Mar 04, 2014 5:27 am
Posts: 1108
Crumble14 wrote:
Well, I'm compiling with -Wall -Wextra -Werror and I'm not getting any errors. Should I have more flags?

-std=c99 to restrict to standard C w/o most (or all?) GNU extensions
-O2 (or -O3) will enable code analysis and make a number of warnings possible
-pedantic
Play with those.

Crumble14 wrote:
Anyway, I fixed everything you pointed out and it seems to work now. Thank you very much :)

Cool!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot], SemrushBot [Bot] and 121 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group