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