Проектирование GDT для маленького ядра
В настоящее время я пишу небольшой код ядра. Вот что я скопировал из проекта ядра где-то. Он содержит код для загрузки ядра в ячейку памяти 0x1000 и перехода в ячейку 0x1000:
;
; The Bootsector Code (First 512 bytes of the floppy)
;
;
; Define Code Segment and Data Segment Rights details for inputting to GDTFill function
;
%define CS_ACCES 10011011b ; CS and DS Access Rights (Details in GDT.INC)
%define DS_ACCES 10010011b
;
; 16 Bit Addressing initially
;
[bits 16]
;
; Code begins at 0x7c00
;
[org 0x7c00]
;
; Bios Jumps to 0xf000:0xffff
; Then it loads the first 512 bytes (BootSector)
; from first boot device to 0x0000:0x7c00
;
jmp boot
;
; Includes
;
%include "GDT.INC"
;
; Define Stack
;
boot:
mov ax,0x07C0
mov ds,ax
mov es,ax
mov ax,0x8FFF
mov ss,ax
;
; Stack begins at 0xf000 and fills from there downwards
;
mov sp,0xFFFF
;
; Note:
; Linear Address = Shift Segment by 1 byte and add Offset to it
;
; Read Kernel From Floppy to Memory location es:bx (0x1000 here)
; Cylinder Head Sector and Buffer are as follows:
;
; es:bx - buffer where to load Kernel to
; ch - track number
; cl - starting sector
; dh - head number
; dl - drive number (0 here)
; Then call interrupt 0x13
;
xor ax,ax
int 0x13
;
; Do the floppy int 13 reading
;
push es
mov ax,0x100
mov es,ax
mov bx,0
mov ah,2
mov al,30
mov ch,0
mov cl,2
mov dh,0
mov dl,0
int 0x13
;
; Now es holds stack addresss
;
pop es
;
; Fill GDT
; Refer to GDT.INC for details
;
GDTFill 0, 0xFFFFF,CS_ACCES,1101b,gdt_cs
GDTFill 0, 0xFFFFF,DS_ACCES,1101b,gdt_ds
;
; Store Limit of GDT beginning at location marked as gdtptr
; This has to be passed on to lgdt instruction
;
mov ax, gdtend
mov bx, gdt
sub ax,bx
mov word [gdtptr], ax
;
; Store Linear address of GDT at gdtptr after allowing space for the previous data
; Linear Address = Shift Segment by 1 byte and add Offset to it
;
xor ax,ax
mov ax,ds
mov bx,gdt
call LinearAdd
mov dword [gdtptr+2], ecx
;
; Load gdt using lgdt. Disable interrupts before that
;
cli
lgdt[gdtptr]
;
; Move to protected mode. Set cr0's first bit to 1 by or'ing it
;
mov eax,cr0
or ax,1
mov cr0,eax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Once in Protected Mode,
; Except cs all are defined w.r.t DataSegment
; DataSegment Descriptor from beginning of GDT is 8 bytes
; CodeSegment Descriptor from beginning of GDT is 10 bytes
; Stack (Very important! - This is what i messed with initially:
; --------------------------------------------------------------
; Defined w.r.t Data segment
; Beginning - 0x9f000 (Fills downwards)
; Stack size is = 0x9f000 - DataSegment Value (= 0x0) (Have to change this)
; Quite enough for some small operations and LIBC Functions
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
jmp next
next:
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov esp,0x9F000 ; Stack begins filling at this address
;
; protected mode segmented address = cs:0x1000
; This is nothing but 0x0:0x1000 in protected mode (Have to change these)
; Which is where kernel was loaded earlier from floppy
;
jmp dword 0x8:0x1000
end:
jmp end ; Just in case it slips the earlier step !
;
; Initially fill GDT with 0's
;
gdt:
gdt_null:
dw 0,0,0,0
gdt_cs:
dw 0,0,0,0
gdt_ds:
dw 0,0,0,0
gdtend:
;
; The following is the GDT Pointer
; This is used for passing it on to LGDT Instruction
;
gdtptr:
dw 0x0000 ; 16 bit size of GDT
dd 0 ; 32 bit linear address of GDT
;
; Filling the rest of space with NOP
; Or else boot sector might start executing invalid instructions there
; Because of Junk Data
;
times 510-($-$$) db 144
;
; The signature of boot sector
;
dw 0xaa55
Следующее в GDT.INC:
; Calculate Linear Address
; Called with following Values:
; ax - has segment number
; bx - has offset number
;
; Output:
; ecx - has Linear Address
;
; We are in 16 bit mode
;
; Linear Address = Shift Segment by 1 byte and add Offset to it
; So 0x07c0:0x0 = 0x7c00
LinearAdd:
xor ecx,ecx
mov cx,ax
shl ecx,4
and ebx,0x0000FFFF
add ecx,ebx
ret
; Filling Global Descriptor Table
; -------------------------------
;
; Note:
; -----
; 1. Order of variables input: Base (32 bits),
; Limit (20 bits), Access Rights(8 bits), Flags(4 bits), Segment Address
; (32 bits)
;
; 2. Variables input to function are moved LS 4 bits First (Right to Left) to table
;
; 3. While reading LS 4 bits or MS 4 bits, read from left to right
;
; Base:
; -----
; Bits in Table Bits in Field 'Base' Location w.r.t Beginning of Segment
;---------------------------------------------------------------------------------------
; 16 - 31 0 - 15 +2 (2 bytes in length)
; 32 - 39 16 - 23 +4 (1 byte in length)
; 55 - 63 24 - 31 +7 (1 byte in length)
;
;
; Howto:
; ------
;
; Below %5 ie., the fifth variable input to function GDTFill is Beginning of GDT's Code Segment
; [%5+2] represents entry 1 in above table
; [%5+4] represents entry 2 in above table
; [$5+7] represents entry 3 in above table
; Entry 1 is 2 bytes (so ax is moved to word [%5+2]
; Entry 2 and 3 are 1 byte (so al is moved to byte[%5+4] and [%5+7] respectively
;
; Limit:
; ------
;
; Bits in Table Bits in Field 'Limit' Location w.r.t Beginning of Segment
;---------------------------------------------------------------------------------------
; 0 - 15 0 - 15 +0 (2 bytes in length)
; 16 - 20 48 - 51 +6 (1 byte in length)
;
; Refer 'Howto' above for detailed description
;
; Access Rights:
; --------------
;
; Access Rights (Bit 40 to Bit 47) = Type (Bit 40 to Bit 43) + System Flag (Bit 44) + DPL (Bit 45 and 46) + Reserved (Bit 47);
; Type - Your Call (Say A is Kernel Code and B is User code (Here it is 11 and 3 respectively)
; System Flag - Both Code and Data Segment have S Flag = 1
; DPL - Privilege level (Ring 0 or 3?) (Ring 0 - 00 and Ring 3 - 11)
;
; Flags:
; ------
; G B O AVL
;
; G - Granularity = 1 here (means Segment Size is 4096 bytes)
; B - Address offsets used for accessing segments are 32 bits long
; O - 0 (Don't know what it is!)
; AVL - 1 here (You Can Ignore it)
;
;
%macro GDTFill 5
push eax
; Base
mov eax,%1
mov word [%5+2],ax
shr eax,16 ; Shift Right to
mov byte [%5+4],al
shr eax,8
mov byte [%5+7],al
; Limit
mov eax,%2
and eax,0x000FFFFF
mov word [%5],ax ; ecrit (0..15)
shr eax,16 ; place (16..19) sur le nibble inferieur
mov byte [%5+6],0 ; initialise flags+lim(16..19) a 0
or [%5+6],al ; ecrit (16..19)
; flags :
mov al,%4
and al,0x0F
shl al,4
or [%5+6],al
; acces :
mov byte [%5+5],%3
pop eax
%endmacro
Вышеуказанное работало очень долго. Однако, когда мое ядро стало больше, сегменты данных и кода пересекались. Хотя оба начинаются с 0, данные в сегменте данных перекрываются кодом в сегменте кода или чем-то в этом роде. Из-за этого я не смог распечатать сообщения полностью.
Есть ли способ изменить базовый адрес сегмента данных и сегмента кода так, чтобы между базами было некоторое пространство для записи небольшого ядра размером примерно до 1 МБ в двоичном виде?
Я приложил ссылку на ядро и подробно описал проблему ниже:
Я делаю ядро, используя:
make clean; make
внутри папки src
Загрузите его в QEMU, используя:
sudo qemu-system-i386 -net nic,vlan=0,model=pcnet -net tap,vlan=0,ifname=tap,script=no -fda ../flp/fileb.flp -boot a -m 128
После загрузки я запускаю следующую команду для проверки строковых операций:
testnum
Затем я запускаю следующую команду и вижу, что сообщения разбиваются между ними и не печатаются после определенной строки:
pcnetops
Если я комментирую следующие строки в console.c и запускаю pcnetops, я печатаю все вещи:
print( "sizeof(char) == "); print(htos(sizeof(char))); print(CRLF); print("htos(stoh(ffffffff, LEFT_TO_RIGHT)): "); print(htos(stoh((unsigned char *) "ffffffff", LEFT_TO_RIGHT))); print(CRLF);
По этой причине я сомневаюсь, что мне следует разделять адреса сегмента кода и базы данных (на данный момент оба равны 0).
2 ответа
Проблема заключалась в том, что все сектора дискет не читались в вызове int 13. Увеличил значение al до 40 (с 30 - количество прочитанных секторов), а затем снова скомпилировал. Сейчас работает нормально. Я понимаю, что проблема не была полностью решена правильно, хотя:)
Я вижу следующее в вашем коде:
gdt_cs:
dw 0,0,0,0
gdt_ds:set
dw 0,0,0,0
Что вам нужно сделать, это изменить дескрипторы, чтобы включить базу, которая в настоящее время установлена на 0. Вы можете использовать это изображение в качестве руководства:
Установив базу и лимит соответствующим образом, вы можете убедиться, что CS и DS не перекрываются.