1/**
2 * \file
3 * \brief Elver - Intermediary stage bootloader
4 *
5 * Elver is used to switch the system into 64-bit long-mode and load
6 * the kernel, which is a relocatable ELF64 image. Unfortunately, GRUB
7 * is not able to this without a patch. This is purely for
8 * backwards-compatibility. As soon as bootloaders support loading
9 * relocatable ELF64 images into 64-bit mode, this can be dropped.
10 */
11
12/*
13 * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
14 * All rights reserved.
15 *
16 * This file is distributed under the terms in the attached LICENSE file.
17 * If you do not find this file, copies can be found by writing to:
18 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
19 */
20
21#include <stdio.h>
22#include <stdint.h>
23#include <stddef.h>
24#include <string.h>
25#include <barrelfish_kpi/types.h>
26#include <errors/errno.h>
27#include <elf/elf.h>
28#include <multiboot.h>
29
30#include <xeon_phi/xeon_phi.h>
31
32/* the boot magic */
33#define K1OM_BOOT_MAGIC         0xB001B001
34
35/* the address of the Xeon Phi SBOX registers used for status prints*/
36#define SBOX_BASE           0x08007D0000ULL
37
38/* reference to the end of bootloader */
39extern char _end_bootloader;
40
41/// Round up n to the next multiple of size
42#define ROUND_UP(n, size)           ((((n) + (size) - 1)) & (~((size) - 1)))
43#define MAX(x,y)  ((x)>(y) ? (x) : (y))
44#define BASE_PAGE_SIZE 0x1000
45
46/* Pointer to the multiboot struct we use */
47struct multiboot_info *multiboot_info;
48
49/* Address where we can safely allocate memory */
50static lpaddr_t phys_alloc_start;
51
52/* the entry address of the loaded kernel */
53genvaddr_t kernel_entry;
54
55/**
56 * C level entry point for the boot loader
57 *
58 * \param magic this field must hold the value K1OM_BOOT_MAGIC
59 * \param mb    pointer to the boot_params struct setup by the boot loader
60 */
61int
62loader(uint64_t magic,
63       struct xeon_phi_boot_params *bp);
64
65/*
66 * ----------------------------------------------------------------------------
67 *  Basic Error Reporting Mechanism
68 * ----------------------------------------------------------------------------
69 */
70union status
71{
72    uint32_t raw;
73    char vals[4];
74};
75
76static void
77print_status(char a,
78             char b)
79{
80
81    volatile uint32_t *p = (volatile uint32_t *) ((SBOX_BASE) + 0x0000AB40);
82    volatile uint32_t *p2 = (volatile uint32_t *) ((SBOX_BASE) + 0x0000AB5C);
83
84    union status s;
85
86    s.vals[3] = 0x0a;
87    s.vals[2] = b;
88    s.vals[1] = a;
89    s.vals[0] = '>';
90
91    *p2 = s.raw;
92    *p = 0x7A7A7A7A;
93}
94
95static inline void
96eabort(char a,
97       char b)
98{
99    print_status(a, b);
100    while (1)
101        ;
102}
103
104static inline void notify_host(void)
105{
106    volatile uint32_t *p = (volatile uint32_t *) ((SBOX_BASE) + 0xAB28);
107    *p = (*p) | 0x1;
108}
109
110
111/*
112 * ----------------------------------------------------------------------------
113 *  ELF Utility Functions
114 * ----------------------------------------------------------------------------
115 */
116
117static errval_t
118linear_alloc(void *s,
119             genvaddr_t base,
120             size_t size,
121             uint32_t flags,
122             void **ret)
123{
124    // round to base page size
125    uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE;
126
127    *ret = (void *) phys_alloc_start;
128
129    phys_alloc_start += npages * BASE_PAGE_SIZE;
130    return SYS_ERR_OK;
131}
132
133
134static struct multiboot_modinfo *
135multiboot_find_module(const char *basename)
136{
137    struct multiboot_modinfo *mod;
138    mod = (struct multiboot_modinfo *) (uintptr_t) multiboot_info->mods_addr;
139
140    for (size_t i = 0; i < multiboot_info->mods_count; i++) {
141        const char *modname = strrchr((char *) (uintptr_t) mod[i].string, '/');
142
143        if (modname == NULL) {
144            modname = (char *) (uintptr_t) mod[i].string;
145        } else {
146            modname++;
147        }
148
149        if (!strncmp(modname, basename, strlen(basename))) {
150            return &mod[i];
151        }
152    }
153
154    return NULL;
155}
156
157static void
158set_elf_headers(uintptr_t base)
159{
160    struct Elf64_Ehdr *head = (struct Elf64_Ehdr *) (base);
161
162    multiboot_info->syms.elf.num = head->e_shnum;
163    multiboot_info->syms.elf.size = head->e_shentsize;
164    multiboot_info->syms.elf.addr = base + head->e_shoff;
165    multiboot_info->syms.elf.shndx = head->e_shstrndx;
166}
167
168/*
169 * ----------------------------------------------------------------------------
170 *  Loader
171 * ----------------------------------------------------------------------------
172 */
173
174/**
175 * Entry point from boot.S
176 * Long mode, paging and protected mode enabled
177 *
178 * \param magic         magic value
179 * \param bootparam     pointer to struct boot param
180 *
181 */
182int
183loader(uint64_t magic,
184       struct xeon_phi_boot_params *bp)
185{
186    errval_t err;
187
188    print_status('S', '0');
189
190    if (magic != K1OM_BOOT_MAGIC) {
191        /* wrong value */
192        eabort('E', '0');
193    }
194
195    if (((uintptr_t)bp)>>32) {
196        /*
197         * sanity check that the boot params has a value which is less than 4G,
198         * since we store the boot params in a 32bit value
199         */
200        eabort('E', 'a');
201    }
202
203    print_status('S', '1');
204
205    /*
206     * XXX: copying the boot loader information structure around
207     */
208
209
210    multiboot_info = (struct multiboot_info *)(uint64_t)bp->mbi;
211
212    print_status('S', '2');
213
214    /* look up the kernel module */
215    struct multiboot_modinfo *kernel;
216    kernel = multiboot_find_module("cpu");
217    if (kernel == NULL) {
218        kernel = multiboot_find_module("kernel");
219    }
220    if (kernel == NULL) {
221        eabort('E', '3');
222    }
223
224    /* set the start address where we can allocate ram */
225    phys_alloc_start = ROUND_UP(bp->ramdisk_image + bp->ramdisk_size,
226                                BASE_PAGE_SIZE)+BASE_PAGE_SIZE;
227
228    lpaddr_t kernel_start = phys_alloc_start;
229
230    /* overwrite the cmd line with the one supplied by the host */
231    multiboot_info->cmdline = bp->cmdline_ptr;
232    multiboot_info->flags |= MULTIBOOT_INFO_FLAG_HAS_CMDLINE;
233
234    /* we use the mem_lower and mem_upper for the mulitboot image location */
235
236    multiboot_info->mem_lower = bp->ramdisk_image;
237    multiboot_info->mem_upper = bp->ramdisk_image+bp->ramdisk_size;
238    multiboot_info->flags |= MULTIBOOT_INFO_FLAG_HAS_MEMINFO;
239
240
241    /* we use the config table to store the pointer to struct boot param */
242    multiboot_info->config_table = (uint32_t)(uintptr_t)bp;
243    multiboot_info->flags |= MULTIBOOT_INFO_FLAG_HAS_CONFIG;
244
245    if ((bp->xeon_phi_id & 0xFF00) != 0xFF00) {
246        eabort('E', '4');
247    }
248
249    multiboot_info->xeon_phi_id = (uint8_t)(bp->xeon_phi_id & 0xff);
250
251
252    print_status('S', '3');
253
254    err = elf64_load(EM_K1OM, linear_alloc, NULL, kernel->mod_start,
255                     MULTIBOOT_MODULE_SIZE(*kernel), &kernel_entry, NULL, NULL,
256                     NULL);
257
258    if (err_is_fail(err)) {
259        switch(err_no(err)) {
260            case ELF_ERR_FILESZ :
261                eabort('E', '5');
262                break;
263            case ELF_ERR_HEADER:
264                eabort('E', '6');
265                break;
266            case ELF_ERR_PROGHDR:
267                eabort('E', '7');
268                break;
269            case ELF_ERR_ALLOCATE:
270                eabort('E', '8');
271                break;
272            default:
273                eabort('E', '9');
274                break;
275        }
276    }
277
278    struct Elf64_Ehdr *cpu_head = (struct Elf64_Ehdr *) (uint64_t) kernel->mod_start;
279    struct Elf64_Shdr *rela, *symtab, *symhead;
280
281    symhead = (struct Elf64_Shdr *) (kernel->mod_start
282            + (uintptr_t) cpu_head->e_shoff);
283
284    genvaddr_t elfbase = elf_virtual_base64(cpu_head);
285
286    rela = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_RELA);
287
288    symtab = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_DYNSYM);
289
290    print_status('S', '4');
291
292    elf64_relocate(
293            kernel_start, elfbase,
294            (struct Elf64_Rela *) (uintptr_t) (kernel->mod_start + rela->sh_offset),
295            rela->sh_size,
296            (struct Elf64_Sym *) (uintptr_t) (kernel->mod_start + symtab->sh_offset),
297            symtab->sh_size, elfbase, (void *) kernel_start);
298
299    kernel_entry = kernel_entry - elfbase + kernel_start;
300
301    print_status('S', '5');
302
303    set_elf_headers(kernel->mod_start);
304
305    print_status('S', '6');
306
307    return kernel_entry;
308
309}
310
311