I am attempting to load the kernel into memory by writing my own boot loader. I've been able to successfully load the kernel into memory - I know that because I used the bochs debugger set the breakpoint to 0x7c00 and stepped through and the system does jump into the kernel. The problem is that after jumping into the kernel none of the print statements (in kernel.s) work. That indicates on the terminal that the kernel has been loaded into memory.
Here is the bootblock.s file (majority of the relevant code resides at label booter
:
# bootblock.s
# Empty boot block file
# .equ symbol, expression
# These directive set the value of the symbol to the expression
.equ BOOT_SEGMENT,0x07c0
.equ DISPLAY_SEGMENT,0xb800
.equ KERNEL_LOCATION, 0x1000
.equ STACK_SP, 0xffff
.equ STACK_SS, 0x0
.text # Code segment
.globl _start # The entry point must be global
.code16 # Real mode
_start:
###MAKE BOOTABLE###
#. = _start + 510
#.byte = 0x55
#.byte = 0xaa
jmp booter
os_size:
#Place where createimage writes the OS size
.word 0
.word 0
print:
movw $BOOT_SEGMENT,%ax
movw %ax,%ds
print_loop:
lodsb
cmpb $0,%al
je print_done
movb $14,%ah
movl $0x0002,%ebx
int $0x10
jmp print_loop
print_done:
retw
booter:
###SET UP STACK###
#Allocating the stack
movw $STACK_SS, %ax
movw %ax, %ss
movw $STACK_SP, %sp
movl $allocating, %esi
call print
movl $done, %esi
call print
#Resetting the disk drive, setting %dl and calling int 0x13
#movb $0x0, %ah
#movb $0x0, %dl
#int $0x13
movl $bootblock_test, %esi
call print
movl $hellostring, %esi
call print
###LOAD KERNEL###
movl $loadingkernel, %esi
call print
#Number of sectors to read
#movb $0x24, %al
#movb $0x80, %al
movb $0x08, %al
movb $0x02, %ah
#track number
#movb $0x00, %ch
#which sector to read from (sector 2 where kernel should be)
movb $0x02, %cl
#set up head number
movb $0x0, %dh
#Set the drive number to 0x0 (floppy)
movb $0x0, %dl
#Time to set es:bx to read from the correct place (0:1000)
movw $0x0100, %bx
movw %bx, %es
movw $0x0, %bx
#movw $0x0, %ax
#Setting %ah = 2 and calling int 0x13 (read sector)
int $0x13
movl $done, %esi
call print
#Booting up at 0x07c0
#movw $BOOT_SEGMENT, %ax
#movw %ax, %ds
#movl $bootmessage, %esi
#call print
#%dh/%ch control head numbers, setting them to 0
#movb $0x0, %dh
#movb $0x0, %ch
#movw %ds,
###INVOKE KERNEL###
#Kernel jump
movl $readymessage, %esi
call print
#Setting %ds = 0x7c0
movw $0x0100, %ax
movw %ax, %ds
#Time to set es:bx to read from the correct place (0:1000)
movw $0x0100, %bx
movw %bx, %es
movw $0x0, %bx
movl $0x1000, %ax
jmp %ax
mov $0x0, %ax
#If any errors, message will be displayed here
movl $errormessage, %esi
call print
forever:
jmp forever
#Error handling
error:
movl $errormessage, %esi
call print
# messages
mystring:
.asciz "test.\n\r"
bootblock_test:
.asciz "\nBootblock Test\n\r"
hellostring:
.asciz "How are you today?\n\r"
myname:
.asciz "Welcome\n\r"
loadingkernel:
.asciz "Loading Kernel...\n\r"
done:
.asciz "Done!\n\r"
bootmessage:
.asciz "Booting up...\n\r"
readymessage:
.asciz "Sliding into yo Kernel like... \n\r"
errormessage:
.asciz "Something went terribly wrong...\n\r"
rebootmessage:
.asciz "Press any key to reboot the OS!\n\r"
allocating:
.asciz "Allocating Stack...\n\r"
Here is the kernel.s file:
.data # Data segment
# Some strings
kernel:
.asciz "[Kernel]-> "
testing:
.asciz "Running a trivial test... "
works:
.asciz "Seems Ok. Now go get some sleep :)."
not:
.asciz "*Failed*"
# 'Newline' string ('carriage return', 'linefeed', '\0')
newline:
.byte 10
.byte 13
.byte 0
# An integer
result:
.word 1000
.text # Code segment
.code16 # Real mode
.globl _start # The entry point must be global
#
# The first instruction to execute in a program is called the entry
# point. The linker expects to find the entry point in the "symbol" _start
# (with underscore).
#
_start:
pushw %bp # Setup stack frame
movw %sp,%bp
pushw $newline
call displayString # Print messages
pushw $kernel
call displayString
pushw $testing
call displayString
pushw $1000
call trivialTest # trivialTest(1000)
addw $8,%sp # Pop newline, kernel, testing, and '1000'
cmpw %ax,result
jne .L6 # If (trivialTest(1000) != 1000) goto L6
pushw $works
jmp .L12
.L6: # Test failed
pushw $not
.L12:
call displayString # Print ok/failed message
addw $2,%sp
pushw $newline
call displayString
addw $2,%sp
.L8: # Loop forever
jmp .L8
#
# int trivialTest(n)
# {
# if (n > 0) {
# trivialTest(n-1);
# }
# return n;
# }
trivialTest:
pushw %bp # Setup stack frame
movw %sp,%bp
movw 4(%bp),%ax # Move argument to ax
testw %ax,%ax # Logical compare (sets SF, ZF and PF)
jg .L2 # if (argument > 0) goto L2
xorw %ax,%ax # else return 0
popw %bp
retw
.L2:
decw %ax
pushw %ax
call trivialTest # trivialTest(argument - 1)
# (Recursive calls until argument == 0)
addw $2,%sp # Pop argument
incw %ax
popw %bp
retw # Return (argument in ax)
displayString:
pushw %bp # Setup stack frame
movw %sp,%bp
pushw %ax # Save ax, bx, cx, si, es
pushw %bx
pushw %cx
pushw %si
pushw %es
movw %ds, %ax # Make sure ES points to the right
movw %ax, %es # segment
movw 4(%bp),%cx # Move string adr to cx
movw %cx, %si
loop:
lodsb # Load character to write (c) into al,
# and increment si
cmpb $0, %al
jz done # if (c == '\0') exit loop
movb $14,%ah # else print c
movw $0x0002,%bx
# int 0x10 sends a character to the display
# ah = 0xe (14)
# al = character to write
# bh = active page number (we use 0x00)
# bl = foreground color (we use 0x02)
int $0x10
jmp loop
done:
popw %es # Restore saved registers
popw %si
popw %cx
popw %bx
popw %ax
popw %bp
retw # Return to caller
Once again, I've checked in the debugger that the kernel is being loaded into memory (0x1000). I believe the problem is with how I am setting/using certain registers in bootblock.s (mainly : ds
,ax
) but I am not exactly sure what it is.