I'm writing a little kernel in c for x86 platform, but I'm having trouble to load the gdt and reload the segment selectors.
I am using bochs to test my kernel.
The issue is, when I load the GDT but don't reload the segment selectors, I can stop my program, type info gdt and get a nice result:
When I dont load my GDT:
<bochs:2> info gdt
Global Descriptor Table (base=0x00000000000010b0, limit=32):
GDT[0x0000]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0008]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0010]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, Accessed, 32-bit
GDT[0x0018]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
<bochs:3>
When I load my GDT:
<bochs:2> info gdt
Global Descriptor Table (base=0x00000000001022a0, limit=48):
GDT[0x0000]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0008]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, 32-bit
GDT[0x0010]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write
GDT[0x0018]=Code segment, base=0x00000000, limit=0x00000fff, Execute-Only, Non-Conforming, 32-bit
GDT[0x0020]=Data segment, base=0x00000000, limit=0x00000fff, Read-Only
GDT[0x0028]=??? descriptor hi=0x00000000, lo=0x00000000
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
<bochs:3>
So it seems that my GDT is loaded properly.
Now comes the tricky part.
When I want to reload the segment selectors, I'm having this error:
04641352650e[CPU0 ] fetch_raw_descriptor: GDT: index (ff57) 1fea > limit (30)
04641352650e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.limit = 0x0
04641352650e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.limit = 0x0
04641352650i[CPU0 ] CPU is in protected mode (active)
04641352650i[CPU0 ] CS.mode = 32 bit
04641352650i[CPU0 ] SS.mode = 32 bit
04641352650i[CPU0 ] EFER = 0x00000000
04641352650i[CPU0 ] | EAX=0000ff53 EBX=00010000 ECX=001022e0 EDX=00000000
04641352650i[CPU0 ] | ESP=00102294 EBP=001022b0 ESI=00000000 EDI=00000000
04641352650i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
04641352650i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
04641352650i[CPU0 ] | CS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
04641352650i[CPU0 ] | DS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
04641352650i[CPU0 ] | SS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
04641352650i[CPU0 ] | ES:0018( 0003| 0| 0) 00000000 ffffffff 1 1
04641352650i[CPU0 ] | FS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
04641352650i[CPU0 ] | GS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
04641352650i[CPU0 ] | EIP=001001d8 (001001d8)
04641352650i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
04641352650i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
(0).[4641352650] [0x0000001001d8] 0010:00000000001001d8 (unk. ctxt): mov ds, ax ; 8ed8
04641352650e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
And with that, when I type info gdt again, it gives me a very huge array,
which doesn't even fit in my terminal scrollback capacity.
Here are the last lines:
GDT[0xffd8]=??? descriptor hi=0x72670074, lo=0x64696c61
GDT[0xffe0]=16-Bit TSS (available) at 0x6c65725f, length 0xc6275
GDT[0xffe8]=Data segment, base=0x5f726700, limit=0x0002636f, Read-Only, Expand-down, Accessed
GDT[0xfff0]=Data segment, base=0x00657266, limit=0x00086572, Read/Write, Accessed
GDT[0xfff8]=Data segment, base=0x675f6275, limit=0x00057267, Read/Write
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
It says me that I want to access data outside of my GDT.
Here is the code I have written so far:
enum SEG_TYPE {
// Data
SEG_TYPE_DRO = 0b0000,
SEG_TYPE_DRW = 0b0010,
SEG_TYPE_DROE = 0b0100,
SEG_TYPE_DRWE = 0b0110,
// Code
SEG_TYPE_CEO = 0b1000,
SEG_TYPE_CER = 0b1010,
SEG_TYPE_CEOC = 0b1100,
SEG_TYPE_CERC = 0b1110,
};
enum SEG_AC {
SEG_AC_KERNEL = 0b11,
SEG_AC_USER = 0b00,
};
void gdt_entry_init(struct gdt_entry* entry, u32 base, u32 limit, enum SEG_TYPE type, enum SEG_AC access_rights) {
// Base address
entry->base_0_15 = base;
entry->base_16_23 = base >> 16;
entry->base_24_31 = base >> 24;
// Limit
entry->limit_0_15 = limit;
entry->limit_16_19 = limit >> 16;
// Segment type
entry->type = type;
// Access rights
entry->dpl = access_rights;
// AVL
entry->avl = 0;
// Default operation set to 32 bits
entry->db = 1;
// Code segment
entry->l = 0;
// Present (always present)
entry->p = 1;
// Descriptor type (code or data)
entry->s = 1;
// Granularity (enabled with 4KBytes increment)
entry->g = 1;
}
struct gdt_entry {
u32 limit_0_15 : 16;
u32 base_0_15 : 16;
u32 base_16_23 : 8;
u32 type : 4;
u32 s : 1;
u32 dpl : 2;
u32 p : 1;
u32 limit_16_19 : 4;
u32 avl : 1;
u32 l : 1;
u32 db : 1;
u32 g : 1;
u32 base_24_31 : 8;
} __attribute__((packed));
struct gdt_r {
u16 limit;
u32 base;
} __attribute__((packed));
struct gdt_entry gdt[6];
void gdt_init() {
// Null segment
struct gdt_entry null_entry = { 0 };
gdt[0] = null_entry;
// Kernel code segment
gdt_entry_init(gdt + 1, 0x0, 0xFFFFFFFF, SEG_TYPE_CER, SEG_AC_KERNEL);
// Kernel data segment
gdt_entry_init(gdt + 2, 0x0, 0xFFFFFFFF, SEG_TYPE_DRW, SEG_AC_KERNEL);
// User code segment
gdt_entry_init(gdt + 3, 0x0, 0x0, SEG_TYPE_CEO, SEG_AC_USER);
// User data segment
gdt_entry_init(gdt + 4, 0x0, 0x0, SEG_TYPE_DRO, SEG_AC_USER);
// TSS
gdt[5] = null_entry;
struct gdt_r gdtr;
gdtr.base = (u32)gdt;
gdtr.limit = sizeof(gdt);
asm volatile("lgdt %0\n"
: /* no output */
: "m" (gdtr)
: "memory");
// 0x10 is the address of the the kernel data segment
asm volatile("movw 0x10, %%ax\n":);
asm volatile("movw %%ax, %%ds\n":);
asm volatile("movw %%ax, %%fs\n":);
asm volatile("movw %%ax, %%gs\n":);
asm volatile("movw %%ax, %%ss\n":);
// 0x8 is the address of the kernel code segment
asm volatile("pushl 0x8\n"
"pushl $1f\n"
"lret\n"
"1:\n"
: /* no output */);
}
If you guys have any idea whats going on with this.
It turned out a really smart person find the issues:
I missed the $ in front of direct value when writing inline asm:
// 0x10 is the address of the the kernel data segment
asm volatile("movw $0x10, %%ax\n":);
asm volatile("movw %%ax, %%ds\n":);
asm volatile("movw %%ax, %%fs\n":);
asm volatile("movw %%ax, %%gs\n":);
asm volatile("movw %%ax, %%ss\n":);
// 0x8 is the address of the kernel code segment
asm volatile("pushl $0x8\n"
"pushl $1f\n"
"lret\n"
"1:\n"
: /* no output */);
I exchanged permissions for KERNEL and USER, correct should be
enum SEG_AC {
SEG_AC_KERNEL = 0b00,
SEG_AC_USER = 0b11,
};
Related
I have a working initial page map setup and exception handler in my i386 kernel, and for the most part everything is going smoothly, but in testing page fault conditions I noticed that accesses to anywhere in page 0 do not trigger a page fault, even though that page is unmapped and has the 'present' bit cleared. This is odd because accessing any other unmapped page does generate the expected page fault, just not page 0. It would be nice for accesses here to cause an exception so that the kernel will panic if future buggy code ends up dereferencing a null pointer. I'm seeing this behavior on both QEMU and a real x86 PC, and I can't find any indication of this being normal behavior, so I must be doing something wrong.
Here's my page map initialization code. Currently the kernel's code and data are identity-mapped starting at 0x100000.
extern uint32_t *page_directory;
extern uint32_t *page_table;
// set by linker script
extern void *kernel_code_end;
extern void *kernel_end;
void paging_init()
{
uint32_t i, addr;
uint8_t flags;
memset(page_directory, 0, 4096);
memset(page_table, 0, 4096);
for (i = 256; i < 1024; i++) {
addr = 4096 * i;
if (addr > (uint32_t) &kernel_end)
break;
if (addr < (uint32_t) &kernel_code_end)
flags = 0x1; // mark kernel code read-only
else
flags = 0x3;
page_table[i] = addr | flags;
}
page_table[255] = 0xb8000 | 0x3; // map VGA text memory to 0xff000
page_directory[0] = (uint32_t) page_table | 0x3;
asm("movl %0, %%eax" : : "a" (page_directory));
asm("movl %eax, %cr3");
asm("movl %cr0, %eax");
asm("orl $0x80010000, %eax");
asm("movl %eax, %cr0");
}
As an example of the behavior I'm seeing, I put this memory dump loop at the end of the kernel's main function:
for (int i = 0x000000; i < 0x100000; i += 4)
kprintf("%x: %x\n", i, *((uint32_t*) i));
It's able to dump the entirety of page 0, addresses 0x0000 through 0x0fff, but finally generates a page fault as soon as it reaches 0x1000.
Nevermind! I didn't realize that, like the symbols kernel_end and kernel_code_end that were defined in the linker script, page_table and page_directory -- defined in an external assembly file as 4k buffers within the bss segment -- are also linked to this C file as if they're located at that buffer rather than pointing to it. This means that they were actually uninitialized and pointing to address 0, so page 0 was set up to be both the page directory and page table! This is how page 0 ended up mapped into the valid virtual address space.
I modified the paging initialization function as such and now everything works as expected:
extern uint32_t *page_directory;
extern uint32_t *page_table;
// set by linker script
extern void *kernel_code_end;
extern void *kernel_end;
static void paging_init()
{
uint32_t *pdir = (uint32_t*) &page_directory;
uint32_t *ptab = (uint32_t*) &page_table;
uint32_t i, addr;
uint8_t flags;
memset(pdir, 0, 4096);
memset(ptab, 0, 4096);
for (i = 256; i < 1024; i++) {
addr = 4096 * i;
if (addr > (uint32_t) &kernel_end)
break;
if (addr < (uint32_t) &kernel_code_end)
flags = 0x1; // mark kernel code read-only
else
flags = 0x3;
ptab[i] = addr | flags;
}
ptab[255] = 0xb8000 | 0x3; // map VGA text memory to 0xff000
pdir[0] = (uint32_t) ptab | 0x3;
asm("movl %0, %%eax" : : "a" (pdir));
asm("movl %eax, %cr3");
asm("movl %cr0, %eax");
asm("orl $0x80010000, %eax");
asm("movl %eax, %cr0");
}
The PMA memory for the USB peripheral on some STM32s is structured as 256 16-bit words that are 32-bit aligned. This format is demonstrated in the following table:
Address + offset
0x0
0x1
0x2
0x3
0x40006000
0x000
0x001
-----
-----
0x40006004
0x002
0x003
-----
-----
0x40006008
0x004
0x005
-----
-----
0x4000600C
0x006
0x007
-----
-----
0x40006010
0x008
0x009
-----
-----
....
....
....
-----
-----
0x400063F8
0x1FC
0x1FD
-----
-----
0x400063FC
0x1FE
0x1FF
-----
-----
For example, the PMA address 0x006 is accessed by the CPU at address 0x4000600C. Access to any address with an offset of 0x2 and 0x3 is invalid.
Ideally, data in the PMA could be accessed by defining structs such as the following, but count would occupy offsets 0x3 and 0x4, assuming the struct points to an address with an offset of 0x0.
struct buf_desc_entry {
volatile uint16_t addr;
volatile uint16_t count;
} __attribute__((packed));
One solution would be to pad the struct so that count occupies offsets 0x0 and 0x1. This would be tedious and insecure since it would have to be done manually for all structs.
struct buf_desc_entry {
volatile uint16_t addr;
volatile uint16_t _1;
volatile uint16_t count;
volatile uint16_t _2;
} __attribute__((packed));
Another solution for this example would be to 32-bit align all variables either using __attribute__((aligned (4))) or using a linker script, however, this solution would not work for structs that contain 8-bit variables, since they can occupy offsets of 0x0 or 0x1.
Is there a more elegant way of doing this?
I'm implementing my own kernel and I'm stuck. I'm trying to load my kernel into the high half virtual addresses. I've tackled the identity addresses problem by identity mapping the low 1M of RAM. I've created an init section that is relocated to the kernel's physical address in order to take care of paging initialisation. My kernel's virtual offset is 0xc0000000. This is my linker script:
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
KERNEL_VIRTUAL_OFFSET = 0xC0000000;
SECTIONS
{
. = 1M;
kernel_start = .;
start_init = .;
.init ALIGN(4K) :
{ *(.multiboot);
*(.init);
*(.tables);
}
end_init = .;
. += KERNEL_VIRTUAL_OFFSET;
kernel_high_half_start = .;
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_VIRTUAL_OFFSET)
{*(.text) }
.data ALIGN(4K) : AT(ADDR(.data) - KERNEL_VIRTUAL_OFFSET)
{ *(.data) }
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_VIRTUAL_OFFSET)
{ *(.rodata) }
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_VIRTUAL_OFFSET)
{ *(.bss) }
kernel_high_half_end = .;
kernel_end = . - KERNEL_VIRTUAL_OFFSET;
}
Here's my entry point. I'm using GRUB as my bootloader. It successfully boots and jumps into my entry point because of the init section:
its 32
section .multiboot
;grub bootloader header
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
; Declarations
global start
extern kmain
extern paging_init
extern kernel_page_directory
section .init
enable_paging:
mov eax, kernel_page_directory
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax ; ***** PAGING ENABLED HERE *****
ret
start:
cli ;block interrupts
mov esp, init_stack
call paging_init
call enable_paging
;mov eax, 0xb8000
;mov byte[eax], 'h'
;mov byte[eax+1], 0x7
; Now high half kernel is mapped to the page directory
mov esp, stack_space ;set stack pointer
push ebx ; grub boot info
call kmain
loop:
hlt ;halt the CPU
jmp loop
resb 4096; 4KB small stack for my init section.
init_stack:
section .bss
resb 8192 ;8KB for stack
stack_space:
Here is my code the fills the page table and the page directory of the kernel. As you can see, the whole code is linked into the init section, to avoid relocation problems:
page_table_t kernel_page_directory[PAGE_DIR_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};
page_pointer_t kernel_page_tables[PAGE_TABLE_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};
page_pointer_t identity_page_table[PAGE_TABLE_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};
/* Identity map the low 1M
* In early boot stage.
*/
static void __attribute__((section(".init"))) map_identity()
{
//map bios
unsigned int current_page = 0;
for(int i = 0; i < BIOS_PAGE_TABLE_ENTRIES; i++, current_page += PAGE_SIZE)
{
identity_page_table[i] = (current_page) | 0x3;
}
//map init
current_page = INIT_START;
for(int i = INIT_START >> 12 & 0x3FF;
i < ((INIT_START >> 12 & 0x3FF) + (INIT_SIZE / PAGE_SIZE));
i++, current_page += PAGE_SIZE)
{
identity_page_table[i] = (current_page) | 0x3;
}
kernel_page_directory[0] = ((unsigned long)(identity_page_table)) | 0x3;
}
/* Map the kernel memory to its page directory,
* **in early boot stage.
* We don't need to map the init section, we don't need it anymore.
*/
__attribute__((section(".init"))) static void map_kernel_memory()
{
//Identity map the init section
//Start at 1MB i.e. its page aligned.
unsigned int start_index = 256;
unsigned long current_page = KERNEL_START;
for(int i = start_index;
i < start_index + (KERNEL_SIZE / PAGE_SIZE) + 1;
i++, current_page += PAGE_SIZE)
{
kernel_page_tables[i] = current_page | 0x3;
}
kernel_page_directory[KERNEL_DIRECTORY_ENTRY] = ((unsigned long)kernel_page_tables) | 0x3;
}
__attribute__((section(".init"))) void paging_init()
{
map_identity();
map_kernel_memory();
}
I tried to point to the exact assembly instruction, but that makes my kernel work incorrectly and I think it is because of mov cr0, eax when I enable paging. CR3 does contain the address of kernel_page_directory or with 0x3. As soon as I enable paging, QEMU stops responding and the system reboots constantly. The screen is being flushed and then printed repeatedly. Any ideas why this is happening? How I can fix it?
Is the address of your page directory Page-Aligned? The size of each page (frame) is 4 KB. I suggest to create a struct for page directory like this:
typedef struct page_directory{
page_table_t *tables[1024];
size_t tablesPhysical[1024]; // Physical address of page tables
size_t physicalAddr; // Physical address of `tablesPhysical'
} page_directory_t;
So, your address of the directory must be the multiple of 4 KB (0x1000). James Molloy's Tutorial may help you.
I would want to create a macro to get easy access to a single bit from a structure like the following:
typedef union
{
struct
{
uint8_t bit0 : 1;
uint8_t bit1 : 1;
uint8_t bit2 : 1;
uint8_t bit3 : 1;
uint8_t bit4 : 1;
uint8_t bit5 : 1;
uint8_t bit6 : 1;
uint8_t bit7 : 1;
};
uint8_t raw;
} Bitfield;
I have a bi-dimensional array(x) of this structure. The best that I could make was :
#define xstr(r,c,b) str(r,c,b)
#define str(r,c,b) (x[r][c].bit##b)
#define getBit(bitCollum,row)(xstr(row,(bitCollum/8),(bitCollum%8))
When I try to use the macro like uint8_t a = getBit(15,2); it will expand to
uint8_t a = ( ( img [ 2 ] [ ( 15 / 8 ) ] . bit 15 % 8 ) );
and I would want to create a structure that will expand to:
uint8_t a = ( ( img [ 2 ] [ ( 15 / 8 ) ] . bit7 ) );
Is this even possible?
bitCollum and row will always be literal integers; the expression will not be run in a loop or something like that.
EDIT:
After seeing that it wasn't possible i looked at the disassembly of a simple increment and I saw different instructions but for my surprise the masking was faster.
` x.raw = 0b10101001;
00000040 LDI R24,0xA9 Load immediate
00000041 STD Y+8,R24 Store indirect with displacement
uint8_t y = 0b10101001;
00000042 LDI R24,0xA9 Load immediate
00000043 STD Y+1,R24 Store indirect with displacement
uint16_t xSum=0;
00000044 STD Y+3,R1 Store indirect with displacement
00000045 STD Y+2,R1 Store indirect with displacement
uint16_t ySum=0;
00000046 STD Y+5,R1 Store indirect with displacement
00000047 STD Y+4,R1 Store indirect with displacement
xSum+=x.bit3;
00000048 LDD R24,Y+8 Load indirect with displacement
00000049 BST R24,3 Bit store from register to T
0000004A CLR R24 Clear Register
0000004B BLD R24,0 Bit load from T to register
0000004C MOV R24,R24 Copy register
0000004D LDI R25,0x00 Load immediate
0000004E LDD R18,Y+2 Load indirect with displacement
0000004F LDD R19,Y+3 Load indirect with displacement
00000050 ADD R24,R18 Add without carry
00000051 ADC R25,R19 Add with carry
00000052 STD Y+3,R25 Store indirect with displacement
00000053 STD Y+2,R24 Store indirect with displacement
ySum+=y&0b00010000;
00000054 LDD R24,Y+1 Load indirect with displacement
00000055 MOV R24,R24 Copy register
00000056 LDI R25,0x00 Load immediate
00000057 ANDI R24,0x10 Logical AND with immediate
00000058 CLR R25 Clear Register
00000059 LDD R18,Y+4 Load indirect with displacement
0000005A LDD R19,Y+5 Load indirect with displacement
0000005B ADD R24,R18 Add without carry
0000005C ADC R25,R19 Add with carry
0000005D STD Y+5,R25 Store indirect with displacement
0000005E STD Y+4,R24 Store indirect with displacement `
Instead of the structures, use simple bytes - uint8_t
#define GETBIT(r,c) (img[r][(c) >> 3] & (1 << ((c) & 7)))
#define SETBIT(r,c) img[r][(c) >> 3] |= (1 << ((c) & 7))
#define CLRBIT(r,c) img[r][(c) >> 3] &= ~(1 << ((c) & 7))
However, if you want it efficient, you better avoid manipulating things one bit at a time.
It could be that I'm missing some "trick", but, AFAIK, this is not possible.
Basically, you're trying to compute a value and then append it to some token. The problem here is that the preprocessor doesn't do computations (except in #if and such statements). So, for example:
#define X2(A,B) A##B
#define X(A,B) X2(A,B)
int x = X(13 + 4, 4);
this will expand to:
int x = 13 + 44;
and not to:
int x = 174;
If you try to put parenthesis, you will just get compiler errors, 'cause this is not valid:
int x = (13+4)4;
While processing macros, everything is just a "string" (token) to the preprocessor. Actually, it is the compiler that will, in the example above, see that 13 + 44 is constant and compile that as int x = 57; (well, an intelligent compiler, I've worked with some C compilers in my day that were not so smart :) ).
#define GET_BIT(VAR8,IDX) ((VAR8>>IDX) & 1)
int main(void){
unsigned char c=3;
int i;
printf("Bits of char %d: ",c);
for(i=0; i<8;i++){
printf("%d ",GET_BIT(c,i));
}
printf("\n");
return 0;
}
I've been working from some time on easy os kernel. So far I've properly setup IDT with one INT (software).
Next step was to try to get to ring 3 of kernel. This was dane by me using 'trick' with iret ring switch was successful, as
state of segment register indicate. Problems occurred when I tried to use int $20 to switch to ring 0 to handle this interrupt.
Of course I have setup TSS to be able to do such action. Here is some code:
This function encodes descriptor (I've enclosed this because I think this can be problem with encoding)
void register_descriptor(unsigned entryIdx, uint32_t base, uint32_t limit, uint16_t flag)
{
uint64_t descriptor;
descriptor = limit & 0x000F0000;
descriptor |= (flag << 8) & 0x00F0FF00;
descriptor |= (base >> 16) & 0x000000FF;
descriptor |= base & 0xFF000000;
descriptor <<= 32;
descriptor |= base << 16;
descriptor |= limit & 0x0000FFFF;
GDT[entryIdx] = descriptor;
}
TSS loading to GDT (zeroTSS() just set all members of TSS struct to 0):
static void write_tss(unsigned num, uint16_t ss0, uint32_t esp0)
{
register_descriptor(num, (uint32_t) &tss_entry, sizeof(tss_entry), (TSS));
zeroTSS();
tss_entry.ss0 = ss0;
tss_entry.esp0 = esp0;
tss_entry.cs = 0x18;
tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x20;
}
TSS Flush(My GDT have 6 entries (NULL+TSS+4 Segment)):
.global flush_tss
flush_tss:
mov $0x28, %ax
ltr %ax
ret
And interupt handler register:
IDTEntry fillIDTEntry(uint32_t intHandler,
uint16_t selector,
uint8_t type_attr)
{ IDTEntry newEntry;
newEntry.offset_low = LOW_FUN_ADDR(intHandler);
newEntry.selector = selector;
newEntry.zero = 0;
newEntry.type_attr = type_attr;
newEntry.offset_up = UP_FUN_ADDR(intHandler);
return newEntry;
}
extern void _lidt(_IDT_PTR* idtPtr);
void loadIDT()
{
zeroIDT();
_IDT_PTR idtPtr;
idtPtr.idtSize = sizeof(struct __InteruptDescriptorTableEntry)*256 - 1;
idtPtr.idtBaseAddr = (uint32_t) &InteruptDescriptorTable;
IDTEntry printOnScreenInt = fillIDTEntry((uint32_t)interupt_pritnOnScreen, 0x18, 0x8e);
registerInterupt(printOnScreenInt, 32);
_lidt(&idtPtr);
}
.global _lidt
_lidt:
push %ebp
mov %esp,%ebp
mov 8(%esp), %eax
lidt (%eax)
leave
ret
Finally print form Bochs:
[CPU0 ] interrupt(): soft_int && (gate.dpl < CPL)
[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
[CPU0 ] CPU is in protected mode (active)
[CPU0 ] CS.mode = 32 bit
[CPU0 ] SS.mode = 32 bit
[CPU0 ] EFER = 0x00000000
[CPU0 ] | EAX=00100d48 EBX=001058b4 ECX=00000720 EDX=001058c2
[CPU0 ] | ESP=0010589c EBP=0010589c ESI=00101000 EDI=00000000
[CPU0 ] | IOPL=0 ID vip vif ac vm RF nt of df if tf sf zf af PF cf
[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
[CPU0 ] | CS:000b( 0001| 0| 3) 00000000 ffffffff 1 1
[CPU0 ] | DS:0013( 0002| 0| 3) 00000000 ffffffff 1 1
[CPU0 ] | SS:0013( 0002| 0| 3) 00000000 ffffffff 1 1
[CPU0 ] | ES:0013( 0002| 0| 3) 00000000 ffffffff 1 1
[CPU0 ] | FS:0013( 0002| 0| 3) 00000000 ffffffff 1 1
[CPU0 ] | GS:0013( 0002| 0| 3) 00000000 ffffffff 1 1
[CPU0 ] | EIP=001008ed (001008ed)
[CPU0 ] | CR0=0x60000011 CR2=0x00000000
[CPU0 ] | CR3=0x00000000 CR4=0x00000000
[CPU0 ] 0x001008ed>> int 0x20 : CD20
I will only mention that before introducing two privilege levels this interrupt handler was working.