1// See LICENSE for license details.
2
3#include "bbl.h"
4#include "mtrap.h"
5#include "atomic.h"
6#include "vm.h"
7#include "bits.h"
8#include "config.h"
9#include "fdt.h"
10#include <string.h>
11
12extern char _payload_start, _payload_end; /* internal payload */
13static const void* entry_point;
14long disabled_hart_mask;
15
16static uintptr_t dtb_output()
17{
18  /*
19   * Place DTB after the payload, either the internal payload or a
20   * preloaded external payload specified in device-tree, if present.
21   *
22   * Note: linux kernel calls __va(dtb) to get the device-tree virtual
23   * address. The kernel's virtual mapping begins at its load address,
24   * thus mandating device-tree is in physical memory after the kernel.
25   */
26  uintptr_t end = kernel_end ? (uintptr_t)kernel_end : (uintptr_t)&_payload_end;
27  return (end + MEGAPAGE_SIZE - 1) / MEGAPAGE_SIZE * MEGAPAGE_SIZE;
28}
29
30static void filter_dtb(uintptr_t source)
31{
32  uintptr_t dest = dtb_output();
33  uint32_t size = fdt_size(source);
34  memcpy((void*)dest, (void*)source, size);
35
36  // Remove information from the chained FDT
37  filter_harts(dest, &disabled_hart_mask);
38  filter_plic(dest);
39  filter_compat(dest, "riscv,clint0");
40  filter_compat(dest, "riscv,debug-013");
41}
42
43static void protect_memory(void)
44{
45  // Check to see if up to four PMP registers are implemented.
46  // Ignore the illegal-instruction trap if PMPs aren't supported.
47  uintptr_t a0 = 0, a1 = 0, a2 = 0, a3 = 0, tmp, cfg;
48  asm volatile ("la %[tmp], 1f\n\t"
49                "csrrw %[tmp], mtvec, %[tmp]\n\t"
50                "csrw pmpaddr0, %[m1]\n\t"
51                "csrr %[a0], pmpaddr0\n\t"
52                "csrw pmpaddr1, %[m1]\n\t"
53                "csrr %[a1], pmpaddr1\n\t"
54                "csrw pmpaddr2, %[m1]\n\t"
55                "csrr %[a2], pmpaddr2\n\t"
56                "csrw pmpaddr3, %[m1]\n\t"
57                "csrr %[a3], pmpaddr3\n\t"
58                ".align 2\n\t"
59                "1: csrw mtvec, %[tmp]"
60                : [tmp] "=&r" (tmp),
61                  [a0] "+r" (a0), [a1] "+r" (a1), [a2] "+r" (a2), [a3] "+r" (a3)
62                : [m1] "r" (-1UL));
63
64  // We need at least four PMP registers to protect M-mode from S-mode.
65  if (!(a0 & a1 & a2 & a3))
66    return setup_pmp();
67
68  // Prevent S-mode access to our part of memory.
69  extern char _ftext, _end;
70  a0 = (uintptr_t)&_ftext >> PMP_SHIFT;
71  a1 = (uintptr_t)&_end >> PMP_SHIFT;
72  cfg = PMP_TOR << 8;
73  // Give S-mode free rein of everything else.
74  a2 = -1;
75  cfg |= (PMP_NAPOT | PMP_R | PMP_W | PMP_X) << 16;
76  // No use for PMP 3 just yet.
77  a3 = 0;
78
79  // Plug it all in.
80  asm volatile ("csrw pmpaddr0, %[a0]\n\t"
81                "csrw pmpaddr1, %[a1]\n\t"
82                "csrw pmpaddr2, %[a2]\n\t"
83                "csrw pmpaddr3, %[a3]\n\t"
84                "csrw pmpcfg0, %[cfg]"
85                :: [a0] "r" (a0), [a1] "r" (a1), [a2] "r" (a2), [a3] "r" (a3),
86                   [cfg] "r" (cfg));
87}
88
89void boot_other_hart(uintptr_t unused __attribute__((unused)))
90{
91  const void* entry;
92  do {
93    entry = entry_point;
94    mb();
95  } while (!entry);
96
97  long hartid = read_csr(mhartid);
98  if ((1 << hartid) & disabled_hart_mask) {
99    while (1) {
100      __asm__ volatile("wfi");
101#ifdef __riscv_div
102      __asm__ volatile("div x0, x0, x0");
103#endif
104    }
105  }
106
107#ifdef BBL_BOOT_MACHINE
108  enter_machine_mode(entry, hartid, dtb_output());
109#else /* Run bbl in supervisor mode */
110  protect_memory();
111  enter_supervisor_mode(entry, hartid, dtb_output());
112#endif
113}
114
115void boot_loader(uintptr_t dtb)
116{
117  filter_dtb(dtb);
118#ifdef PK_ENABLE_LOGO
119  print_logo();
120#endif
121#ifdef PK_PRINT_DEVICE_TREE
122  fdt_print(dtb_output());
123#endif
124  mb();
125  /* Use optional FDT preloaded external payload if present */
126  entry_point = kernel_start ? kernel_start : &_payload_start;
127  boot_other_hart(0);
128}
129