summaryrefslogtreecommitdiffstats
path: root/arch/x86_64/sys/init.c
blob: 2bd86d512b78e6e54e1a69e392144f8aae8d86c7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "init.h"
#include "arch/x86_64/hal/msr.h"
#include "config.h"
#include "jove.h"
#include "hal/obj.h"
#include "lib/string.h"
#include "lib/panic.h"
#include "lib/printf.h"
#include "arch/x86_64/lib/elf.h"
#include "arch/x86_64/hal/paging.h"
#include "boot/cmdline.h"
#include "boot/initrd.h"

PAGEALIGN static page_t s_init_program_pml3[512];
PAGEALIGN static page_t s_init_program_pml2[512];
PAGEALIGN static page_t s_init_program_pml1[512];
PAGEALIGN static uint8_t s_init_program_pages[INIT_PROGRAM_PAGES_MAX][0x1000];

PAGEALIGN static page_t s_init_stack_pml3[512];
PAGEALIGN static page_t s_init_stack_pml2[512];
PAGEALIGN static page_t s_init_stack_pml1[512];
PAGEALIGN static uint8_t s_init_stack_pages[INIT_STACK_PAGES][0x1000];

static int
elf_is_valid(struct elf_header *ehdr)
{
    if(ehdr->e_ident[0] != EI_MAG0 ||
       ehdr->e_ident[1] != EI_MAG1 ||
       ehdr->e_ident[2] != EI_MAG2 ||
       ehdr->e_ident[3] != EI_MAG3) {
        kprintf("ELF: Magic does not match!\n");
        return 0;
    }
    if(ehdr->e_ident[4] != EI_CLASS_64) {
        kprintf("ELF: Class is not 64-bit!\n");
        return 0;
    }
    if(ehdr->e_ident[5] != EI_DATA_LE) {
        kprintf("ELF: Data is not little-endian!\n");
        return 0;
    }
    if(ehdr->e_type != ET_EXEC) {
        kprintf("ELF: Program is not standard executable type!\n");
        return 0;
    }
    if(ehdr->e_machine != EM_x86_64) {
        kprintf("ELF: Executable is not x86_64!\n");
        return 0;
    }
    return 1;
}

