1/*
2 * Copyright 2016, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(D61_BSD)
11 */
12
13#include "state.h"
14#include "common.h"
15#include <simple/simple.h>
16#include <sel4platsupport/platsupport.h>
17#include <sel4platsupport/plat/serial.h>
18#include <sel4debug/debug.h>
19#include <autoconf.h>
20#include <refos/refos.h>
21#include <refos-rpc/rpc.h>
22
23/*! @file
24    @brief Global statuc struct & helper functions for process server. */
25
26#define PROCSERV_IRQ_HANDLER_HASHTABLE_SIZE 32
27#define ALLOCATOR_VIRTUAL_POOL_SIZE ((1 << seL4_PageBits) * 100)
28#ifndef CONFIG_PROCSERV_INITIAL_MEM_SIZE
29    #define CONFIG_PROCSERV_INITIAL_MEM_SIZE (4096 * 32)
30#endif
31
32void *serial_paddr;
33static char _procservInitialMemPool[CONFIG_PROCSERV_INITIAL_MEM_SIZE];
34struct procserv_state procServ;
35const char* dprintfServerName = "PROCSERV";
36int dprintfServerColour = 32;
37
38uint32_t faketime() {
39    return procServ.faketime++;
40}
41
42static void procserv_nameserv_callback_free_cap(seL4_CPtr cap);
43
44/*! @brief display a heartwarming welcome message.
45    @param info The Bootinfo structure.
46*/
47static void
48initialise_welcome_message(seL4_BootInfo *info)
49{
50    dprintf("================= RefOS Version 2.0 =================\n");
51    dprintf("  Built on "__DATE__" "__TIME__".\n");
52    dprintf("  �� Copyright 2016 Data61, CSIRO\n");
53    dprintf("=====================================================\n");
54
55    debug_print_bootinfo(info);
56}
57
58/*! @brief Initialises the kernel object allocator.
59    @param info The BootInfo struct passed in from the kernel.
60    @param s The process server global state.
61 */
62static void
63initialise_allocator(seL4_BootInfo *info, struct procserv_state *s)
64{
65    assert(info && s);
66    int error = -1;
67    memset(s, 0, sizeof(struct procserv_state));
68    (void) error;
69    reservation_t virtual_reservation;
70    /* Create and initialise allocman allocator, and create a virtual kernel allocator (VKA)
71       interface from it. */
72    s->allocman = bootstrap_use_bootinfo(info, CONFIG_PROCSERV_INITIAL_MEM_SIZE,
73            _procservInitialMemPool);
74    assert(s->allocman);
75    allocman_make_vka(&s->vka, s->allocman);
76    s->vkaPtr = &s->vka;
77
78    /* Manage our own root server VSpace using this newly created allocator. */
79    error = sel4utils_bootstrap_vspace_with_bootinfo_leaky(&s->vspace, &s->vspaceData,
80            seL4_CapInitThreadPD, &s->vka, info);
81    assert(!error);
82
83    void *vaddr;
84    virtual_reservation = vspace_reserve_range(&s->vspace,
85                                               ALLOCATOR_VIRTUAL_POOL_SIZE, seL4_AllRights, 1, &vaddr);
86
87    if (virtual_reservation.res == 0) {
88        ZF_LOGF("Failed to provide virtual memory for allocator");
89    }
90
91    bootstrap_configure_virtual_pool(s->allocman, vaddr,
92                                     ALLOCATOR_VIRTUAL_POOL_SIZE, seL4_CapInitThreadPD);
93
94    simple_default_init_bootinfo(&s->simpleEnv, info);
95}
96
97/*! @brief Initialise the process server modules.
98    @param s The process server global state.
99 */
100static void
101initialise_modules(struct procserv_state *s)
102{
103    pd_init(&s->PDList);
104    pid_init(&s->PIDList);
105    w_init(&s->windowList);
106    ram_dspace_init(&s->dspaceList);
107    nameserv_init(&s->nameServRegList, procserv_nameserv_callback_free_cap);
108}
109
110#ifdef CONFIG_ARCH_ARM
111/*! @brief Wrapper function for allocating a portion of an untyped into an object.
112    @param data cookie for the underlying allocator.
113    @param dest path to an empty cslot to place the cap to the allocated object.
114    @param type the seL4 object type to allocate (as passed to Untyped_Retype).
115    @param size_bits the size of the object to allocate (as passed to Untyped_Retype).
116    @param paddr The desired physical address that this object should start at.
117    @param cookie pointer to a location to store the cookie representing this allocation.
118    @return 0 on success.
119*/
120static int
121serial_utspace_alloc_at_fn(void *data, const cspacepath_t *dest, seL4_Word type,
122                           seL4_Word size_bits, uintptr_t paddr, seL4_Word *cookie)
123{
124    /* since DEFAULT_SERIAL_PADDR memory has already been allocated, we do not
125       allocate it again */
126    if ((uintptr_t) serial_paddr == paddr) {
127        return vka_cnode_copy(dest, &procServ.serial_frame_cap_path, seL4_AllRights);
128    }
129
130    assert(procServ.vka.utspace_alloc_at);
131    return procServ.vka.utspace_alloc_at(data, dest, type, size_bits, paddr, cookie);
132}
133
134/*! @brief Wrapper function for getting the cap to the physical frame of
135           memory and putting it at specified location.
136    @param data cookie for the underlying implementation.
137    @param paddr aligned physical address.
138    @param size_bits of the region in bits.
139    @param path The path to where to put this cap.
140*/
141seL4_Error
142serial_get_frame_cap(void *data, void *paddr, int size_bits, cspacepath_t *path)
143{
144    /* since DEFAULT_SERIAL_PADDR has already been initialised, we do not
145       initialise it again */
146    if ((void *) serial_paddr == paddr) {
147        return vka_cnode_copy(path, &procServ.serial_frame_cap_path, seL4_AllRights);
148    }
149
150    return procServ.original_simple_get_frame_cap(data, paddr, size_bits, path);
151}
152#endif /* CONFIG_ARCH_ARM */
153
154/*! @brief Initialise the process server.
155    @param info The BootInfo struct passed in from the kernel.
156    @param s The process server global state to initialise.
157 */
158void
159initialise(seL4_BootInfo *info, struct procserv_state *s)
160{
161    initialise_allocator(info, s);
162    int error;
163    (void) error;
164
165    vka_t serial_vka = s->vka;
166
167#ifdef CONFIG_ARCH_ARM
168    /* create wrapper functions for initialising DEFAULT_SERIAL_PADDR
169       since it has already been set up */
170    vka_object_t result;
171    serial_paddr = (void *) DEFAULT_SERIAL_PADDR;
172    error = vka_alloc_frame_at(&s->vka, PAGE_BITS_4K, DEFAULT_SERIAL_PADDR, &result);
173    vka_cspace_make_path(&s->vka, result.cptr, &s->serial_frame_cap_path);
174
175    serial_vka.utspace_alloc_at = serial_utspace_alloc_at_fn;
176
177    s->original_simple_get_frame_cap = s->simpleEnv.frame_cap;
178
179    s->simpleEnv.frame_cap = serial_get_frame_cap;
180#endif /* CONFIG_ARCH_ARM */
181
182    /* Enable printf and then print welcome message. */
183    platsupport_serial_setup_simple(&s->vspace, &s->simpleEnv, &serial_vka);
184    initialise_welcome_message(info);
185
186    /* Set up process server global objects. */
187    dprintf("Allocating main process server endpoint...\n");
188    error = vka_alloc_endpoint(&s->vka, &s->endpoint);
189    assert(!error);
190
191    /* Initialise recieving cslot. */
192    dprintf("Setting recv cslot...\n");
193    error = vka_cspace_alloc_path(&s->vka, &s->IPCCapRecv);
194    assert(!error);
195    rpc_setup_recv_cspace(s->IPCCapRecv.root, s->IPCCapRecv.capPtr, s->IPCCapRecv.capDepth);
196
197    /* Initialise miscellaneous states. */
198    dprintf("Initialising process server modules...\n");
199    initialise_modules(s);
200    chash_init(&s->irqHandlerList, PROCSERV_IRQ_HANDLER_HASHTABLE_SIZE);
201    s->unblockClientFaultPID = PID_NULL;
202
203    /* Procserv initialised OK. */
204    dprintf("PROCSERV initialised.\n");
205    dprintf("==========================================\n\n");
206}
207
208cspacepath_t
209procserv_mint_badge(int badge)
210{
211    cspacepath_t path, pathSrc;
212    memset(&path, 0, sizeof(cspacepath_t));
213    int error = vka_cspace_alloc_path(&procServ.vka, &path);
214    if (error) {
215        ROS_WARNING("procserv_mint_badge could not allocate a cslot.");
216        return path;
217    }
218    vka_cspace_make_path(&procServ.vka, procServ.endpoint.cptr, &pathSrc);
219    error = vka_cnode_mint(
220            &path, &pathSrc, seL4_NoRead,
221            seL4_CapData_Badge_new(badge)
222    );
223    if (error) {
224        ROS_WARNING("procserv_mint_badge could not mint endpoint cap.");
225        vka_cspace_free(&procServ.vka, path.capPtr);
226        memset(&path, 0, sizeof(cspacepath_t));
227        return path;
228    }
229    return path;
230}
231
232int
233procserv_frame_write(seL4_CPtr frame, const char* src, size_t len, size_t offset)
234{
235    if (offset + len > REFOS_PAGE_SIZE) {
236        ROS_ERROR("procserv_frame_write invalid offset and length.");
237        return EINVALIDPARAM;
238    }
239    char* addr = (char*) vspace_map_pages(&procServ.vspace, &frame, NULL, seL4_AllRights, 1,
240                                          seL4_PageBits, true);
241    if (!addr) {
242        ROS_ERROR ("procserv_frame_write couldn't map frame.");
243        return ENOMEM;
244    }
245    memcpy((void*)(addr + offset), (void*) src, len);
246    procserv_flush(&frame, 1);
247    vspace_unmap_pages(&procServ.vspace, addr, 1, seL4_PageBits, VSPACE_PRESERVE);
248    return ESUCCESS;
249}
250
251int
252procserv_frame_read(seL4_CPtr frame, const char* dst, size_t len, size_t offset)
253{
254    if (offset + len > REFOS_PAGE_SIZE) {
255        ROS_ERROR("procserv_frame_read invalid offset and length.");
256        return EINVALIDPARAM;
257    }
258
259    char* addr = (char*) vspace_map_pages(&procServ.vspace, &frame, NULL, seL4_AllRights, 1,
260                                          seL4_PageBits, true);
261    if (!addr) {
262        ROS_ERROR ("procserv_frame_read couldn't map frame.");
263        return ENOMEM;
264    }
265    procserv_flush(&frame, 1);
266    memcpy((void*) dst, (void*)(addr + offset), len);
267    vspace_unmap_pages(&procServ.vspace, addr, 1, seL4_PageBits, VSPACE_PRESERVE);
268    return ESUCCESS;
269}
270
271/*! @brief The free EP cap callback function, used by the nameserv implementation helper library.
272    @param cap The endpoint cap to free.
273 */
274static void
275procserv_nameserv_callback_free_cap(seL4_CPtr cap)
276{
277    if (!cap) {
278        ROS_WARNING("procserv_nameserv_callback_free_cap called on NULL cap!");
279        return;
280    }
281    cspacepath_t path;
282    vka_cspace_make_path(&procServ.vka, cap, &path);
283
284    /* Name server service does not revoke the given anon caps, clients get to keep them. */
285    vka_cnode_delete(&path);
286    vka_cspace_free(&procServ.vka, cap);
287}
288
289cspacepath_t
290procserv_find_device(void *paddr, int size)
291{
292    cspacepath_t path;
293    path.capPtr = 0;
294
295    /* Figure out device size in bits. */
296    int sizeBits = -1;
297    for (int i = 0; i < 32; i++) {
298        if ((1 << i) == size) {
299            sizeBits = i;
300            break;
301        }
302    }
303    if (sizeBits == -1) {
304        ROS_ERROR("procserv_find_device invalid size 0x%x!\n", size);
305        return path;
306    }
307
308    /* Allocate a cslot. */
309    int error = vka_cspace_alloc_path(&procServ.vka, &path);
310    if (error) {
311        ROS_ERROR("procserv_find_device failed to allocate cslot.");
312        path.capPtr = 0;
313        return path;
314    }
315
316    /* Perform the device lookup. */
317    error = simple_get_frame_cap(&procServ.simpleEnv, paddr, sizeBits, &path);
318    if (error) {
319        vka_cspace_free(&procServ.vka, path.capPtr);
320        path.capPtr = 0;
321        return path;
322    }
323
324    assert(path.capPtr);
325    return path;
326}
327
328void
329procserv_flush(seL4_CPtr *frame, int nFrames)
330{
331#ifdef CONFIG_ARCH_ARM
332    if (!frame) {
333        return;
334    }
335    for (int i = 0; i < nFrames; i++) {
336        if (!frame[i]) {
337            continue;
338        }
339        seL4_ARM_Page_Unify_Instruction(frame[i], 0, REFOS_PAGE_SIZE);
340    }
341#endif /* CONFIG_ARCH_ARM */
342}
343
344seL4_CPtr
345procserv_get_irq_handler(int irq)
346{
347    /* Check whether we have already made a handler for this IRQ. */
348    seL4_CPtr existingHandler = (seL4_CPtr) chash_get(&procServ.irqHandlerList, irq);
349    if (existingHandler) {
350        return existingHandler;
351    }
352
353    /* Allocate a new cslot to store the IRQ handler. */
354    cspacepath_t handler;
355    int error = vka_cspace_alloc_path(&procServ.vka, &handler);
356    if (error) {
357        ROS_WARNING("procserv_get_irq_handler could not allocate IRQ handler cslot.");
358        return (seL4_CPtr) 0;
359    }
360
361    /* Get the handler. */
362    error = seL4_IRQControl_Get(seL4_CapIRQControl, irq,
363            handler.root, handler.capPtr, handler.capDepth);
364    if (error) {
365        ROS_WARNING("procserv_get_irq_handler could not get IRQ handler for irq %u.\n", irq);
366        vka_cspace_free(&procServ.vka, handler.capPtr);
367        return (seL4_CPtr) 0;
368    }
369
370    chash_set(&procServ.irqHandlerList, irq, (chash_item_t) handler.capPtr);
371
372    return handler.capPtr;
373}
374