QEMU error

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

QEMU error

Post by Thpertic »

I'm trying to implement an higher-half kernel like in the tutorial in the wiki (before setting up paging), but when I run it in QEMU it throws "qemu: fatal: Trying to execute code outside RAM or ROM at 0x00100029" with all the registers.
It stops while setting cr0 at:

Code: Select all

mov %ecx, %cr0

Can't understand why... Can you help me with this?

Thanks in advance

boot.S

Code: Select all

# AT&T's Syntax
.global _loader

# Declare multiboot headers constants.
.set ALIGN,    1<<0                                         # align loaded modules on page boundaries
.set MEMINFO,  1<<1                                         # provide memory map
.set FLAGS,    ALIGN | MEMINFO                              # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002                                   # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS)                             # checksum of above, to prove we are multiboot

# This is the virtual base address of kernel space.
# It must be used to convert virtual addresses into physical addresses 
# until paging is enabled.
# Note that this is not the virtual address where the kernel image itself is loaded
# just the amount that must be subtracted from a virtual address to get physical address.
.set KERNEL_VIRTUAL_BASE, 0xC0000000                         # 3 GB
.set KERNEL_PAGE_NUMBER, (KERNEL_VIRTUAL_BASE >> 2)          # Page directory index of kernel's 4MB PTE

.section .multiboot
.align 4
    .long MAGIC
    .long FLAGS
    .long CHECKSUM

.section .data
.align 0x1000
BootPageDirectory:
    push %ecx
    # This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
    # All bits are clear except the following:  
    #   - bit 7: PS The kernel page is 4MB.
    #   - bit 1: RW The kernel page is read/write.
    #   - bit 0: P  The kernel page is present (in RAM).
    # This entry must be here -- otherwise the kernel will crash immediately after paging is enabled 
    # because it can't fetch the next instruction! It's ok to unmap this page later.
    .quad 0x00000083
    mov $(KERNEL_PAGE_NUMBER - 1), %ecx
    1: 
        .quad 0                                             # Pages afetr the kernel image
        cmp $0, %ecx
        jnz 1
    # This page directory entry defines a 4MB page containing the kernel
    .quad 0x00000083
    mov $(1024 - KERNEL_PAGE_NUMBER - 1), %ecx
    2: 
        .quad 0                                             # Pages afetr the kernel image
        cmp $0, %ecx
        jnz 2
    
    pop %ecx

# Declare a header as in the Multiboot Standard.
.section .text
# Reserve initial kernel stack space -- that's 16k
.set STACKSIZE, 0x4000

# Setting up entry point for linker.
# The kernel entry point.
.global __start__
.set __start__, (setup)
setup:    
    # NOTE: Until paging is set up, the code must be position-independent and 
    # use physical addresses, not virtual ones!
    mov $(BootPageDirectory - KERNEL_VIRTUAL_BASE), %ecx
    mov %ecx, %cr3                                          # Load Page Directory Base Register.

    mov %cr4, %ecx
    or $0x00000010, %ecx                                    # Set PSE bit in CR4 to enable 4MB pages.
    mov %ecx, %cr4    

    mov %cr0, %ecx
    or $0x80000000, %ecx                                    # Set PG bit in CR0 to enable paging.
    mov %ecx, %cr0

    # Start fetching instructions in kernel space.
    # Since 'eip' at this point holds physical address of this command (approximately 0x00100000)
    # we need to do a long jump to the correct virtual address of 
    # StartInHigherHalf which is approximately in 0xC0100000.
    lea (StartInHigherHalf), %ecx
    jmp *(%ecx)                                                # Note: Must be absolute jump!

StartInHigherHalf:
    # Unmap the identity-mapped firts 4MB of physical address space.
    # It should not be needed anymore.
    movw $0, (BootPageDirectory)
    invlpg (0)

    # Note: From now on, paging should enabled. The first 4MB of physical address space
    # is mapped starting at KERNEL_VIRTUAL_BASE. Everything is linked to this address,
    # so no more position-independent code or funny business with virtual-to-physical address
    # translation should be necessary. We now have a higher-half kernel.
    mov $(stack + STACKSIZE), %esp                          # Setup the stack
    push %eax                                               # Pass Multiboot magic number

    # Pass Multiboot info structure --
    # WARNING: This is a physical address and may not be in the first 4 MB!
    push %ebx

    # Call the global constructors.
    call _init

    # Transfer control to the main kernel.
    call kmain

    # Hang if kmain unexpectedly returns.
    cli
    1:	hlt
        jmp 1

