diff --git a/Makefile b/Makefile
index 0482834accd3b966deb274ec770bffc2c516b9ad..0ca1e7bf777d275ad240668770d019fa746d0dbc 100644
--- a/Makefile
+++ b/Makefile
@@ -5,22 +5,25 @@
 # @version 0.1
 
 ARCH          ?= x86_64
+#ARCH          ?= x86
 PROFILE       ?= debug
 #PROFILE       ?= release
 
+# binary config
 NASM          ?= nasm
 LD            ?= ld.lld
 OBJCOPY       ?= llvm-objcopy
 CARGO         ?= cargo
 #CARGO         ?= cargo-clif
 
+# common directories
 TARGET_DIR    ?= target
 HYPER_DIR     := ${TARGET_DIR}/hyperion/${ARCH}
 ARCH_DIR      := src/arch/${ARCH}
+CARGO_DIR      = ${TARGET_DIR}/${RUST_T_${ARCH}}/${PROFILE}
 
 # hyperion kernel lib
 RUST_T_x86_64 := x86_64-unknown-none
-CARGO_DIR     := ${TARGET_DIR}/${RUST_T_${ARCH}}/${PROFILE}
 RUST_F_debug  :=
 RUST_F_release:= --release
 CARGO_FLAGS   ?=
diff --git a/src/.dir-locals.el b/src/.dir-locals.el
new file mode 100644
index 0000000000000000000000000000000000000000..df82ef173787a5970a17c5df92c808e67a900a0e
--- /dev/null
+++ b/src/.dir-locals.el
@@ -0,0 +1,3 @@
+((nil . ((lsp-rust-analyzer-cargo-target . "x86_64-unknown-none")
+         (lsp-rust-all-targets . nil)
+         (lsp-rust-analyzer-cargo-all-targets . nil))))
diff --git a/src/arch/x86_64/link.ld b/src/arch/x86_64/link.ld
index eb5ae92337ff526991988521a56ef5b15407d977..ae313ee218f5225a7688de46aa7efbee9c872eb4 100644
--- a/src/arch/x86_64/link.ld
+++ b/src/arch/x86_64/link.ld
@@ -3,12 +3,25 @@ ENTRY(start)
 SECTIONS {
     . = 1M;
 
-    .boot_text : ALIGN(4k) {
-          KEEP(*(.multiboot1))
-          *(.boot)
+    .boot : ALIGN(4k) {
+        KEEP(*(.multiboot1))
+        *(.boot)
     }
 
     .text : ALIGN(4k) {
-          *(.text)
+        *(.text)
+    }
+
+    .rodata : ALIGN(4k) {
+        *(.rodata)
+    }
+
+    .data : ALIGN(4k) {
+        *(.data)
+    }
+
+    .bss : ALIGN(4k) {
+        *(COMMON)
+        *(.bss)
     }
 }
diff --git a/src/arch/x86_64/start.asm b/src/arch/x86_64/start.asm
index c5ef80103f930633ba73799a8e83e938bcd5c0ef..e4b6d60ea745e9a64cbd1d6c744ff919e49075ae 100644
--- a/src/arch/x86_64/start.asm
+++ b/src/arch/x86_64/start.asm
@@ -1,3 +1,6 @@
+    global start
+    extern kernel_main
+
     ;; ----------
     ;; Multiboot2
     ;; ----------
@@ -27,7 +30,205 @@ header_end:
     global start
     bits 32
 
+    ;; ----------
+    ;; Boot entry
+    ;; ----------
+
 start:
     cli
     cld
+
+    ;; init stack
+    mov esp, stack_top
+
+    ;; support checks
+    call check_multiboot1
+    call check_cpuid
+    call check_long_mode
+
+    ;; setup
+    call setup_page_tables
+    call enable_paging
+
+    ;; enter long mode
+    lgdt [gdt64.pointer]
+    jmp gdt64.code_segment: long_mode_start
+    jmp halt
+
+error:
+    ;; print 'ERR: <err>'
+    mov dword [0xb8000], 0x4f524f45
+    mov dword [0xb8004], 0x4f3a4f52
+    mov dword [0xb8008], 0x4f204f20
+    mov byte  [0xb800a], al
+    jmp halt
+
+halt:
+    ;; print ZZZ
+	mov word [0xb8f00], 0x0f5a
+	mov word [0xb8f02], 0x0f5a
+	mov word [0xb8f04], 0x0f5a
     hlt
+    jmp halt
+
+    ;; ------
+    ;; Checks
+    ;; ------
+
+    section .boot
+    bits 32
+
+check_multiboot2:
+	cmp eax, 0x36D76289
+	jne .no_multiboot2
+	ret
+
+.no_multiboot2:
+	mov al, 'M'
+	jmp error
+
+check_multiboot1:
+	cmp eax, 0x2BADB002
+	jne .no_multiboot1
+	ret
+
+.no_multiboot1:
+	mov al, 'M'
+	jmp error
+
+check_cpuid:
+	pushfd
+	pop eax
+	mov ecx, eax
+	xor eax, 1 << 21
+	push eax
+	popfd
+
+	pushfd
+	pop eax
+	push ecx
+	popfd
+
+	cmp eax, ecx
+	je .no_cpuid
+	ret
+
+.no_cpuid:
+	mov al, 'C'
+	jmp error
+
+check_long_mode:
+	mov eax, 0x80000000
+	cpuid
+	cmp eax, 0x80000001
+	jb .no_long_mode
+
+	mov eax, 0x80000001
+	cpuid
+	test edx, 1 << 29
+	jz .no_long_mode
+
+	ret
+
+.no_long_mode:
+	mov al, 'L'
+	jmp error
+
+    ;; ----------
+    ;; Page setup
+    ;; ----------
+
+setup_page_tables:
+	mov eax, page_table_l3
+	or  eax, 0b11 ; present, writeable
+	mov [page_table_l4], eax
+
+	mov eax, page_table_l2
+	or  eax, 0b11 ; present, writeable
+	mov [page_table_l3], eax
+
+	mov ecx, 0 ; counter
+
+.loop:
+	mov eax, 0x200000 ; 2MiB
+	mul ecx,
+	or  eax, 0b10000011 ; present, writeable, huge page
+	mov [page_table_l2 + ecx * 8], eax
+
+	inc ecx ; inc counter
+	cmp ecx, 512 ; check if the whole table is mapped
+	jne .loop ; if not: continue
+
+	ret
+
+enable_paging:
+	; pass page table location to the cpu
+	mov eax, page_table_l4
+	mov cr3, eax
+
+	; enable PAE
+	mov eax, cr4
+	or  eax, 1 << 5
+	mov cr4, eax
+
+	; enable long mode
+	mov ecx, 0xC0000080
+	rdmsr
+	or  eax, 1 << 8
+	wrmsr
+
+	; enable paging
+	mov eax, cr0
+	or  eax, 1 << 31
+	mov cr0, eax
+
+	ret
+
+    ;; ---------
+    ;; Long mode
+    ;; ---------
+
+    section .text
+    bits 64
+long_mode_start:
+    mov ax, 0
+	mov ss, ax
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+
+	; print 'OK'
+	mov dword [0xb8000], 0x2f4b2f4f
+
+	call kernel_main
+.halt:
+	hlt
+    jmp halt
+
+    ;; ------
+    ;; Memory
+    ;; ------
+
+    section .bss
+
+page_table_l4:
+    resb 4096
+page_table_l3:
+    resb 4096
+page_table_l2:
+    resb 4096
+
+stack_bottom:
+    ;; 16 KiB
+    resb 4096 * 4
+stack_top:
+
+    section .rodata
+gdt64:
+    dq 0                        ; zero entry
+.code_segment: equ $ - gdt64
+    dq (1 << 43) | (1 << 44) | (1 << 47) | (1 << 53)
+.pointer:
+    dw $ - gdt64 - 1
+    dq gdt64
diff --git a/src/lib.rs b/src/lib.rs
index 1c26aad36a307c9123bab5c7443f91041fd278d2..eda8e74d96fbc10c33fec94130e77a0d1e3a8aa6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,4 +8,14 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! {
 
 #[no_mangle]
 #[link_section = ".boot"]
-pub extern "C" fn _start() {}
+pub extern "C" fn kernel_main() -> ! {
+    unsafe {
+        *(0xB8000 as *mut u32) = 0x4f524f45;
+    }
+
+    loop {
+        unsafe {
+            core::arch::asm!("hlt");
+        }
+    }
+}