HelenOS sources

root/kernel/arch/amd64/src/boot/multiboot.S

/* [<][>][^][v][top][bottom][index][help] */
/*
 * Copyright (c) 2005 Ondrej Palkovsky
 * Copyright (c) 2006 Martin Decky
 * Copyright (c) 2008 Jakub Jermar
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <abi/asmtool.h>
#include <arch/boot/boot.h>
#include <arch/mm/page.h>
#include <arch/mm/ptl.h>
#include <arch/pm.h>
#include <genarch/multiboot/multiboot.h>
#include <arch/cpuid.h>
#include <arch/cpu.h>

#define START_STACK  (BOOT_OFFSET - BOOT_STACK_SIZE)

.section K_TEXT_START, "ax"

.code32

.macro pm_error msg
        movl \msg, %esi
        jmp pm_error_halt
.endm

.macro pm_status msg
#ifdef CONFIG_EGA
        pushl %esi
        movl \msg, %esi
        call pm_early_puts
        popl %esi
#endif
.endm

.macro pm2_status msg
#ifndef CONFIG_FB
        pm_status \msg
#endif
.endm

.align 4
multiboot_header:
        .long MULTIBOOT_HEADER_MAGIC
        .long MULTIBOOT_HEADER_FLAGS
        .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)  /* checksum */
        .long multiboot_header
        .long unmapped_start
        .long 0
        .long 0
        .long multiboot_image_start

SYMBOL(multiboot_image_start)
        cli
        cld

        /* Initialize stack pointer */
        movl $START_STACK, %esp

        /*
         * Initialize Global Descriptor Table and
         * Interrupt Descriptor Table registers
         */
        lgdtl bootstrap_gdtr
        lidtl bootstrap_idtr

        /* Kernel data + stack */
        movw $GDT_SELECTOR(KDATA_DES), %cx
        movw %cx, %es
        movw %cx, %ds
        movw %cx, %ss

        /*
         * Simics seems to remove hidden part of GS on entering user mode
         * when _visible_ part of GS does not point to user-mode segment.
         */
        movw $GDT_SELECTOR(UDATA_DES), %cx
        movw %cx, %fs
        movw %cx, %gs

        jmpl $GDT_SELECTOR(KTEXT32_DES), $multiboot_meeting_point
        multiboot_meeting_point:

        /*
         * Protected 32-bit. We want to reuse the code-seg descriptor,
         * the Default operand size must not be 1 when entering long mode.
         */

        /* Save multiboot arguments */
        movl %eax, multiboot_eax
        movl %ebx, multiboot_ebx

        pm_status $status_prot

        movl $(INTEL_CPUID_EXTENDED), %eax
        cpuid
        cmp $(INTEL_CPUID_EXTENDED), %eax
        ja extended_cpuid_supported

                pm_error $err_extended_cpuid

        extended_cpuid_supported:

        movl $(AMD_CPUID_EXTENDED), %eax
        cpuid
        bt $(AMD_EXT_LONG_MODE), %edx
        jc long_mode_supported

                pm_error $err_long_mode

        long_mode_supported:

        bt $(AMD_EXT_NOEXECUTE), %edx
        jc noexecute_supported

                pm_error $err_noexecute

        noexecute_supported:

        movl $(INTEL_CPUID_STANDARD), %eax
        cpuid
        bt $(INTEL_FXSAVE), %edx
        jc fx_supported

                pm_error $err_fx

        fx_supported:

        bt $(INTEL_SSE2), %edx
        jc sse2_supported

                pm_error $err_sse2

        sse2_supported:

#include "vesa_prot.inc"

        pm2_status $status_prot2

        /*
         * Enable 64-bit page translation entries - CR4.PAE = 1.
         * Paging is not enabled until after long mode is enabled.
         */

        movl %cr4, %eax
        orl $CR4_PAE, %eax
        movl %eax, %cr4

        /* Set up paging tables */
        leal ptl_0, %eax
        movl %eax, %cr3

        /* Enable long mode */
        movl $AMD_MSR_EFER, %ecx
        rdmsr                     /* read EFER */
        orl $AMD_LME, %eax        /* set LME = 1 */
        wrmsr

        /* Enable paging to activate long mode (set CR0.PG = 1) */
        movl %cr0, %eax
        orl $CR0_PG, %eax
        movl %eax, %cr0

        /* At this point we are in compatibility mode */
        jmpl $GDT_SELECTOR(KTEXT_DES), $start64

