LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-02-2017, 03:04 AM   #1
TheStr3ak5
Member
 
Registered: Feb 2016
Location: Zaragoza, Spain
Distribution: Xubuntu, Tails, etc.
Posts: 55

Rep: Reputation: 9
Problems with a pseudo 64 bits "Hello world" kernel


Hi, this is my problem:
I'm doing a 64 bits in long mode kernel just to print "hi" (so much things for just a hi but well), and when I run qemu I dont get any output, but no error too, what can be going wrong? I lave you the code here:
The proyect have 4 files, one multiboot header in asm, one asm boot file, another asm file to switch to long mode and the kernel in c.
File boot.asm (the boot asm file)
Code:
global start
global gdt64_code_offset

extern long_mode_start

section .text
bits 32
start:
    mov esp, kernel_stack_top           ; point esp to the start of the stack (end of memory, stack grows downwards)

    call test_multiboot
    call test_cpuid
    call test_long_mode

    call setup_page_tables
    call enable_paging

    ; load the 64bit GDT
    lgdt [gdt64.pointer]

    ; update selectors
    mov ax, gdt64.data
    mov ss, ax  ; stack selector
    mov ds, ax  ; data selector
    mov es, ax  ; extra selector

    ;; No way of setting the cs (code selector) manually.
    ;; A far jump to 64 bit code is needed
    jmp gdt64.code:long_mode_start

test_multiboot:
    cmp eax, 0x36d76289
    jne .no_multiboot
    ret
.no_multiboot:
    mov al, "0"
    jmp error

test_cpuid:
    pushfd               ; Store the FLAGS-register.
    pop eax              ; Restore the A-register.
    mov ecx, eax         ; Set the C-register to the A-register.
    xor eax, 1 << 21     ; Flip the ID-bit, which is bit 21.
    push eax             ; Store the A-register.
    popfd                ; Restore the FLAGS-register.
    pushfd               ; Store the FLAGS-register.
    pop eax              ; Restore the A-register.
    push ecx             ; Store the C-register.
    popfd                ; Restore the FLAGS-register.
    xor eax, ecx         ; Do a XOR-operation on the A-register and the C-register.
    jz .no_cpuid         ; The zero flag is set, no CPUID.
    ret                  ; CPUID is available for use.
.no_cpuid:
    mov al, "1"
    jmp error

test_long_mode:
    mov eax, 0x80000000    ; Set the A-register to 0x80000000.
    cpuid                  ; CPU identification.
    cmp eax, 0x80000001    ; Compare the A-register with 0x80000001.
    jb .no_long_mode       ; It is less, there is no long mode.
    mov eax, 0x80000001    ; Set the A-register to 0x80000001.
    cpuid                  ; CPU identification.
    test edx, 1 << 29      ; Test if the LM-bit, which is bit 29, is set in the D-register.
    jz .no_long_mode       ; They aren't, there is no long mode.
    ret
.no_long_mode:
    mov al, "2"
    jmp error

; Setup Paging. 
; In Long Mode, x86 use a 4 level page table with page size of 4096
; Each page table contains 512 entries, each entry is 8 bytes (512*8=4096)
; Each 64bit virtual address is used to index the the tables:
;   1) First 9 bits entry (2^9 = 512 entries) in PML4 table (base address in CR3 register)
;   2) Second 9 bits offset in PDP table
;   3) Third 9 bits offset in PD table
;   4) Fourth 9 bits offset in PT (Page Table) table
;   5) Last 12 bits to offset 4K physical memory page (2^12 = 4096)
;   NM) Bits 48-63 rest unused, actual copies of bit 47.
; Each entry in the tables contains the page aligned 52bit physical address of
; the next table, ORed in with some bit flags (present 0, writable 1, etc.).
; Finally, to identity map the first gigabytes of memory, use the following mapping:
; 1 PML4 -> 1 PDP -> 512 2MiB PD tables
setup_page_tables:
    ; map first P4 entry to P3
    mov eax, p3_table
    or eax, 0b11            ; Set present and writable flags
    mov [p4_table], eax     ; p4_table is a address. [addr] deferences the memory at the addr

    ; map first P3 entry to P2
    mov eax, p2_table
    or eax, 0b11            ; Set present and writable flags
    mov [p3_table], eax

    ; map each P2 entry to a 2MiB page
    mov ecx, 0              ; counter variable
.map_p2_table:
    ; map ecx-th P2 entry to a huge page that start at address ecx*2MiB
    mov eax, 0x200000               ; 2 MiB
    mul ecx                         ; Multiply what's in eax with ecx and store in eax (start address of ecx-th page)
    or eax, 0b10000011              ; Set present, writable and huge page (in P2 means 2 MiB pages) flags
    mov [p2_table + ecx * 8], eax   ; Set each ecx-th entry of P2 to the ecx-th page

    inc ecx                         ; Increment counter
    cmp ecx, 512                    ; if ecx == 512, we're done
    jne .map_p2_table               ; else loop again

    ret

