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