0

I am writing a basic kernel to learn and am having difficulty enabling paging, so that I can work in long mode.

I slimmed down my code for simplicity.

boot.asm:

;==================================================
; Entry point.
;
; In this file, we are in 32-bit mode.
;==================================================

%include "multiboot2.inc"

global _start

PAGE_TABLE_SIZE         equ  4096
PAGE_TABLE_ENTRY_COUNT  equ  512

; Section "text"
;--------------------------------------------------

section .text

bits 32

;
; Entry point.
;
_start:
  call  setup_ptables
  call  enable_paging

  mov  word [0xb8000], 0x0247 ; 'G'.

  cli

;
; Sets up page tables.
;
setup_ptables:
  ; Point the first entry of level 4 page table to the first entry in the level 3 page table.
  mov  eax, ptable_l3
  or   eax, 0b11
  mov  dword [ptable_l4 + 0], eax

  ; Point the first entry of level 3 page table to the first entry in the level 2 page table.
  mov  eax, ptable_l2
  or   eax, 0b11
  mov  dword [ptable_l3 + 0], eax

  mov  ecx, 0  ; Loop counter.
.map_ptable_l2:
  mov  eax, 0x200000               ; 2 MB.
  mul  ecx
  or   eax, 0b10000011
  mov  [ptable_l2 + ecx * 8], eax

  ; Increment counter and if there are more entries to fill, loop.
  inc  ecx
  cmp  ecx, PAGE_TABLE_ENTRY_COUNT
  jne  .map_ptable_l2

  ; Otherwise, return.
  ret

;
; Enables paging (IA-32e mode).
;
; Reference: https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf.
;
enable_paging:
  ; Enable PAE.
  ; Set the PAE flag in CR4 to 1.
  mov  eax, cr4
  or   eax, 1 << 5
  mov  cr4, eax

  ; Load CR3 with the physical base address of the level 4 page table.
  mov  eax, ptable_l4
  mov  cr3, eax

  ; Enable long mode.
  ; Set the LME flag in MSR to 1.
  mov  ecx, 0xC0000080
  rdmsr
  or   eax, 1 << 8
  wrmsr

  ; Enable paging.
  ; Set the PG flag in CR0 to 1
  mov  eax, cr0
  or   eax, 1 << 31
  ;or   eax, 1 << 16
  mov  cr0, eax

  ; Return
  ret

; Section "bss"
;--------------------------------------------------

section .bss

align PAGE_TABLE_SIZE

ptable_l4:
  resb  PAGE_TABLE_SIZE
ptable_l3:
  resb  PAGE_TABLE_SIZE
ptable_l2:
  resb  PAGE_TABLE_SIZE

header.asm:

;==================================================
; Multiboot header.
;
; Reference: https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html.
;
; Multiboot header fields:
; ┏━━━━━━━━┯━━━━━━┯━━━━━━━━━━━━━━━┓
; ┃ Offset │ Type │ Field Name    ┃
; ┠────────┼──────┼───────────────┨
; ┃ 0      │ u32  │ magic         ┃
; ┃ 4      │ u32  │ architecture  ┃
; ┃ 8      │ u32  │ header_length ┃
; ┃ 12     │ u32  │ checksum      ┃
; ┃ 16-XX  │      │ tags          ┃
; ┗━━━━━━━━┷━━━━━━┷━━━━━━━━━━━━━━━┛
;==================================================


%include "multiboot2.inc"

; Header magic fields.
MAGIC     equ  MULTIBOOT2_HEADER_MAGIC
ISA       equ  MULTIBOOT_ARCHITECTURE_I386
LEN       equ  header_end - header_start
CHECKSUM  equ  0x100000000 - (MAGIC + ISA + LEN)

; Section "multiboot"
;--------------------------------------------------

section .multiboot

align MULTIBOOT_HEADER_ALIGN

header_start:
  ; Header magic fields.
  dd  MAGIC     ; magic.
  dd  ISA       ; architecture.
  dd  LEN       ; header_length.
  dd  CHECKSUM  ; checksum.

align MULTIBOOT_TAG_ALIGN