enable_paging:
    ; load P4 adress into cr3 register
    mov eax, p4_table
    mov cr3, eax

    ; enable PAE flag in cr4 (Physical Adrees Extension)
    mov eax, cr4
    or eax, 1 << 5
    mov cr4, eax

    ; set the long mode bit in the EFER MSR (model specific register)
    mov ecx, 0xC0000080
    rdmsr
    or eax, 1 << 8
    wrmsr

    ; enable paging in the cr0 register
    mov eax, cr0
    or eax, 1 << 31
    mov cr0, eax

    ret

; Prints `ERR: ` and the given error code to screen and hangs.
; parameter: error code (in ascii) in al
error:
    mov dword [0xb8000], 0x4f524f45
    mov dword [0xb8004], 0x4f3a4f52
    mov dword [0xb8008], 0x4f204f20
    mov byte  [0xb800a], al
    hlt

section .bss:
align 4096
p4_table:                   ; Page-Map Level-4 Table (PML4) or P4
    resb 4096
p3_table:                   ; Page-Directory Pointer Table (PDP) or P3
    resb 4096
p2_table:                   ; Page-Directory Table (PD) or P2
    resb 4096

;;; Reserve space for the kernel stack.
kernel_stack_bottom:
    resb 8192               ; Reserve 8192 bytes for the kernel stack
kernel_stack_top:

section .rodata
;;; Global Description Table. Used to describe available segments.
gdt64:
    dq 0                                                    ; zero entry
.code: equ $ - gdt64
    dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53)      ; code segment
.data: equ $ - gdt64
    dq (1<<44) | (1<<47) | (1<<41)                          ; data segment
.pointer:
    dw $ - gdt64 - 1
    dq gdt64

;;; Export code selector so C can read it.
gdt64_code_offset:
    dw gdt64.code
The file long_mode.asm, to switch to long mode and called by boot.asm, and calls k_main in the c file
Code:
global long_mode_start

section .text
bits 64
long_mode_start:
    call setup_SSE

    ; call the c main
    extern k_main
    call k_main
    
.os_returned:
    ; c main returned, print `OS returned!`
    mov rax, 0x4f724f204f534f4f
    mov [0xb8000], rax
    mov rax, 0x4f724f754f744f65
    mov [0xb8008], rax
    mov rax, 0x4f214f644f654f6e
    mov [0xb8010], rax

    hlt

; Check for SSE and enable it. If it's not supported throw error "a".
setup_SSE:
    ; check for SSE
    mov rax, 0x1
    cpuid
    test edx, 1<<25
    jz .no_SSE

    ; enable SSE
    mov rax, cr0
    and ax, 0xFFFB      ; clear coprocessor emulation CR0.EM
    or ax, 0x2          ; set coprocessor monitoring  CR0.MP
    mov cr0, rax
    mov rax, cr4
    or ax, 3 << 9       ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
    mov cr4, rax

    ret
.no_SSE:
    mov al, "a"
    jmp error

; Prints `ERROR: ` and the given error code to screen and hangs.
; parameter: error code (in ascii) in al
; We need this new function, as the other one is in now invalid 32 bit code
error:
    mov rbx, 0x4f4f4f524f524f45
    mov [0xb8000], rbx
    mov rbx, 0x4f204f204f3a4f52
    mov [0xb8008], rbx
    mov byte [0xb800e], al
    hlt
    jmp error
And the main c file, called kernel.c, that print "hi"
Code:
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

static const uint8_t COLOR_BLACK = 0;
static const uint8_t COLOR_BLUE = 1;
static const uint8_t COLOR_GREEN = 2;
static const uint8_t COLOR_CYAN = 3;
static const uint8_t COLOR_RED = 4;
static const uint8_t COLOR_MAGENTA = 5;
static const uint8_t COLOR_BROWN = 6;
static const uint8_t COLOR_LIGHT_GREY = 7;
static const uint8_t COLOR_DARK_GREY = 8;
static const uint8_t COLOR_LIGHT_BLUE = 9;
static const uint8_t COLOR_LIGHT_GREEN = 10;
static const uint8_t COLOR_LIGHT_CYAN = 11;
static const uint8_t COLOR_LIGHT_RED = 12;
static const uint8_t COLOR_LIGHT_MAGENTA = 13;
static const uint8_t COLOR_LIGHT_BROWN = 14;
static const uint8_t COLOR_WHITE = 15;

uint8_t make_color(uint8_t fg, uint8_t bg)
{
	return fg | bg << 4;
}

uint16_t make_vgaentry(char c, uint8_t color)
{
	uint16_t c16 = c;
	uint16_t color16 = color;
	return c16 | color16 << 8;
}

