general protection fault when running os on iso - nasm

I have the following bootloader code which seems to run perfectly fine on a hard disk:
[bits 16]
[org 0x7c00]
bootld_start:
KERNEL_OFFSET equ 0x2000
xor ax, ax ; Explicitly set ES = DS = 0
mov ds, ax
mov es, ax
mov bx, 0x8C00 ; Set SS:SP to 0x8C00:0x0000 . The stack will exist
; between 0x8C00:0x0000 and 0x8C00:0xFFFF
mov ss, bx
mov sp, ax
mov [BOOT_DRIVE], dl
mov bx, boot_msg
call print_string
mov dl, [BOOT_DRIVE]
call disk_load
jmp pm_setup
jmp $
BOOT_DRIVE:
db 0
disk_load:
mov si, dap
mov ah, 0x42
int 0x13
;cmp al, 4
;jne disk_error_132
ret
dap:
db 0x10 ; Size of DAP
db 0
; You can only read 46 sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/512 = 46
dw 46 ; Number of sectors to read
dw KERNEL_OFFSET ; Offset
dw 0 ; Segment
dd 1
dd 0
disk_error_132:
mov bx, disk_error_132_msg
call print_string
jmp $
disk_error_132_msg:
db 'Error! Error! Something is VERY wrong! (0x132)', 0
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
boot_msg:
db 'OS is booting files... ', 0
done_msg:
db 'Done! ', 0
%include "boot/print_string.asm"
pm_setup:
mov bx, done_msg
call print_string
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Place stack below EBDA in lower memory
mov ebp, 0x9c000
mov esp, ebp
mov ebx, pmode_msg
call print32
call KERNEL_OFFSET
jmp $
pmode_msg:
db 'Protected mode enabled!', 0
kernel:
mov ebx, pmode_msg
call print32
jmp $
pmode_tst:
db 'Testing...'
times 510-($-$$) db 0
db 0x55
db 0xAA
The problem is that when I convert it to an ISO with these commands:
mkdir iso
mkdir iso/boot
cp image.flp iso/boot/boot
xorriso -as mkisofs -R -J -c boot/bootcat \
-b boot/boot -no-emul-boot -boot-load-size 4 \
-o image.iso iso
...it fails with a triple fault. When I run it with qemu-system-i386 -boot d -cdrom os-image.iso -m 512 -d int -no-reboot -no-shutdown, it outputs (excluding useless SMM exceptions):
check_exception old: 0xffffffff new 0xd
     0: v=0d e=0000 i=0 cpl=0 IP=0008:0000000000006616
