Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: middle_endian am 23. July 2007, 15:24
-
hi,
kurz zur ausgangslage. bis jetzt sieht der ablauf meines codes so aus:
- bootloader wird vom ersten sektor der diskette nach 0x7c00 geladen
- der kernel, der direkt hinter dem bootloader im zweiten sektor der diskette liegt, wird eingelesen
- lgdt [gdt_desc] wird ausgeführt
- jump in den protected-mode
- lidt [idt_desc] wird ausgeführt
- es wird zum zuvor geladenen kernel gesprungen
das funktioniert alles wunderbar. das problem ist jetzt folgendes. die interrupt service routinen sind bis jetzt natürlich im bootloader, da ich ja im bootloader die idt lade. nun würd ich aber gern das switchen in den protected mode, sowie das laden der gdt/idt in den kernel verlagern, damit ich meine interrupt-service-routinen im kernel definieren kann.
ich hab also den funktionierenden code hergenommen und anders auf bootloader und kernel verteilt. also:
bootloader:
- laden des kernels(nach 0x1000)
- springen zum kernel
kernel:
- interrupts deaktivieren
- lgdt [gdt_desc] <-- nach diesem befehl steht im gdt-register nichts drinnen(base=0, limit=0)
- jump in den protected mode (funktioniert damit natürlich nicht)
was stimmt da nicht?
würde diese "neue" reihenfolge der ausführung überhaupt funktionieren bzw macht sie sinn?
-
Ich finde sie macht Sinn...Darum hab ich sie auch so implementiert und bei mir funktioniert sie...Gib uns mal etwas Code am Design kanns nicht liegen.
Gruss
Nooooooooooos
-
also hier der minimalistische bootloader
%include "config32.asm"
[BITS 16]
org 0x7c00
init:
xor ax, ax
mov ds, ax
mov es, ax
mov ax, 0100h
mov ss, ax
mov ax, stack
mov sp, ax ; relative adressing to stack segment
mov [bootdrv], dl
call load ; load kernel
;; jump to kernel
mov ax, KERNEL_ADR
push ax
xor ax, ax
push ax
retf
;; kernel-loading routine
load:
push ds
mov ax, 0 ; reset function
mov dl, [bootdrv] ; what drive?
int 13h ; interrupt
pop ds
jc load
.loop0:
mov ax, KERNEL_ADR
mov es, ax ; buffer address for reading
xor bx, bx ; offset = 0
;; read in sectors
mov ah, 2 ; read function
mov al, 1 ; 1 sectors
mov cx, 2 ; cylinder = 0, sector = 2
xor dx, dx ; head = 0, drive = 0
int 13h ; es:bx = 0x1000 (internal multiplication with 16)
jc .loop0
ret
bootdrv db 0
;; little realmode stack
resd 64
stack:
times 510-($-$$) db 0
dw 0aa55h
;; size = 512B
und hier der relevante teil vom kernel
%include "config32.asm"
[BITS 16]
start:
;; setup stack
xor ax, ax
mov ds, ax
mov es, ax
mov ds, ax
mov esp, stack
;; set gdt
cli
lgdt [gdt_desc]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword gdt_code:pmode ; far jump reloads the cs register and flushes
; the real-mode instructions from the prefetch queue
[BITS 32]
pmode:
;; all register except of cs still contain 16bit values
;; -> put valid selectors into ds, ss and es
;; setup stack
mov ax, gdt_data
mov ds, ax
mov ss, ax
mov es, ax
mov esp, stack
lidt [idt_desc] ; load idt
;; ...
jmp $
;; ---------------------------
;; interrupt service routines
;; ---------------------------
void_isr
mov eax, FRAME_BUFFER
mov dword [eax], ':-( '
jmp $
isr_test:
mov eax, FRAME_BUFFER
mov dword [eax], ':-) '
iret
msg db "test message ..."
;; kernel stack
resd 1024
stack:
;; ------------------------
;; global descriptor table
;; ------------------------
;; 8B/descriptor, 8192 descriptors maximum -> 64KB max size
gdt:
dd 0
dd 0
gdt_code equ $-gdt
dw 0ffffh ; limit 0:15
dw 0 ; base 0:15
db 0 ; base 16:23
db 10011010b ; present, ring 0 priv(2bit), code segment, executable, non-conforming, readable
db 11001111b ; page-granular, 32-bit, ..., limit 16:19(1111)
db 0 ; base 24:31
gdt_data equ $-gdt
dw 0ffffh
dw 0
db 0
db 10010010b ; data segment(bit4)
db 11001111b
db 0
gdt_end:
;; gdt descriptor
gdt_desc:
dw gdt_end-gdt-1
dd gdt
;; ---------------------------
;; interrupt descriptor table
;; ---------------------------
;; idt descriptor
idt_desc:
dw idt_end-idt-1
dd idt
idt:
%rep 32
dw void_isr
dw gdt_code ; gdt selector
db 0 ; always zero
db 8eh ; access, attributes
dw 0 ; void_isr >> 16 == 0
%endrep
dw isr_test
dw gdt_code
db 0
db 8eh
dw 0
idt_end:
times ((2880*512-512)-($-$$)) db 0 ; complete floppy disk for bochs
der far-jump in den protected mode hat einen triple-fault zur folge.
00000904030e[CPU ] fetch_raw_descriptor: GDT: index (f)1 > limit (0)
00000904030e[CPU ] interrupt(): gate descriptor is not valid sys seg
00000904030e[CPU ] interrupt(): gate descriptor is not valid sys seg
00000904030i[CPU ] protected mode
00000904030i[CPU ] CS.d_b = 16 bit
00000904030i[CPU ] SS.d_b = 16 bit
00000904030i[CPU ] | EAX=00000011 EBX=00000000 ECX=00130002 EDX=00000000
00000904030i[CPU ] | ESP=00001137 EBP=00000000 ESI=000088f0 EDI=0000ffde
00000904030i[CPU ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00000904030i[CPU ] | SEG selector base limit G D
00000904030i[CPU ] | SEG sltr(index|ti|rpl) base limit G D
00000904030i[CPU ] | CS:1000( 1e00| 0| 0) 00010000 0000ffff 0 0
00000904030i[CPU ] | DS:0000( 0000| 0| 0) 00000000 0000ffff 0 0
00000904030i[CPU ] | SS:0100( 0000| 0| 0) 00001000 0000ffff 0 0
00000904030i[CPU ] | ES:0000( 0000| 0| 0) 00000000 0000ffff 0 0
00000904030i[CPU ] | FS:0000( 0000| 0| 0) 00000000 0000ffff 0 0
00000904030i[CPU ] | GS:0000( 0000| 0| 0) 00000000 0000ffff 0 0
00000904030i[CPU ] | EIP=00000020 (00000020)
00000904030i[CPU ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00000904030i[CPU ] | CR3=0x00000000 CR4=0x00000000
00000904030i[CPU ] >> jmp far 0008:00000028 : 66EA280000000800
00000904030e[CPU ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00000904030i[SYS ] bx_pc_system_c::Reset(SOFTWARE) called
00000904030e[CPU ] CPU_LOOP bx_guard.interrupt_requested=1
Next at t=904030
(0) [0x00010020] 1000:0020 (unk. ctxt): jmp far 0008:00000028 ; 66ea280000000800
-
1. dein Kernel ist mit Sicherheit größer als 512Byte, dann musst du das im bootloader natürlich auch anpassen
2. du verwendest kein org im kernel, ist dein code wirklich bei 0x0? :wink: -> org 0x1000 einfügen
-
der hund muss leider wo anders begraben liegen. selbst wenn ich den stack-bereich derart verkleinere (oder auf ne höhere adresse auslagere), dass die 512B grenze nicht überschritten wird, funktionierts nicht. auf das org 0x1000 ändert nichts.
fehlt mir vielleicht irgend eine initialisierung im kernel oder verändert sich durch den far jump vom bootloader in den kernel irgendwas, was vor dem laden der gdt berücksichtigt werden muss?
EDIT: habs jetzt hingekriegt. org 0x10000 zusammen mit dem korrekten setzen des datensegments am beginn des kernels haben gefehlt. also:
[BITS 16]
org KERNEL_ADR << 4 ; internal multiplication by 16
start:
cli
mov ax, cs
mov ds, ax ; set ds to cs
mov sp, stack
;; set gdt
lgdt [gdt_desc]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword 0x8:pmode ; far jump reloads the cs register and flushes
; ....
-
Ist dein Kernel bei 0x1000 oder 0x10000? Dein Kommentar sagt 0x1000, aber dein Code sagt was anderes :wink: Was genau ist KERNEL_ADR?
-
siehe oben, war zu langsam beim editieren :)
-
genau das hätte ich danach auch vorgeschlagen. Schön, dass du es selbst hingekriegt hast. :-)
-
hab jetzt allerdings noch ein problem mit der idt. meine isr wird nämlich nicht aufgerufen
%include "config32.asm"
[BITS 16]
org KERNEL_ADR << 4 ; internal multiplication by 16
start:
cli
mov ax, cs
mov ds, ax ; set ds to cs
mov sp, STACK_ADR
;; set gdt
lgdt [gdt_desc]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword gdt_code:pmode ; far jump reloads the cs register and flushes
[BITS 32]
pmode:
;; all register except of cs still contain 16bit values
;; -> put valid selectors into ds, ss and es
;; setup stack
mov ax, gdt_data
mov ds, ax
mov ss, ax
mov es, ax
mov esp, STACK_ADR
lidt [idt_desc] ; load idt
int 20h
jmp $
;; ---------------------------
;; interrupt service routines
;; ---------------------------
void_isr
mov eax, FRAME_BUFFER
mov dword [eax], ':-( '
jmp $
isr_test:
mov eax, FRAME_BUFFER
mov dword [eax], ':-) '
iret
;; ------------------------
;; global descriptor table
;; ------------------------
;; 8B/descriptor, 8192 descriptors maximum -> 64KB max size
gdt:
dd 0
dd 0
gdt_code equ $-gdt
dw 0ffffh ; limit 0:15
dw 0 ; base 0:15
db 0 ; base 16:23
db 10011010b ; present, ring 0 priv(2bit), code segment, executable, non-conforming, readable
db 11001111b ; page-granular, 32-bit, ..., limit 16:19(1111)
db 0 ; base 24:31
gdt_data equ $-gdt
dw 0ffffh
dw 0
db 0
db 10010010b ; data segment(bit4)
db 11001111b
db 0
gdt_end:
;; gdt descriptor
gdt_desc:
dw gdt_end-gdt-1
dd gdt
;; ---------------------------
;; interrupt descriptor table
;; ---------------------------
;; idt descriptor
idt_desc:
dw idt_end-idt-1
dd idt
idt:
%rep 32
dw void_isr ;
dw gdt_code ; gdt selector
db 0 ; always zero
db 8eh ; access, attributes
dw 0 ; void_isr >> 16 == 0
%endrep
dw isr_test
dw gdt_code
db 0
db 8eh
dw 0
idt_end:
;; kernel stack
;resd 128
;stack:
msg db "test message ..."
times ((2880*512-512)-($-$$)) db 0 ;
beim aufruf des interrupts springt er mir nach 0x8:0xfd55. er sollte allerdings nach 0x8:0x1003b springen.
der code ist vom prinzip her wieder mit dem früheren code, in dem alles im bootloader gemacht wurde, identisch.
was passt da jetzt schon wieder nicht?
EDIT: problem behoben. durch den sprung des kernels nach 0x10000 reicht das einfache zuweisen der isr in der idt nicht mehr aus, da ich mit 2B adressen nicht mehr auskomm. hab also nach dem laden der idt (testweise) folgendes gemacht:
mov eax, isr_test
mov [idt], ax
shr eax, 16
mov [idt+6], ax