0

My operating system is Ubuntu. I am trying to follow through a tutorial building a kernel..Even though i ve built the kernel, when i try to compile a simple C program that prints an 'X' on the top left corner of the screen in qemu,the screen just loops infinitely between some Real-Mode print commands, without ever landing in protected mode.. The program includes loading some extra sectors with disk_load.asm, then switching to 32 bit protected mode with switch_to_pm, and then executing the C code

Here is the code.

boot_sect.asm

    ;boot sector that boots a C kernel in 32-bit protected mode
[org 0x7c00]
KERNEL_OFFSET equ 0x1000 ;This is the memory offset to which we will load our kernel
mov [BOOT_DRIVE],dl ;BIOS stores our boot drive in DL,so it's
        ;best to remember this for later.
mov bp, 0x9000;Set-up the stack.  
mov sp,bp
mov bx, MSG_REAL_MODE ;Announce that we are starting
call print_string      ;booting from 16-bit real mode
call load_kernel ;Load our kernel
call switch_to_pm;Switch to protected mode,from which
     ;we will not return
jmp $

Include our useful,hard-earned routines
%include "print_string.asm"
%include "disk_load.asm"
%include "gdt.asm"
;%include "print_string_pm.asm"
%include "switch_to_pm.asm"
[bits 16]
load_kernel
load_kernel:
;mov bx, MSG_LOAD_KERNEL ;Print a message to say we are loading the kernel
                      ;call print_string
mov bx, KERNEL_OFFSET   ;Set-up parameters for our disk_load routine,so
mov dh, 15              ;that we load the first 15 sectors(excluding
mov dl, [BOOT_DRIVE]    ;the boot sector)from the boot disk(i.e.our
call disk_load          ;kernel code)to address KERNEL_OFFSET
ret

[bits 32]
;This is where we arrive after switching to and initialising protected mode


BEGIN_PM:
;mov ebx, MSG_PROT_MODE  ;Use our 32-bit print routine to
;call print_string_pm    ;announce we are in protected mode 
call KERNEL_OFFSET      ;Now jump to the address of our loaded
                    ;kernel code,assume the brace position,
        ;and cross your fingers.Here we go!
jmp $                   ;Hang.
;Globalvariables
BOOT_DRIVE      db 0
MSG_REAL_MODE   db "Started in 16-bit Real Mode", 0
;MSG_PROT_MODE   db "Successfully landed in 32-bit Protected Mode", 0
MSG_LOAD_KERNEL db "Loading kernel into memory.", 0
;Bootsectorpadding
times 510-($-$$) db 0
dw 0xaa55

disk_load.asm

;load DH sectors to ES:BX from drive DL
;works for floppies

disk_load:
push dx
                    ;Store DX on stack so later we can recall
                    ;how many sectors were request to be read,
                    ;even if it is altered in the mean time
mov ah, 0x02            ;BIOS read sector function
mov al, dh              ;Read DH sectors
mov ch, 0x00            ;Select cylinder 0
mov dh, 0x00            ;Select head 0
mov cl, 0x02            ;Start reading from second sector(i.e.
                    ;after the boot sector)
int 0x13                ;BIOS interrupt
jc disk_error           ;Jump if error(i.e.carryflagset)
pop dx                  ;Restore DX from thestack
cmp dh, al;ifAL(sectorsread)!=DH(sectorsexpected) 
jne disk_error          ;display error message
ret
disk_error :
mov bx, DISK_ERROR_MSG
call print_string
jmp $                   ;Variables
DISK_ERROR_MSG db "Disk read error!", 0

switch_to_pm.asm

switch_to_pm:
cli        ;We must switch of interrupts until we have  cli->clear interrupt
       ;set-up the protected mode interrupt vector
       ;otherwise interrupts will run riot

lgdt [gdt_descriptor] ;Load our global descriptor table,which defines
          ;the protected mode segments(e.g.for code and data)
mov eax,cr0            ;To make the switch to protected mode,we set
or eax, 0x1            ;the first bit of CR0,a control register
mov cr0,eax           ;update register, we cannot set the bit directly on the register
jmp CODE_SEG:init_pm ;Make a far jump(i.e.to a new segment)to our 32-bit
         ;code.This also forces the CPU to flush its cache of
                 ;pre-fetched and real mode decoded instructions,which can
             ;cause problems. we can use the or
         ;instruction to include certain bits into a value (i.e. without
         ;disturbing any other bits that, for some important reason, may have been set 
                 ;already in the control register)
[bits 32]
             ;Initialise registers and the stack once in PM.
init_pm:
mov ax, DATA_SEG ;Now in PM,our old segments are meaningless,
mov ds,ax        ;so we point our segment registers to the
mov ss,ax        ;data selector we defined in our GDT
mov es,ax
mov fs,ax
mov gs,ax
mov ebp, 0x90000    ;position so it is right
mov esp,ebp      ;at the top of the free space.
call BEGIN_PM    ;Finally,call somewell-known label7

the GDT code, that contains the gdt_descriptor

gdt.asm

