blob: bcc67e647b50e919185d776be017c654ed94d21c [file] [log] [blame]
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +01001#include "api.h"
2#include "arch_api.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01003#include "cpu.h"
4#include "dlog.h"
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +01005#include "psci.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01006#include "vm.h"
7
8#include "msr.h"
9
10struct hvc_handler_return {
Wedson Almeida Filho87009642018-07-02 10:20:07 +010011 long user_ret;
12 struct vcpu *new;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010013};
14
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010015int32_t smc(size_t arg0, size_t arg1, size_t arg2, size_t arg3);
16void cpu_entry(struct cpu *c);
17
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018void irq_current(void)
19{
20 dlog("IRQ from current\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010021 for (;;) {
22 /* do nothing */
23 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010024}
25
26void sync_current_exception(uint64_t esr, uint64_t elr)
27{
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010028 switch (esr >> 26) {
29 case 0x25: /* EC = 100101, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +010030 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", elr, esr,
31 esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010032 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +010033 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
34 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +010035 } else {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010036 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +010037 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010038
39 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010040 for (;;) {
41 /* do nothing */
42 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010043
44 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +010045 dlog("Unknown current sync exception pc=0x%x, esr=0x%x, "
46 "ec=0x%x\n",
47 elr, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010048 for (;;) {
49 /* do nothing */
50 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010051 }
Andrew Scull7364a8e2018-07-19 15:39:29 +010052 for (;;) {
53 /* do nothing */
54 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010055}
56
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010057/**
58 * Handles PSCI requests received via HVC or SMC instructions from the primary
59 * VM only.
60 *
61 * Returns true if the request was a PSCI one, false otherwise.
62 */
63static bool psci_handler(uint32_t func, size_t arg0, size_t arg1, size_t arg2,
64 long *ret)
65{
66 struct cpu *c;
67 int32_t sret;
68
69 switch (func & ~PSCI_CONVENTION_MASK) {
70 case PSCI_VERSION:
71 /* Version is 0.2. */
72 *ret = 2;
73 break;
74
75 case PSCI_MIGRATE_INFO_TYPE:
76 /* Trusted OS does not require migration. */
77 *ret = 2;
78 break;
79
80 case PSCI_SYSTEM_OFF:
81 smc(PSCI_SYSTEM_OFF, 0, 0, 0);
82 for (;;) {
83 }
84 break;
85
86 case PSCI_SYSTEM_RESET:
87 smc(PSCI_SYSTEM_RESET, 0, 0, 0);
88 for (;;) {
89 }
90 break;
91
92 case PSCI_AFFINITY_INFO:
93 c = cpu_find(arg0);
94 if (!c) {
95 *ret = PSCI_RETURN_INVALID_PARAMETERS;
96 break;
97 }
98
99 if (arg1 != 0) {
100 *ret = PSCI_RETURN_NOT_SUPPORTED;
101 break;
102 }
103
104 sl_lock(&c->lock);
105 if (c->is_on) {
106 *ret = 0; /* ON */
107 } else {
108 *ret = 1; /* OFF */
109 }
110 sl_unlock(&c->lock);
111 break;
112
113 case PSCI_CPU_OFF:
114 cpu_off(cpu());
115 smc(PSCI_CPU_OFF, 0, 0, 0);
116 for (;;) {
117 }
118 break;
119
120 case PSCI_CPU_ON:
121 c = cpu_find(arg0);
122 if (!c) {
123 *ret = PSCI_RETURN_INVALID_PARAMETERS;
124 break;
125 }
126
Andrew Scull1b8d0442018-08-06 15:47:04 +0100127 if (cpu_on(c, ipa_init(arg1), arg2)) {
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100128 *ret = PSCI_RETURN_ALREADY_ON;
129 break;
130 }
131
132 /*
133 * There's a race when turning a CPU on when it's in the
134 * process of turning off. We need to loop here while it is
135 * reported that the CPU is on (because it's about to turn
136 * itself off).
137 */
138 do {
139 sret = smc(PSCI_CPU_ON, arg0, (size_t)&cpu_entry,
140 (size_t)c);
141 } while (sret == PSCI_RETURN_ALREADY_ON);
142
143 if (sret == PSCI_RETURN_SUCCESS) {
144 *ret = PSCI_RETURN_SUCCESS;
145 } else {
146 dlog("Unexpected return from PSCI_CPU_ON: 0x%x\n",
147 sret);
148 *ret = PSCI_RETURN_INTERNAL_FAILURE;
149 }
150 break;
151
152 default:
153 return false;
154 }
155
156 return true;
157}
158
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100159struct hvc_handler_return hvc_handler(size_t arg0, size_t arg1, size_t arg2,
160 size_t arg3)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100161{
162 struct hvc_handler_return ret;
163
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100164 ret.new = NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100165
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100166 if (cpu()->current->vm == &primary_vm &&
167 psci_handler(arg0, arg1, arg2, arg3, &ret.user_ret)) {
168 return ret;
169 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100170
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100171 switch ((uint32_t)arg0 & ~PSCI_CONVENTION_MASK) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100172 case HF_VM_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100173 ret.user_ret = api_vm_get_count();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100174 break;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100175
176 case HF_VCPU_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100177 ret.user_ret = api_vcpu_get_count(arg1);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100178 break;
179
180 case HF_VCPU_RUN:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100181 ret.user_ret = api_vcpu_run(arg1, arg2, &ret.new);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100182 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100183
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100184 case HF_VM_CONFIGURE:
Andrew Scull265ada92018-07-30 15:19:01 +0100185 ret.user_ret = api_vm_configure(ipa_init(arg1), ipa_init(arg2));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100186 break;
187
188 case HF_RPC_REQUEST:
189 ret.user_ret = api_rpc_request(arg1, arg2);
190 break;
191
192 case HF_RPC_READ_REQUEST:
193 ret.user_ret = api_rpc_read_request(arg1, &ret.new);
194 break;
195
196 case HF_RPC_ACK:
197 ret.user_ret = api_rpc_ack();
198 break;
199
200 case HF_RPC_REPLY:
201 ret.user_ret = api_rpc_reply(arg1, arg2, &ret.new);
202 break;
203
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100204 default:
205 ret.user_ret = -1;
206 }
207
208 return ret;
209}
210
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100211struct vcpu *irq_lower(void)
212{
213 /* TODO: Only switch if we know the interrupt was not for the secondary
214 * VM. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100215 /* Switch back to primary VM, interrupts will be handled there. */
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100216 return api_switch_to_primary(HF_VCPU_YIELD, vcpu_state_ready);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100217}
218
219struct vcpu *sync_lower_exception(uint64_t esr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100220{
221 struct cpu *c = cpu();
222 struct vcpu *vcpu = c->current;
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100223 long ret;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100224
225 switch (esr >> 26) {
226 case 0x01: /* EC = 000001, WFI or WFE. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100227 /* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100228 if (esr & 1) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100229 return NULL;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100230 }
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100231 return api_wait_for_interrupt();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100232
233 case 0x24: /* EC = 100100, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100234 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", vcpu->regs.pc,
235 esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100236 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100237 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
238 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100239 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100241 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100242
243 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100244 for (;;) {
245 /* do nothing */
246 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100248 case 0x20: /* EC = 100000, Instruction abort. */
249 dlog("Instruction abort: pc=0x%x, esr=0x%x, ec=0x%x",
250 vcpu->regs.pc, esr, esr >> 26);
251 if (!(esr & (1u << 10))) { /* Check FnV bit. */
252 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
253 read_msr(hpfar_el2) << 8);
254 } else {
255 dlog(", far=invalid");
256 }
257
258 dlog(", vttbr_el2=0x%x", read_msr(vttbr_el2));
259 dlog("\n");
260 for (;;) {
261 /* do nothing */
262 }
263
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100264 case 0x17: /* EC = 010111, SMC instruction. */
265 if (vcpu->vm != &primary_vm ||
266 !psci_handler(vcpu->regs.r[0], vcpu->regs.r[1],
267 vcpu->regs.r[2], vcpu->regs.r[3], &ret)) {
268 dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
269 ret = -1;
270 }
271
272 /* Skip the SMC instruction. */
273 vcpu->regs.pc += (esr & (1u << 25)) ? 4 : 2;
274 break;
275
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100276 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100277 dlog("Unknown lower sync exception pc=0x%x, esr=0x%x, "
278 "ec=0x%x\n",
Andrew Scull4f170f52018-07-19 12:58:20 +0100279 vcpu->regs.pc, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100280 for (;;) {
281 /* do nothing */
282 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100283 }
284
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100285 vcpu->regs.r[0] = ret;
286
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100287 return NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100288}