/** Print string to EGA display (in light red) and halt.
 *
 * Should be executed from 32 bit protected mode with paging
 * turned off. Stack is not required. This routine is used even
 * if CONFIG_EGA is not enabled. Since we are going to halt the
 * CPU anyway, it is always better to at least try to print
 * some hints.
 *
 * @param %esi Pointer to the NULL-terminated string
 *             to be print.
 *
 */
pm_error_halt:
        movl $0xb8000, %edi  /* base of EGA text mode memory */
        xorl %eax, %eax

        /* Read bits 8 - 15 of the cursor address */
        movw $0x3d4, %dx
        movb $0xe, %al
        outb %al, %dx

        movw $0x3d5, %dx
        inb %dx, %al
        shl $8, %ax

        /* Read bits 0 - 7 of the cursor address */
        movw $0x3d4, %dx
        movb $0xf, %al
        outb %al, %dx

        movw $0x3d5, %dx
        inb %dx, %al

        /* Sanity check for the cursor on screen */
        cmp $2000, %ax
        jb err_cursor_ok

                movw $1998, %ax

        err_cursor_ok:

        movw %ax, %bx
        shl $1, %eax
        addl %eax, %edi

        err_ploop:
                lodsb

                cmp $0, %al
                je err_ploop_end

                movb $0x0c, %ah  /* black background, light red foreground */
                stosw

                /* Sanity check for the cursor on the last line */
                inc %bx
                cmp $2000, %bx
                jb err_ploop

                /* Scroll the screen (24 rows) */
                movl %esi, %edx
                movl $0xb80a0, %esi
                movl $0xb8000, %edi
                movl $960, %ecx
                rep movsl

                /* Clear the 24th row */
                xorl %eax, %eax
                movl $40, %ecx
                rep stosl

                /* Go to row 24 */
                movl %edx, %esi
                movl $0xb8f00, %edi
                movw $1920, %bx

                jmp err_ploop
        err_ploop_end:

        /* Write bits 8 - 15 of the cursor address */
        movw $0x3d4, %dx
        movb $0xe, %al
        outb %al, %dx

        movw $0x3d5, %dx
        movb %bh, %al
        outb %al, %dx

        /* Write bits 0 - 7 of the cursor address */
        movw $0x3d4, %dx
        movb $0xf, %al
        outb %al, %dx

        movw $0x3d5, %dx
        movb %bl, %al
        outb %al, %dx

        cli
        hlt1:
                hlt
                jmp hlt1

/** Print string to EGA display (in light green).
 *
 * Should be called from 32 bit protected mode with paging
 * turned off. A stack space of at least 24 bytes is required,
 * but the function does not establish a stack frame.
 *
 * Macros such as pm_status and pm2_status take care that
 * this function is used only when CONFIG_EGA is enabled
 * and CONFIG_FB is disabled.
 *
 * @param %esi Pointer to the NULL-terminated string
 *             to be print.
 *
 */
pm_early_puts:
        pushl %eax
        pushl %ebx
        pushl %ecx
        pushl %edx
        pushl %edi

        movl $0xb8000, %edi  /* base of EGA text mode memory */
        xorl %eax, %eax

        /* Read bits 8 - 15 of the cursor address */
        movw $0x3d4, %dx
        movb $0xe, %al
        outb %al, %dx

        movw $0x3d5, %dx
        inb %dx, %al
        shl $8, %ax

        /* Read bits 0 - 7 of the cursor address */
        movw $0x3d4, %dx
        movb $0xf, %al
        outb %al, %dx

        movw $0x3d5, %dx
        inb %dx, %al

        /* Sanity check for the cursor on screen */
        cmp $2000, %ax
        jb pm_puts_cursor_ok

                movw $1998, %ax

        pm_puts_cursor_ok:

        movw %ax, %bx
        shl $1, %eax
        addl %eax, %edi

        pm_puts_ploop:
                lodsb

                cmp $0, %al
                je pm_puts_ploop_end

                movb $0x0a, %ah  /* black background, light green foreground */
                stosw

                /* Sanity check for the cursor on the last line */
                inc %bx
                cmp $2000, %bx
                jb pm_puts_ploop

                /* Scroll the screen (24 rows) */
                movl %esi, %edx
                movl $0xb80a0, %esi
                movl $0xb8000, %edi
                movl $960, %ecx
                rep movsl

                /* Clear the 24th row */
                xorl %eax, %eax
                movl $40, %ecx
                rep stosl

                /* Go to row 24 */
                movl %edx, %esi
                movl $0xb8f00, %edi
                movw $1920, %bx

                jmp pm_puts_ploop
        pm_puts_ploop_end:

        /* Write bits 8 - 15 of the cursor address */
        movw $0x3d4, %dx
        movb $0xe, %al
        outb %al, %dx

        movw $0x3d5, %dx
        movb %bh, %al
        outb %al, %dx

        /* Write bits 0 - 7 of the cursor address */
        movw $0x3d4, %dx
        movb $0xf, %al
        outb %al, %dx

        movw $0x3d5, %dx
        movb %bl, %al
        outb %al, %dx

        popl %edi
        popl %edx
        popl %ecx
        popl %ebx
        popl %eax

        ret

