summaryrefslogtreecommitdiff
path: root/src/arch/riscv/sbi.c
blob: a5f3fd4065476aacfbe85cf27b9befbfcc95a51d (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
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2018 HardenedLinux
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <mcall.h>
#include <stdint.h>
#include <commonlib/compiler.h>
#include <arch/exception.h>
#include <sbi.h>
#include <vm.h>
#include <console/uart.h>
#include <console/console.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;
}


/*
 * sbi is triggered by the s-mode ecall
 * parameter : register a0 a1 a2
 * function  : register a7
 * return    : register a0
 */
void handle_sbi(trapframe *tf)
{
	uintptr_t ret = 0;
	uintptr_t arg0 = tf->gpr[10];
	__unused uintptr_t arg1 = tf->gpr[11];
	uintptr_t which = tf->gpr[17];

	switch (which) {
	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;
	default:
		ret = -SBI_ENOSYS;
		break;
	}
	tf->gpr[10] = ret;
	write_csr(mepc, read_csr(mepc) + 4);
}