blob: 7eba337ffd950da3f04f0df22c66ff996caeb468 [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 Filho00df6c72018-10-18 11:19:24 +010035static struct vcpu *current(void)
36{
37 return (struct vcpu *)read_msr(tpidr_el2);
38}
39
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010040void irq_current(void)
41{
42 dlog("IRQ from current\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010043 for (;;) {
44 /* do nothing */
45 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010046}
47
48void sync_current_exception(uint64_t esr, uint64_t elr)
49{
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010050 switch (esr >> 26) {
51 case 0x25: /* EC = 100101, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +010052 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", elr, esr,
53 esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010054 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +010055 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
56 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +010057 } else {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010058 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +010059 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010060
61 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010062 for (;;) {
63 /* do nothing */
64 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010065
66 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +010067 dlog("Unknown current sync exception pc=0x%x, esr=0x%x, "
68 "ec=0x%x\n",
69 elr, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010070 for (;;) {
71 /* do nothing */
72 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010073 }
Andrew Scull7364a8e2018-07-19 15:39:29 +010074 for (;;) {
75 /* do nothing */
76 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010077}
78
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010079/**
80 * Handles PSCI requests received via HVC or SMC instructions from the primary
81 * VM only.
82 *
83 * Returns true if the request was a PSCI one, false otherwise.
84 */
85static bool psci_handler(uint32_t func, size_t arg0, size_t arg1, size_t arg2,
Andrew Scullc0e569a2018-10-02 18:05:21 +010086 int32_t *ret)
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010087{
88 struct cpu *c;
89 int32_t sret;
90
91 switch (func & ~PSCI_CONVENTION_MASK) {
92 case PSCI_VERSION:
93 /* Version is 0.2. */
94 *ret = 2;
95 break;
96
97 case PSCI_MIGRATE_INFO_TYPE:
98 /* Trusted OS does not require migration. */
99 *ret = 2;
100 break;
101
102 case PSCI_SYSTEM_OFF:
103 smc(PSCI_SYSTEM_OFF, 0, 0, 0);
104 for (;;) {
105 }
106 break;
107
108 case PSCI_SYSTEM_RESET:
109 smc(PSCI_SYSTEM_RESET, 0, 0, 0);
110 for (;;) {
111 }
112 break;
113
114 case PSCI_AFFINITY_INFO:
115 c = cpu_find(arg0);
116 if (!c) {
117 *ret = PSCI_RETURN_INVALID_PARAMETERS;
118 break;
119 }
120
121 if (arg1 != 0) {
122 *ret = PSCI_RETURN_NOT_SUPPORTED;
123 break;
124 }
125
126 sl_lock(&c->lock);
127 if (c->is_on) {
128 *ret = 0; /* ON */
129 } else {
130 *ret = 1; /* OFF */
131 }
132 sl_unlock(&c->lock);
133 break;
134
135 case PSCI_CPU_OFF:
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100136 cpu_off(current()->cpu);
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100137 smc(PSCI_CPU_OFF, 0, 0, 0);
138 for (;;) {
139 }
140 break;
141
142 case PSCI_CPU_ON:
143 c = cpu_find(arg0);
144 if (!c) {
145 *ret = PSCI_RETURN_INVALID_PARAMETERS;
146 break;
147 }
148
Andrew Scull1b8d0442018-08-06 15:47:04 +0100149 if (cpu_on(c, ipa_init(arg1), arg2)) {
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100150 *ret = PSCI_RETURN_ALREADY_ON;
151 break;
152 }
153
154 /*
155 * There's a race when turning a CPU on when it's in the
156 * process of turning off. We need to loop here while it is
157 * reported that the CPU is on (because it's about to turn
158 * itself off).
159 */
160 do {
161 sret = smc(PSCI_CPU_ON, arg0, (size_t)&cpu_entry,
162 (size_t)c);
163 } while (sret == PSCI_RETURN_ALREADY_ON);
164
165 if (sret == PSCI_RETURN_SUCCESS) {
166 *ret = PSCI_RETURN_SUCCESS;
167 } else {
168 dlog("Unexpected return from PSCI_CPU_ON: 0x%x\n",
169 sret);
170 *ret = PSCI_RETURN_INTERNAL_FAILURE;
171 }
172 break;
173
174 default:
175 return false;
176 }
177
178 return true;
179}
180
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100181struct hvc_handler_return hvc_handler(size_t arg0, size_t arg1, size_t arg2,
182 size_t arg3)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100183{
184 struct hvc_handler_return ret;
185
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100186 ret.new = NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100187
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100188 if (current()->vm->id == HF_PRIMARY_VM_ID) {
Andrew Scullc0e569a2018-10-02 18:05:21 +0100189 int32_t psci_ret;
190 if (psci_handler(arg0, arg1, arg2, arg3, &psci_ret)) {
191 ret.user_ret = psci_ret;
192 return ret;
193 }
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100194 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100195
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100196 switch ((uint32_t)arg0 & ~PSCI_CONVENTION_MASK) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100197 case HF_VM_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100198 ret.user_ret = api_vm_get_count();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100199 break;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100200
201 case HF_VCPU_GET_COUNT:
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100202 ret.user_ret = api_vcpu_get_count(arg1, current());
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100203 break;
204
205 case HF_VCPU_RUN:
Andrew Scull6d2db332018-10-10 15:28:17 +0100206 ret.user_ret = hf_vcpu_run_return_encode(
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100207 api_vcpu_run(arg1, arg2, current(), &ret.new));
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100208 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100209
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100210 case HF_VM_CONFIGURE:
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100211 ret.user_ret = api_vm_configure(ipa_init(arg1), ipa_init(arg2),
212 current());
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100213 break;
214
Andrew Scullaa039b32018-10-04 15:02:26 +0100215 case HF_MAILBOX_SEND:
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100216 ret.user_ret =
217 api_mailbox_send(arg1, arg2, current(), &ret.new);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100218 break;
219
Andrew Scullaa039b32018-10-04 15:02:26 +0100220 case HF_MAILBOX_RECEIVE:
Andrew Scull6d2db332018-10-10 15:28:17 +0100221 ret.user_ret = hf_mailbox_receive_return_encode(
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100222 api_mailbox_receive(arg1, current(), &ret.new));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100223 break;
224
Andrew Scullaa039b32018-10-04 15:02:26 +0100225 case HF_MAILBOX_CLEAR:
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100226 ret.user_ret = api_mailbox_clear(current());
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100227 break;
228
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100229 default:
230 ret.user_ret = -1;
231 }
232
233 return ret;
234}
235
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100236struct vcpu *irq_lower(void)
237{
238 /* TODO: Only switch if we know the interrupt was not for the secondary
239 * VM. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100240 /* Switch back to primary VM, interrupts will be handled there. */
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100241 return api_yield(current());
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100242}
243
244struct vcpu *sync_lower_exception(uint64_t esr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100245{
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100246 struct vcpu *vcpu = current();
Andrew Scullc0e569a2018-10-02 18:05:21 +0100247 int32_t ret;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100248
249 switch (esr >> 26) {
250 case 0x01: /* EC = 000001, WFI or WFE. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100251 /* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100252 if (esr & 1) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100253 return NULL;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100254 }
Wedson Almeida Filho00df6c72018-10-18 11:19:24 +0100255 return api_wait_for_interrupt(current());
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100256
257 case 0x24: /* EC = 100100, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100258 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", vcpu->regs.pc,
259 esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100260 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100261 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
262 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100263 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100264 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100265 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100266
267 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100268 for (;;) {
269 /* do nothing */
270 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100271
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100272 case 0x20: /* EC = 100000, Instruction abort. */
273 dlog("Instruction abort: pc=0x%x, esr=0x%x, ec=0x%x",
274 vcpu->regs.pc, esr, esr >> 26);
275 if (!(esr & (1u << 10))) { /* Check FnV bit. */
276 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
277 read_msr(hpfar_el2) << 8);
278 } else {
279 dlog(", far=invalid");
280 }
281
282 dlog(", vttbr_el2=0x%x", read_msr(vttbr_el2));
283 dlog("\n");
284 for (;;) {
285 /* do nothing */
286 }
287
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100288 case 0x17: /* EC = 010111, SMC instruction. */
Andrew Scull19503262018-09-20 14:48:39 +0100289 if (vcpu->vm->id != HF_PRIMARY_VM_ID ||
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100290 !psci_handler(vcpu->regs.r[0], vcpu->regs.r[1],
291 vcpu->regs.r[2], vcpu->regs.r[3], &ret)) {
292 dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
293 ret = -1;
294 }
295
296 /* Skip the SMC instruction. */
297 vcpu->regs.pc += (esr & (1u << 25)) ? 4 : 2;
298 break;
299
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100300 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100301 dlog("Unknown lower sync exception pc=0x%x, esr=0x%x, "
302 "ec=0x%x\n",
Andrew Scull4f170f52018-07-19 12:58:20 +0100303 vcpu->regs.pc, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100304 for (;;) {
305 /* do nothing */
306 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100307 }
308
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100309 vcpu->regs.r[0] = ret;
310
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100311 return NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100312}