blob: 8fa5733cd8d764dd5ccf2051a1891fa250855005 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/api.h"
18#include "hf/cpu.h"
19#include "hf/dlog.h"
20#include "hf/vm.h"
21
Andrew Scullf35a5c92018-08-07 18:09:46 +010022#include "vmapi/hf/call.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010023
24#include "msr.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010025#include "psci.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010026
27struct hvc_handler_return {
Andrew Scullc0e569a2018-10-02 18:05:21 +010028 uint64_t user_ret;
Wedson Almeida Filho87009642018-07-02 10:20:07 +010029 struct vcpu *new;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010030};
31
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010032int32_t smc(size_t arg0, size_t arg1, size_t arg2, size_t arg3);
33void cpu_entry(struct cpu *c);
34
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010035void irq_current(void)
36{
37 dlog("IRQ from current\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010038 for (;;) {
39 /* do nothing */
40 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010041}
42
43void sync_current_exception(uint64_t esr, uint64_t elr)
44{
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010045 switch (esr >> 26) {
46 case 0x25: /* EC = 100101, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +010047 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", elr, esr,
48 esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010049 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +010050 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
51 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +010052 } else {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010053 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +010054 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010055
56 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010057 for (;;) {
58 /* do nothing */
59 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010060
61 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +010062 dlog("Unknown current sync exception pc=0x%x, esr=0x%x, "
63 "ec=0x%x\n",
64 elr, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010065 for (;;) {
66 /* do nothing */
67 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010068 }
Andrew Scull7364a8e2018-07-19 15:39:29 +010069 for (;;) {
70 /* do nothing */
71 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010072}
73
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010074/**
75 * Handles PSCI requests received via HVC or SMC instructions from the primary
76 * VM only.
77 *
78 * Returns true if the request was a PSCI one, false otherwise.
79 */
80static bool psci_handler(uint32_t func, size_t arg0, size_t arg1, size_t arg2,
Andrew Scullc0e569a2018-10-02 18:05:21 +010081 int32_t *ret)
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010082{
83 struct cpu *c;
84 int32_t sret;
85
86 switch (func & ~PSCI_CONVENTION_MASK) {
87 case PSCI_VERSION:
88 /* Version is 0.2. */
89 *ret = 2;
90 break;
91
92 case PSCI_MIGRATE_INFO_TYPE:
93 /* Trusted OS does not require migration. */
94 *ret = 2;
95 break;
96
97 case PSCI_SYSTEM_OFF:
98 smc(PSCI_SYSTEM_OFF, 0, 0, 0);
99 for (;;) {
100 }
101 break;
102
103 case PSCI_SYSTEM_RESET:
104 smc(PSCI_SYSTEM_RESET, 0, 0, 0);
105 for (;;) {
106 }
107 break;
108
109 case PSCI_AFFINITY_INFO:
110 c = cpu_find(arg0);
111 if (!c) {
112 *ret = PSCI_RETURN_INVALID_PARAMETERS;
113 break;
114 }
115
116 if (arg1 != 0) {
117 *ret = PSCI_RETURN_NOT_SUPPORTED;
118 break;
119 }
120
121 sl_lock(&c->lock);
122 if (c->is_on) {
123 *ret = 0; /* ON */
124 } else {
125 *ret = 1; /* OFF */
126 }
127 sl_unlock(&c->lock);
128 break;
129
130 case PSCI_CPU_OFF:
131 cpu_off(cpu());
132 smc(PSCI_CPU_OFF, 0, 0, 0);
133 for (;;) {
134 }
135 break;
136
137 case PSCI_CPU_ON:
138 c = cpu_find(arg0);
139 if (!c) {
140 *ret = PSCI_RETURN_INVALID_PARAMETERS;
141 break;
142 }
143
Andrew Scull1b8d0442018-08-06 15:47:04 +0100144 if (cpu_on(c, ipa_init(arg1), arg2)) {
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100145 *ret = PSCI_RETURN_ALREADY_ON;
146 break;
147 }
148
149 /*
150 * There's a race when turning a CPU on when it's in the
151 * process of turning off. We need to loop here while it is
152 * reported that the CPU is on (because it's about to turn
153 * itself off).
154 */
155 do {
156 sret = smc(PSCI_CPU_ON, arg0, (size_t)&cpu_entry,
157 (size_t)c);
158 } while (sret == PSCI_RETURN_ALREADY_ON);
159
160 if (sret == PSCI_RETURN_SUCCESS) {
161 *ret = PSCI_RETURN_SUCCESS;
162 } else {
163 dlog("Unexpected return from PSCI_CPU_ON: 0x%x\n",
164 sret);
165 *ret = PSCI_RETURN_INTERNAL_FAILURE;
166 }
167 break;
168
169 default:
170 return false;
171 }
172
173 return true;
174}
175
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100176struct hvc_handler_return hvc_handler(size_t arg0, size_t arg1, size_t arg2,
177 size_t arg3)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100178{
179 struct hvc_handler_return ret;
180
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100181 ret.new = NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100182
Andrew Scullc0e569a2018-10-02 18:05:21 +0100183 if (cpu()->current->vm->id == HF_PRIMARY_VM_ID) {
184 int32_t psci_ret;
185 if (psci_handler(arg0, arg1, arg2, arg3, &psci_ret)) {
186 ret.user_ret = psci_ret;
187 return ret;
188 }
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100189 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100190
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100191 switch ((uint32_t)arg0 & ~PSCI_CONVENTION_MASK) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100192 case HF_VM_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100193 ret.user_ret = api_vm_get_count();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100194 break;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100195
196 case HF_VCPU_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100197 ret.user_ret = api_vcpu_get_count(arg1);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100198 break;
199
200 case HF_VCPU_RUN:
Andrew Scull6d2db332018-10-10 15:28:17 +0100201 ret.user_ret = hf_vcpu_run_return_encode(
202 api_vcpu_run(arg1, arg2, &ret.new));
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100203 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100204
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100205 case HF_VM_CONFIGURE:
Andrew Scull265ada92018-07-30 15:19:01 +0100206 ret.user_ret = api_vm_configure(ipa_init(arg1), ipa_init(arg2));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100207 break;
208
Andrew Scullaa039b32018-10-04 15:02:26 +0100209 case HF_MAILBOX_SEND:
210 ret.user_ret = api_mailbox_send(arg1, arg2, &ret.new);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100211 break;
212
Andrew Scullaa039b32018-10-04 15:02:26 +0100213 case HF_MAILBOX_RECEIVE:
Andrew Scull6d2db332018-10-10 15:28:17 +0100214 ret.user_ret = hf_mailbox_receive_return_encode(
215 api_mailbox_receive(arg1, &ret.new));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100216 break;
217
Andrew Scullaa039b32018-10-04 15:02:26 +0100218 case HF_MAILBOX_CLEAR:
219 ret.user_ret = api_mailbox_clear();
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100220 break;
221
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100222 default:
223 ret.user_ret = -1;
224 }
225
226 return ret;
227}
228
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100229struct vcpu *irq_lower(void)
230{
231 /* TODO: Only switch if we know the interrupt was not for the secondary
232 * VM. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100233 /* Switch back to primary VM, interrupts will be handled there. */
Andrew Scull6bca35e2018-10-02 12:05:32 +0100234 return api_yield();
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100235}
236
237struct vcpu *sync_lower_exception(uint64_t esr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100238{
239 struct cpu *c = cpu();
240 struct vcpu *vcpu = c->current;
Andrew Scullc0e569a2018-10-02 18:05:21 +0100241 int32_t ret;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100242
243 switch (esr >> 26) {
244 case 0x01: /* EC = 000001, WFI or WFE. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100245 /* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100246 if (esr & 1) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100247 return NULL;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100248 }
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100249 return api_wait_for_interrupt();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100250
251 case 0x24: /* EC = 100100, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100252 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", vcpu->regs.pc,
253 esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100254 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100255 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
256 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100257 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100258 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100259 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100260
261 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100262 for (;;) {
263 /* do nothing */
264 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100265
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100266 case 0x20: /* EC = 100000, Instruction abort. */
267 dlog("Instruction abort: pc=0x%x, esr=0x%x, ec=0x%x",
268 vcpu->regs.pc, esr, esr >> 26);
269 if (!(esr & (1u << 10))) { /* Check FnV bit. */
270 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
271 read_msr(hpfar_el2) << 8);
272 } else {
273 dlog(", far=invalid");
274 }
275
276 dlog(", vttbr_el2=0x%x", read_msr(vttbr_el2));
277 dlog("\n");
278 for (;;) {
279 /* do nothing */
280 }
281
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100282 case 0x17: /* EC = 010111, SMC instruction. */
Andrew Scull19503262018-09-20 14:48:39 +0100283 if (vcpu->vm->id != HF_PRIMARY_VM_ID ||
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100284 !psci_handler(vcpu->regs.r[0], vcpu->regs.r[1],
285 vcpu->regs.r[2], vcpu->regs.r[3], &ret)) {
286 dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
287 ret = -1;
288 }
289
290 /* Skip the SMC instruction. */
291 vcpu->regs.pc += (esr & (1u << 25)) ? 4 : 2;
292 break;
293
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100294 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100295 dlog("Unknown lower sync exception pc=0x%x, esr=0x%x, "
296 "ec=0x%x\n",
Andrew Scull4f170f52018-07-19 12:58:20 +0100297 vcpu->regs.pc, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100298 for (;;) {
299 /* do nothing */
300 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100301 }
302
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100303 vcpu->regs.r[0] = ret;
304
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100305 return NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100306}