.code64

.macro long_status msg
        pushq %rdi
        movq \msg, %rdi
        call early_puts
        popq %rdi
.endm

start64:

        /*
         * Long mode.
         */

        movq $(PA2KA(START_STACK)), %rsp

        /* Create the first stack frame */
        pushq $0
        movq %rsp, %rbp

        long_status $status_long

        /* Call amd64_pre_main(multiboot_eax, multiboot_ebx) */
        movl multiboot_eax, %edi
        movl multiboot_ebx, %esi

#ifdef MEMORY_MODEL_large
        movabsq $amd64_pre_main, %rax
        callq *%rax
#else
        callq amd64_pre_main
#endif

        long_status $status_main

        /* Call main_bsp() */
#ifdef MEMORY_MODEL_large
        movabsq $main_bsp, %rax
        callq *%rax
#else
        callq main_bsp
#endif

        /* Not reached */
        cli
        hlt0:
                hlt
                jmp hlt0

/** Print string to EGA display.
 *
 * Should be called from long mode (with paging enabled
 * and stack established). This function is ABI compliant
 * (without red-zone).
 *
 * If CONFIG_EGA is undefined or CONFIG_FB is defined
 * then this function does nothing.
 *
 * @param %rdi Pointer to the NULL-terminated string
 *             to be printed.
 *
 */
early_puts:

#if ((defined(CONFIG_EGA)) && (!defined(CONFIG_FB)))

        /* Prologue, save preserved registers */
        pushq %rbp
        movq %rsp, %rbp
        pushq %rbx

        movq %rdi, %rsi
        movq $(PA2KA(0xb8000)), %rdi  /* base of EGA text mode memory */
        xorq %rax, %rax

        /* Read bits 8 - 15 of the cursor address */
        movw $0x3d4, %dx
        movb $0xe, %al
        outb %al, %dx

        movw $0x3d5, %dx
        inb %dx, %al
        shl $8, %ax

        /* Read bits 0 - 7 of the cursor address */
        movw $0x3d4, %dx
        movb $0xf, %al
        outb %al, %dx

        movw $0x3d5, %dx
        inb %dx, %al

        /* Sanity check for the cursor on screen */
        cmp $2000, %ax
        jb early_puts_cursor_ok

                movw $1998, %ax

        early_puts_cursor_ok:

        movw %ax, %bx
        shl $1, %rax
        addq %rax, %rdi

        early_puts_ploop:
                lodsb

                cmp $0, %al
                je early_puts_ploop_end

                movb $0x0e, %ah  /* black background, yellow foreground */
                stosw

                /* Sanity check for the cursor on the last line */
                inc %bx
                cmp $2000, %bx
                jb early_puts_ploop

                /* Scroll the screen (24 rows) */
                movq %rsi, %rdx
                movq $(PA2KA(0xb80a0)), %rsi
                movq $(PA2KA(0xb8000)), %rdi
                movl $480, %ecx
                rep movsq

                /* Clear the 24th row */
                xorl %eax, %eax
                movl $20, %ecx
                rep stosq

                /* Go to row 24 */
                movq %rdx, %rsi
                movq $(PA2KA(0xb8f00)), %rdi
                movw $1920, %bx

                jmp early_puts_ploop
        early_puts_ploop_end:

        /* Write bits 8 - 15 of the cursor address */
        movw $0x3d4, %dx
        movb $0xe, %al
        outb %al, %dx

        movw $0x3d5, %dx
        movb %bh, %al
        outb %al, %dx

        /* Write bits 0 - 7 of the cursor address */
        movw $0x3d4, %dx
        movb $0xf, %al
        outb %al, %dx

        movw $0x3d5, %dx
        movb %bl, %al
        outb %al, %dx

        /* Epilogue, restore preserved registers */
        popq %rbx
        leave

