J'essaye de passer de mon chargeur de démarrage à mon noyau chargé après être passé en mode protégé. Le noyau se charge correctement et se trouve au bon endroit, mais lorsque le deuxième étage du chargeur appelle la fonction principale du noyau, il appelle la mauvaise adresse.L'assembly d'assemblage mappe la mauvaise adresse
Voici la deuxième étape du chargeur (loader.asm
):
global load_kern
extern main
[bits 32]
load_kern:
call main
cli
hlt
Je puis assemblez et établir un lien avec le fichier objet ac pour créer un exécutable elfe:
nasm -f elf32 loader.asm -o loader.o
ld -melf_i386 -n -Tos.lds loader.o os.o -o kernel.elf
J'utilise ce fichier pour les relier:
ENTRY(load_kern);
PHDRS
{
headers PT_PHDR FILEHDR PHDRS;
code PT_LOAD;
}
SECTIONS
{
.text 0x500: ALIGN(0x100) { *(.text) } :code
.data : { *(.data) }
.bss : { *(.bss) }
/DISCARD/ : { *(.eh_frame) }
}
je place alors ce kernel.elf
dans le 2n d secteur de mon image de disquette (après le secteur de démarrage).
Quand j'objdump kernel.elf
la sortie est correcte:
os/kernel.elf: file format elf32-i386
Disassembly of section .text:
00000500 <load_kern>:
500: e8 43 00 00 00 call 548 <main>
505: fa cli
506: f4 hlt
...
00000548 <main>:
548: 55 push %ebp
549: 89 e5 mov %esp,%ebp
54b: 68 5c 05 00 00 push $0x55c
550: 6a 05 push $0x5
552: e8 b0 ff ff ff call 507 <write_string>
557: 83 c4 08 add $0x8,%esp
55a: eb fe jmp 55a <main+0x12>
L'adresse de la principale semble être mis en correspondance bien mais quand je charge mon secteur du noyau et y sauter c'est ce que gdb montre:
┌─────────────────────────────────────────────────────────────────┐
>│0x600 call 0x646 │
│0x603 add BYTE PTR [bx+si],al │
│0x605 cli │
│0x606 hlt
...
│0x646 leave │
│0x647 ret │
│0x648 push bp │
│0x649 mov bp,sp │
│0x64b push 0x55c |
Le noyau est chargé à 0x500 mais la section de texte du fichier a un alignement de 0x100, de sorte que le code commence à 0x600 (après l'en-tête elf) au lieu de 0x500. Le code se charge correctement, mais l'appel loader.asm
utilise 0x646 au lieu de 0x648, où le démarrage principal. Notez qu'il existe une ligne d'assemblage supplémentaire à 0x603 qui n'est pas censée être là. Cela ressemble à une instruction indésirable et je pense que c'est peut-être ce qui le rejette. Il ressemble à l'instruction d'appel
500: e8 43 00 00 00 call 548 <main>
est interprété correctement, mais les zéros mènent à l'instruction suivante en quelque sorte à créer l'instruction inattendue supplémentaire. Je ne sais pas si c'est une possibilité.
Je n'arrive pas à comprendre pourquoi il utilise une adresse qui est désactivée 2, cela se produit également dans d'autres parties du code du noyau. N'hésitez pas à signaler d'autres erreurs que j'ai faites, j'apprends comme je vais. Je ne suis pas sûr si cela a à voir avec la façon dont je les relie, le format que j'utilise ou quelque chose de différent.
EDIT:
Ainsi, il semble que c'est en fait un problème avec mon bootloader:
global start
[bits 16]
[org 0x7c00]
start: jmp boot
boot:
cli ; clear interrupts
cld ; clear direction flag
mov ax, 0
mov es, ax
mov bx, 0x500 ; set bx to where we load the kernel
mov al, 0x12 ; set lower byte of ax to read 18 sectors
mov ch, 0 ; set higher byte of cx to read track 0 (first track)
mov cl, 2 ; set lower byte of cx to read sector 2 (bootloader is sec1)
mov dh, 0 ; set higher byte of dx to read head 0 (top side of disk)
mov dl, 0 ; set lower byte of dx to read drive 0 (floppy drive)
mov ah, 0x02 ; read
int 0x13 ; call BIOS interrupt 13 to read drive
int 0x10 ; clear screen
jmp pm_switch
hlt ; this instruction should not execute
pm_switch:
xor ax, ax ; clear ds (used by lgdt)
mov ds, ax
cli
lgdt [gdt_desc]
mov eax, cr0
or eax, 1 ; switch to protected mode
mov cr0, eax
jmp 08h:select_jump
select_jump:
xor eax, eax
mov ax, 0x10 ; set data segments to data selector (0x10)
mov ds, ax
mov ss, ax
mov esp, 09000h
mov ax, 0
mov es, ax
; do a far jump to set cs and go to kernel code
jmp 08h:0x600
gdt: ; Address for the GDT
gdt_null: ; Null Segment
dd 0
dd 0
;KERNEL_CODE equ $-gdt
gdt_kernel_code:
dw 0FFFFh ; Limit 0xFFFF
dw 0 ; Base 0:15
db 0 ; Base 16:23
db 09Ah ; Present, Ring 0, Code, Non-conforming, Readable
db 00Fh ; Page-granular
db 0 ; Base 24:31
;KERNEL_DATA equ $-gdt
gdt_kernel_data:
dw 0FFFFh ; Limit 0xFFFF
dw 0 ; Base 0:15
db 0 ; Base 16:23
db 092h ; Present, Ring 0, Data, Expand-up, Writable
db 00Fh ; Page-granular
db 0 ; Base 24:32
gdt_end: ; Used to calculate the size of the GDT
gdt_desc: ; The GDT descriptor
dw gdt_end - gdt - 1 ; Limit (size)
dd gdt ; Address of the GDT
; pad the file to file the rest of the sector (512 bytes) and add boot sig
times 510 - ($-$$) db 0
dw 0xAA55
SOLUTION:
tourne mon TDG segment de code était s et à 16 bits. Je l'ai changé à 32 et ajouté une instruction [bits 32]
juste après que je mette le commutateur à protégé (juste avant select jump
).
C'est ce que je soupçonnais. Cela signifie-t-il que je ne passe pas en mode protégé correctement dans mon bootloader? – Serial
Probablement pas. Où essayez-vous d'activer le mode protégé? Comment configurez-vous les tables de pages? – duskwuff
Dans mon bootloader. J'utilise un GDT très basique, en passant à cr0, en configurant les registres de segments puis en sautant très loin dans le code du noyau. code du bootloader: http://pastebin.com/RLWC7KkA – Serial