pc=0000000000006616
SP=0010:000000000009bff8 env->regs[R_EAX]=0000000000000000
EAX=00000000 EBX=00007d72 ECX=00000000 EDX=000000e0
ESI=00007cb0 EDI=00000010 EBP=0009c000 ESP=0009bff8
EIP=00006616 EFL=00000083 [--S---C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00007c73 00000018
IDT=     00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000e0 CCD=000001b3 CCO=ADDB    
EFER=0000000000000000
check_exception old: 0xd new 0xd
     1: v=08 e=0000 i=0 cpl=0 IP=0008:0000000000006616 pc=0000000000006616 SP=0010:000000000009bff8 env- >regs[R_EAX]=0000000000000000
EAX=00000000 EBX=00007d72 ECX=00000000 EDX=000000e0
ESI=00007cb0 EDI=00000010 EBP=0009c000 ESP=0009bff8
EIP=00006616 EFL=00000083 [--S---C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00007c73 00000018
IDT=     00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000e0 CCD=000001b3 CCO=ADDB    
EFER=0000000000000000
check_exception old: 0x8 new 0xd
Which means that I got a 0x0d (general protection fault), then a 0x08 (double fault), then it triple faulted. Why is this happening?
EDIT: I have changed the command to:
xorriso -as mkisofs -R -J -c boot/bootcat -b boot/boot.flp -o nmos.iso nmos.flp
But I am now getting the following error:
xorriso : FAILURE : Cannot find in ISO image: -boot_image ... bin_path='/boot/boot.flp'
xorriso : NOTE : -return_with SORRY 32 triggered by problem severity FAILURE
Does anyone know what this means?
EDIT 2:
I have changed the code to read using ah=0x02 like this:
mov bx, KERNEL_OFFSET
mov ah, 0x02
mov al, 46
mov ch, 0x00
mov dh, 0x00
mov cl, 0x02
mov dl, [BOOT_DRIVE]
int 0x13
But it is still triple-faulting. Why?

I am the developer of xorriso. If image.flp is a floppy disk image
with MBR, possibly a partition table, and a filesystem, then the hint
of Michael goes to the right direction. El Torito specifies emulations
which lets the boot image file appear to the BIOS as floppy or hard disk.
The options -no-emul-boot -boot-load-size 4 causes BIOS to load the
first 2048 bytes of file image.flp and to execute them as x86 program.
Obviously a floppy image is not suitable as plain program.
According to mkisofs traditions floppy emulation is the default with
option -b. So you would just have to remove the option -no-emul-boot
from your xorriso command line in order to get the El Torito boot image
as floppy. (-boot-load-size 4 is then obsolete, too.)
The floppy image must have either 2400, or 2880, or 5760 sectors of 512
bytes, or else it will be rejected by xorriso.
Images of other sizes may be emulated as hard disks where the first
(and only) partition entry in the MBR partition table tells the size of
the disk. xorriso -as mkisofs option -hard-disk-boot chooses this emulation.

The primary cause of all the triple faults in your question really come down to the fact that your kernel isn't being loaded properly into the memory at 0x0000:0x2000. When you transfer control to this location with a JMP you end up running what happens to be in the memory region and the CPU executes until it hits an instruction that causes a fault.
Bootable CDs are strange beasts that have a number of different modes, and there are many BIOSes that boot such CDs but they too may have their own quirks. When you use -no-emul-boot with XORRISO you are requesting the disk neither be treated as a floppy nor hard disk. You could remove -no-emul-boot -boot-load-size 4 that should generate an ISO that gets treated as a floppy. The problem with that is many real BIOSes, Emulators (BOCHs and QEMU) and Virtual machines do not support Int 13h/AH=42h extended disk reads when the CD is booted using floppy emulation. You may be forced to use regular disk read via Int 13h/AH=02h.
You should be able to use extended disk reads via Int 13h/AH=42h if you use -no-emul-boot -boot-load-size 4 but it will require some changes to your bootloader. When using -no-emul-boot -boot-load-size 4 CDROMs sector sizes are 2048 bytes, not 512. This will require a bit of modification to your bootloader and kernel. The -boot-load-size 4 writes information to the ISO that informs the BIOS to read 4 512-byte chunks from the beginning of the disk image inside the ISO. The 0xaa55 boot signature is no longer needed.
If you use -no-emul-boot there is one other snag that needs to be dealt with. On the CD-ROM LBA 0 isn't where the disk image gets placed in the final ISO. The question is, how can you get the LBA where the disk image is in the ISO? You can have XORRISO write this information into a special section of the bootloader you create, and you enable this feature with -boot-info-table.
Creating the special section at the beginning of the bootloader is relatively easy. In the El Torito Specification Supplement they mention this:
EL TORITO BOOT INFORMATION TABLE
...
The format of this table is as follows; all integers are in sec-
tion 7.3.1 ("little endian") format.
Offset Name Size Meaning
8 bi_pvd 4 bytes LBA of primary volume descriptor
12 bi_file 4 bytes LBA of boot file
16 bi_length 4 bytes Boot file length in bytes
20 bi_csum 4 bytes 32-bit checksum
24 bi_reserved 40 bytes Reserved
The 32-bit checksum is the sum of all the 32-bit words in the
boot file starting at byte offset 64. All linear block addresses
(LBAs) are given in CD sectors (normally 2048 bytes).
This is talking about the 56 bytes at offset 8 of the virtual disk we create holding our bootloader. If we modify the top of your bootloader code to look like this we effectively create a blank boot information table:
start:
jmp bootld_start
times 8-($-$$) db 0 ; Pad out first 8 bytes
; Boot info table
bi_pvd dd 0
bi_file dd 0
bi_kength dd 0
bi_csum dd 0
bi_reserved times 40 db 0 ; 40 bytes reserved
When using XORRISO with -boot-info-table this table will be filled in once the ISO is generated. bi_file is the important piece of information we will need since it is the LBA where our disk image is placed inside the ISO. We can use this to fill in the Disk Access Packet used by extended disk reads to read from the proper location of the ISO.
To make the DAP a little more readable and to account for 2048 byte sectors I've amended it to look like:
dap:
dap_size: db 0x10 ; Size of DAP
dap_zero db 0
; You can only read 11 2048 byte sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/2048 = 11 (rounded down)
dap_numsec: dw 11 ; Number of sectors to read
dap_offset: dw KERNEL_OFFSET ; Offset
dap_segment: dw 0 ; Segment
dap_lba_low: dd 0
dap_lba_high:dd 0
One issue is that the LBA placed into the Boot Information table is from the start of the disk image (sector with our bootloader). We need to increment that LBA by 1 and place it into the DAP so we are using the LBA where our kernel starts. Using 32-bit instruction we can just read the 32-bit value from the Boot Information Table, add 1 and save it to the DAP. If using strictly 16-bit instructions add one to a 32-bit value is more complex. Since we are going into 386 protected mode we can assume instruction with 32-bit operands are supported in real mode. The code to update the DAP with the LBA of the kernel could look like:
mov ebx, [bi_file] ; Get LBA of our disk image in ISO
inc ebx ; Add sector to get LBA for start of kernel
mov [dap_lba_low], ebx ; Update DAP with LBA of kernel in the ISO
The only other issue is that the bootloader sector needs to be padded out to 2048 (the size of a CD-ROM sector) rather than 512 and we can remove the boot signature. Change:
times 510-($-$$) db 0
db 0x55
db 0xAA
To:
times 2048-($-$$) db 0
The modified bootloader code could look like:
[bits 16]
[org 0x7c00]
KERNEL_OFFSET equ 0x2000
start:
jmp bootld_start
times 8-($-$$) db 0 ; Pad out first 8 bytes
; Boot info table
bi_pvd dd 0
bi_file dd 0
bi_kength dd 0
bi_csum dd 0
bi_reserved times 40 db 0 ; 40 bytes reserved
bootld_start:
xor ax, ax ; Explicitly set ES = DS = 0
mov ds, ax
mov es, ax
mov bx, 0x8C00 ; Set SS:SP to 0x8C00:0x0000 . The stack will exist
; between 0x8C00:0x0000 and 0x8C00:0xFFFF
mov ss, bx
mov sp, ax
mov ebx, [bi_file] ; Get LBA of our disk image in ISO
inc ebx ; Add sector to get LBA for start of kernel
mov [dap_lba_low], ebx ; Update DAP with LBA of kernel in the ISO
mov [BOOT_DRIVE], dl
mov bx, boot_msg
call print_string
mov dl, [BOOT_DRIVE]
call disk_load
jmp pm_setup
jmp $
BOOT_DRIVE:
db 0
disk_load:
mov si, dap
mov ah, 0x42
int 0x13
;cmp al, 4
;jne disk_error_132
ret
dap:
dap_size: db 0x10 ; Size of DAP
dap_zero db 0
; You can only read 11 2048 byte sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/2048 = 11 (rounded down)
dap_numsec: dw 11 ; Number of sectors to read
dap_offset: dw KERNEL_OFFSET ; Offset
dap_segment: dw 0 ; Segment
dap_lba_low: dd 0
dap_lba_high:dd 0
disk_error_132:
mov bx, disk_error_132_msg
call print_string
jmp $
disk_error_132_msg:
db 'Error! Error! Something is VERY wrong! (0x132)', 0
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
boot_msg:
db 'OS is booting files... ', 0
done_msg:
db 'Done! ', 0
%include "boot/print_string.asm"
pm_setup:
mov bx, done_msg
call print_string
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Place stack below EBDA in lower memory
mov ebp, 0x9c000
mov esp, ebp
mov ebx, pmode_msg
call print32
call KERNEL_OFFSET
jmp $
pmode_msg:
db 'Protected mode enabled!', 0
kernel:
mov ebx, pmode_msg
call print32
jmp $
pmode_tst:
db 'Testing...'
times 2048-($-$$) db 0
You can then modify your original XORRISO command to be:
xorriso -as mkisofs -R -J -c boot/bootcat \
-b boot/boot -no-emul-boot -boot-load-size 4 \
-boot-info-table -o image.iso iso

Related

NASM ASSEMBLY - Print "Hello World"

I've created a string and turned it into an array. Looping through each index and moving to the al register so it can print out to the vga. The problem is, it prints the size of the string with no problem, but the characters in gibberish. Can you please help me figure out what the problem is in the code. It will be highly appreciated.
org 0
bits 16
section .text
global _start
_start:
mov si, msg
loop:
inc si
mov ah, 0x0e
mov al, [si]
or al, al
jz end
mov bh, 0x00
int 0x10
jmp loop
end:
jmp .done
.done:
jmp $
msg db 'Hello, world!',0xa
len equ $ - msg
TIMES 510 - ($ - $$) db 0
DW 0xAA55
bootloader code
ORG 0x7c00
BITS 16
boot:
mov ah, 0x02
mov al, 0x01
mov ch, 0x00
mov cl, 0x02
mov dh, 0x00
mov dl, 0x00
mov bx, 0x1000
mov es, bx
int 0x13
jmp 0x1000:0x00
times 510 - ($ - $$) db 0
dw 0xAA55
The bootloader
Before tackling the kernel code, let's look at the bootloader that brings the kernel in memory.
You have written a very minimalistic version of a bootloader, one that omits much of the usual stuff like setting up segment registers, but thanks to its reduced nature that's not really a problem.
What could be a problem is that you wrote mov dl, 0x00, hardcoding a zero to select the first floppy as your bootdisk. No problem if this is indeed the case, but it would be much better to just use whatever value the BIOS preloaded the DL register with. That's the ID for the disk that holds your bootloader and kernel.
What is a problem is that you load the kernel to the segmented address 0x1000:0x1000 and then later jump to the segmented address 0x1000:0x0000 which is 4096 bytes short of the kernel. You got lucky that the kernel code did run in the end, thanks to the memory between these two addresses most probably being filled with zero-bytes that (two by two) translate into the instruction add [bx+si], al. Because you omitted setting up the DS segment register, we don't know what unlucky byte got overwritten so many times. Let's hope it was not an important byte...
mov bx, 0x1000
mov es, bx
xor bx, bx <== You forgot to write this instruction!
int 0x13
jmp 0x1000:0x0000
What is a problem is that you ignore the possibility of encountering troubles when loading a sector from the disk. At the very least you should inspect the carry flag that the BIOS.ReadSector function 02h reports and if the flag is set you could abort cleanly. A more sophisticated approach would also retry a limited number of times, say 3 times.
ORG 0x7C00
BITS 16
; IN (dl)
mov dh, 0x00 ; DL is bootdrive
mov cx, 0x0002
mov bx, 0x1000
mov es, bx
xor bx, bx
mov ax, 0x0201 ; BIOS.ReadSector
int 0x13 ; -> AH CF
jc ERR
jmp 0x1000:0x0000
ERR:
cli
hlt
jmp ERR
times 510 - ($ - $$) db 0
dw 0xAA55
The kernel
After the jmp 0x1000:0x0000 instruction has brought you to the first instruction of your kernel, the CS code segment register holds the value 0x1000. None of the other segment registers did change, and since you did not setup any of them in the bootloader, we still don't know what any of them contain. However in order to retrieve the bytes from the message at msg with the mov al, [si] instruction, we need a correct value for the DS data segment register. In accordance with the ORG 0 directive, the correct value is the one we already have in CS. Just two 1-byte instructions are needed: push cs pop ds.
There's more to be said about the kernel code:
The printing loop uses a pre-increment on the pointer in the SI register. Because of this the first character of the string will not get displayed. You could compensate for this via mov si, msg - 1.
The printing loop processes a zero-terminating string. You don't need to prepare that len equate. What you do need is an explicit zero byte that terminates the string. You should not rely on that large number of zero bytes thattimes produced. In some future version of the code there might be no zero byte at all!
You (think you) have included a newline (0xa) in the string. For the BIOS.Teletype function 0Eh, this is merely a linefeed that moves down on the screen. To obtain a newline, you need to include both carriage return (13) and linefeed (10).
There's no reason for your kernel code to have the bootsector signature bytes at offset 510. Depending on how you get this code to the disk, it might be necessary to pad the code up to (a multiple of) 512, so keep times 512 - ($ - $$) db 0.
The kernel:
ORG 0
BITS 16
section .text
global _start
_start:
push cs
pop ds
mov si, msg
mov bx, 0x0007 ; DisplayPage=0, GraphicsColor=7 (White)
jmp BeginLoop
PrintLoop:
mov ah, 0x0E ; BIOS.Teletype
int 0x10
BeginLoop:
mov al, [si]
inc si
test al, al
jnz PrintLoop
cli
hlt
jmp $-2
msg db 'Hello, world!', 13, 10, 0
TIMES 512 - ($ - $$) db 0

Error while creating a bootable floppy that contains a bootloader and a kernel

I'm trying to create a bootable floppy drive for my boot.bin and kernel.bin files. My boot.asm code is:
bits 16
section .text
start:
JMP MAIN
WAIT_FOR_KEY:
MOV AH, 00H
INT 16H
RET
CLEAR:
MOV AH, 0H ;CHANGING THE VIDEO MODE TO CLEAR THE SCREEN
MOV AL, 03H ;VIDEO MODE COD
INT 10H
RET
LOG_TO_HTS:
PUSH BX
PUSH AX
MOV BX, AX
MOV DX, 0
DIV WORD[ALL_SECTORS]
ADD DL, 01H
MOV CL, DL
MOV AX, BX
MOV DX, 0
DIV WORD [ALL_SECTORS]
MOV DX, 0
DIV WORD[FACES]
MOV DH, DL
MOV CH, AL
POP AX
POP BX
MOV DL, BYTE [BOOT_DEVICE]
RET
ECHO: ;-=-=-=PRINTING FUNCTION =-=-=-;
LODSB ;MOV ONE CHAR FROM SI TO AL AND DELETE IT FROM SI
CMP AL, 0 ;CHECK IF THE VALUE IN AL=0, IF ITS ZERO THEN WE
JE DONE ;WE ARE DONE PRINTING, AND JUMP BACK TO THE MAIN FUNCTION
CMP AL, 59
JE NEWLINE
MOV AH, 0EH ;THE FUNCTION CODE FOR PRINTING (TELETYPE PRINTING)
INT 10H ;BIOS CALL
JMP ECHO ;IF WE ARRIVED TO THIS LINE THATS MEAN WE ARE NOT DONE WITH
;PRINTING THE WHOLE STREING SO JUMP BACK AND COMPLETE THE PROCESS
DONE: ;LABEL CONTAINS ONE INSTRUCTION TO JUMP BACK TO THE LINE
RET ;WHERE WE CALLED THE ECHO FUNCTION
NEWLINE:
PUSHA
MOV AH, 0EH
MOV AL, 13
INT 10H
MOV AL, 10
INT 10H
POPA
RET
RESET_DRIVE: ;RESET FLOPPY DRIVE FUNCTION
MOV AX, 0 ;THE FUNCTION CODE TO RESET DRIVE
MOV DL, BYTE [BOOT_DEVICE] ;DRIVE ID TO RESET
INT 13H
RET
MAIN: ;THE MAIN FUNCTION LABEL
;TO BEGIN LOADING EXTERNAL FILE LIKE KERNEL SOME THINGS MUST BE DONE LIKE :
;1- SETTING UP THE STACK
;2- SETTING UP THE SEGMENTS
;3- SPECIFY THE BOOT DRIVE
;SETTING UP THE STACK
CLI ;CLEAR INTERRUPTS TO SET THE SEGMENT
XOR AX, AX ;SET THE BOTTOM OF THE STACK
MOV SS, AX
MOV SP, 0XFFFF ;SET THE TOP OF THE STACK
STI ;RESTORE THE INTERRUPTS
;SETTING UP THE SEGMENTS
MOV AX, 07C0H
MOV DS, AX
MOV ES, AX
;AFTER SETTING UP SEGMENTS AND STACK, YOU ARE FREE TO CODE WHAT EVER YOU WANT
;THE CPU WILL RECOGNISE YOUR CODE AS A BOOTLOADER
CALL CLEAR
;MOV SI, WELCOME_MSG
;CALL ECHO
MOV [BOOT_DEVICE], DL ;SAVE THE DRIVE ID TO USE IT LATER
;WE WILL USE IN 13H TO LOAD ROOT DIR. THAT WE NEED TO LOAD THE FIRST SECTOR OF THE KERNEL
;THEN WE WILL LOAD FAT TO LOAD THE WHOLE KERNEL SINCE THE FILE LOAD PROCESS CONSISTS OF 3 STEPS
;1- LOAD THE FISRT SECTOR OF THE FILE
;2- LOAD THE WHOLE FILE AND COPY IT INTO RAM
;3- EXCUTE THE FILE
;THE ROOT DIR. AND FAT ARE LOCATED IN SOMEWHERE ON THE FLOPPY DRIVE AS FOLLOWS
;1- SETTING UP AND LOADING THE ROOT DIR.
MOV AH, 02H ;THE FUNCTION CODE FOR LOADING ROOT DIR.
MOV AL, 14 ;THE SIZE OF THE ROOT DIR. THE ROOT DIR. = 14 AND WE WANT TO LOAD IT ALL SO AL=14
MOV BX, TEMP ;THE TEMP STORAGE FOR THE DATA WILL BE READED OR LOADED
MOV CH, 0 ;TRACK WE WANT TO LOAD, ITS TRACK 0 BEACUSE THE ROOT DIR. LOCATED THERE (CYLINDER)
MOV CL, 2 ;SECTOR WE WANT TO LOAD, ITS SECTOR 2 BEACUSE THE ROOT DIR. LOCATED THERE
MOV DH, 1 ;HEAD WE WANT TO LOAD, ITS HEAD 1 BEACUSE THE ROOT DIR. LOCATED THERE
PUSHA ;TO BE ABLE TO RETRY TO LOAD ROOT DIR. IF INT 13H FAILED
LOAD_RD:
INT 13H
JNC LOAD_RD_DONE ;IF WE ARRIVE HERE, THATS MEAN THAT THE ROOT DIR. LOADED SUCCESSFULLY
CALL RESET_DRIVE ;RESET FLOPPY FUNCTION CALL
JMP LOAD_RD ;RETRY IF INT 13H FAILED
LOAD_RD_DONE:
POPA
;NOW WE WILL SEARCH IN THE ROOT DIR. TABLE IF IT CONTAINS THE DESIRED FILE NAME WHICH IS KERNEL.BIN
;WE WILL USE CMPSB INSTRUCTION THAT COMPARES THE VALUE OF DS:SI AND ES:DI
;ES AND DS ARE ALREADY SET
;THE SEARCH WILL BE LIMITED TO SEARCH AMONG 11 BYTES BECAUSE THE FILE NAME IS 11 BYTES
MOV DI, TEMP ;THE RESULT WE GOT (THE ENTRY)
MOV CX, 224 ;THE MAX. POSSIBLE FILE NUMBER THAT WE COULD HAVE IS 224 SO WE LIMIT THE SEARCH TO IT (THE VALUE THAT WILL BE DECREASED WHILE LOOPING)
FIND_KERNEL:
PUSH CX ;PUSH CX AND DI BECAUSE THEY HELD IMPORTANT DATA THAT WE DONT WANT TO LOSE
POP DX
MOV SI, FILE_NAME ;THE VALUE THAT WE WILL COMPARE THE RESULT OF ROOT DIR. TO FIND
MOV CX, 11 ;TO REPEATE THE SEARCH PROCESS 11 TIMES(THE FILE NAME LENGTH)
REP CMPSB ;COPMARES SI WITH DI
JE FOUND_KERNEL ;IF WO GO TO THE NEXT LINE, THAT MEANS THAT THE KERNEL IS NOT FOUNDED YET
;AND SINCE DI IS POINTS TO THE FIRST ENTRY, WE NEED TO MOVE TO THE NEXT ONE BY ADDING 32
ADD AX, 32
MOV DI, TEMP
ADD DI, AX
PUSH DX
POP CX
LOOP FIND_KERNEL
;IF WE ARE HERE, THEN THE KERNEL IS NOT EXISTS
;MOV SI, FAILED_TO_LOAD_KERNEL
;CALL ECHO
CLI
INT 18H ;CRASH AND HALT THE PROCESSOR
FOUND_KERNEL: ;IF WE FOUND THE KERNEL WE WILL BE HERE
;NOW WE HAVE THE ENTRY POINT FOR THE KERNEL SAVED IN DL REGISTER
;WE GOT THE KERNEL FROM THE FIRST 11 BYTES SO WE NEED TO ADD 15 TO GET TO THE 26 BYTES
;THE FIRST SECTOR IS LOCATED AT 26 AND 27 SO WE MUST ADD 15 TO LOAD THE KERNEL IN THE FIRST SECTOR
MOV AX, WORD [DI+15] ;WORD ADDED TO COPY BOTH 26 AND 27 BYTES TO AX, THE WORD IS 2 BYTES
MOV [ENTERY_POINT], AX
;2- SETTING UP AND LOADING THE FAT
MOV AX, 1
MOV BX, TEMP ;THE TEMP STORAGE FOR THE DATA WILL BE READED OR LOADED
CALL LOG_TO_HTS
MOV AH, 2 ;THE FUNCTION CODE FOR LOADING ROOT DIR.
MOV AL, 9 ;THE SIZE OF THE ROOT DIR. THE ROOT DIR. = 14 AND WE WANT TO LOAD IT ALL SO AL=14
PUSHA ;TO BE ABLE TO RETRY TO LOAD ROOT DIR. IF INT 13H FAILED
LOAD_FAT:
INT 13H
JNC LOAD_FAT_DONE ;IF WE ARRIVE HERE, THATS MEAN THAT THE FAT LOADED SUCCESSFULLY
CALL RESET_DRIVE ;RESET FLOPPY FUNCTION CALL
JMP LOAD_FAT ;RETRY IF INT 13H FAILED
LOAD_FAT_DONE: ;IF LOADING FAT DONE SUCCESSFULLY, WE WILL BE HERE
MOV AH, 2
MOV AL, 1
PUSH AX
LOAD_SECTOR: ;ROOT DIR. GIVES US A DATA SECTOR AND WE WANT A LOGICAL SECTOR, WE WILL DO IT HERE
MOV AX, WORD [ENTERY_POINT]
ADD AX, 31 ;31 ADDED TO DONE THE CONVERTION
CALL LOG_TO_HTS ;COVERTS THE LOGICAL SECTORS INTO HEAD TRACK SECTORS
MOV AX, 2000H
MOV ES, AX
MOV BX, WORD[OFFSET_]
POP AX
PUSH AX
INT 13H
JNC GET_NEXT
CALL RESET_DRIVE
JMP LOAD_SECTOR
GET_NEXT:
MOV AX, [ENTERY_POINT]
MOV DX, 0
MOV BX, 6
MUL BX
MOV BX, 4
DIV BX
MOV SI, TEMP
ADD SI, AX
MOV AX, WORD[SI]
OR DX, DX
JZ EVEN
ODD:
SHR AX, 4
JMP SHORT COMP_SECTOR
EVEN:
AND AX, 0FFFH
COMP_SECTOR:
MOV WORD [ENTERY_POINT], AX
CMP AX, 0FF8H
JAE END
ADD WORD[OFFSET_], 512
JMP LOAD_SECTOR
END:
;MOV SI, KERNEL_LOADED_MSG
CALL ECHO
CALL WAIT_FOR_KEY
POP AX
MOV DL, BYTE[BOOT_DEVICE]
JMP 2000H:0000H
OFFSET_ DW 0
ALL_SECTORS DW 18
FACES DW 2
ENTERY_POINT DW 0
;FAILED_TO_LOAD_KERNEL DB "Error, Cannot Load The Kernel. Booting Process Aborted;",0
BOOT_DEVICE DB 0
FILE_NAME DB "KERNEL BIN"
;WELCOME_MSG DB "Litesoft bootloader Copyright (C) 2012-2016 By Mohamed El Sayed;",0
;KERNEL_LOADED_MSG DB "Kernel Loaded, Press Any Key To Continue...",0
TIMES 510 - ($-$$) DB 0 ;LOOK FOR EVERY SINGLE EMPTY BIT AND FILL IT WITH ZERO
;TILL THE BOOTLOADER SIZE BE 512 BYTE
DW 0xAA55 ;THE BOOT SIGNETUARE, ALWAYS THE SAME
TEMP: ;HERE IN BYTE 513 WE HAVE SOME MEMORY THAT WILL NOT BE EXECUTED
;AND WE CAN USE IT AS A TEMP MEMORY
The method i planed to use is creating a floppy image using dd and write my bootloader to the first sector then mount it and copy my kernel to it.
The commands i used
1- mkdosfs -C floppy.flp 1440 , This command creates a floppy image
2- dd status=noxfer conv=notrunc if=boot.bin of=floppy.flp ,This command burns the bootloader to the first sector
Those commands working very will and the floppy image with my bootloader at the first sector created successfully
The problem comes when i try to mount the floppy image to copy the kernel to it using the following commands
rm -rf tmp-loop
mkdir tmp-loop && mount -o loop -t vfat floppy.flp tmp-loop && cp kernel.bin tmp-loop/
Every time i try to mount the floppy image i get this error
mount: wrong fs type, bad option, bad superblock on /dev/loop0,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so.
What is the solution for this error? I'm using Ubuntu 16.04.
You lack a proper BIOS Parameter Block(BPB) at the beginning of your bootloader. This structure contains information used/updated by some BIOSes; can be used by the bootloader to read a FAT file system on unpartitioned media like a floppy; must be present for software like mount to determine what type of media a virtual floppy image represents. In the case of FAT12 on unpartitioned media (like a floppy) file system information is present in the Master Boot Record's BPB. A common layout is the DOS 3.4 EBPB and it could look something like:
start:
JMP MAIN
TIMES 3-($-$$) DB 0x90 ; Support 2 or 3 byte encoded JMPs before BPB.
; Dos 3.4 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
Based on the DOS 3.4 EBPB above and your boot.asm code, this BPB represents one similar to a 1.44MB floppy disk boot sector produced with mkdosfs -C floppy.flp 1440:
bits 16
section .text
start:
JMP MAIN
TIMES 3-($-$$) DB 0x90 ; Support 2 or 3 byte encoded JMPs before BPB.
; Dos 3.4 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
WAIT_FOR_KEY:
MOV AH, 00H
INT 16H
RET
CLEAR:
MOV AH, 0H ;CHANGING THE VIDEO MODE TO CLEAR THE SCREEN
MOV AL, 03H ;VIDEO MODE COD
INT 10H
RET
LOG_TO_HTS:
PUSH BX
PUSH AX
MOV BX, AX
MOV DX, 0
DIV WORD[ALL_SECTORS]
ADD DL, 01H
MOV CL, DL
MOV AX, BX
MOV DX, 0
DIV WORD [ALL_SECTORS]
MOV DX, 0
DIV WORD[FACES]
MOV DH, DL
MOV CH, AL
POP AX
POP BX
MOV DL, BYTE [BOOT_DEVICE]
RET
ECHO: ;-=-=-=PRINTING FUNCTION =-=-=-;
LODSB ;MOV ONE CHAR FROM SI TO AL AND DELETE IT FROM SI
CMP AL, 0 ;CHECK IF THE VALUE IN AL=0, IF ITS ZERO THEN WE
JE DONE ;WE ARE DONE PRINTING, AND JUMP BACK TO THE MAIN FUNCTION
CMP AL, 59
JE NEWLINE
MOV AH, 0EH ;THE FUNCTION CODE FOR PRINTING (TELETYPE PRINTING)
INT 10H ;BIOS CALL
JMP ECHO ;IF WE ARRIVED TO THIS LINE THATS MEAN WE ARE NOT DONE WITH
;PRINTING THE WHOLE STREING SO JUMP BACK AND COMPLETE THE PROCESS
DONE: ;LABEL CONTAINS ONE INSTRUCTION TO JUMP BACK TO THE LINE
RET ;WHERE WE CALLED THE ECHO FUNCTION
NEWLINE:
PUSHA
MOV AH, 0EH
MOV AL, 13
INT 10H
MOV AL, 10
INT 10H
POPA
RET
RESET_DRIVE: ;RESET FLOPPY DRIVE FUNCTION
MOV AX, 0 ;THE FUNCTION CODE TO RESET DRIVE
MOV DL, BYTE [BOOT_DEVICE] ;DRIVE ID TO RESET
INT 13H
RET
MAIN: ;THE MAIN FUNCTION LABEL
;TO BEGIN LOADING EXTERNAL FILE LIKE KERNEL SOME THINGS MUST BE DONE LIKE :
;1- SETTING UP THE STACK
;2- SETTING UP THE SEGMENTS
;3- SPECIFY THE BOOT DRIVE
;SETTING UP THE STACK
CLI ;CLEAR INTERRUPTS TO SET THE SEGMENT
XOR AX, AX ;SET THE BOTTOM OF THE STACK
MOV SS, AX
MOV SP, 0XFFFF ;SET THE TOP OF THE STACK
STI ;RESTORE THE INTERRUPTS
;SETTING UP THE SEGMENTS
MOV AX, 07C0H
MOV DS, AX
MOV ES, AX
;AFTER SETTING UP SEGMENTS AND STACK, YOU ARE FREE TO CODE WHAT EVER YOU WANT
;THE CPU WILL RECOGNISE YOUR CODE AS A BOOTLOADER
CALL CLEAR
;MOV SI, WELCOME_MSG
;CALL ECHO
MOV [BOOT_DEVICE], DL ;SAVE THE DRIVE ID TO USE IT LATER
;WE WILL USE IN 13H TO LOAD ROOT DIR. THAT WE NEED TO LOAD THE FIRST SECTOR OF THE KERNEL
;THEN WE WILL LOAD FAT TO LOAD THE WHOLE KERNEL SINCE THE FILE LOAD PROCESS CONSISTS OF 3 STEPS
;1- LOAD THE FISRT SECTOR OF THE FILE
;2- LOAD THE WHOLE FILE AND COPY IT INTO RAM
;3- EXCUTE THE FILE
;THE ROOT DIR. AND FAT ARE LOCATED IN SOMEWHERE ON THE FLOPPY DRIVE AS FOLLOWS
;1- SETTING UP AND LOADING THE ROOT DIR.
MOV AH, 02H ;THE FUNCTION CODE FOR LOADING ROOT DIR.
MOV AL, 14 ;THE SIZE OF THE ROOT DIR. THE ROOT DIR. = 14 AND WE WANT TO LOAD IT ALL SO AL=14
MOV BX, TEMP ;THE TEMP STORAGE FOR THE DATA WILL BE READED OR LOADED
MOV CH, 0 ;TRACK WE WANT TO LOAD, ITS TRACK 0 BEACUSE THE ROOT DIR. LOCATED THERE (CYLINDER)
MOV CL, 2 ;SECTOR WE WANT TO LOAD, ITS SECTOR 2 BEACUSE THE ROOT DIR. LOCATED THERE
MOV DH, 1 ;HEAD WE WANT TO LOAD, ITS HEAD 1 BEACUSE THE ROOT DIR. LOCATED THERE
PUSHA ;TO BE ABLE TO RETRY TO LOAD ROOT DIR. IF INT 13H FAILED
LOAD_RD:
INT 13H
JNC LOAD_RD_DONE ;IF WE ARRIVE HERE, THATS MEAN THAT THE ROOT DIR. LOADED SUCCESSFULLY
CALL RESET_DRIVE ;RESET FLOPPY FUNCTION CALL
JMP LOAD_RD ;RETRY IF INT 13H FAILED
LOAD_RD_DONE:
POPA
;NOW WE WILL SEARCH IN THE ROOT DIR. TABLE IF IT CONTAINS THE DESIRED FILE NAME WHICH IS KERNEL.BIN
;WE WILL USE CMPSB INSTRUCTION THAT COMPARES THE VALUE OF DS:SI AND ES:DI
;ES AND DS ARE ALREADY SET
;THE SEARCH WILL BE LIMITED TO SEARCH AMONG 11 BYTES BECAUSE THE FILE NAME IS 11 BYTES
MOV DI, TEMP ;THE RESULT WE GOT (THE ENTRY)
MOV CX, 224 ;THE MAX. POSSIBLE FILE NUMBER THAT WE COULD HAVE IS 224 SO WE LIMIT THE SEARCH TO IT (THE VALUE THAT WILL BE DECREASED WHILE LOOPING)
FIND_KERNEL:
PUSH CX ;PUSH CX AND DI BECAUSE THEY HELD IMPORTANT DATA THAT WE DONT WANT TO LOSE
POP DX
MOV SI, FILE_NAME ;THE VALUE THAT WE WILL COMPARE THE RESULT OF ROOT DIR. TO FIND
MOV CX, 11 ;TO REPEATE THE SEARCH PROCESS 11 TIMES(THE FILE NAME LENGTH)
REP CMPSB ;COPMARES SI WITH DI
JE FOUND_KERNEL ;IF WO GO TO THE NEXT LINE, THAT MEANS THAT THE KERNEL IS NOT FOUNDED YET
;AND SINCE DI IS POINTS TO THE FIRST ENTRY, WE NEED TO MOVE TO THE NEXT ONE BY ADDING 32
ADD AX, 32
MOV DI, TEMP
ADD DI, AX
PUSH DX
POP CX
LOOP FIND_KERNEL
;IF WE ARE HERE, THEN THE KERNEL IS NOT EXISTS
;MOV SI, FAILED_TO_LOAD_KERNEL
;CALL ECHO
CLI
INT 18H ;CRASH AND HALT THE PROCESSOR
FOUND_KERNEL: ;IF WE FOUND THE KERNEL WE WILL BE HERE
;NOW WE HAVE THE ENTRY POINT FOR THE KERNEL SAVED IN DL REGISTER
;WE GOT THE KERNEL FROM THE FIRST 11 BYTES SO WE NEED TO ADD 15 TO GET TO THE 26 BYTES
;THE FIRST SECTOR IS LOCATED AT 26 AND 27 SO WE MUST ADD 15 TO LOAD THE KERNEL IN THE FIRST SECTOR
MOV AX, WORD [DI+15] ;WORD ADDED TO COPY BOTH 26 AND 27 BYTES TO AX, THE WORD IS 2 BYTES
MOV [ENTERY_POINT], AX
;2- SETTING UP AND LOADING THE FAT
MOV AX, 1
MOV BX, TEMP ;THE TEMP STORAGE FOR THE DATA WILL BE READED OR LOADED
CALL LOG_TO_HTS
MOV AH, 2 ;THE FUNCTION CODE FOR LOADING ROOT DIR.
MOV AL, 9 ;THE SIZE OF THE ROOT DIR. THE ROOT DIR. = 14 AND WE WANT TO LOAD IT ALL SO AL=14
PUSHA ;TO BE ABLE TO RETRY TO LOAD ROOT DIR. IF INT 13H FAILED
LOAD_FAT:
INT 13H
JNC LOAD_FAT_DONE ;IF WE ARRIVE HERE, THATS MEAN THAT THE FAT LOADED SUCCESSFULLY
CALL RESET_DRIVE ;RESET FLOPPY FUNCTION CALL
JMP LOAD_FAT ;RETRY IF INT 13H FAILED
LOAD_FAT_DONE: ;IF LOADING FAT DONE SUCCESSFULLY, WE WILL BE HERE
MOV AH, 2
MOV AL, 1
PUSH AX
LOAD_SECTOR: ;ROOT DIR. GIVES US A DATA SECTOR AND WE WANT A LOGICAL SECTOR, WE WILL DO IT HERE
MOV AX, WORD [ENTERY_POINT]
ADD AX, 31 ;31 ADDED TO DONE THE CONVERTION
CALL LOG_TO_HTS ;COVERTS THE LOGICAL SECTORS INTO HEAD TRACK SECTORS
MOV AX, 2000H
MOV ES, AX
MOV BX, WORD[OFFSET_]
POP AX
PUSH AX
INT 13H
JNC GET_NEXT
CALL RESET_DRIVE
JMP LOAD_SECTOR
GET_NEXT:
MOV AX, [ENTERY_POINT]
MOV DX, 0
MOV BX, 6
MUL BX
MOV BX, 4
DIV BX
MOV SI, TEMP
ADD SI, AX
MOV AX, WORD[SI]
OR DX, DX
JZ EVEN
ODD:
SHR AX, 4
JMP SHORT COMP_SECTOR
EVEN:
AND AX, 0FFFH
COMP_SECTOR:
MOV WORD [ENTERY_POINT], AX
CMP AX, 0FF8H
JAE END
ADD WORD[OFFSET_], 512
JMP LOAD_SECTOR
END:
;MOV SI, KERNEL_LOADED_MSG
CALL ECHO
CALL WAIT_FOR_KEY
POP AX
MOV DL, BYTE[BOOT_DEVICE]
JMP 2000H:0000H
OFFSET_ DW 0
ALL_SECTORS DW 18
FACES DW 2
ENTERY_POINT DW 0
;FAILED_TO_LOAD_KERNEL DB "Error, Cannot Load The Kernel. Booting Process Aborted;",0
BOOT_DEVICE DB 0
FILE_NAME DB "KERNEL BIN"
;WELCOME_MSG DB "Litesoft bootloader Copyright (C) 2012-2016 By Mohamed El Sayed;",0
;KERNEL_LOADED_MSG DB "Kernel Loaded, Press Any Key To Continue...",0
TIMES 510 - ($-$$) DB 0 ;LOOK FOR EVERY SINGLE EMPTY BIT AND FILL IT WITH ZERO
;TILL THE BOOTLOADER SIZE BE 512 BYTE
DW 0xAA55 ;THE BOOT SIGNETUARE, ALWAYS THE SAME
TEMP: ;HERE IN BYTE 513 WE HAVE SOME MEMORY THAT WILL NOT BE EXECUTED
;AND WE CAN USE IT AS A TEMP MEMORY
If you generate boot.bin from this code and place it in a floppy image and use the command to display file type information:
file floppy.flp
It should produce something like this if it worked:
floppy.flp: , code offset 0x99+3, OEM-ID "mkfs.fat", root entries 224, sectors 2880 (volumes <=32 MB) , sectors/FAT 9, sectors/track 18, serial number 0x2d7e5a1a, unlabeled, FAT (12 bit), followed by FAT
If you get similar output it should be mountable with the mount command.
As you may have noticed the data within an EBPB/BPB can be used to navigate over a FAT12 file system.

How to use ORG addresses > 0xFFFF?

I am trying to write a simply bootloader in assembler.
The bootloader copies sector 2 from a floppy to address 0x5000 (segment 0x500, offset 0x0), jumps to the segment and prints a message.
However, when I change the segment address to 0x1000, the message does not get printed anymore. I suspect the org 0x10000 instruction has a problem, which might be related to segmentation. I tried org 0x1000:0 too, but the message won't be printed.
Here is my bootloader code, which gets written to the first sector of the floppy:
[BITS 16]
org 0x7C00
start:
mov ah, 0x02 ; Read sectors from drive
mov al, 1 ; Read 1 sector
mov ch, 0 ; Cylinder 0
mov cl, 2 ; Sector 2
mov dh, 0 ; Head 0
mov bx, sect2dest;
mov es, bx
mov bx, 0x0
int 0x13
jmp sect2dest:0;
data:
sect2dest equ 0x500
The magic identifier in the end is written by a custom linking script, so don't worry about that.
Here is my sector two, which should print a message:
[BITS 16]
org 0x5000
sect2:
mov ah, 0x13
mov al, 1
mov bl, 0x17
mov cx, msg_len
mov dh, 0
mov dl, 0
mov bh, 0
mov bp, 0
mov es, bp
mov bp, msg
int 0x10
jmp $
msg db 13,10,"Hello, World!"
msg_len equ $ - msg
As mentioned above, when I try writing sector 2 to any address larger than 0xFFFF, the message doesn't get printed.
Consider that bp is 16 bit, so if you use an ORG of 10000h any offset won't fit in it.
I was expecting the assembler to raise a warning but a quick test shown otherwise.
Remember also that generally is best to avoid challenging the BIOS, thought I don't know how it is actually handled, I would avoid to print a string that strides two segments.
Since you are putting zero in es, make sure that the ORG is at most 10000h-[msg_len], so that the whole string is reachable within es.

Raising BRK in assembly on i386 Linux

I found and studied x86 memory access segmentation fault and it won't work in my code. The difference perhaps being that I don't use separate .text and .data segments but keep all in a single segment by creating a custom ELF header. Does that explain why the SYS_BRK call fails?
The program then continues by making the memory pages read/write/execute etc.
I tried to find the minimum code sample that illustrated the issue.
In kdbg the sample does work, but not when started from the command line, hence the message printing.
cpu 386
bits 32
; System calls used
%assign SYS_EXIT 1
%assign SYS_WRITE 4
%assign SYS_BRK 45
%assign SYS_MPROTECT 125
; flags for SYS_MPROTECT
%assign PROT_READ 1
%assign PROT_WRITE 2
%assign PROT_EXEC 4
%assign STDOUT 1
memstart: org 0x08048000
ehdr: ; Elf32_Ehdr (see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)
db 0x7F, "ELF" ; e_ident[EI_MAG0..EI_MAG03]
db 1 ; e_ident[EI_CLASS]
db 1 ; e_ident[EI_DATA]
db 1 ; e_ident[EI_VERSION]
db 0 ; e_ident[EI_OSABI]
db 0 ; e_ident[EI_ABIVERSION]
times 7 db 0 ; e_ident[EI_PAD]
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd start ; e_entry
dd phdr - $$ ; e_phoff
dd 0 ; e_shoff
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 1 ; e_phnum
dw 0 ; e_shentsize
dw 0 ; e_shnum
dw 0 ; e_shstrndx
ehdrsize equ $ - ehdr
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd $$ ; p_vaddr
dd $$ ; p_paddr
dd filesize ; p_filesz
dd filesize ; p_memsz
dd 5 ; p_flags
dd 0x1000 ; p_align
phdrsize equ $ - phdr
memsize: dd 16*4096 ; = 16 * 4K pages
memtop: dd 0
start: ;int 3
xor ebx, ebx ; find the amount of allocated memory
mov eax, SYS_BRK
int 0x80 ; eax contains current memtop
sub eax, memstart ; got enough memory?
test eax, [memsize]
ja memgood
mov eax, memstart ; raise memory limit to memstart + memsize
add eax, [memsize]
mov ebx, eax
mov ecx, eax ; save requested memory size in ecx
mov eax, SYS_BRK
int 0x80
cmp eax, ecx
jne brk_error ; raising memory limit failed
memgood: mov edx, (PROT_READ | PROT_WRITE | PROT_EXEC)
mov ecx, [memsize] ; make memory read/write/execute
mov ebx, memstart
mov eax, SYS_MPROTECT
int 0x80
test eax, eax
js bailout
jmp launch ; lets start the party
brk_error: mov edx, brkelen
mov ecx, brke
mov ebx, STDOUT
mov eax, SYS_WRITE
int 0x80
jmp bailout
brke: db 'SYS_BRK failed, bye', 10
brkelen equ $ - brke
bailout: mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
launch: mov edx, succlen
mov ecx, succ
mov ebx, STDOUT
mov eax, SYS_WRITE
int 0x80
jmp bailout
succ: db 'Success with mem config, bye', 10
succlen equ $ - succ
filesize equ $ - $$
You should use cmp here instead of test:
sub eax, memstart ; got enough memory?
test eax, [memsize]
ja memgood
The memory area described by SYS_BRK start a random offset of 0 to 0x02000000 after the executable unless address space layout randomisation is disabled, which I suspect your debugger does. You can use mmap to allocate memory at a specified address (don't set MAP_FIXED unless you want overwrite existing mappings) .
However this whole exercise with brk and mprotect seems rather pointless as apart from the stack upon program start memory is allocated exactly as the ELF header specified, instead you can:
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd $$ ; p_vaddr
dd $$ ; p_paddr
dd filesize ; p_filesz
dd 16*4096 ; p_memsz
dd 7 ; p_flags
dd 0x1000 ; p_align
phdrsize equ $ - phdr

the difference of machine code between mov eax, 0 and mov ax, 0

I use nasm to study assembly. Below is the source code:
[BITS 32]
mov ebx, 0
mov bx, 0
mov bl, 0
then I use 'ndisasm -b 32 test.bin' to get output as following:
00000000 BB00000000 mov ebx,0x0
00000005 66BB0000 mov bx,0x0
00000009 B300 mov bl,0x0
My question is the difference of BB00000000 and 66BB0000, I know the opcode of MOV is B, but what is the 66 before B?
0x66 is operand-size override prefix which means that if the default operand size is 32-bit (as your case is), it becomes 16-bit a the opcode works on 16-bit size registers (bx in your case)

Resources