size_t strlen(const char* str)
{
	size_t ret = 0;
	while ( str[ret] != 0 )
		ret++;
	return ret;
}

static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 24;

size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;

void terminal_initialize()
{
	terminal_row = 0;
	terminal_column = 0;
	terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
	terminal_buffer = (uint16_t*) 0xB8000;
	for ( size_t y = 0; y < VGA_HEIGHT; y++ )
		for ( size_t x = 0; x < VGA_WIDTH; x++ )
		{
			const size_t index = y * VGA_WIDTH + x;
			terminal_buffer[index] = make_vgaentry(' ', terminal_color);
		}
}

void terminal_setcolor(uint8_t color)
{
	terminal_color = color;
}

void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
	const size_t index = y * VGA_WIDTH + x;
	terminal_buffer[index] = make_vgaentry(c, color);
}

void terminal_putchar(char c)
{
	terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
	if ( ++terminal_column == VGA_WIDTH )
	{
		terminal_column = 0;
		if ( ++terminal_row == VGA_HEIGHT )
		{
			terminal_row = 0;
		}
	}
}

void terminal_writestring(const char* data)
{
	size_t datalen = strlen(data);
	for ( size_t i = 0; i < datalen; i++ )
		terminal_putchar(data[i]);
}
void k_main(void)
{
	/*code*/
	terminal_initialize();
	terminal_writestring("Hello\n");

}
All this is compiled like this:
the c script:
Code:
gcc -ffreestanding -mcmodel=large -mno-red-zone -mno-mmx -mnow-sse -mno-sse2 -c kernel.c -o kernel.o
and all the asm like this:
Code:
nasm -f elf64 -o boot.o boot.asm (with every asm file)
And then linked to get a .bin file with this command:
Code:
ld -n --gc-sections -T linker.ld -o cka-0-0-1.bin kernel.o boot.o long_mode.o header.o
And this linker script:
Code:
ENTRY(start)

SECTIONS {
    . = 1M;

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

    .text :
    {
        *(.text)
    }
}
Any ideas? I think maybe it is something wrong with the boot.asm file and the c file, I dont think the multiboot header or the long mode have nothing to do here, or maybe is the build commands that makes error, anyway, thanks in advance!
Note: the binary file is tested in qemu with:
Code:
qemu-system-x86_64 -kernel cka-0-0-1.bin
 
Old 08-02-2017, 08:56 AM   #2
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 3,235

Rep: Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979
Do you have a version that works in 32-bit mode? Or a version that runs in 16-bit protected mode? Or a version that runs in 16-bit real mode?
 
Old 08-02-2017, 09:23 AM   #3
TheStr3ak5
Member
 
Registered: Feb 2016
Location: Zaragoza, Spain
Distribution: Xubuntu, Tails, etc.
Posts: 55

Original Poster
Rep: Reputation: 9
Quote:
Originally Posted by NevemTeve View Post
Do you have a version that works in 32-bit mode? Or a version that runs in 16-bit protected mode? Or a version that runs in 16-bit real mode?
The Code of above is planed to start running in 32 bits protected Mode, following the grub multiboot especification
 
Old 08-02-2017, 09:25 AM   #4
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 3,235

Rep: Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979
Oh, I see! Do you have a version that works in 32-bit mode? Or a version that runs in 16-bit protected mode? Or a version that runs in 16-bit real mode?
 
Old 08-02-2017, 09:35 AM   #5
TheStr3ak5
Member
 
Registered: Feb 2016
Location: Zaragoza, Spain
Distribution: Xubuntu, Tails, etc.
Posts: 55

Original Poster
Rep: Reputation: 9
It works in 32 bits, sorry I didnt got it at the begining since english isnt my first language
 
Old 08-02-2017, 09:43 AM   #6
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 3,235

Rep: Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979Reputation: 979
So the problematic part is the mode-switching from 32-bit mode to 64-bit mode?
 
Old 08-02-2017, 09:49 AM   #7
TheStr3ak5
Member
 
Registered: Feb 2016
Location: Zaragoza, Spain
Distribution: Xubuntu, Tails, etc.
Posts: 55

Original Poster
Rep: Reputation: 9
Yes, the problem is that the kernel functions doesnt boot up, isnt an error of the main c code so the assembly part is the one with errors
 
  


Reply

Tags
assembly, gcc, kernel, ld


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Trying to Compile "hello world" Kernel Module (please help!) antdengineer Programming 9 12-04-2010 04:07 AM
noob having problems to compile first Qt "hello world" application joakim12 Programming 4 06-03-2010 08:39 AM
"Hello world message is not printing" while loading the modules inside kernel Nishant Desai Linux - Kernel 9 08-31-2009 01:38 PM
Common problems explained: "kernel panic - not syncing", "unable to mount..." sundialsvcs Linux - Newbie 2 03-01-2006 12:17 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:57 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration