1/*
2 * Copyright 2021, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "traps.h"
7#include <KernelExport.h>
8#include <arch_cpu_defs.h>
9#include <arch_int.h>
10#include <Htif.h>
11#include <Clint.h>
12
13
14// TODO: Put machine mode code in separate section and keep it loaded when
15// kernel is running.
16
17
18struct iframe {
19	uint64 ra;
20	uint64 t6;
21	uint64 sp;
22	uint64 gp;
23	uint64 tp;
24	uint64 t0;
25	uint64 t1;
26	uint64 t2;
27	uint64 t5;
28	uint64 s1;
29	uint64 a0;
30	uint64 a1;
31	uint64 a2;
32	uint64 a3;
33	uint64 a4;
34	uint64 a5;
35	uint64 a6;
36	uint64 a7;
37	uint64 s2;
38	uint64 s3;
39	uint64 s4;
40	uint64 s5;
41	uint64 s6;
42	uint64 s7;
43	uint64 s8;
44	uint64 s9;
45	uint64 s10;
46	uint64 s11;
47	uint64 t3;
48	uint64 t4;
49	uint64 fp;
50	uint64 epc;
51};
52
53
54__attribute__ ((aligned (16))) char sMStack[64*1024];
55
56
57extern "C" void MVec();
58extern "C" void MVecS();
59
60static void
61InitPmp()
62{
63	// Setup physical memory protecton. By default physical memory can be only
64	// accessed from machine mode.
65
66	// We allow access to whole physical memory from non-machine mode.
67
68	SetPmpaddr0((~0L) >> 10);
69	SetPmpcfg0((1 << pmpR) | (1 << pmpW) | (1 << pmpX) | (pmpMatchNapot));
70}
71
72
73extern "C" status_t __attribute__((naked))
74MSyscall(uint64 op, ...)
75{
76	asm volatile("ecall");
77	asm volatile("ret");
78}
79
80
81extern "C" void
82MTrap(iframe* frame)
83{
84	uint64 cause = Mcause();
85/*
86	HtifOutString("MTrap("); WriteCause(Mcause()); HtifOutString(")\n");
87	dprintf("  mstatus: "); WriteMstatus(Mstatus()); dprintf("\n");
88	dprintf("  mie: "); WriteInterruptSet(Mie()); dprintf("\n");
89	dprintf("  mip: "); WriteInterruptSet(Mip()); dprintf("\n");
90	dprintf("  sie: "); WriteInterruptSet(Sie()); dprintf("\n");
91	dprintf("  sip: "); WriteInterruptSet(Sip()); dprintf("\n");
92	dprintf("  mscratch: 0x%" B_PRIxADDR "\n", Mscratch());
93	DoStackTrace(Fp(), 0);
94*/
95	switch (cause) {
96		case causeMEcall:
97		case causeSEcall: {
98			frame->epc += 4;
99			uint64 op = frame->a0;
100			switch (op) {
101				case kMSyscallSwitchToSmode: {
102					HtifOutString("switchToSmodeMmodeSyscall()\n");
103					if (cause != causeMEcall) {
104						frame->a0 = B_NOT_ALLOWED;
105						return;
106					}
107					MstatusReg status{.val = Mstatus()};
108					status.mpp = modeS;
109					SetMedeleg(
110						0xffff & ~((1 << causeMEcall) | (1 << causeSEcall)));
111					SetMideleg(0xffff & ~(1 << mTimerInt));
112					SetMstatus(status.val);
113					dprintf("modeM stack: 0x%" B_PRIxADDR ", 0x%" B_PRIxADDR
114						"\n", (addr_t)sMStack,
115						(addr_t)(sMStack + sizeof(sMStack)));
116					SetMscratch((addr_t)(sMStack + sizeof(sMStack)));
117					SetMtvec((uint64)MVecS);
118					frame->a0 = B_OK;
119					return;
120				}
121				case kMSyscallSetTimer: {
122					bool enable = frame->a1 != 0;
123					/*
124					dprintf("setTimerMmodeSyscall(%d, %" B_PRIu64 ")\n",
125						enable, frame->a2);
126					*/
127					// dprintf("  mtime: %" B_PRIu64 "\n", gClintRegs->mTime);
128					ClearBitsMip(1 << sTimerInt);
129					if (!enable) {
130						ClearBitsMie(1 << mTimerInt);
131					} else {
132						gClintRegs->mtimecmp[0] = frame->a2;
133						SetBitsMie(1 << mTimerInt);
134					}
135					frame->a0 = B_OK;
136					return;
137				}
138				default:
139					frame->a0 = B_NOT_SUPPORTED;
140					return;
141			}
142			break;
143		}
144		case causeInterrupt + mTimerInt: {
145			ClearBitsMie(1 << mTimerInt);
146			SetBitsMip(1 << sTimerInt);
147			return;
148		}
149	}
150	HtifOutString("unhandled MTrap\n");
151	HtifShutdown();
152}
153
154
155void
156traps_init()
157{
158	SetMtvec((uint64)MVec);
159	MstatusReg mstatus{.val = Mstatus()};
160	mstatus.ie = 1 << modeM;
161	SetMstatus(mstatus.val);
162	InitPmp();
163	MSyscall(kMSyscallSwitchToSmode);
164}
165