void
init_setup(void)
{
    memset(s_init_program_pml3, 0, 0x1000);
    memset(s_init_program_pml2, 0, 0x1000);
    memset(s_init_program_pml1, 0, 0x1000);

    memset(s_init_stack_pml3, 0, 0x1000);
    memset(s_init_stack_pml2, 0, 0x1000);
    memset(s_init_stack_pml1, 0, 0x1000);

    uintptr_t init_program_pml3_pptr = KPTR_TOPHYS(&s_init_program_pml3);
    uintptr_t init_program_pml2_pptr = KPTR_TOPHYS(&s_init_program_pml2);
    uintptr_t init_program_pml1_pptr = KPTR_TOPHYS(&s_init_program_pml1);
    uintptr_t init_program_pml0_pptr = KPTR_TOPHYS(&s_init_program_pages);

    uintptr_t init_stack_pml3_pptr = KPTR_TOPHYS(&s_init_stack_pml3);
    uintptr_t init_stack_pml2_pptr = KPTR_TOPHYS(&s_init_stack_pml2);
    uintptr_t init_stack_pml1_pptr = KPTR_TOPHYS(&s_init_stack_pml1);
    uintptr_t init_stack_pml0_pptr = KPTR_TOPHYS(&s_init_stack_pages);

    //Get the init file data from the initrd.
    const char *init_path = cmdline_getarg("init");
    if(init_path == NULL) {
        kpanic("Kernel commandline is missing 'init' arg.\n");
    }

    jove_owned_obj_t *initrd_obj = obj_take(objdir_getlocal(_rootdir_obj, ROOTDIR_INITRD_I));
    jove_owned_obj_t *init_obj = initrd_seek(initrd_obj, init_path);
    if(init_obj == NULL) {
        kpanic("init: Cannot find file '%s' in initrd.\n", init_path);
    }
    obj_release(initrd_obj);

    void *init_data = initrd_file_data(init_obj);
    obj_release(init_obj);

    struct elf_header *init_ehdr = init_data;
    if(init_ehdr == NULL) {
        kpanic("init: Cannot get file data for file '%s': %i\n", init_path, errno);
    }

    if(!elf_is_valid(init_ehdr)) {
        kpanic("init: Either passed file '%s' is not an ELF file or is not loadable.\n", init_path);
    }

    jove_owned_obj_t *init_pagedir = obj_take(objdir_getlocal(_rootdir_obj, ROOTDIR_PAGEDIR_I));
    page_t *pml4 = obj_deref(init_pagedir);

    //Load the init ELF
    for(size_t i = 0; i < init_ehdr->e_shnum; i++) {
        struct elf_shdr *shdr = init_data + init_ehdr->e_shoff + i * init_ehdr->e_shentsize;
        if(shdr->sh_type != SHT_PROGBITS && shdr->sh_type != SHT_NOBITS) continue;
        if((shdr->sh_flags & SHF_ALLOC) == 0) continue;

        uintptr_t base = shdr->sh_addr;
        size_t limit = shdr->sh_size;

        uintptr_t page_base = base & ~0xFFFULL;
        size_t pml3i = (page_base >> PMLI_SHL(3)) & 0x1FF;
        size_t pml2i = (page_base >> PMLI_SHL(2)) & 0x1FF;
        size_t pml1i = (page_base >> PMLI_SHL(1)) & 0x1FF;
        size_t base_pml0i = (page_base >> PMLI_SHL(0)) & 0x1FF;

        pml4[pml3i].value = init_program_pml3_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER;
        s_init_program_pml3[pml2i].value = init_program_pml2_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER;
        s_init_program_pml2[pml1i].value = init_program_pml1_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER;

        size_t pages = (limit + 4095) / 4096;

        for(size_t pmlo = 0; pmlo < pages; pmlo++) {
            size_t pml0i = base_pml0i + pmlo;
            if(pml0i > INIT_PROGRAM_PAGES_MAX) {
                kpanic("init: Program exceeds maximum program pages in the same hugepage frame!\n");
            }

            uintptr_t pml0_pptr = init_program_pml0_pptr + (pml0i << 12);
            s_init_program_pml1[pml0i].value = pml0_pptr | PAGE_PRESENT | PAGE_USER | PAGE_WRITEABLE;
        }

        //Copy data from section if SHT_PROGBITS
        if(shdr->sh_type == SHT_PROGBITS) {
            memcpy((void*)shdr->sh_addr, init_data + shdr->sh_offset, shdr->sh_size);
        }
    }

    //Map the stack
    pml4[255].value = init_stack_pml3_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER | PAGE_XD;
    s_init_stack_pml3[511].value = init_stack_pml2_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER | PAGE_XD;
    s_init_stack_pml2[511].value = init_stack_pml1_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER | PAGE_XD;
    for(size_t pmlo = 0; pmlo < INIT_STACK_PAGES; pmlo++) {
        size_t pml0i = 511 - pmlo;
        uintptr_t page_pptr = init_stack_pml0_pptr + (pmlo << 12);

        s_init_stack_pml1[pml0i].value = page_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER | PAGE_XD;
    }

    obj_release(init_pagedir);

    //Enable syscalls
    extern void _syscall_entry(void);
    msr_set_lstar(_syscall_entry);

    uintptr_t usp = 0x00007FFFFFFFFFF8;
    uintptr_t uip = init_ehdr->e_entry;

    __asm__ volatile("movq %0, %%rsp; \
                      movq %1, %%rcx; \
                      movq $0x202, %%r11; \
                      swapgs; \
                      sysretq"::
                     "r"(usp), "r"(uip): "memory");
}