#endif

        ret

#include "vesa_real.inc"

.section K_INI_PTLS, "aw", @progbits

/** Generate initial page table contents.
 *
 * @param cnt Number of entries to generate. Must be multiple of 8.
 * @param g   Number of GB that will be added to the mapping.
 *
 */
.macro ptl2gen cnt g
        .if \cnt
                ptl2gen "\cnt - 8" \g
                .quad ((\cnt - 8) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 7) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 6) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 5) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 4) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 3) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 2) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
                .quad ((\cnt - 1) * 0x200000) + (\g * 1024 * 1024 * 1024) | (PTL_WRITABLE | PTL_PRESENT | PTL_2MB_PAGE)
        .endif
.endm

/* Page table for pages in the 1st gigabyte. */
.align 4096
ptl_2_0g:
        ptl2gen 512 0

/* Page table for pages in the 2nd gigabyte. */
.align 4096
ptl_2_1g:
        ptl2gen 512 1

/* Page table for pages in the 3rd gigabyte. */
.align 4096
ptl_2_2g:
        ptl2gen 512 2

/* Page table for pages in the 4th gigabyte. */
.align 4096
ptl_2_3g:
        ptl2gen 512 3

/* Page table for pages in the 5th gigabyte. */
.align 4096
ptl_2_4g:
        ptl2gen 512 4

/* Page table for pages in the 6th gigabyte. */
.align 4096
ptl_2_5g:
        ptl2gen 512 5

/* Page table for pages in the 7th gigabyte. */
.align 4096
ptl_2_6g:
        ptl2gen 512 6

/* Page table for pages in the 8th gigabyte. */
.align 4096
ptl_2_7g:
        ptl2gen 512 7

#ifdef MEMORY_MODEL_kernel
.align 4096
ptl_1:
        /* Identity mapping for [0; 8G) */
        .quad ptl_2_0g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_1g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_2g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_3g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_4g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_5g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_6g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_7g + (PTL_WRITABLE | PTL_PRESENT)
        .fill 502, 8, 0
        /* Mapping of [0; 2G) at -2G */
        .quad ptl_2_0g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_1g + (PTL_WRITABLE | PTL_PRESENT)

.align 4096
SYMBOL(ptl_0)
        .quad ptl_1 + (PTL_WRITABLE | PTL_PRESENT)
        .fill 510, 8, 0
        .quad ptl_1 + (PTL_WRITABLE | PTL_PRESENT)
#endif

#ifdef MEMORY_MODEL_large
.align 4096
ptl_1:
        /* Identity mapping for [0; 8G) */
        .quad ptl_2_0g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_1g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_2g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_3g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_4g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_5g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_6g + (PTL_WRITABLE | PTL_PRESENT)
        .quad ptl_2_7g + (PTL_WRITABLE | PTL_PRESENT)
        .fill 504, 8, 0

.align 4096
SYMBOL(ptl_0)
        .quad ptl_1 + (PTL_WRITABLE | PTL_PRESENT)
        .fill 255, 8, 0
        .quad ptl_1 + (PTL_WRITABLE | PTL_PRESENT)
        .fill 255, 8, 0
#endif

.section K_DATA_START, "aw", @progbits

SYMBOL(bootstrap_idtr)
        .word 0
        .long 0

SYMBOL(bootstrap_gdtr)
        .word GDT_SELECTOR(GDT_ITEMS)
        .long KA2PA(gdt)

SYMBOL(multiboot_eax)
        .long 0

SYMBOL(multiboot_ebx)
        .long 0

err_extended_cpuid:
        .asciz "Error: Extended CPUID not supported -- CPU is not 64-bit. System halted."
err_long_mode:
        .asciz "Error: 64-bit long mode not supported. System halted."
err_noexecute:
        .asciz "Error: No-execute pages not supported. System halted."
err_fx:
        .asciz "Error: FXSAVE/FXRESTORE instructions not supported. System halted."
err_sse2:
        .asciz "Error: SSE2 instructions not supported. System halted."

status_prot:
        .asciz "[prot] "
status_vesa_copy:
        .asciz "[vesa_copy] "
status_multiboot_cmdline:
        .asciz "[multiboot_cmdline] "
status_vesa_real:
        .asciz "[vesa_real] "
status_prot2:
        .asciz "[prot2] "
status_long:
        .asciz "[long] "
status_main:
        .asciz "[main] "

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub