1// Simple boot-loader.
2//
3// This code is only intended for use on M5 where it is started via
4// molly_boot.S which runs on Core 0.  The simulator starts Core 0
5// in 64-bit mode already, so typical initialization code is not
6// needed.
7
8#include <assert.h>
9#include <stdio.h>
10#include <stdint.h>
11#include <stddef.h>
12#include <string.h>
13#include <barrelfish_kpi/types.h>
14#include <errors/errno.h>
15#include <elf/elf.h>
16#include <multiboot.h>
17
18// Prototypes for functions from molly_boot.S:
19extern void molly_to_kernel_transition(void *entry_addr,
20                                       uint64_t RAX,
21                                       uint64_t RBX);
22
23// Prototypes for functions from m5_kernel.c:
24extern struct multiboot_info *molly_get_mbi(void);
25//struct multiboot_info *molly_get_mbi(void) { return NULL; }
26
27// Prototypes for symbols declared via linker script:
28extern void *_start_img;
29extern void *_end_img;
30
31//......................................................................
32//
33// Basic debugging output functions.  These assume M5 with the serial
34// port already initialized, and with flow control unnecessary.
35
36static inline void outb(int port, char data)
37{
38    __asm __volatile("outb %0,%%dx" : : "a" (data), "d" (port));
39}
40
41static int serial_portbase = 0x3f8; // COM1
42
43static void serial_putchar(char c)
44{
45  outb(serial_portbase, c);
46}
47
48static void putstr(const char *cp) {
49  char c;
50  while ((c=*(cp++))) {
51    serial_putchar(c);
52  }
53}
54
55static const char hexdigits[] = "0123456789abcdef";
56
57static void puthex64(unsigned long long x) {
58  putstr("0x");
59  unsigned long long mask = 0xf000000000000000;
60  int s = 60;
61  do {
62    unsigned int idx = (x & mask) >> s;
63    serial_putchar(hexdigits[idx]);
64    s -= 4;
65    mask >>= 4;
66  } while (s >= 0);
67}
68
69static void puthex32(uint32_t x) {
70  putstr("0x");
71  uint32_t mask = 0xf0000000;
72  int s = 28;
73  do {
74    unsigned int idx = (x & mask) >> s;
75    serial_putchar(hexdigits[idx]);
76    s -= 4;
77    mask >>= 4;
78  } while (s >= 0);
79}
80
81static void puthex8(uint32_t x) {
82  uint32_t mask = 0xf0;
83  int s = 4;
84  do {
85    unsigned int idx = (x & mask) >> s;
86    serial_putchar(hexdigits[idx]);
87    s -= 4;
88    mask >>= 4;
89  } while (s >= 0);
90}
91
92static void putptr64(void *ptr) {
93  puthex64((unsigned long long)ptr);
94}
95
96static void putstrptr64(char *str, void *ptr) {
97  putstr(str);
98  putptr64(ptr);
99  putstr("\n");
100}
101
102
103//......................................................................
104//
105// Basic libc functionality needed
106
107void __assert(const char *exp, const char *file, const char *func, int line)
108{
109  putstr("Assertion failure: ");
110  putstr(exp);
111  putstr(" ");
112  putstr(file);
113  putstr(" ");
114  putstr(func);
115  putstr(" ");
116  puthex32(line);
117  putstr("\n");
118  while(1){}
119}
120
121int printf(const char *fmt, ...)
122{
123  putstr(fmt);
124}
125
126void *
127memset (void *s, int c, size_t n)
128{
129    uint8_t *p = (uint8_t *)s;
130    for (size_t m = 0; m < n; m++) {
131        *p++ = c;
132    }
133    return s;
134}
135
136void *
137memcpy(void *dst, const void *src, size_t len)
138{
139    char *d = dst;
140    const char *s = src;
141
142    /* check that we don't overlap (should use memmove()) */
143    assert((src < dst && src + len <= dst) || (dst < src && dst + len <= src));
144
145    while (len--)
146        *d++ = *s++;
147
148    return dst;
149}
150
151char *
152strrchr(const char *s, int c)
153{
154    unsigned int i;
155
156    if(strlen(s) == 0)
157        return NULL;
158
159    for(i = strlen(s) - 1; i >= 0; i--) {
160        if(s[i] == c)
161            return (char *)&s[i];
162    }
163
164    return NULL;
165}
166
167int
168strncmp(const char *s1, const char *s2, size_t n)
169{
170    int result;
171
172    for(unsigned int i = 0; i < n; i++) {
173        if((result = s2[i] - s1[i]) != 0) {
174            return result;
175        }
176
177        if(s1[i] == '\0' || s2[i] == '\0') {
178            break;
179        }
180    }
181
182    return 0;
183}
184
185size_t
186strlen(const char *s)
187{
188    size_t i = 0;
189
190    while (*s != '\0') {
191        i++;
192        s++;
193    }
194
195    return i;
196}
197
198int
199strcmp(const char* a, const char* b)
200{
201    while (*a == *b && *a != '\0')
202    {
203        a++;
204        b++;
205    }
206
207    return *a - *b;
208}
209
210//......................................................................
211//
212// Allocation function used from the ELF loader
213
214#define BASE_PAGE_SIZE 4096
215
216// Round up n to the next multiple of size
217#define ROUND_UP(n, size)           ((((n) + (size) - 1)) & (~((size) - 1)))
218
219static uint64_t next_addr;
220
221static errval_t linear_alloc(void *s,
222                             genvaddr_t base,
223                             size_t size,
224                             uint32_t flags,
225                             void **ret)
226{
227  // round to base page size
228  uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE;
229
230  /* *ret = (void *)(uintptr_t)base; */
231  *ret = (void *)next_addr;
232
233  next_addr += npages * BASE_PAGE_SIZE;
234  return SYS_ERR_OK;
235}
236
237// Helper function to check that addresses assumed to be 32-bit do not
238// have high bits set.
239
240static uint32_t ptr_to_uint32(void *ptr) {
241  uint64_t temp = (uint64_t)ptr;
242  assert(temp < 0x7fffffff);
243  return (uint32_t) temp;
244}
245
246// C code entered from molly_boot.S:
247
248void molly_init(void) {
249  putstr("......................................................................\n");
250  putstr("molly_init:\n");
251  putstrptr64("  boot image start : ", &_start_img);
252  putstrptr64("  boot image end   : ", &_end_img);
253
254  struct multiboot_info *mbi = molly_get_mbi();
255  putstrptr64("  multiboot info   : ", mbi);
256
257  // Start allocating from one page beyond the boot image:
258  next_addr = (ROUND_UP((uint64_t) &_end_img, BASE_PAGE_SIZE) +
259               BASE_PAGE_SIZE);
260  putstrptr64("  allocating from  : ", (void*)next_addr);
261
262  // Load the kernel out from the boot image:
263  struct multiboot_modinfo *mbi_mods;
264  mbi_mods = (struct multiboot_modinfo*)(uint64_t)(mbi->mods_addr);
265  void *kernel = (void*)(uint64_t)(mbi_mods[0].mod_start);
266  uint32_t kernel_bytes = mbi_mods[0].mod_end - mbi_mods[0].mod_start;
267  putstrptr64("  kernel start     : ", kernel);
268  putstrptr64("  kernel bytes     : ", (void*)(uint64_t)kernel_bytes);
269
270  void *kernel_entry = NULL;
271  lpaddr_t kernel_start = next_addr;
272  genvaddr_t tls_base = 0;
273  size_t tls_init_len = 0;
274  size_t tls_total_len = 0;
275  errval_t err = elf64_load(EM_X86_64,
276                            linear_alloc, NULL,
277                            (uint64_t) kernel, kernel_bytes,
278                            (genvaddr_t *) &kernel_entry,
279                            &tls_base,
280                            &tls_init_len,
281                            &tls_total_len);
282  if (err_is_fail(err)) {
283    putstr("Kernel loading failed\n");
284    return;
285  }
286  putstrptr64("  kernel entry pt  : ", kernel_entry);
287
288  // Relocate kernel image
289  struct Elf64_Ehdr *cpu_head = (struct Elf64_Ehdr *)kernel;
290  struct Elf64_Shdr *rela, *symtab, *symhead =
291    (struct Elf64_Shdr *)(kernel + (uintptr_t)cpu_head->e_shoff);
292  genvaddr_t elfbase = elf_virtual_base64(cpu_head);
293  rela = elf64_find_section_header_type(symhead,
294                                        cpu_head->e_shnum,
295                                        SHT_RELA);
296  symtab = elf64_find_section_header_type(symhead,
297                                          cpu_head->e_shnum,
298                                          SHT_DYNSYM);
299  elf64_relocate(kernel_start, elfbase,
300                 (struct Elf64_Rela *)(uintptr_t)(kernel + rela->sh_offset),
301                 rela->sh_size,
302                 (struct Elf64_Sym *)(uintptr_t)(kernel + symtab->sh_offset),
303                 symtab->sh_size,
304                 elfbase, (void *)kernel_start);
305  kernel_entry = kernel_entry - elfbase + kernel_start;
306  putstrptr64("  ...relocated to  : ", kernel_entry);
307
308  // Initialize multiboot symbol information for the
309  // relocated kernel:
310  mbi->syms.elf.num = cpu_head->e_shnum;
311  mbi->syms.elf.size = cpu_head->e_shentsize;
312  mbi->syms.elf.addr = ptr_to_uint32(kernel) + cpu_head->e_shoff;
313  mbi->syms.elf.shndx = cpu_head->e_shstrndx;
314
315  putstr("......................................................................\n");
316
317  molly_to_kernel_transition(kernel_entry,
318                             MULTIBOOT_INFO_MAGIC,
319                             ptr_to_uint32(mbi)
320                             );
321}
322
323