环境:
virtual-box:版本 6.0.10 r132072 (Qt5.6.2)运行的的ubuntu18.04系统。
nasm汇编器:NASM version 2.13.02
gdt:
dq 0x0000000000000000 ; GDT 表。第 1 个描述符不用。
dq 0x00c09a00000007ff ; 第 2 个是内核代码段描述符。其选择符是 0x08。
dq 0x00c09200000007ff ; 第 3 个是内核数据段描述符。其选择符是 0x10。
dq 0x00c0920b80000002 ; 第 4 个是显示内存段描述符。其选择符是 0x18。
dw 0x68, tss0, 0xe900, 0x0 ; 第 5 个是 TSS0 段的描述符。其选择符是 0x20
dw 0x40, ldt0, 0xe200, 0x0 ; 第 6 个是 LDT0 段的描述符。其选择符是 0x28
dw 0x68, tss1, 0xe900, 0x0 ; 第 7 个是 TSS1 段的描述符。其选择符是 0x30
dw 0x40, ldt1, 0xe200, 0x0 ; 第 8 个是 LDT1 段的描述符。其选择符是 0x38
; 下面是任务 0 的 LDT 表段中的局部段描述符。
align 8
ldt0:
dq 0x0000000000000000 ; 第 1 个描述符,不用。
dq 0x00c0fa00000003ff ; 第 2 个局部代码段描述符,对应选择符是 0x0f。
dq 0x00c0f200000003ff ; 第 3 个局部数据段描述符,对应选择符是 0x17。
由于像上图这样的描述符,第一眼很难看出对应的段基址和段限长,因此创建一个转换工具,方便描述符转换对应内容。
工具代码如下:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define GDT_SEG_LIMIT0_OFFSET 0
#define GDT_BASE_ADDR0_OFFSET 16
#define GDT_TYPE_OFFSET 40
#define GDT_S_OFFSET 44
#define GDT_DPL_OFFSET 45
#define GDT_P_OFFSET 47
#define GDT_SEG_LIMIT1_OFFSET 48
#define GDT_AVL_OFFSET 52
#define GDT_O_OFFSET 53
#define GDT_D_B_OFFSET 54
#define GDT_G_OFFSET 55
#define GDT_BASE_ADDR1_OFFSET 56
struct gdt_descriptor {
uint16_t seg_limit0;
uint32_t base_addr0;
uint8_t type:4;
uint8_t s:1;
uint8_t dpl:2;
uint8_t p:1;
uint8_t seg_limit1:4;
uint8_t avl:1;
uint8_t o:1;
uint8_t d_b:1;
uint8_t g:1;
uint8_t base_addr1;
};
extern void gdt_desc2layout(int64_t gdt_64);
extern void idt_desc2layout(int64_t idt_64);
extern void layout2gdt_desc(const struct gdt_descriptor gdt_in, int64_t *gdt_64);
static void usage(char **argv)
{
printf("Usage0: %s gdt/idt word0(hex) word1(hex) word2(hex) word3(hex)\n", argv[0]);
printf("\texample0: %s gdt/idt 0x07FF 0x0000 0x9A00 0x00C0\n", argv[0]);
printf("Usage1: %s gdt/idt dq0\n", argv[0]);
printf("\texample1: %s gdt/idt 0x00c09a00000007ff\n", argv[0]);
}
int main(int argc, char **argv)
{
struct gdt_descriptor gdt_in;
int8_t tmp[16];
int16_t gdt[4];
int64_t gdt_64;
int16_t idt[4];
int64_t idt_64;
int i;
if (strcmp(argv[1], "gdt") == 0) {
switch (argc) {
case 3:
gdt_64 = strtoull(argv[2], NULL, 16);
gdt_desc2layout(gdt_64);
break;
case 6:
for (i = 0; i < 4; i++) {
gdt[i] = strtoul(argv[i + 2], NULL, 16);
if (gdt[i] & ~0xffff) {
printf("The number %s is over-ranging, it"
" must <= 0xffff\n", argv[i + 2]);
return -EINVAL;
}
gdt_64 |= gdt[i] << (i * 16);
}
gdt_desc2layout(gdt_64);
break;
default:
usage(argv);
break;
}
}
if (strcmp(argv[1], "idt") == 0) {
switch (argc) {
case 3:
idt_64 = strtoull(argv[2], NULL, 16);
idt_desc2layout(idt_64);
break;
case 6:
for (i = 0; i < 4; i++) {
idt[i] = strtoul(argv[i + 2], NULL, 16);
if (idt[i] & ~0xffff) {
printf("The number %s is over-ranging, it"
" must <= 0xffff\n", argv[i + 2]);
return -EINVAL;
}
idt_64 |= idt[i] << (i * 16);
}
idt_desc2layout(idt_64);
break;
default:
usage(argv);
break;
}
}
return 0;
}
void gdt_desc2layout(int64_t gdt_64)
{
/* Base_addr */
printf("Base_addr:%02lx", gdt_64 >> 58 & 0xff);
printf("%02lx", gdt_64 >> 32 & 0xff);
printf("%04lx ", gdt_64 >> 16 & 0xffff);
/* Seg_limit */
printf("Seg_limit:%01lx", gdt_64 >> 48 & 0xf);
printf("%04lx ", gdt_64 & 0xffff);
printf("DPL:%01lx ", gdt_64 >> 45 & 0x3);
printf("TYPE:%01lx ", gdt_64 >> 40 & 0xf);
printf("AVL:%01lx ", gdt_64 >> 52 & 0x1);
printf("O:%01lx ", gdt_64 >> 53 & 0x1);
printf("D/B:%01lx ", gdt_64 >> 54 & 0x1);
printf("G:%01lx ", gdt_64 >> 55 & 0x1);
printf("P:%01lx ", gdt_64 >> 47 & 0x1);
printf("S:%01lx ", gdt_64 >> 44 & 0x1);
printf("\n");
}
/*
* $ ./a.out 0x00c09a00000007ff
* Base_addr:00000000 Seg_limit:007ff DPL:0 TYPE:a AVL:0 O:0 D/B:1 G:1 P:1 S:1
*/
void layout2gdt_desc(const struct gdt_descriptor gdt_in, int64_t *gdt_64)
{
#if 0
gdt_in.base_addr0 = 0;
gdt_in.seg_limit0 = 0x07ff;
gdt_in.base_addr0 = 0x07ff;
gdt_in.type = 0xa;
gdt_in.s = 1;
gdt_in.dpl = 0;
gdt_in.p = 1;
gdt_in.seg_limit1 = 0;
gdt_in.avl = 0;
gdt_in.o = 0;
gdt_in.d_b = 1;
gdt_in.g = 1;
gdt_in.base_addr1 = 0;
#endif
uint64_t seg_limit0 = (uint64_t)(gdt_in.seg_limit0 & 0xffff) <<
GDT_SEG_LIMIT0_OFFSET;
uint64_t base_addr0 = (uint64_t)(gdt_in.base_addr0 & 0xffffff) <<
GDT_BASE_ADDR0_OFFSET;
uint64_t type = (uint64_t)(gdt_in.type & 0xf) << GDT_TYPE_OFFSET;
uint64_t s = (uint64_t)(gdt_in.s & 0xf) << GDT_S_OFFSET;
uint64_t dpl = (uint64_t)(gdt_in.dpl & 0x03) << GDT_DPL_OFFSET;
uint64_t p = (uint64_t)(gdt_in.p & 0x01) << GDT_P_OFFSET;
uint64_t seg_limit1 = (uint64_t)(gdt_in.seg_limit1 & 0xf00000) <<
GDT_SEG_LIMIT1_OFFSET;
uint64_t avl = (uint64_t)(gdt_in.avl & 0xf00000) << GDT_AVL_OFFSET;
uint64_t o = (uint64_t)(gdt_in.o & 0x01) << GDT_O_OFFSET;
uint64_t d_b = (uint64_t)(gdt_in.d_b & 0x01) << GDT_D_B_OFFSET;
uint64_t g = (uint64_t)(gdt_in.g & 0x01) << GDT_G_OFFSET;
uint64_t base_addr1 = (uint64_t)(gdt_in.base_addr1 & 0xff) <<
GDT_BASE_ADDR1_OFFSET;
*gdt_64 = seg_limit0 | base_addr0 | type | s | dpl | p | seg_limit1 |
avl | o | d_b | g | base_addr1;
printf("0x%#16lx\n", *gdt_64);
}
void idt_desc2layout(int64_t idt_64)
{
/* Entry_offset */
printf("Entry_offset:%04lx", idt_64 >> 48 & 0xffff);
printf("%04lx ", idt_64 & 0xffff);
/* Seg_selector */
printf("Seg_selector:%04lx ", idt_64 >> 16 & 0xffff);
printf("TYPE:%01lx ", idt_64 >> 40 & 0xf);
printf("S:%01lx ", idt_64 >> 44 & 0x1);
printf("DPL:%01lx ", idt_64 >> 45 & 0x3);
printf("P:%01lx ", idt_64 >> 47 & 0x1);
printf("\n");
}
效果如下: