Bootloader crash

2

I'm currently developing my bootloader, but I have problem.

I use Bochs to test bootloader, I compile bootloader and make disk image with:

rm disk.bin
rm boot.bin
rm post.bin
nasm bootloader.asm -o boot.bin
nasm postmbr.asm -o post.bin
cat boot.bin post.bin > disk.bin

This is bootloader.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code
[ORG 0x7C00]    ;Origin, tell the assembler that where the code will

MOV SI, HelloString ;Store string pointer to SI
MOV AH, 02h          ; read sector command
MOV AL, 01h
MOV CX, 0001h
MOV DH, 00h
MOV DL, 80h ;disk
MOV AX, 7E00h
MOV ES, AX ;buffer
MOV BX, 00h ;offset
CALL PrintString
JMP 0x7E00              ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
        ;Assume that ASCII value is in register AL
MOV AH, 0Eh     ;Tell BIOS that we need to print one charater on screen.
MOV BH, 00h     ;Page no.
MOV BL, 07h     ;Text attribute 0x07 is lightgrey font on black background

INT 10h ;Call video interrupt
RET             ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
        ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI          ;Increment SI pointer
OR AL, AL       ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character      ;Fetch next character from string
exit_function:  ;End label
RET             ;Return from procedure


;Data
HelloString db 'Loading OS demo...', 0  ;HelloWorld string ending with 0

TIMES 510 - ($ - $$) db 0       ;Fill the rest of sector with 0
DW 0xAA55                       ;Add boot signature at the end of bootloader

And postmbr.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code

MOV SI, HelloString ;Store string pointer to SI
CALL PrintString        ;Call print string procedure
JMP $           ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
        ;Assume that ASCII value is in register AL
MOV AH, 0x0E    ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00    ;Page no.
MOV BL, 0x07    ;Text attribute 0x07 is lightgrey font on black background

INT 0x10        ;Call video interrupt
RET             ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
        ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI          ;Increment SI pointer
OR AL, AL       ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character      ;Fetch next character from string
exit_function:  ;End label
RET             ;Return from procedure


;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0

TIMES 512 - ($ - $$) db 0       ;Fill the rest of sector with 0

This is crashlog from Bochs:

00000004661i[BIOS ] $Revision: 11318 $ $Date: 2012-08-06 19:59:54 +0200 (Mo, 06. Aug 2012) $
00000319074i[KBD  ] reset-disable command received
00000321808i[BIOS ] Starting rombios32
00000322242i[BIOS ] Shutdown flag 0
00000322837i[BIOS ] ram_size=0x02000000
00000323258i[BIOS ] ram_end=32MB
00000363787i[BIOS ] Found 1 cpu(s)
00000377969i[BIOS ] bios_table_addr: 0x000fa438 end=0x000fcc00
00000396429i[BIOS ] bios_table_cur_addr: 0x000fa438
00000524046i[VBIOS] VGABios $Id: vgabios.c,v 1.75 2011/10/15 14:07:21 vruppert Exp $
00000524117i[BXVGA] VBE known Display Interface b0c0
00000524149i[BXVGA] VBE known Display Interface b0c5
00000527074i[VBIOS] VBE Bios $Id: vbe.c,v 1.64 2011/07/19 18:25:05 vruppert Exp $
00000800003i[XGUI ] charmap update. Font Height is 16
00000866078i[BIOS ] ata0-0: PCHS=1/1/2 translation=none LCHS=1/1/2
00004743252i[BIOS ] IDE time out
00016726470i[BIOS ] Booting from 0000:7c00
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553i[CPU0 ] CPU is in real mode (active)
00016755553i[CPU0 ] CS.mode = 16 bit
00016755553i[CPU0 ] SS.mode = 16 bit
00016755553i[CPU0 ] EFER   = 0x00000000
00016755553i[CPU0 ] | EAX=000000aa  EBX=00000007  ECX=00090001  EDX=00000080
00016755553i[CPU0 ] | ESP=00000001  EBP=00000000  ESI=000e7c45  EDI=0000ffac
00016755553i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of DF if tf SF zf af PF cf
00016755553i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00016755553i[CPU0 ] |  CS:0000( 0004| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  ES:7e00( 0005| 0|  0) 0007e000 0000ffff 0 0
00016755553i[CPU0 ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] | EIP=0000fd34 (0000fd34)
00016755553i[CPU0 ] | CR0=0x60000010 CR2=0x00000000
00016755553i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00016755553i[CPU0 ] 0x0000fd34>> pusha  : 60
00016755553e[CPU0 ] exception(): 3rd (12) exception with no resolution, shutdown status is 00h, resetting
00016755553i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00016755553i[CPU0 ] cpu hardware reset
00016755553i[APIC0] allocate APIC id=0 (MMIO enabled) to 0x00000000fee00000
00016755553i[CPU0 ] CPUID[0x00000000]: 00000002 68747541 444d4163 69746e65
00016755553i[CPU0 ] CPUID[0x00000001]: 00000633 00010800 00000008 17cbfbff
00016755553i[CPU0 ] CPUID[0x00000002]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000000]: 80000008 68747541 444d4163 69746e65
00016755553i[CPU0 ] CPUID[0x80000001]: 00000633 00000000 00000000 c1c3f3ff
00016755553i[CPU0 ] CPUID[0x80000002]: 20444d41 6c687441 74286e6f 7020296d
00016755553i[CPU0 ] CPUID[0x80000003]: 65636f72 726f7373 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000004]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000005]: 01ff01ff 01ff01ff 40020140 40020140
00016755553i[CPU0 ] CPUID[0x80000006]: 00000000 42004200 02008140 00000000
00016755553i[CPU0 ] CPUID[0x80000007]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000008]: 00002028 00000000 00000000 00000000
00016755553i[     ] reset of 'cmos' plugin device by virtual method
00016755553i[     ] reset of 'dma' plugin device by virtual method
00016755553i[     ] reset of 'pic' plugin device by virtual method
00016755553i[     ] reset of 'pit' plugin device by virtual method
00016755553i[     ] reset of 'floppy' plugin device by virtual method
00016755553i[     ] reset of 'vga' plugin device by virtual method
00016755553i[     ] reset of 'ioapic' plugin device by virtual method
00016755553i[     ] reset of 'keyboard' plugin device by virtual method
00016755553i[     ] reset of 'harddrv' plugin device by virtual method
00016755553i[     ] reset of 'unmapped' plugin device by virtual method
00016755553i[     ] reset of 'biosdev' plugin device by virtual method
00016755553i[     ] reset of 'speaker' plugin device by virtual method
00016755553i[     ] reset of 'extfpuirq' plugin device by virtual method
00016755553i[     ] reset of 'parallel' plugin device by virtual method
00016755553i[     ] reset of 'serial' plugin device by virtual method

