1// See LICENSE for license details.
2
3#include "pk.h"
4#include "mmap.h"
5#include "boot.h"
6#include "elf.h"
7#include "mtrap.h"
8#include "frontend.h"
9#include <stdbool.h>
10
11elf_info current;
12long disabled_hart_mask;
13
14static void handle_option(const char* s)
15{
16  switch (s[1])
17  {
18    case 's': // print cycle count upon termination
19      current.cycle0 = 1;
20      break;
21
22    case 'p': // disable demand paging
23      demand_paging = 0;
24      break;
25
26    default:
27      panic("unrecognized option: `%c'", s[1]);
28      break;
29  }
30}
31
32#define MAX_ARGS 256
33typedef union {
34  uint64_t buf[MAX_ARGS];
35  char* argv[MAX_ARGS];
36} arg_buf;
37
38static size_t parse_args(arg_buf* args)
39{
40  long r = frontend_syscall(SYS_getmainvars, va2pa(args), sizeof(*args), 0, 0, 0, 0, 0);
41  if (r != 0)
42    panic("args must not exceed %d bytes", (int)sizeof(arg_buf));
43
44  kassert(r == 0);
45  uint64_t* pk_argv = &args->buf[1];
46  // pk_argv[0] is the proxy kernel itself.  skip it and any flags.
47  size_t pk_argc = args->buf[0], arg = 1;
48  for ( ; arg < pk_argc && *(char*)(uintptr_t)pk_argv[arg] == '-'; arg++)
49    handle_option((const char*)(uintptr_t)pk_argv[arg]);
50
51  for (size_t i = 0; arg + i < pk_argc; i++)
52    args->argv[i] = (char*)(uintptr_t)pk_argv[arg + i];
53  return pk_argc - arg;
54}
55
56static void init_tf(trapframe_t* tf, long pc, long sp)
57{
58  memset(tf, 0, sizeof(*tf));
59  tf->status = (read_csr(sstatus) &~ SSTATUS_SPP &~ SSTATUS_SIE) | SSTATUS_SPIE;
60  tf->gpr[2] = sp;
61  tf->epc = pc;
62}
63
64static void run_loaded_program(size_t argc, char** argv, uintptr_t kstack_top)
65{
66  // copy phdrs to user stack
67  size_t stack_top = current.stack_top - current.phdr_size;
68  memcpy((void*)stack_top, (void*)current.phdr, current.phdr_size);
69  current.phdr = stack_top;
70
71  // copy argv to user stack
72  for (size_t i = 0; i < argc; i++) {
73    size_t len = strlen((char*)(uintptr_t)argv[i])+1;
74    stack_top -= len;
75    memcpy((void*)stack_top, (void*)(uintptr_t)argv[i], len);
76    argv[i] = (void*)stack_top;
77  }
78
79  // copy envp to user stack
80  const char* envp[] = {
81    // environment goes here
82  };
83  size_t envc = sizeof(envp) / sizeof(envp[0]);
84  for (size_t i = 0; i < envc; i++) {
85    size_t len = strlen(envp[i]) + 1;
86    stack_top -= len;
87    memcpy((void*)stack_top, envp[i], len);
88    envp[i] = (void*)stack_top;
89  }
90
91  // align stack
92  stack_top &= -sizeof(void*);
93
94  struct {
95    long key;
96    long value;
97  } aux[] = {
98    {AT_ENTRY, current.entry},
99    {AT_PHNUM, current.phnum},
100    {AT_PHENT, current.phent},
101    {AT_PHDR, current.phdr},
102    {AT_PAGESZ, RISCV_PGSIZE},
103    {AT_SECURE, 0},
104    {AT_RANDOM, stack_top},
105    {AT_NULL, 0}
106  };
107
108  // place argc, argv, envp, auxp on stack
109  #define PUSH_ARG(type, value) do { \
110    *((type*)sp) = (type)value; \
111    sp += sizeof(type); \
112  } while (0)
113
114  #define STACK_INIT(type) do { \
115    unsigned naux = sizeof(aux)/sizeof(aux[0]); \
116    stack_top -= (1 + argc + 1 + envc + 1 + 2*naux) * sizeof(type); \
117    stack_top &= -16; \
118    long sp = stack_top; \
119    PUSH_ARG(type, argc); \
120    for (unsigned i = 0; i < argc; i++) \
121      PUSH_ARG(type, argv[i]); \
122    PUSH_ARG(type, 0); /* argv[argc] = NULL */ \
123    for (unsigned i = 0; i < envc; i++) \
124      PUSH_ARG(type, envp[i]); \
125    PUSH_ARG(type, 0); /* envp[envc] = NULL */ \
126    for (unsigned i = 0; i < naux; i++) { \
127      PUSH_ARG(type, aux[i].key); \
128      PUSH_ARG(type, aux[i].value); \
129    } \
130  } while (0)
131
132  STACK_INIT(uintptr_t);
133
134  if (current.cycle0) { // start timer if so requested
135    current.time0 = rdtime();
136    current.cycle0 = rdcycle();
137    current.instret0 = rdinstret();
138  }
139
140  trapframe_t tf;
141  init_tf(&tf, current.entry, stack_top);
142  __clear_cache(0, 0);
143  write_csr(sscratch, kstack_top);
144  start_user(&tf);
145}
146
147static void rest_of_boot_loader(uintptr_t kstack_top)
148{
149  arg_buf args;
150  size_t argc = parse_args(&args);
151  if (!argc)
152    panic("tell me what ELF to load!");
153
154  // load program named by argv[0]
155  long phdrs[128];
156  current.phdr = (uintptr_t)phdrs;
157  current.phdr_size = sizeof(phdrs);
158  load_elf(args.argv[0], &current);
159
160  run_loaded_program(argc, args.argv, kstack_top);
161}
162
163void boot_loader(uintptr_t dtb)
164{
165  extern char trap_entry;
166  write_csr(stvec, &trap_entry);
167  write_csr(sscratch, 0);
168  write_csr(sie, 0);
169  set_csr(sstatus, SSTATUS_SUM | SSTATUS_FS);
170
171  file_init();
172  enter_supervisor_mode(rest_of_boot_loader, pk_vm_init(), 0);
173}
174
175void boot_other_hart(uintptr_t dtb)
176{
177  // stall all harts besides hart 0
178  while (1)
179    wfi();
180}
181