Page 1 of 1

Loading a long mode kernel

Posted: Thu Jun 23, 2016 3:14 pm
by eof
I've just read the multiboot spec and I'm trying to figure exactly how to load a kernel in long mode. I can already successfully switch the CPU into long mode as long as the virtual address to physical address is the identity function. I would like my memory layout to consist of the following during the execution of a process:

1. Kernel memory at the top (this memory mapping would always be fixed). Let's call this KERNEL_BASE_ADDR.

2. User stack growing down

3. Heap growing up

4. Data

5. Userland code

The purpose of this question is how to get the kernel up there. In particular, how to set up linker scripts and the assembly files, so that generated code is made relative to the correct address where it's going to sit (virtual address that is).

In principle it's all very easy and I would need GRUB to load things up as follows:

1. Multiboot header + initial code which sets up page table and switches to long mode.

2. Followed by code with .code64 specifier aligned to a 4KB page boundary.

Essentially,. I would just like to map the virtual address KERNEL_BASE_ADDR to the physical address where (2) sits and then jump to that code to get myself up and running. I can easily have a linker.ld like:

Code: Select all

ENTRY(start)

SECTIONS {
    . = 1M;

    .text :
    {
        /* ensure that the multiboot header is at the beginning */
        *(.multiboot_header)
        *(.text)
    }
}
where .multiboot_header is a section containing those magic values and .text contains the 32-bit code that sets up the CPU long mode. Where should I then put the "pure" 64-bit code and how do I generate code that thinks it's loaded at address KERNER_BASE_ADDR when GRUB has to simultaneously load it at a different physical address?

I'm using the GNU assembler.

Re: Loading a long mode kernel

Posted: Thu Jun 23, 2016 3:23 pm
by Boris
Hi,
I'd suggest using a special section for your boot loader.
I use this

Code: Select all

/* Physical address at 1Mb*/
KERNEL_PHY = 0x100000;

/* Virtual address at -1Gb */
KERNEL_VMA = 0xffffffffC0000000 ;

/* Entry point of multiboot access */
ENTRY(_xx_start)


SECTIONS {

    /* Here we use physical addresses*/
  . = KERNEL_PHY;

  .boot_kernel_bootstrap :
  {
    /* This section is used for code that resides BEFORE pagination*/
      *(.boot_kernel_bootstrap);
  }


  /* For the rest of kernel, we use virtual memory addresses */


  . += KERNEL_VMA;

   .kernel_initialize  ALIGN(0x1000): AT(ADDR(.kernel_initialize) - KERNEL_VMA)
   {
      /*  Here pagination is activated. The goal of
          this section is to be able to be discarded.
          Every "one shot" method called during the initialization
          should be in that section.
          It should be 2mb aligned (0x200000) if we want
          to make its page free for use
      */
   		_start_kernel_initialize_section = .;
   		*(.kernel_initialize)
   		end_kernel_initialize_section = .;

   		 /*
       Disabled for now (it makes smaller kernel)
       . = ALIGN(0x200000);
       */
   }


   .text  ALIGN(0x1000) :AT(ADDR(.text) - KERNEL_VMA) {
       *(.text.startup)
       *(.text)
       *(.text.initialize)
       *(.text.unlikely)
   }



   .data   ALIGN(0x1000): AT(ADDR(.data) - KERNEL_VMA) {
       *(.data)
   }

   .rodata   ALIGN(0x1000): AT(ADDR(.rodata) - KERNEL_VMA) {
       *(.rodata*)
   }


   .bss   ALIGN(0x1000): AT(ADDR(.bss) - KERNEL_VMA) {
       _sbss = .;
       *(COMMON)
       *(.bss)
      _ebss = .;


   }

   .end_of_kernel : AT(ADDR(.end_of_kernel) - KERNEL_VMA)
   {
      _endOfKernel = .;
   }
Then within assembly boot code :

Code: Select all

bits 32]

; Everything in this file should be in
; That section
section .boot_kernel_bootstrap

Re: Loading a long mode kernel

Posted: Thu Jun 23, 2016 3:28 pm
by iansjack
One way is to load the 32-bit code as the kernel and then load the 64-bit code as a module. That is very easy although it may require you to pass a few variables between the different bits of code, which is fairly simple. I just pass any information in a fixed memory location.