Bugfixes Xeon Phi Driver startup - removed check for boot info - added initialization...
[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 <vfs/vfs.h>
22 #include <elf/elf.h>
23
24 #include <xeon_phi/xeon_phi.h>
25
26 #include <dev/xeon_phi/xeon_phi_boot_dev.h>
27 #include <dev/xeon_phi/xeon_phi_apic_dev.h>
28
29 struct bootinfo *bi = NULL;
30
31 #include "xeon_phi.h"
32 #include "messaging.h"
33 #include "sleep.h"
34
35 #define BOOT_TIMEOUT 3000
36 #define BOOT_COUNTER 0xFFFFF
37
38 /*
39  * TODO: Verify these values if they are really needed
40  */
41 #define MEMORY_RESERVE_PERCENT 50
42 #define UOS_RESERVE_SIZE_MIN    ((128) * 1024 * 1024)
43 #define UOS_RESERVE_SIZE_MAX    (((4) * 1024 * 1024 * 1024ULL) - ((4) * 1024))
44
45 /*
46  * Helper macros
47  */
48 #define MAX(a, b)   ( ((a) > (b)) ? (a) : (b) )
49 #define MIN(a, b)   ( ((a) < (b)) ? (a) : (b) )
50 #define ALIGN(x) ((x + BASE_PAGE_SIZE-1) & ~(BASE_PAGE_SIZE-1))
51
52 static xeon_phi_boot_t boot_registers;
53 static xeon_phi_apic_t apic_registers;
54
55 /**
56  * \brief   get the load offset to where to place the bootloader
57  *
58  * The bootstrap on the card will write the offset into the SBOX_SCRATCH2
59  * register once the bootstrap is finished
60  */
61 static inline lvaddr_t get_load_offset(struct xeon_phi *phi)
62 {
63     return ((lvaddr_t) xeon_phi_boot_download_offset_rdf(&boot_registers)) << 12;
64 }
65
66 static uint64_t get_adapter_memsize(void)
67 {
68     xeon_phi_boot_meminfo_t meminfo = xeon_phi_boot_meminfo_rd(&boot_registers);
69
70     uint64_t memsize = xeon_phi_boot_meminfo_size_kb_extract(meminfo);
71     memsize *= 1024;
72
73     switch (xeon_phi_boot_meminfo_usage_extract(meminfo)) {
74         case xeon_phi_boot_mem_all:
75             return memsize;
76         case xeon_phi_boot_mem_half:
77             return (memsize / 2);
78         case xeon_phi_boot_mem_third:
79             return (memsize / 3);
80             break;
81         case xeon_phi_boot_mem_fourth:
82             return (memsize / 4);
83         default:
84             return memsize;
85     }
86 }
87
88
89 static errval_t load_module(char *path,
90                             void *buf,
91                             uint64_t *ret_size)
92 {
93     errval_t err;
94
95
96     /* read file into memory */
97     vfs_handle_t fh;
98     err = vfs_open(path, &fh);
99     if (err_is_fail(err)) {
100         return err_push(err, SPAWN_ERR_LOAD);
101     }
102
103     struct vfs_fileinfo info;
104     err = vfs_stat(fh, &info);
105     if (err_is_fail(err)) {
106         vfs_close(fh);
107         return err_push(err, SPAWN_ERR_LOAD);
108     }
109
110     assert(info.type == VFS_FILE);
111
112     uint8_t *image = buf;
113
114     size_t pos = 0, readlen;
115     do {
116         err = vfs_read(fh, &image[pos], info.size - pos, &readlen);
117         if (err_is_fail(err)) {
118             vfs_close(fh);
119             return err_push(err, SPAWN_ERR_LOAD);
120         } else if (readlen == 0) {
121             vfs_close(fh);
122             return SPAWN_ERR_LOAD; // XXX
123         } else {
124             pos += readlen;
125         }
126     } while (err_is_ok(err) && readlen > 0 && pos < info.size);
127
128     err = vfs_close(fh);
129     if (err_is_fail(err)) {
130         DEBUG_ERR(err, "failed to close file %s", path);
131     }
132
133     if (ret_size) {
134         *ret_size = info.size;
135     }
136
137     return SYS_ERR_OK;
138
139 }
140
141 /**
142  * \brief Loads the bootloader image onto the card
143  *
144  * \param phi           the xeon phi card information
145  * \param xloader_img   name of the bootloader image
146  * \param ret_imgsize   returned image size
147  *
148  * Note: it is important that the bootloader just uses statically allocated
149  *       memory and does not exceed its image size with additional memory.
150  *       Otherwise the CMD line or the multiboot image will be overwritten.
151  */
152 static errval_t load_bootloader(struct xeon_phi *phi,
153                                 char *xloader_img)
154 {
155     errval_t err;
156 #if 0
157     /*
158      * find the boot loader image in the host multiboot
159      */
160     struct mem_region *module = multiboot_find_module(bi, xloader_img);
161     if (module == NULL) {
162         return SPAWN_ERR_FIND_MODULE;
163     }
164
165     lvaddr_t binary = 0;
166     size_t imgsize = 0;
167
168     err = spawn_map_module(module, &imgsize, &binary, NULL);
169     if (err_is_fail(err)) {
170         return err_push(err, SPAWN_ERR_ELF_MAP);
171     }
172
173     imgsize = module->mrmod_size;
174
175
176
177     /*
178      * get the load offset: we do not want to write into the boot loade
179      */
180     lvaddr_t loadoffset = get_load_offset(phi);
181
182     get_adapter_memsize();
183
184     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
185
186     XBOOT_DEBUG("Loading xloader onto card... offset = 0x%lx\n", loadoffset);
187
188     memcpy((void *) (phi->apt.vbase + loadoffset), (void *) binary, imgsize);
189
190 #endif
191     lvaddr_t loadoffset = get_load_offset(phi);
192     uint64_t imgsize;
193
194     void *buf = (void *) (phi->apt.vbase + loadoffset);
195
196     err = load_module(xloader_img, buf, &imgsize);
197     if (err_is_fail(err)) {
198         return err;
199     }
200
201     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
202
203     phi->os_offset = loadoffset;
204     phi->os_size = imgsize;
205
206     return SYS_ERR_OK;
207 }
208
209 /**
210  *
211  */
212 static errval_t load_multiboot_image(struct xeon_phi *phi,
213                                      char *multiboot_img,
214                                      lvaddr_t load_offset)
215 {
216     errval_t err;
217
218     assert(phi->os_offset != 0);
219
220 #if 0
221     /*
222      * find the boot loader image in the host multiboot
223      */
224     struct mem_region *module = multiboot_find_module(bi, multiboot_img);
225     if (module == NULL) {
226         return SPAWN_ERR_FIND_MODULE;
227     }
228
229     lvaddr_t image = 0;
230     size_t imgsize = 0;
231
232     err = spawn_map_module(module, &imgsize, &image, NULL);
233     if (err_is_fail(err)) {
234         return err_push(err, SPAWN_ERR_ELF_MAP);
235     }
236
237     imgsize = module->mrmod_size;
238
239     XBOOT_DEBUG("loading multiboot image onto card... offset = 0x%lx\n",
240                 load_offset);
241
242     memcpy((void *) (phi->apt.vbase + load_offset), (void *) image, imgsize);
243 #endif
244
245     uint64_t imgsize;
246
247     void *buf = (void *) (phi->apt.vbase + load_offset);
248
249     err = load_module(multiboot_img, buf, &imgsize);
250     if (err_is_fail(err)) {
251         return err;
252     }
253
254     /*
255      * we are using the Linux style way in booting. The following will update
256      * the corresponding fields in struct boot_param of the header.
257      */
258     struct xeon_phi_boot_params *bp;
259     bp = (struct xeon_phi_boot_params *)(phi->apt.vbase + phi->os_offset);
260     bp->ramdisk_image = (uint32_t)load_offset;
261     bp->ramdisk_size = (uint32_t)imgsize;
262
263     return SYS_ERR_OK;
264 }
265
266 /**
267  * \brief   generates the cmdline supplied to the card kernel
268  *
269  * \param   phi         the card information structure
270  * \param   load_offset offset where to load the cmdline
271  * \param   ret_size    size of the cmdline in bytes
272  */
273 static errval_t load_cmdline(struct xeon_phi *phi,
274                              lvaddr_t load_offset)
275 {
276     uint32_t cmdlen = 0;
277
278     struct xeon_phi_boot_params *bp;
279     bp = (struct xeon_phi_boot_params *)(phi->apt.vbase + phi->os_offset);
280
281     XBOOT_DEBUG("copying cmdline onto card, offset = 0x%lx\n", load_offset);
282
283     void *buf = (void *) (phi->apt.vbase + load_offset);
284
285     if (phi->msg) {
286         cmdlen += sprintf(buf + cmdlen,
287                           "msg_base=%lx, msg_size=%lx",
288                           phi->msg->base,
289                           phi->msg->size);
290     }
291
292     if (phi->cmdline) {
293         cmdlen += sprintf(buf + cmdlen, "%s", phi->cmdline);
294     }
295
296     cmdlen += sprintf(buf + cmdlen, "card_id=%i", phi->id);
297
298     /*
299      * id
300      */
301     /*
302      * TODO: Add multihop / communication information here..
303      */
304
305     printf("cmdline = %x,  %s\n", (uint32_t)load_offset, (char*)buf);
306
307     phi->cmdline = buf;
308     phi->cmdlen = cmdlen;
309
310
311     bp->cmdline_ptr = (uint32_t)(load_offset);
312     bp->cmdline_size = (uint32_t)cmdlen;
313
314     return SYS_ERR_OK;
315 }
316
317 static errval_t bootstrap_notify(struct xeon_phi *phi)
318 {
319     // set the bootimage size to tell the bootloader
320     xeon_phi_boot_os_size_rawwr(&boot_registers, phi->os_size);
321
322     uint64_t memsize = get_adapter_memsize();
323
324     uint64_t reserved = (memsize * MEMORY_RESERVE_PERCENT / 100);
325
326     // Keep in mind maximum uos reserve size is uint32_t, so we never overflow
327     reserved = MIN(reserved, UOS_RESERVE_SIZE_MAX);
328     reserved = MAX(reserved, UOS_RESERVE_SIZE_MIN);
329
330     // Always align uos reserve size to a page
331     reserved = (reserved & ~(BASE_PAGE_SIZE - 1));
332
333     xeon_phi_boot_res_size_rawwr(&boot_registers, (uint32_t) reserved);
334
335     // sending the bootstrap interrupt
336     xeon_phi_apic_icr_lo_t icr_lo = xeon_phi_apic_icr_lo_default;
337     icr_lo = xeon_phi_apic_icr_lo_vector_insert(icr_lo, xeon_phi_apic_vec_bsp);
338     icr_lo = xeon_phi_apic_icr_lo_boot_notify_insert(icr_lo, 0x1);
339
340     assert(icr_lo == (229 | (1 << 13)));
341
342     xeon_phi_apic_icr_hi_wr(&apic_registers, xeon_phi_apic_bootstrap, phi->apicid);
343
344     xeon_phi_apic_icr_lo_wr(&apic_registers, xeon_phi_apic_bootstrap, icr_lo);
345
346     return SYS_ERR_OK;
347 }
348
349 /**
350  * \brief boots the card with the given loader and multiboot image
351  *
352  * \param phi           pointer to the card information
353  * \param xloader_img   pointer to the card bootloader image
354  * \param multiboot_img pointer to the card multiboot image
355  */
356 errval_t xeon_phi_boot(struct xeon_phi *phi,
357                        char *xloader_img,
358                        char *multiboot_img)
359 {
360     errval_t err;
361     lvaddr_t offset;
362
363     xeon_phi_boot_initialize(&boot_registers,
364                              XEON_PHI_MMIO_TO_SBOX(phi),
365                              XEON_PHI_MMIO_TO_DBOX(phi));
366     xeon_phi_apic_initialize(&apic_registers, XEON_PHI_MMIO_TO_SBOX(phi));
367
368     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
369
370     // load the coprocessor OS
371     err = load_bootloader(phi, xloader_img);
372     if (err_is_fail(err)) {
373         USER_PANIC_ERR(err, "Could not load bootloader image");
374     }
375
376     // round to next page
377     offset = ALIGN(phi->os_offset + phi->os_size);
378
379     err = messaging_init(phi, NULL_CAP);
380     if (err_is_fail(err)) {
381         USER_PANIC_ERR(err, "Could not initialize messagin");
382     }
383
384     // load cmdline
385     err = load_cmdline(phi, offset);
386     if (err_is_fail(err)) {
387         USER_PANIC_ERR(err, "Could not load multiboot image");
388     }
389
390     // round to next page
391     offset = ALIGN(offset+phi->cmdlen);
392
393     // load multiboot image
394     err = load_multiboot_image(phi, multiboot_img, offset);
395     if (err_is_fail(err)) {
396         USER_PANIC_ERR(err, "Could not load multiboot image");
397     }
398
399     xeon_phi_boot_download_status_wrf(&boot_registers, 0x0);
400
401     phi->apicid = xeon_phi_boot_download_apicid_rdf(&boot_registers);
402
403     xeon_phi_serial_init(phi);
404
405     // notify the bootstrap
406     bootstrap_notify(phi);
407
408     xeon_phi_boot_postcode_t postcode;
409     xeon_phi_boot_postcodes_t pc, pc_prev = 0;
410     uint32_t counter = BOOT_COUNTER;
411     while (--counter) {
412         postcode = xeon_phi_boot_postcode_rd(&boot_registers);
413         pc = xeon_phi_boot_postcode_code_extract(postcode);
414         if (pc_prev != pc) {
415             debug_printf("Xeon Phi Booting: %s\n",
416                          xeon_phi_boot_postcodes_describe(pc));
417         }
418         if (postcode == xeon_phi_boot_postcode_done) {
419             break;
420         }
421         pc_prev = pc;
422     }
423
424     XBOOT_DEBUG("Bootstrap has finished execution. Waiting for Firmware...\n");
425
426     uint32_t time = 0, time_steps = 0;
427     while (time < BOOT_TIMEOUT) {
428         /* read all the pending messages */
429         xeon_phi_serial_handle_recv();
430         milli_sleep(100);
431         if (xeon_phi_boot_download_status_rdf(&boot_registers)) {
432             XBOOT_DEBUG("Firmware signaled with ready bit. \n");
433             break;
434         }
435         if (time % 50) {
436             debug_printf("Xeon Phi Booting: Waiting for ready signal %u\n",
437                          time_steps);
438             time_steps += 5;
439         }
440     }
441
442     if (!xeon_phi_boot_download_status_rdf(&boot_registers)) {
443         USER_PANIC("Firmware not responding with ready bit");
444         // TODO return error code
445     }
446
447     // we don't need the aperture mapped anymore so unmap it
448     err = xeon_phi_unmap_aperture(phi);
449     if (err_is_fail(err)) {
450         USER_PANIC_ERR(err, "Failed to map aperture range");
451     }
452
453     return SYS_ERR_OK;
454 }