Re: How to make a GDT?
Posted: Tue Aug 16, 2022 3:08 pm
Ah, OK, thank you for that correction.
So if I'm correct, I'll have 4 of these structs set up.Schol-R-LEA wrote: Now, if you want to use a C struct to organize that memory, you will want something like this:
Code: Select all
struct __attribute__((packed, aligned(4))) GDT { uint16_t base_low; uint16_t limit_low; uint8_t base_mid; uint8_t access; uint8_t limit_high_and_flags; uint8_t base_high; };
Specifically an array of four of these structs.zap8600 wrote:So if I'm correct, I'll have 4 of these structs set up.
The array of structs is your GDT. Are you asking how to put information into your GDT, or are you asking how to tell the CPU to use your GDT?zap8600 wrote:So how do I load this into the GDT?
Sorry. I was confused.Octocontrabass wrote: The array of structs is your GDT. Are you asking how to put information into your GDT, or are you asking how to tell the CPU to use your GDT?
Create a GDTR descriptor (you can also use a struct) and then use the LGDT instruction to load the GDTR descriptor into GDTR. After you tell the CPU where your GDT is, you should load new selectors into the segment registers. Your code might look something like this, if you use a struct for the GDTR descriptor:zap8600 wrote:I meant telling the CPU where the table is.
Code: Select all
struct __attribute__((packed)) GDTR
{
uint16_t limit;
uint32_t base;
};
void example( uint32_t base, uint16_t limit )
{
struct GDTR gdtr = { limit, base };
asm( "lgdt %0" :: "m"(gdtr) );
asm( "ljmp %0,$1f\n1:" :: "i"(KERNEL_CODE_SEG) );
asm( "mov %0, %%ds" :: "r"(KERNEL_DATA_SEG) );
asm( "mov %0, %%es" :: "r"(KERNEL_DATA_SEG) );
asm( "mov %0, %%ss" :: "r"(KERNEL_DATA_SEG) );
//asm( "mov %0, %%fs" :: "r"(KERNEL_FS_SEG) );
//asm( "mov %0, %%gs" :: "r"(KERNEL_GS_SEG) );
}
Take your base address and put the low 16 bits into base_low, the next 8 bits into base_mid, and the top 8 bits into base_high.zap8600 wrote:Also, how would I find the values for base_low, mid and high?
Take your limit and put the low 16 bits into limit_low and the high four bits into limit_high_and_flags. The limit is only 20 bits; it indicates either bytes or pages according to the granularity flag.zap8600 wrote:Same question for limit_low and limit_high_and_flags.
The high four bits of the limit go into the low four bits of limit_high_and_flags. Four bits of flags go into the high four bits of limit_high_and_flags. You can use bitwise operations to place the values you want in the correct locations. The Intel and AMD manuals have excellent diagrams showing where everything goes.zap8600 wrote:Also, how would I add both limit_high and the flags to limit_high_and_flags?
I see what I'm doing wrong. I'm confusing the GDT struct for the Segment Struct. I'm very dumb.Octocontrabass wrote:Create a GDTR descriptor (you can also use a struct) and then use the LGDT instruction to load the GDTR descriptor into GDTR. After you tell the CPU where your GDT is, you should load new selectors into the segment registers. Your code might look something like this, if you use a struct for the GDTR descriptor:
Unlike the earlier example, this struct must be packed because the compiler would insert padding otherwise. Alignment is unnecessary.Code: Select all
struct __attribute__((packed)) GDTR { uint16_t limit; uint32_t base; }; void example( uint32_t base, uint16_t limit ) { struct GDTR gdtr = { limit, base }; asm( "lgdt %0" :: "m"(gdtr) ); asm( "ljmp %0,$1f\n1:" :: "i"(KERNEL_CODE_SEG) ); asm( "mov %0, %%ds" :: "r"(KERNEL_DATA_SEG) ); asm( "mov %0, %%es" :: "r"(KERNEL_DATA_SEG) ); asm( "mov %0, %%ss" :: "r"(KERNEL_DATA_SEG) ); //asm( "mov %0, %%fs" :: "r"(KERNEL_FS_SEG) ); //asm( "mov %0, %%gs" :: "r"(KERNEL_GS_SEG) ); }
So what should the size of it be? Do I find the size in the Intel and AMD Manual? I'm not really sure how large it should be. I'm using the segments from here.iansjack wrote:You don’t “find” those values, you choose them according to your requirements. Where do you want your segments to start; how big do you want them to be; those are design decisions that you make.
You don't "find" the size, you choose it. How do you want to use segments in your OS? Do you want to use segments at all? The values that go into your GDT depend on what you want the segments to do in your OS.zap8600 wrote:So what should the size of it be? Do I find the size in the Intel and AMD Manual? I'm not really sure how large it should be. I'm using the segments from here.
Well I would like to setup Userspace at some point. I also thought that the IDT required the GDT.Octocontrabass wrote: You don't "find" the size, you choose it. How do you want to use segments in your OS? Do you want to use segments at all? The values that go into your GDT depend on what you want the segments to do in your OS.
Both of those do require a GDT, but your GDT doesn't have to set up segmentation - you can use a "flat" setup that bypasses segmentation as much as possible.zap8600 wrote:Well I would like to setup Userspace at some point. I also thought that the IDT required the GDT.
If it isn't necessary, then I won't add it.Octocontrabass wrote: Both of those do require a GDT, but your GDT doesn't have to set up segmentation - you can use a "flat" setup that bypasses segmentation as much as possible.
So, do you want your OS to use segmentation? Or would you prefer the "flat" memory model that doesn't use segmentation?
For a flat memory model, the base of the code and data segments should be 0 and the limit should be 0xFFFFFFFF bytes (0xFFFFF pages).zap8600 wrote:So then what would the base and limit of the GDT be? Would it just be 0 for both?
No. You can bypass segmentation and still use paging.zap8600 wrote:Also, does paging require segmentation?
I already gave you example code for that.zap8600 wrote:I'm unsure how to write the setGDT function from here with inline assembly. How would I write it with the GDT descriptor struct
I'm sorry. I just wasn't sure it it would wotk with GRUB.Octocontrabass wrote: I already gave you example code for that.