1/**
2 * \file
3 * \brief functionality to spawn domains
4 */
5
6/*
7 * Copyright (c) 2007-2010, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <string.h>
16#include <stdio.h>
17#include <inttypes.h>
18#include <barrelfish/barrelfish.h>
19#include <spawndomain/spawndomain.h>
20#include <barrelfish/dispatcher_arch.h>
21#include <elf/elf.h>
22#include "spawn.h"
23#include "../../arch.h"
24
25#if defined(__aarch64__)
26#define EM_HOST EM_AARCH64
27#else
28#error "Unexpected architecture."
29#endif
30
31/**
32 * \brief Convert elf flags to vregion flags
33 */
34static vregion_flags_t elf_to_vregion_flags(uint32_t flags)
35{
36    vregion_flags_t vregion_flags = 0;
37
38    if (flags & PF_R) {
39        vregion_flags |= VREGION_FLAGS_READ;
40    }
41    if (flags & PF_W) {
42        vregion_flags |= VREGION_FLAGS_WRITE;
43    }
44    if (flags & PF_X) {
45        vregion_flags |= VREGION_FLAGS_EXECUTE;
46    }
47
48    return vregion_flags;
49}
50
51static errval_t elf_allocate(void *state, genvaddr_t base, size_t size,
52                             uint32_t flags, void **retbase)
53{
54    errval_t err;
55
56    struct spawninfo *si = state;
57
58    // Increase size by space wasted on first page due to page-alignment
59    size_t base_offset = BASE_PAGE_OFFSET(base);
60    size += base_offset;
61    base -= base_offset;
62    // Page-align
63    size = ROUND_UP(size, BASE_PAGE_SIZE);
64
65    cslot_t vspace_slot = si->elfload_slot;
66    cslot_t spawn_vspace_slot = si->elfload_slot;
67
68    // Allocate the frames
69    size_t sz = 0;
70    for (lpaddr_t offset = 0; offset < size; offset += sz) {
71        sz = 1UL << log2floor(size - offset);
72        struct capref frame = {
73            .cnode = si->segcn,
74            .slot  = si->elfload_slot++,
75        };
76        err = frame_create(frame, sz, NULL);
77        if (err_is_fail(err)) {
78            return err_push(err, LIB_ERR_FRAME_CREATE);
79        }
80    }
81
82    /* Map into my vspace */
83    struct memobj *memobj = malloc(sizeof(struct memobj_anon));
84    if (!memobj) {
85        return LIB_ERR_MALLOC_FAIL;
86    }
87    struct vregion *vregion = malloc(sizeof(struct vregion));
88    if (!vregion) {
89        return LIB_ERR_MALLOC_FAIL;
90    }
91    // Create the objects
92    err = memobj_create_anon((struct memobj_anon*)memobj, size, 0);
93    if (err_is_fail(err)) {
94        return err_push(err, LIB_ERR_MEMOBJ_CREATE_ANON);
95    }
96    err = vregion_map(vregion, get_current_vspace(), memobj, 0, size,
97                      VREGION_FLAGS_READ_WRITE);
98    if (err_is_fail(err)) {
99        return err_push(err, LIB_ERR_VSPACE_MAP);
100    }
101    for (lvaddr_t offset = 0; offset < size; offset += sz) {
102        sz = 1UL << log2floor(size - offset);
103        struct capref frame = {
104            .cnode = si->segcn,
105            .slot  = vspace_slot++,
106        };
107        genvaddr_t genvaddr = vspace_lvaddr_to_genvaddr(offset);
108        err = memobj->f.fill(memobj, genvaddr, frame, sz);
109        if (err_is_fail(err)) {
110            return err_push(err, LIB_ERR_MEMOBJ_FILL);
111        }
112        err = memobj->f.pagefault(memobj, vregion, offset, 0);
113        if (err_is_fail(err)) {
114            DEBUG_ERR(err, "lib_err_memobj_pagefault_handler");
115            return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
116        }
117    }
118
119    /* Map into spawn vspace */
120    struct memobj *spawn_memobj = NULL;
121    struct vregion *spawn_vregion = NULL;
122    err = spawn_vspace_map_anon_fixed_attr(si, base, size, &spawn_vregion,
123                                           &spawn_memobj,
124                                           elf_to_vregion_flags(flags));
125    if (err_is_fail(err)) {
126        return err_push(err, SPAWN_ERR_VSPACE_MAP);
127    }
128    for (lvaddr_t offset = 0; offset < size; offset += sz) {
129        sz = 1UL << log2floor(size - offset);
130        struct capref frame = {
131            .cnode = si->segcn,
132            .slot = spawn_vspace_slot++,
133        };
134        genvaddr_t genvaddr = vspace_lvaddr_to_genvaddr(offset);
135        err = memobj->f.fill(spawn_memobj, genvaddr, frame, sz);
136        if (err_is_fail(err)) {
137            return err_push(err, LIB_ERR_MEMOBJ_FILL);
138        }
139        err = spawn_memobj->f.pagefault(spawn_memobj, spawn_vregion, offset, 0);
140        if (err_is_fail(err)) {
141            DEBUG_ERR(err, "lib_err_memobj_pagefault_handler");
142            return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
143        }
144    }
145
146    genvaddr_t genvaddr = vregion_get_base_addr(vregion) + base_offset;
147    *retbase = (void*)vspace_genvaddr_to_lvaddr(genvaddr);
148    return SYS_ERR_OK;
149}
150
151/**
152 * \brief Load the elf image
153 */
154errval_t spawn_arch_load(struct spawninfo *si,
155                         lvaddr_t binary, size_t binary_size,
156                         genvaddr_t *entry, void** arch_info)
157{
158    errval_t err;
159
160    // Reset the elfloader_slot
161    si->elfload_slot = 0;
162    struct capref cnode_cap = {
163        .cnode = si->rootcn,
164        .slot  = ROOTCN_SLOT_SEGCN,
165    };
166    struct capref local_cnode_cap;
167    // XXX: this code assumes that elf_load never needs more than 256 slots for
168    // text frame capabilities.
169    err = cnode_create_l2(&local_cnode_cap, &si->segcn);
170
171    if (err_is_fail(err)) {
172        return err_push(err, SPAWN_ERR_CREATE_SEGCN);
173    }
174    // Copy SegCN into new domain's cspace
175    err = cap_copy(cnode_cap, local_cnode_cap);
176    if (err_is_fail(err)) {
177        return err_push(err, SPAWN_ERR_MINT_SEGCN);
178    }
179
180
181    // TLS is NYI
182    si->tls_init_base = 0;
183    si->tls_init_len = si->tls_total_len = 0;
184
185    // Load the binary
186    err = elf_load(EM_HOST, elf_allocate, si, binary, binary_size, entry);
187    if (err_is_fail(err)) {
188        return err;
189    }
190
191    struct Elf64_Shdr* got_shdr =
192        elf64_find_section_header_name(binary, binary_size, ".got");
193    if (got_shdr)
194    {
195        *arch_info = (void*)got_shdr->sh_addr;
196    }
197    else {
198        return SPAWN_ERR_LOAD;
199    }
200
201    return SYS_ERR_OK;
202}
203
204void spawn_arch_set_registers(void *arch_load_info,
205                              dispatcher_handle_t handle,
206                              arch_registers_state_t *enabled_area,
207                              arch_registers_state_t *disabled_area)
208{
209    assert(arch_load_info != NULL);
210    uintptr_t got_base = (uintptr_t)arch_load_info;
211
212    struct dispatcher_shared_aarch64* disp_arm =
213        get_dispatcher_shared_aarch64(handle);
214    disp_arm->got_base = got_base;
215
216    enabled_area->regs[REG_OFFSET(PIC_REGISTER)] = got_base;
217    disabled_area->regs[REG_OFFSET(PIC_REGISTER)] = got_base;
218}
219