分类:
2012-03-17 00:22:13
原文地址:GDT简介(翻译中) 作者:sonald
如果你使用intel的SYSENTER/SYSEXIT例程,那么GDT必须是如下的结构:
参考Intel Instruction Reference关于SYSENTER and SYSEXIT部分获取更多信息。
一种对4G内存的简单安排:
|
在此模式下,因为代码段和数据段重叠,代码还是有被覆写的危险。
由于某种原因,你想使代码和数据清晰的独立开,可以这样设置:
|
That means whatever you load at physical address 4 MiB will appear as code at CS:0 and what you load at physical address 8 MiB will appear as data at DS:0. However it might not be the best design.
我们该如何做?
如果中断处于打开状态,则禁止它们。
你也许注意到我并没有给出一个GDT的真实结构,这样做是有原因的。由于世界的描述符结构很复杂,基址被拆成3个不同的域(field),并且你不能为限长(limit)指定随意数值。而且如果想一切正常工作,必须为许多标志设置正确的值。
|
Okay, that's rather ugly, but it's the most 'for dummies' i can come with ... hope you know about masking and shifting. You can hard-code that rather than convert it at runtime, of course. It assumes you want 32 bits stuff, too, and it's probably not valid for gates and other things i didn't talk about.
Some assembly example is required here. While you could use inline assembly, the memory packing expected by LGDT and LIDT makes it much easier to write a small assembly routine instead. As said above, you'll use LGDT instruction to load the base address and the limit of the GDT. Since the base address should be a linear address, you'll need a bit of tweaking depending of your current MMU setup.
The linear address should here be computed as segment * 16 + offset. I'll assume GDT and GDT_end are symbols in the current data segment.
|
"Flat" meaning the base of your data segment is 0 (regardless of whether paging is on or off). This is the case if you're just been booted by GRUB, for instance. I'll assume you call setGdt(GDT, sizeof(GDT)).
|
If your data segment has a non-zero base (e.g. you're using a HigherHalfKernel during the segmentation trick), you'll have to "ADD EAX, base_of_your_data_segment_which_you_should_know" between the "MOV EAX, ..." and the "MOV ..., EAX" instructions of the sequence above.
Whatever you do with the GDT has no effect on the CPU until you load selectors into segment registers. You can do this using
|
Much like the GDT (global descriptor table), the LDT (local descriptor table) contains descriptors for memory segments description, call gates, etc. The good thing with the LDT is that each task can have its own LDT and that the processor will automatically switch to the right LDT when you use hardware task switching.
Since its content may be different in each task, the LDT is not a suitable place to put system stuff such as TSS or other LDT descriptors: Those are the sole property of the GDT. Since it is meant to change often, the command used for loading an LDT is a bit different from the GDT and IDT loading. Rather than giving directly the LDT's base address and size, those parameters are stored in a descriptor of the GDT (with proper "LDT" type) and the selector of that entry is given.
GDTR (base + limit) +-- GDT ------------+ | | SELECTOR ---> [LDT descriptor ]----> LDTR (base + limit) | | +-- LDT ------------+ | | | | ... ... ... ... +-------------------+ +-------------------+
Note that with 386+ processors, the paging has made LDT almost obsolete, and there's no longer need for multiple LDT descriptors, so you can almost safely ignore the LDT for OS developing, unless you have by design many different segments to store.
What is the IDT and is it needed?
As said above, the IDT (Interrupt Descriptor Table) loads much the same way as the GDT and its structure is roughly the same except that it only contains gates and not segments. Each gate gives a full reference to a piece of code (code segment, priviledge level and offset to the code in that segment) that is now bound to a number between 0 and 255 (the slot in the IDT).
The IDT will be one of the first things to be enabled in your kernel sequence, so that you can catch hardware exceptions, listen to external events, etc. See Interrupts for dummies for more information about the interrupts of X86 family.