# Reserve a stack for the initial thread.
.section .bss
.align 32

.lcomm stack, STACKSIZE                                 # Reserve 16k stack on a uint64_t boundary


# This part is not reachable (beacuse of the endless loop), 
# except if you call _gdtFlush


# This will set up our new segment registers. We need to do
# something special in order to set CS. We do what is called a
# far jump. A jump that includes a segment as well as an offset.  

.global gdtFlush                                        # Allows the C code to link to this
.extern gp                                              # Says that 'gp' is in another file

gdtFlush:
    # Set our own GDT, can't rely GDT register being valid after bootloader
    # transfers control to our entry point  
    lgdt (gp)                                           # Load the GDT with 'gp' which is a special pointer

    mov $0x10, %eax                                     # 0x10 is the offset in the GDT to our data segment
    mov %eax, %ds               
    mov %eax, %es
    mov %eax, %gs
    mov %eax, %fs
    mov %eax, %ss

    ljmp $0x08, $setcs                                  # Set new CS at 0x08 

    setcs:
        ret
linker.ld

Code: Select all

/* The bootloader will look at this image and start execution at the symbol
   designated at the entry point. */
ENTRY(__start__)
OUTPUT_FORMAT(elf32-i386)

/* Tell where the various sections of the object files will be put in the final
   kernel image. */
SECTIONS {
	/* The kernel will live at 3GB + 1MB in the virtual address space,
	   which will be mapped to 1MB in the physical address space. */
	. = 0xC0100000;

	/* First put the multiboot header, as it is required to be put very early
	   early in the image or the bootloader won't recognize the file format.
	   Next we'll put the .text section. */
	.text ALIGN(0x1000) : AT(ADDR(.text) - 0xC0000000) {
		*(.multiboot)
		*(.text)
	}

	.rodata ALIGN(0x1000) : AT(ADDR(.rodata) - 0xC0000000) {
		/* Read-only data */
		*(.rodata*)
	} 

	/* Read-write data (initialized) */
	.data ALIGN(0x1000) : AT(ADDR(.data) - 0xC0000000) {
		*(.data)
	}

	/* Read-write data (uninitialized) and stack */
	.bss ALIGN(0x1000) : AT(ADDR(.bss) - 0xC0000000) {
		_sbss = .;
		*(COMMON)
		*(.bss)
		_ebss = .;
	}

	/* The compiler may produce other sections, put them in the proper place in
	   in this file, if you'd like to include them in the final kernel. */
}
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: QEMU error

Post by thepowersgang »

You have code in the page directory it seems, which is being interpreted as the page directory (instead of being executed, which is what I think you thought it would do).

You'll want to use the assembler's macro support to generate the boot-time paging structures.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Re: QEMU error

Post by Thpertic »

With this

Code: Select all

mov $(KERNEL_PAGE_NUMBER - 1), %ecx
    1: 
        .quad 0                                             # Pages afetr the kernel image
        cmp $0, %ecx
        jnz 1 
I'm trying to emulate the "times" prefix which in GAS isn't an instruction. What macro should I use to replace it?
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: QEMU error

Post by thepowersgang »

Gas should have similar features (I believe it has `.rept/.endr` directives that do a similar thing).

It might be worth reading through the "Assembler Directives" page of the GAS manual - https://sourceware.org/binutils/docs/as/Pseudo-Ops.html
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: QEMU error

Post by iansjack »

You are confusing assembler directives and run-time behaviour. A loop/jmp can't repeat an assembler directive. As thepowersgang says, use

Code: Select all

.rept KERNEL_PAGE_NUMBER
.quad 0
.endr
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Re: QEMU error

Post by Thpertic »

I've fixed a shift on the set of KERNEL_PAGE_NUMBER (">> 2" in ">> 22") and used the .rept and .endr directives but the error still remains... Other advices?
Post Reply