1#include <bootstage.h>
2#include <bootstage_condition.h>
3#include <console/printf.h>
4#include <global.h>
5#include <log.h>
6#include <rw_once.h>
7#include <string.h>
8
9/* The standard bootstage.h API and the fancy condition stuff are both here */
10
11static LOG_HANDLE_DECLARE_DEFAULT(bootstage);
12const char *bootstage_str[BOOTSTAGE_COUNT] = {
13 [BOOTSTAGE_NONE] = "None",
14 [BOOTSTAGE_EARLY_FB] = "Early - Framebuffer",
15 [BOOTSTAGE_EARLY_ALLOCATORS] = "Early - Allocators",
16 [BOOTSTAGE_EARLY_DEVICES] = "Early - Devices",
17 [BOOTSTAGE_MID_MP] = "Mid - SMP",
18 [BOOTSTAGE_MID_TOPOLOGY] = "Mid - Topology",
19 [BOOTSTAGE_MID_ALLOCATORS] = "Mid - Allocators",
20 [BOOTSTAGE_LATE] = "Late",
21 [BOOTSTAGE_COMPLETE] = "Complete",
22};
23
24enum bootstage bootstage_get() {
25 return global.current_bootstage;
26}
27
28static void bootstage_write_jump(struct bootstage_condition_entry *ent) {
29 vaddr_t vjump = (vaddr_t) ent->code;
30 vaddr_t vdest = (vaddr_t) ent->target;
31 vaddr_t rel32ptr = vjump + 1; /* A byte over */
32 uint32_t rel32 = vdest - vjump - 5; /* For the jump insn */
33
34 /* Write 0xE9 to the jump place, then the rel32 dest one byte over LE */
35 WRITE_ONCE(*(uint8_t *) ent->code, (uint8_t) 0xE9);
36 WRITE_ONCE(*(uint32_t *) rel32ptr, (uint32_t) rel32);
37}
38
39static void bootstage_write_nop(struct bootstage_condition_entry *ent) {
40 uint8_t *vjump = ent->code;
41
42 /* ingenuity */
43 const uint8_t nop5[5] = {0x0F, 0x1F, 0x44, 0x00, 0x00};
44 memcpy(vjump, nop5, 5);
45}
46
47static bool bootstage_taken(struct bootstage_condition_entry *ent,
48 enum bootstage bs) {
49 enum bootstage ebs = ent->stage;
50 enum bootstage_condition cond = ent->cond;
51 switch (cond) {
52 case BOOTSTAGE_CONDITION_EQ: return bs == ebs;
53 case BOOTSTAGE_CONDITION_LE: return bs <= ebs;
54 case BOOTSTAGE_CONDITION_LT: return bs < ebs;
55 case BOOTSTAGE_CONDITION_GE: return bs >= ebs;
56 case BOOTSTAGE_CONDITION_GT: return bs > ebs;
57 default: kassert_unreachable("invalid bootstage_condition_entry condition");
58 }
59}
60
61static void bootstage_patch_all(enum bootstage bs) {
62 struct bootstage_condition_entry *ent;
63 linker_section_for_each_object(ent, bootstage_condition_entries) {
64 if (bootstage_taken(ent, bs)) {
65 bootstage_write_jump(ent);
66 } else {
67 bootstage_write_nop(ent);
68 }
69 }
70}
71
72void bootstage_advance(enum bootstage new) {
73 /* disable interrupts to be safe */
74 bool ints = are_interrupts_enabled();
75 disable_interrupts();
76
77 global.current_bootstage = new;
78 atomic_thread_fence(memory_order_seq_cst);
79
80 /* EARLY_FB leaves the kernel RO, we don't have patchability */
81 if (new > BOOTSTAGE_EARLY_FB)
82 bootstage_patch_all(bs: new);
83
84 if (ints)
85 enable_interrupts();
86
87 log_info_global(LOG_HANDLE(bootstage), "Reached bootstage \'%s\'",
88 bootstage_str[new]);
89}
90