Patches b03c1b through 3f00b5
[barrelfish] / usr / drivers / xeon_phi / boot.c
1 /**
2  * \file
3  * \brief Boot module for the Xeon Phi
4  *
5  * Loads the co processor OS onto the card and boots it
6  */
7
8 /*
9  * Copyright (c) 2014 ETH Zurich.
10  * All rights reserved.
11  *
12  * This file is distributed under the terms in the attached LICENSE file.
13  * If you do not find this file, copies can be found by writing to:
14  * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
15  */
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <barrelfish/barrelfish.h>
20 #include <spawndomain/spawndomain.h>
21 #include <elf/elf.h>
22
23 #include <dev/xeon_phi/xeon_phi_boot_dev.h>
24 #include <dev/xeon_phi/xeon_phi_apic_dev.h>
25
26 struct bootinfo *bi = NULL;
27
28 #include "xeon_phi.h"
29 #include "messaging.h"
30 #include "sleep.h"
31
32 #define BOOT_TIMEOUT 3000
33 #define BOOT_COUNTER 0xFFFFF
34
35 /*
36  * TODO: Verify these values if they are really needed
37  */
38 #define MEMORY_RESERVE_PERCENT 50
39 #define UOS_RESERVE_SIZE_MIN    ((128) * 1024 * 1024)
40 #define UOS_RESERVE_SIZE_MAX    (((4) * 1024 * 1024 * 1024ULL) - ((4) * 1024))
41
42 /*
43  * Helper macros
44  */
45 #define MAX(a, b)   ( ((a) > (b)) ? (a) : (b) )
46 #define MIN(a, b)   ( ((a) < (b)) ? (a) : (b) )
47 #define ALIGN(x) ((x + BASE_PAGE_SIZE-1) & ~(BASE_PAGE_SIZE-1))
48
49 static xeon_phi_boot_t boot_registers;
50 static xeon_phi_apic_t apic_registers;
51
52 /**
53  * \brief   get the load offset to where to place the bootloader
54  *
55  * The bootstrap on the card will write the offset into the SBOX_SCRATCH2
56  * register once the bootstrap is finished
57  */
58 static inline lvaddr_t get_load_offset(struct xeon_phi *phi)
59 {
60     return ((lvaddr_t) xeon_phi_boot_download_offset_rdf(&boot_registers)) << 12;
61 }
62
63 static uint64_t get_adapter_memsize(void)
64 {
65     xeon_phi_boot_meminfo_t meminfo = xeon_phi_boot_meminfo_rd(&boot_registers);
66
67     uint64_t memsize = xeon_phi_boot_meminfo_size_kb_extract(meminfo);
68     memsize *= 1024;
69
70     switch (xeon_phi_boot_meminfo_usage_extract(meminfo)) {
71         case xeon_phi_boot_mem_all:
72             return memsize;
73         case xeon_phi_boot_mem_half:
74             return (memsize / 2);
75         case xeon_phi_boot_mem_third:
76             return (memsize / 3);
77             break;
78         case xeon_phi_boot_mem_fourth:
79             return (memsize / 4);
80         default:
81             return memsize;
82     }
83 }
84
85 /**
86  * \brief Loads the bootloader image onto the card
87  *
88  * \param phi           the xeon phi card information
89  * \param xloader_img   name of the bootloader image
90  * \param ret_imgsize   returned image size
91  *
92  * Note: it is important that the bootloader just uses statically allocated
93  *       memory and does not exceed its image size with additional memory.
94  *       Otherwise the CMD line or the multiboot image will be overwritten.
95  */
96 static errval_t load_bootloader(struct xeon_phi *phi,
97                                 char *xloader_img)
98 {
99     errval_t err;
100     /*
101      * find the boot loader image in the host multiboot
102      */
103     struct mem_region *module = multiboot_find_module(bi, xloader_img);
104     if (module == NULL) {
105         return SPAWN_ERR_FIND_MODULE;
106     }
107
108     lvaddr_t binary = 0;
109     size_t imgsize = 0;
110
111     err = spawn_map_module(module, &imgsize, &binary, NULL);
112     if (err_is_fail(err)) {
113         return err_push(err, SPAWN_ERR_ELF_MAP);
114     }
115
116     imgsize = module->mrmod_size;
117
118     /*
119      * get the load offset: we do not want to write into the boot loade
120      */
121     lvaddr_t loadoffset = get_load_offset(phi);
122
123     get_adapter_memsize();
124
125     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
126
127     XBOOT_DEBUG("Loading xloader onto card... offset = 0x%lx\n", loadoffset);
128
129     memcpy((void *) (phi->apt.vbase + loadoffset), (void *) binary, imgsize);
130
131     phi->os_offset = loadoffset;
132     phi->os_size = imgsize;
133
134     return SYS_ERR_OK;
135 }
136
137 /**
138  *
139  */
140 static errval_t load_multiboot_image(struct xeon_phi *phi,
141                                      char *multiboot_img,
142                                      lvaddr_t load_offset)
143 {
144     errval_t err;
145
146     assert(phi->os_offset != 0);
147     /*
148      * find the boot loader image in the host multiboot
149      */
150     struct mem_region *module = multiboot_find_module(bi, multiboot_img);
151     if (module == NULL) {
152         return SPAWN_ERR_FIND_MODULE;
153     }
154
155     lvaddr_t image = 0;
156     size_t imgsize = 0;
157
158     err = spawn_map_module(module, &imgsize, &image, NULL);
159     if (err_is_fail(err)) {
160         return err_push(err, SPAWN_ERR_ELF_MAP);
161     }
162
163     imgsize = module->mrmod_size;
164
165     XBOOT_DEBUG("loading multiboot image onto card... offset = 0x%lx\n",
166                 load_offset);
167
168     memcpy((void *) (phi->apt.vbase + load_offset), (void *) image, imgsize);
169
170     /*
171      * we are using the Linux style way in booting. The following will update
172      * the corresponding fields in struct boot_param of the header.
173      */
174     uint32_t *ramfs_addr_ptr = (uint32_t *) (phi->apt.vbase + phi->os_offset + 0x218);
175     *ramfs_addr_ptr = (uint32_t)load_offset;
176     ramfs_addr_ptr = (uint32_t *) (phi->apt.vbase + phi->os_offset + 0x21c);
177     *ramfs_addr_ptr = (uint32_t)imgsize;
178
179     return SYS_ERR_OK;
180 }
181
182 /**
183  * \brief   generates the cmdline supplied to the card kernel
184  *
185  * \param   phi         the card information structure
186  * \param   load_offset offset where to load the cmdline
187  * \param   ret_size    size of the cmdline in bytes
188  */
189 static errval_t load_cmdline(struct xeon_phi *phi,
190                              lvaddr_t load_offset)
191 {
192     uint32_t cmdlen = 0;
193
194     XBOOT_DEBUG("copying cmdline onto card, offset = 0x%lx\n", load_offset);
195
196     void *buf = (void *) (phi->apt.vbase + load_offset);
197
198     if (phi->msg) {
199         cmdlen += sprintf(buf + cmdlen,
200                           "msg_base=%lx, msg_size=%lx",
201                           phi->msg->base,
202                           phi->msg->size);
203     }
204
205     if (phi->cmdline) {
206         cmdlen += sprintf(buf + cmdlen, "%s", phi->cmdline);
207     }
208
209     cmdlen += sprintf(buf + cmdlen, "card_id=%i", phi->id);
210
211     /*
212      * id
213      */
214     /*
215      * TODO: Add multihop / communication information here..
216      */
217
218
219     phi->cmdline = buf;
220     phi->cmdlen = cmdlen;
221
222     uint32_t *cmdline_ptr = (uint32_t *) (phi->apt.vbase + phi->os_offset + 0x228);
223     *cmdline_ptr = (uint32_t)((uint64_t)phi->cmdline);
224
225     return SYS_ERR_OK;
226 }
227
228 static errval_t bootstrap_notify(struct xeon_phi *phi)
229 {
230     // set the bootimage size to tell the bootloader
231     xeon_phi_boot_os_size_rawwr(&boot_registers, phi->os_size);
232
233     uint64_t memsize = get_adapter_memsize();
234
235     uint64_t reserved = (memsize * MEMORY_RESERVE_PERCENT / 100);
236
237     // Keep in mind maximum uos reserve size is uint32_t, so we never overflow
238     reserved = MIN(reserved, UOS_RESERVE_SIZE_MAX);
239     reserved = MAX(reserved, UOS_RESERVE_SIZE_MIN);
240
241     // Always align uos reserve size to a page
242     reserved = (reserved & ~(BASE_PAGE_SIZE - 1));
243
244     xeon_phi_boot_res_size_rawwr(&boot_registers, (uint32_t) reserved);
245
246     // sending the bootstrap interrupt
247     xeon_phi_apic_icr_lo_t icr_lo = xeon_phi_apic_icr_lo_default;
248     icr_lo = xeon_phi_apic_icr_lo_vector_insert(icr_lo, xeon_phi_apic_vec_bsp);
249     icr_lo = xeon_phi_apic_icr_lo_boot_notify_insert(icr_lo, 0x1);
250
251     assert(icr_lo == (229 | (1 << 13)));
252
253     xeon_phi_apic_icr_hi_wr(&apic_registers, xeon_phi_apic_bootstrap, phi->apicid);
254
255     xeon_phi_apic_icr_lo_wr(&apic_registers, xeon_phi_apic_bootstrap, icr_lo);
256
257     return SYS_ERR_OK;
258 }
259
260 /**
261  * \brief boots the card with the given loader and multiboot image
262  *
263  * \param phi           pointer to the card information
264  * \param xloader_img   pointer to the card bootloader image
265  * \param multiboot_img pointer to the card multiboot image
266  */
267 errval_t xeon_phi_boot(struct xeon_phi *phi,
268                        char *xloader_img,
269                        char *multiboot_img)
270 {
271     errval_t err;
272     lvaddr_t offset;
273
274     if (bi == NULL) {
275         return SYS_ERR_ILLEGAL_INVOCATION;
276     }
277
278     xeon_phi_boot_initialize(&boot_registers,
279                              XEON_PHI_MMIO_TO_SBOX(phi),
280                              XEON_PHI_MMIO_TO_DBOX(phi));
281     xeon_phi_apic_initialize(&apic_registers, XEON_PHI_MMIO_TO_SBOX(phi));
282
283     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
284
285     // load the coprocessor OS
286     err = load_bootloader(phi, xloader_img);
287     if (err_is_fail(err)) {
288         USER_PANIC_ERR(err, "Could not load bootloader image");
289     }
290
291     // round to next page
292     offset = ALIGN(phi->os_offset + phi->os_size);
293
294     err = messaging_init(phi);
295     if (err_is_fail(err)) {
296         USER_PANIC_ERR(err, "Could not initialize messagin");
297     }
298
299     // load cmdline
300     err = load_cmdline(phi, offset);
301     if (err_is_fail(err)) {
302         USER_PANIC_ERR(err, "Could not load multiboot image");
303     }
304
305     // round to next page
306     offset = ALIGN(offset+phi->cmdlen);
307
308     // load multiboot image
309     err = load_multiboot_image(phi, multiboot_img, offset);
310     if (err_is_fail(err)) {
311         USER_PANIC_ERR(err, "Could not load multiboot image");
312     }
313
314     xeon_phi_boot_download_status_wrf(&boot_registers, 0x0);
315
316     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
317
318     xeon_phi_serial_init(phi);
319
320     // notify the bootstrap
321     bootstrap_notify(phi);
322
323     xeon_phi_boot_postcode_t postcode;
324     xeon_phi_boot_postcodes_t pc, pc_prev = 0;
325     uint32_t counter = BOOT_COUNTER;
326     while (--counter) {
327         postcode = xeon_phi_boot_postcode_rd(&boot_registers);
328         pc = xeon_phi_boot_postcode_code_extract(postcode);
329         if (pc_prev != pc) {
330             debug_printf("Xeon Phi Booting: %s\n",
331                          xeon_phi_boot_postcodes_describe(pc));
332         }
333         if (postcode == xeon_phi_boot_postcode_done) {
334             break;
335         }
336         pc_prev = pc;
337     }
338
339     XBOOT_DEBUG("Bootstrap has finished execution. Waiting for Firmware...\n");
340
341     uint32_t time = 0, time_steps = 0;
342     while (time < BOOT_TIMEOUT) {
343         /* read all the pending messages */
344         xeon_phi_serial_handle_recv();
345         milli_sleep(100);
346         if (xeon_phi_boot_download_status_rdf(&boot_registers)) {
347             XBOOT_DEBUG("Firmware signaled with ready bit. \n");
348             break;
349         }
350         if (time % 50) {
351             debug_printf("Xeon Phi Booting: Waiting for ready signal %u\n",
352                          time_steps);
353             time_steps += 5;
354         }
355     }
356
357     if (!xeon_phi_boot_download_status_rdf(&boot_registers)) {
358         USER_PANIC("Firmware not responding with ready bit");
359         // TODO return error code
360     }
361
362     return SYS_ERR_OK;
363 }