; GDT
gdt_start:
gdt_null: ;the mandatory null descriptor
dd 0x0  ;'dd'means define double word(i.e.4bytes)
dd 0x0
gdt_code: ;the code segment descriptor
;base=0x0,limit=0xfffff ,
;1st flags:(present)1(privilege)00(descriptortype)1->1001b 
;type flags:(code)1(conforming)0(readable)1(accessed)0->1010b
;2nd flags:(granularity)1(32-bitdefault)1(64-bitseg)0(AVL)0->1100b
dw 0xffff ;Limit(bits0-15)
dw 0x0  ;Base(bits0-15)
db 0x0  ;Base(bits16-23)
db 10011010b    ;1st flags,type flags
db 11001111b    ;2n dflags,Limit(bits16-19)
db 0x0       ;Base(bits24-31)
gdt_data:    ;the data segment descriptor
         ;Same as code segment except for the type flags:
   ;type flags:(code)0(expanddown)0(writable)1(accessed)0->0010b
dw 0xffff ;Limit(bits0-15)
dw 0x0  ;Base(bits0-15)
db 0x0  ;Base(bits16-23)
db 10010010b    ;1stflags,type flags
db 11001111b    ;2ndflags,Limit(bits16-19)
db 0x0      ;Base(bits24-31)
gdt_end:  ;The reason for putting a label at the end of the
      ;GDT is so we can have the assembler calculate
   ;the size of the GDT for the GDT decriptor(below)
 ;GDT descriptior
gdt_descriptor:
dw gdt_end - gdt_start - 1  ;Size of our GDT,always less one
            ;of the true size 
dd gdt_start            ;Start address of our GDT

;Define some handy constants for the GDT segment descriptor offsets,which
;are what segment registers must contain when in protected mode.For example,
;when we set DS=0x10 in PM,the CPU knows that we mean it to use the
;segment described at offset 0x10(i.e.16bytes)in our GDT,which in our
;case is the DATA segment(0x0->NULL;0x08->CODE;0x10->DATA)
CODE_SEG equ gdt_code - gdt_start -10;-10 so it is withing memory limits 
DATA_SEG equ gdt_data - gdt_start -10;

print_string.asm

print_string:
pusha
mov ah, 0x0e

loop:
    mov al, [bx]
    cmp al, 0
    je return
    int 0x10
    inc bx
    jmp loop

return:
    popa
    ret

That is the C program that prints an 'X'

kernel.c

void main() {
//Create a pointer to a char,and point it to the first text cell of
//video memory(i.e.the top-left of the screen)
char* video_memory = (char *) 0xb800;
//At the address pointed to by video_memory,store the character'X'
//(i.e.display 'X' in the top-left of the screen).
*video_memory = 'X';
}

In order to make the C code, raw machine code we type in the directory we have saved the kernel.c file

$gcc -ffreestanding -c kernel.c -o kernel.o
$ld -o kernel.bin -Ttext 0x1000 kernel.o --oformat binary

then in order to create the binary file for qemu

$nasm boot_sect.asm -f bin  -o boot_sect.bin

and finally

$cat bootsect.bin kernel.bin > os-image

to execute the code as a floppy

$qemu -fda os-iamge

and now instead of printing an 'X' it just loops infinitely, printing only the Real Mode strings("Started in 16 bit Real-Mode"and "Loading Kernel into memory.", and not entering 32bit protected mode. I tried running every piece of code in nasm one by one,and then adding every individual piece of code together, and it seemed like the loop began when the switch_to_pm piece of code was added.Why is that? (i think something is faulty with switch_to_pm code) Thank you a lot.

4

2 回答 2

0

这是高级编程。因此,它有一些先决条件,例如能够调试您的程序。我现在已经为你做了,但你不应该每次遇到问题都来 SO。

主要问题在于您的gdt.asm,即以下几行:

CODE_SEG equ gdt_code - gdt_start -10;-10 so it is withing memory limits 
DATA_SEG equ gdt_data - gdt_start -10;

我什至无法猜测您想用 the 做什么,-10而评论也无济于事。正确的定义是:

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

cpu 使用段选择器(底部 3 位被屏蔽)来寻址 GDT。如果要修改覆盖的线性地址,则需要编辑GDT条目,不能随便乱用选择器。

解决这个问题,您的代码将不再崩溃,但也不会工作。原因是 VGA 文本内存处于实模式 0xb800,但这当然是物理地址0xb8000(又是一个零)。你需要在你的kernel.c. 此外,由于您只写入一个字节,因此您没有触及属性字节。您也应该这样做,以确保X无论屏幕内存的当前内容是什么都显示您的内容。

于 2014-10-20T18:19:01.527 回答
0

When you declare your code and data segments in gdt.asm, you have the following lines.

CODE_SEG equ gdt_code - gdt_start -10;-10 so it is withing memory limits 
DATA_SEG equ gdt_data - gdt_start -10;

I have no idea as to why you would think you need to subtract 10 to stay within memory. This step is not necessary, and you should have this:

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

An unrelated issue: in your kernel you assign the hex value 0xb800 to video_memory. It should be 0xb8000.

I'm guessing that you followed the tutorial by nick blundell Writing a Simple Operating System from Scratch(http://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf/)

I've done the same thing, and if you code is correctly copied from that .pdf, then it should work fine.

于 2014-12-02T16:55:25.697 回答