summaryrefslogtreecommitdiff
path: root/boot/main.c
blob: 570b7037c13db2b7717c0887a34071ca412368c0 (plain)
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
#include "kjarna.h"
#include <stdnoreturn.h>

/*
 * Fake syscall mechanism -
 *
 * We have a GDT with entries for supervisory mode. These serve as the
 * entries to satisfy the requirements of the SYSCALL instruction, as
 * for some reason (possibly intentionally) the OVMF GDT is not laid out
 * in a way to make use of the SYSCALL instruction possible.
 *
 * This causes us to have to work inside of constraints during loading time
 * - All "user" mode execution entirely blocks interrupts processing. That
 *   means that user mode code must not execute "hlt", or the system will
 *   be locked.
 * - When (if?) user input is required, it is always buffered. It is possible
 *   to simulate unbuffered input, at the cost of one syscall per transfer
 *   from "kernel" side to "user" side. This will cause high input lag.
 *   We will not be running Quake in this environment.
 *
 * These constraints are probably fine, as the loading process only needs to 
 * open files, map memory, etc.
 */


struct dtr64
{
	uint16_t limit;
	uint64_t base;
} __attribute__((packed, aligned(16)));

struct segment_descriptor
{
    uint16_t limit0;
    uint16_t base0;
    uint8_t base16;
    uint8_t access;
    uint8_t flags_limit16;
    uint8_t base24;
} __attribute__((packed, aligned(8)));

union msr_star
{
	struct 
	{
		uint32_t target_eip;
		uint16_t syscall_selector_base;
		uint16_t sysret_selector_base;
	}
	fields;
	uint64_t value;
};

union msr_lstar
{
	void (*syscall_handler)(void);
	uint64_t value;
};

void *stack_alloc(void **stack_pointer, size_t alloc_size)
{
	void *block = *(char **)stack_pointer -= alloc_size;

	// maintain alignment
	*(char **)stack_pointer -= alloc_size % sizeof(size_t);

	return block;
}

static void msr_write(uint32_t index, uint64_t value)
{
    uint32_t low = value & 0xffffffff;
    uint32_t high = value >> 32;

    __asm__ volatile
        (
         "wrmsr\n\t"
         :: "a"(low), "d"(high), "c"(index)
        );
}

uint64_t msr_read(uint32_t index)
{
    uint32_t low;
    uint32_t high;

    __asm__ volatile
        (
         "rdmsr\n\t"
         : "=a"(low), "=d"(high)
         : "c"(index)
        );

    return ((uint64_t)high << 32)|low;
}

static struct segment_descriptor const fake_syscall_gdt[] =
{
	{ 0 },
	{ 0xffff, 0, 0, 0x9a, 0xaf, 0},
	{ 0xffff, 0, 0, 0x92, 0xcf, 0}
};

static noreturn void fake_syscall_entry(void)
{
	while (true);
}

SYSV_ABI void fake_syscall_return(void *target_rsp);

static void install_syscall_handler(void)
{
	union msr_lstar lstar = { fake_syscall_entry };
	union msr_star star = { { 0, 1 << 3, 1 << 3 | 3 } };

	msr_write(0xc0000082, lstar.value);
	msr_write(0xc0000081, star.value);

	uint64_t efer = msr_read(0xc0000080);
	efer |= 1;
	msr_write(0xc0000080, efer);
}

struct context_stack_frame
{
	struct dtr64 lret_gdtr;
	uint64_t lret_ds;
	uint64_t lret_rip;
	uint64_t lret_cs;
};

static noreturn void enter_boot_image(struct kjarna_boot_image *image)
{
	size_t boot_stack_size = 0x10000;
	char *boot_image_stack = calloc(1, boot_stack_size);
	void *boot_image_stack_head = boot_image_stack + boot_stack_size;

	struct context_stack_frame *stack_frame = stack_alloc(&boot_image_stack_head, sizeof(*stack_frame));

	stack_frame->lret_cs = 8;
	stack_frame->lret_ds = 16;
	stack_frame->lret_rip = (uintptr_t)image->entry;

	stack_frame->lret_gdtr.limit = sizeof(fake_syscall_gdt) - 1;
	stack_frame->lret_gdtr.base = (uintptr_t)fake_syscall_gdt;

	fake_syscall_return(boot_image_stack_head);

	while (true);
}

#include <libc/stdio.h>

int main(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	printf("DEBUG: alignof(struct dtr64) = %zu", alignof(struct dtr64));
	printf("DEBUG: sizeof(struct dtr64) = %zu", sizeof(struct dtr64));
	struct kjarna_boot_image boot_image = get_boot_image();
	install_syscall_handler();
	enter_boot_image(&boot_image);
}