summaryrefslogtreecommitdiffstats
path: root/arch/x86_64/sys/init.c
blob: 681ba09f9e8643e41b708df1508deb2c41d19257 (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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "init.h"
#include "arch/x86_64/hal/msr.h"
#include "config.h"
#include "hal/process.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 = 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 = objdir_getlocal(_rootdir_obj, ROOTDIR_PAGEDIR_I);
    page_t *pml4 = obj_deref(init_pagedir);
    size_t last_base = 0;

    //Load the init ELF
    for(size_t i = 0; i < init_ehdr->e_phnum; i++) {
        struct elf_phdr *phdr = init_data + init_ehdr->e_phoff + i * init_ehdr->e_phentsize;
        if(phdr->p_type != PT_LOAD) continue;
        kprintf("init: phdr %i flags %b\n", i, phdr->p_flags);

        uintptr_t base = phdr->p_vaddr;
        size_t limit = phdr->p_memsz;

        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");
            }
            if(last_base < base + limit) last_base = base + limit;

            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
        memcpy((void*)phdr->p_vaddr, init_data + phdr->p_offset, phdr->p_memsz);
    }

    //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;
    }

    //Map the syscall message.
    last_base = (last_base + 0xFFF) & ~0xFFF;
    size_t last_page = (last_base >> 12) & 0x1FF;
    uintptr_t message_pptr = init_program_pml0_pptr + (last_page << 12);
    s_init_program_pml1[last_page].value = message_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_USER | PAGE_XD;

    jove_owned_obj_t *message_obj = objdir_getlocal(_rootdir_obj, ROOTDIR_MESSAGE_I);
    obj_settype(message_obj, JOVE_OBJ_MESSAGE);
    obj_setvalue(message_obj, (message_pptr & ~0xFFF) | 1);
    obj_release(message_obj);

    obj_release(init_pagedir);

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

    //Setup KSP for syscalls
    jove_owned_obj_t *pcb_obj = process_current();
    pcb_t *pcb = obj_deref(pcb_obj);

    obj_release(pcb_obj);

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

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