summaryrefslogtreecommitdiff
path: root/src/arch/riscv/sbi.c
blob: 2ef7625f57442be9caab7187d10340fba3a394df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/* SPDX-License-Identifier: GPL-2.0-only */

#include <console/console.h>
#include <mcall.h>
#include <stdint.h>
#include <arch/exception.h>
#include <sbi.h>
#include <vm.h>
#include <console/uart.h>
#include <commonlib/helpers.h>

static uintptr_t send_ipi(uintptr_t *pmask, intptr_t type)
{
	uintptr_t mask = mprv_read_uintptr_t(pmask);
	for (int i = 0; mask; i++) {
		if (mask & 1) {
			OTHER_HLS(i)->ipi_pending |= type;
			/* send soft interrupt to target hart */
			set_msip(i, 1);
		}
		mask = mask >> 1;
	}
	return 0;
}

static uintptr_t sbi_set_timer(uint64_t when)
{
	clear_csr(mip, MIP_STIP);
	set_csr(mie, MIP_MTIP);
	*(HLS()->timecmp) = when;
	return 0;
}

#if CONFIG(CONSOLE_SERIAL)
static uintptr_t sbi_console_putchar(uint8_t ch)
{
	uart_tx_byte(CONFIG_UART_FOR_CONSOLE, ch);
	return 0;
}

static uintptr_t sbi_console_getchar(void)
{
	return uart_rx_byte(CONFIG_UART_FOR_CONSOLE);
}
#endif

static uintptr_t sbi_clear_ipi(void)
{
	clear_csr(mip, MIP_SSIP);
	return 0;
}

static void print_sbi_trap(uintptr_t trap)
{
	switch (trap) {
	case SBI_SHUTDOWN:
		printk(BIOS_EMERG, "SBI_SHUTDOWN\n");
		break;
	case SBI_REMOTE_SFENCE_VMA_ASID:
		printk(BIOS_EMERG, "SBI_REMOTE_SFENCE_VMA_ASID\n");
		break;
	case SBI_REMOTE_SFENCE_VMA:
		printk(BIOS_EMERG, "SBI_REMOTE_SFENCE_VMA\n");
		break;
	case SBI_REMOTE_FENCE_I:
		printk(BIOS_EMERG, "SBI_REMOTE_FENCE_I\n");
		break;
	case SBI_SEND_IPI:
		printk(BIOS_EMERG, "SBI_SEND_IPI\n");
		break;
	case SBI_CLEAR_IPI:
		printk(BIOS_EMERG, "SBI_CLEAR_IPI\n");
		break;
	case SBI_CONSOLE_GETCHAR:
		printk(BIOS_EMERG, "SBI_CONSOLE_GETCHAR\n");
		break;
	case SBI_CONSOLE_PUTCHAR:
		printk(BIOS_EMERG, "SBI_CONSOLE_PUTCHAR\n");
		break;
	case SBI_SET_TIMER:
		printk(BIOS_EMERG, "SBI_SET_TIMER\n");
		break;
	case SBI_BASE_EXTENSION:
		printk(BIOS_EMERG, "SBI_BASE_EXTENSION\n");
		break;
	default:
		printk(BIOS_EMERG, "%lx is an unknown SBI trap\n", trap);
		break;
	}
}

/*
 * These are the default SBI extension values.
 * They can be overridden, by specialized code,
 * but since this is a GPL SBI, it may be better
 * that they evolve as we extend this SBI.
 * over time, the will be updated.
 */
int sbi_features[] = {1, 0, 0, 0, 0, 0, 0};

/*
 * sbi is triggered by the s-mode ecall
 * parameter : register a0 a1 a2
 * function  : register a7
 * return    : register a0
 */
void handle_sbi(struct trapframe *tf)
{
	uintptr_t ret = 0;
	uintptr_t sbiret = 0;
	uintptr_t arg0 = tf->gpr[10];
	__maybe_unused uintptr_t arg1 = tf->gpr[11];
	uintptr_t fid = tf->gpr[16];
	uintptr_t eid = tf->gpr[17];
	uintptr_t retpc = read_csr(mepc) + 4;

	switch (eid) {
	case SBI_SET_TIMER:
#if __riscv_xlen == 32
		ret = sbi_set_timer(arg0 + ((uint64_t)arg1 << 32));
#else
		ret = sbi_set_timer(arg0);
#endif
		break;
#if CONFIG(CONSOLE_SERIAL)
	case SBI_CONSOLE_PUTCHAR:
		ret = sbi_console_putchar(arg0);
		break;
	case SBI_CONSOLE_GETCHAR:
		ret = sbi_console_getchar();
		break;
#endif
	case SBI_CLEAR_IPI:
		ret = sbi_clear_ipi();
		break;
	case SBI_SEND_IPI:
		ret = send_ipi((uintptr_t *)arg0, IPI_SOFT);
		break;
	case SBI_REMOTE_FENCE_I:
		ret = send_ipi((uintptr_t *)arg0, IPI_FENCE_I);
		break;
	case SBI_REMOTE_SFENCE_VMA:
		ret = send_ipi((uintptr_t *)arg0, IPI_SFENCE_VMA);
		break;
	case SBI_REMOTE_SFENCE_VMA_ASID:
		ret = send_ipi((uintptr_t *)arg0, IPI_SFENCE_VMA_ASID);
		break;
	case SBI_SHUTDOWN:
		ret = send_ipi((uintptr_t *)arg0, IPI_SHUTDOWN);
		break;
	case SBI_BASE_EXTENSION:
		/* zero is an allowed return value for most features,
		 * and the only required one is feature 0.
		 * So this test will return legal values
		 * for all possible values of fid.
		 */
		if (fid < ARRAY_SIZE(sbi_features))
			ret = sbi_features[fid];
		break;
	default:
		print_sbi_trap(eid);
		printk(BIOS_EMERG, "SBI: %lx: ENOSYS\n", fid);
		sbiret = -SBI_ENOSYS;
		break;
	}
	tf->gpr[10] = sbiret;
	tf->gpr[11] = ret;

	write_csr(mepc, retpc);
}