And this is configuration file for Bochs:

# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1
config_interface: textconfig
display_library: x
memory: host=32, guest=32
romimage: file="/usr/share/bochs/BIOS-bochs-latest"
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest"
boot: disk
floppy_bootsig_check: disabled=0
# no floppya
# no floppyb
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, mode=flat, translation=auto, path="disk.bin", cylinders=1, heads=1, spt=2, biosdetect=auto, model="Generic 1234"
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0
ata3: enabled=0
pci: enabled=0
vga: extension=vbe, update_freq=5
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: family=6, model=0x03, stepping=3, mmx=1, apic=xapic, sse=sse2, sse4a=0, sep=1, aes=0, xsave=0, xsaveopt=0, movbe=0, adx=0, smep=0, mwait=1
cpuid: vendor_string="AuthenticAMD"
cpuid: brand_string="AMD Athlon(tm) processor"

print_timestamps: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
# no loader
log: -
logprefix: %t%e%d
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
keyboard: type=mf, serial_delay=250, paste_delay=100000, keymap=
user_shortcut: keys=none
mouse: enabled=0, type=ps2, toggle=ctrl+mbutton
parport1: enabled=1, file=""
parport2: enabled=0
com1: enabled=1, mode=null, dev=""
com2: enabled=0
com3: enabled=0
com4: enabled=0

I don't know what is wrong, I haven't tested my bootloader on other platforms.

assembly
x86
nasm
osdev
bochs
asked on Stack Overflow Oct 22, 2012 by djmati11 • edited Oct 22, 2012 by djmati11

3 Answers

4

Well, the crash dump unambiguosly points at the pusha instruction attempting to push registers onto a destroyed stack.

Relevant bits:

00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553i[CPU0 ] | EAX=000000aa EBX=00000007 ECX=00090001 EDX=00000080
00016755553i[CPU0 ] | ESP=00000001 EBP=00000000 ESI=000e7c45 EDI=0000ffac
00016755553i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00016755553i[CPU0 ] | CS:0000( 0004| 0| 0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] | SS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] | EIP=0000fd34 (0000fd34)
00016755553i[CPU0 ] 0x0000fd34>> pusha : 60
00016755553e[CPU0 ] exception(): 3rd (12) exception with no resolution, shutdown status is 00h, resetting

That instruction is attempting to cross the lower segment boundary at offset 0 when writing data to the stack. And that causes the unrecoverable exception and the following reset.

The most probable cause is a jump to a wrong location and execution of random stuff there, possibly data.

Look at this instruction:

JMP 0x7E00              ;Infinite loop, hang it here.

After the CPU executes it, CS:IP is going to be 0:0x7E00.

Now look at your postmbr.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code

MOV SI, HelloString ;Store string pointer to SI
...

There's no org in there, in which case org 0 is implied.

So, this part of the code is compiled with the assumption that IP=0 when it starts and all offsets are counted from the beginning of this part of the code, and, logically, this implies that CS=DS=0 is wrong as well. But you're jumping to it with wrong, unexpected values in the registers.

And so this code isn't going to be executed as expected. IOW, it can do pretty much anything and, I bet, it does and leads to the stack destruction and the crash.

Key lesson here: x86 code is not position-independent.

You should either insert the appropriate org (org 0x7E00) or set up the registers accordingly and do a far jump to this part of the code (using e.g. jmp 0x7e0:0).

UPDATE: And now is a time for a lesson for me... The above problem indeed exists. However, as pointed out by @ughoavgfhw, there's another one almost in the same place but occurring a tad earlier. The 2nd 512 bytes are never loaded. Yep. The BIOS itself loads the first sector only. This first sector has to load the second sector by explicitly calling BIOS int 13h.

answered on Stack Overflow Oct 22, 2012 by Alexey Frunze • edited Oct 22, 2012 by Alexey Frunze
1
JMP 0x7E00

Jumping to 0x7E00 at this point will cause unexpected behavior. It looks like you want to jump to post.bin, which you expect to be loaded at 0x7E00. It's not. The bios has only loaded 1 sector, boot.bin, at 0x7C00. You'll have to load any additional sectors manually.

-1

When your bootloader starts running, the values of most registers could be anything. This includes the stack pointer. Your crash report is showing that esp is 1 and the crash occurs due to a triple fault on a pusha instruction, which basically means you are trying to push data onto a non-existent stack. This is because you never set the stack pointer, so you don't know where it is. You need to set the stack pointer as one of the first things you do. I would suggest setting it to 0x7c00, so that it is placed just below your code. Place this near the beginning of your bootloader:

mov  sp, 0x7C00
answered on Stack Overflow Oct 22, 2012 by ughoavgfhw

User contributions licensed under CC BY-SA 3.0