.efi_boot_services_tag:
  dw  MULTIBOOT_HEADER_TAG_EFI_BS  ; type.
  dw  0                            ; flags.
  dd  8                            ; size.

align MULTIBOOT_TAG_ALIGN

.efi_amd64_tag:
  dw  MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64  ; type.
  dw  0                                         ; flags.
  dd  12                                        ; size.

align MULTIBOOT_TAG_ALIGN

.end_tag:
  dw  MULTIBOOT_HEADER_TAG_END  ; type.
  dw  0                         ; flags.
  dd  8                         ; size.

header_end:

multiboot2.inc:

;
; Multiboot2 constants.
;
; Reference: https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html.
;

%ifndef __SPOT_MULTIBOOT2__
%define __SPOT_MULTIBOOT2__

%define  MULTIBOOT_HEADER_ALIGN  8

%define  MULTIBOOT2_HEADER_MAGIC  0xe85250d6

%define  MULTIBOOT2_BOOTLOADER_MAGIC  0x36d76289

%define  MULTIBOOT_TAG_ALIGN  8

%define  MULTIBOOT_HEADER_TAG_END                  0
%define  MULTIBOOT_HEADER_TAG_EFI_BS               7
%define  MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64  9

%define  MULTIBOOT_ARCHITECTURE_I386  0

%endif ;__SPOT_MULTIBOOT2__

In _start, mov word [0xb8000], 0x0247 appears to never be reached.

All help is appreciated!

I use CMake, but put together a quick script for readers:

nasm -f elf64 boot.asm -o boot.o
nasm -f elf64 header.asm -o header.o

x86_64-elf-ld -n -o iso/boot/kernel.bin -T iso/linker.ld boot.o header.o

grub-mkrescue /usr/lib/grub/i386-pc -o dist/kernel.iso iso

The above assumes that you have you have a grub.cfg in iso/boot/grub. Mine looks like:

set timeout=0
set default=0

menuentry "spot" {
  multiboot2 /boot/kernel.bin
  boot
}

And linker.ld:

ENTRY(_start)

SECTIONS
{
  . = 1M;

  .boot :
  {
    KEEP(*(.multiboot))
  }

  .text :
  {
    *(.text)
  }
}

Then run in QEmu: qemu-system-x86_64 -cdrom dist/kernel.iso.

Here's my build environment's Dockerfile:

FROM randomdude/gcc-cross-x86_64-elf

RUN apt-get update
RUN apt-get upgrade -y

RUN apt-get install -y gdb

RUN apt-get install -y xorriso

RUN apt-get install -y grub-pc-bin
RUN apt-get install -y grub-common

RUN apt-get install -y nasm

ARG       cmake_version=3.21
ARG       cmake_revision=4
WORKDIR   /root
RUN       wget https://github.com/Kitware/CMake/releases/download/v${cmake_version}.${cmake_revision}/cmake-${cmake_version}.${cmake_revision}-linux-x86_64.tar.gz
RUN       tar -xzf cmake-${cmake_version}.${cmake_revision}-linux-x86_64.tar.gz
WORKDIR   /root/cmake-${cmake_version}.${cmake_revision}-linux-x86_64
RUN       cp -rf ./bin ./share /usr/local

VOLUME /root/env
WORKDIR /root/env

UPDATE #1:

I noticed that checking for Multiboot2 fails:

check_multiboot:
  ; EAX should have a magic value.
  cmp  eax, MULTIBOOT2_BOOTLOADER_MAGIC
  jne  .no_multiboot

  ret
.no_multiboot:
  mov  word [0xb8000], 0x044D ; 'M'.
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Oliver
  • 1,465
  • 4
  • 17
  • 1
    Your `_start` falls through into `setup_ptables`. That can't be good. Fixing that seems to get me a green `G` in the top left corner. (Full disclosure: I also got rid of the `MULTIBOOT_HEADER_TAG_EFI_BS` and `MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64` because apparently my grub is too old.) – Jester Feb 21 '22 at 21:14
  • @Jester Thank you. I am glad it was a silly NASM mistake. If you post your comment as an answer, I will mark it. – Oliver Feb 24 '22 at 16:54

0 Answers0