Added tracing for core start-up.
[barrelfish] / kernel / arch / x86_64 / start_aps.c
1
2 /**
3  * \file
4  * \brief Start the application processors
5  *
6  *  This file sends all needed IPIs to the other cores to start them.
7  */
8
9 /*
10  * Copyright (c) 2007, 2008, 2010, ETH Zurich.
11  * All rights reserved.
12  *
13  * This file is distributed under the terms in the attached LICENSE file.
14  * If you do not find this file, copies can be found by writing to:
15  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
16  */
17
18 #include <kernel.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <arch/x86/apic.h>
22 #include <arch/x86/start_aps.h>
23 #include <x86.h>
24 #include <arch/x86/cmos.h>
25 #include <init.h>
26 #include <arch/x86/kputchar.h>
27 #include "xapic_dev.h"
28 #include <target/x86_64/offsets_target.h>
29 #include <trace/trace.h>
30
31 /**
32  * start_ap and start_ap_end mark the start end the end point of the assembler
33  * startup code to be copied
34  */
35 extern uint64_t x86_64_start_ap;
36 extern uint64_t x86_64_start_ap_end;
37 extern uint64_t x86_64_init_ap_absolute_entry;
38 extern uint64_t x86_64_init_ap_wait;
39 extern uint64_t x86_64_init_ap_lock;
40 extern uint64_t x86_64_start;
41 extern uint64_t x86_64_init_ap_global;
42
43 /**
44  * \brief Boot a app core of x86_64 type
45  *
46  * The processors are started by a sequency of INIT and STARTUP IPIs
47  * which are sent by this function.
48  * CMOS writes to the shutdown status byte are used to execute
49  * different memory locations.
50  *
51  * \param core_id   APIC ID of the core to try booting
52  * \param entry     Entry address for new kernel in the destination
53  *                  architecture's lvaddr_t given in genvaddr_t
54  *
55  * \returns Zero on successful boot, non-zero (error code) on failure
56  */
57 int start_aps_x86_64_start(uint8_t core_id, genvaddr_t entry)
58 {
59     trace_event(TRACE_SUBSYS_KERNEL,
60                 TRACE_EVENT_KERNEL_START_CORE_REQUEST, core_id);
61
62     /* Copy the startup code to the real-mode address */
63     uint8_t *real_dest = (uint8_t *) local_phys_to_mem(X86_64_REAL_MODE_LINEAR_OFFSET);
64     uint8_t *real_src = (uint8_t *) &x86_64_start_ap;
65     uint8_t *real_end = (uint8_t *) &x86_64_start_ap_end;
66     memcpy(real_dest, real_src, real_end - real_src);
67
68     /* Pointer to the entry point called from init_ap.S */
69     volatile uint64_t *absolute_entry_ptr = (volatile uint64_t *)
70                                             local_phys_to_mem(
71                                                     (lpaddr_t) &x86_64_init_ap_absolute_entry -
72                                                     ((lpaddr_t) &x86_64_start_ap) +
73                                                     X86_64_REAL_MODE_LINEAR_OFFSET);
74     //copy the address of the function start (in boot.S) to the long-mode
75     //assembler code to be able to perform an absolute jump
76     *absolute_entry_ptr = entry;
77
78     /* pointer to the pseudo-lock used to detect boot up of new core */
79     volatile uint32_t *ap_wait = (volatile uint32_t *)
80                                  local_phys_to_mem((lpaddr_t) &x86_64_init_ap_wait -
81                                          ((lpaddr_t) &x86_64_start_ap) +
82                                          X86_64_REAL_MODE_LINEAR_OFFSET);
83
84     /* Pointer to the lock variable in the realmode code */
85     volatile uint8_t *ap_lock = (volatile uint8_t *)
86                                 local_phys_to_mem((lpaddr_t) &x86_64_init_ap_lock -
87                                         ((lpaddr_t) &x86_64_start_ap) +
88                                         X86_64_REAL_MODE_LINEAR_OFFSET);
89
90     /* pointer to the shared global variable amongst all kernels */
91     volatile uint64_t *ap_global = (volatile uint64_t *)
92                                    local_phys_to_mem((lpaddr_t) &x86_64_init_ap_global -
93                                            ((lpaddr_t) &x86_64_start_ap) +
94                                            X86_64_REAL_MODE_LINEAR_OFFSET);
95     *ap_global = (uint64_t)mem_to_local_phys((lvaddr_t)global);
96
97     lvaddr_t *init_vector;
98     init_vector = (lvaddr_t *)local_phys_to_mem(CMOS_RAM_BIOS_WARM_START_INIT_VECTOR);
99
100     *ap_wait = AP_STARTING_UP;
101
102     if (CPU_IS_M5_SIMULATOR) {
103         printk(LOG_WARN, "Warning: skipping shutdown/init of APs on M5\n");
104     } else {
105         // set shutdown status to WARM_SHUTDOWN and set start-vector
106         cmos_write( CMOS_RAM_SHUTDOWN_ADDR, CMOS_RAM_WARM_SHUTDOWN);
107         *init_vector = X86_64_REAL_MODE_ADDR_TO_REAL_MODE_VECTOR(X86_64_REAL_MODE_SEGMENT,
108                        X86_64_REAL_MODE_OFFSET);
109
110         //INIT 1 assert
111         apic_send_init_assert(core_id, xapic_none);
112
113         //set shutdown status to WARM_SHUTDOWN and set start-vector
114         cmos_write( CMOS_RAM_SHUTDOWN_ADDR, CMOS_RAM_WARM_SHUTDOWN);
115         *init_vector = X86_64_REAL_MODE_ADDR_TO_REAL_MODE_VECTOR(X86_64_REAL_MODE_SEGMENT,
116                        X86_64_REAL_MODE_OFFSET);
117
118         //INIT 2 de-assert
119         apic_send_init_deassert();
120     }
121
122     //SIPI1
123     apic_send_start_up(core_id, xapic_none,
124                        X86_64_REAL_MODE_SEGMENT_TO_REAL_MODE_PAGE(X86_64_REAL_MODE_SEGMENT));
125
126     //SIPI2
127     apic_send_start_up(core_id, xapic_none,
128                        X86_64_REAL_MODE_SEGMENT_TO_REAL_MODE_PAGE(X86_64_REAL_MODE_SEGMENT));
129
130     trace_event(TRACE_SUBSYS_KERNEL,
131                 TRACE_EVENT_KERNEL_CORE_START_IPI_SENT, core_id);
132
133     //give the new core a bit time to start-up and set the lock
134     for (uint64_t i = 0; i < STARTUP_TIMEOUT; i++) {
135         if (*ap_lock != 0) {
136             break;
137         }
138     }
139
140     //if the lock is set, the core has been started, otherwise assume, that
141     //a core with this APIC ID doesn't exist.
142     if (*ap_lock != 0) {
143         while (*ap_wait != AP_STARTED);
144         trace_event(TRACE_SUBSYS_KERNEL,
145                     TRACE_EVENT_KERNEL_CORE_IS_UP, core_id);
146         *ap_lock = 0;
147         debug(SUBSYS_STARTUP, "booted CPU%hhu\n", core_id);
148         return 0;
149     }
150     return -1;
151 }
152