blob: 53f8c887cc7dc43064788c9301c6644719d45dd5 [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:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100201 ret.user_ret = api_vcpu_run(arg1, arg2, &ret.new);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100202 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100203
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100204 case HF_VM_CONFIGURE:
Andrew Scull265ada92018-07-30 15:19:01 +0100205 ret.user_ret = api_vm_configure(ipa_init(arg1), ipa_init(arg2));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100206 break;
207
Andrew Scullaa039b32018-10-04 15:02:26 +0100208 case HF_MAILBOX_SEND:
209 ret.user_ret = api_mailbox_send(arg1, arg2, &ret.new);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100210 break;
211
Andrew Scullaa039b32018-10-04 15:02:26 +0100212 case HF_MAILBOX_RECEIVE:
213 ret.user_ret = api_mailbox_receive(arg1, &ret.new);
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100214 break;
215
Andrew Scullaa039b32018-10-04 15:02:26 +0100216 case HF_MAILBOX_CLEAR:
217 ret.user_ret = api_mailbox_clear();
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100218 break;
219
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100220 default:
221 ret.user_ret = -1;
222 }
223
224 return ret;
225}
226
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100227struct vcpu *irq_lower(void)
228{
229 /* TODO: Only switch if we know the interrupt was not for the secondary
230 * VM. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100231 /* Switch back to primary VM, interrupts will be handled there. */
Andrew Scull6bca35e2018-10-02 12:05:32 +0100232 return api_yield();
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100233}
234
235struct vcpu *sync_lower_exception(uint64_t esr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100236{
237 struct cpu *c = cpu();
238 struct vcpu *vcpu = c->current;
Andrew Scullc0e569a2018-10-02 18:05:21 +0100239 int32_t ret;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240
241 switch (esr >> 26) {
242 case 0x01: /* EC = 000001, WFI or WFE. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100243 /* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100244 if (esr & 1) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100245 return NULL;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100246 }
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100247 return api_wait_for_interrupt();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100248
249 case 0x24: /* EC = 100100, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100250 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", vcpu->regs.pc,
251 esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100252 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100253 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
254 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100255 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100256 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100257 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100258
259 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100260 for (;;) {
261 /* do nothing */
262 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100263
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100264 case 0x20: /* EC = 100000, Instruction abort. */
265 dlog("Instruction abort: pc=0x%x, esr=0x%x, ec=0x%x",
266 vcpu->regs.pc, esr, esr >> 26);
267 if (!(esr & (1u << 10))) { /* Check FnV bit. */
268 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
269 read_msr(hpfar_el2) << 8);
270 } else {
271 dlog(", far=invalid");
272 }
273
274 dlog(", vttbr_el2=0x%x", read_msr(vttbr_el2));
275 dlog("\n");
276 for (;;) {
277 /* do nothing */
278 }
279
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100280 case 0x17: /* EC = 010111, SMC instruction. */
Andrew Scull19503262018-09-20 14:48:39 +0100281 if (vcpu->vm->id != HF_PRIMARY_VM_ID ||
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100282 !psci_handler(vcpu->regs.r[0], vcpu->regs.r[1],
283 vcpu->regs.r[2], vcpu->regs.r[3], &ret)) {
284 dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
285 ret = -1;
286 }
287
288 /* Skip the SMC instruction. */
289 vcpu->regs.pc += (esr & (1u << 25)) ? 4 : 2;
290 break;
291
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100292 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100293 dlog("Unknown lower sync exception pc=0x%x, esr=0x%x, "
294 "ec=0x%x\n",
Andrew Scull4f170f52018-07-19 12:58:20 +0100295 vcpu->regs.pc, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100296 for (;;) {
297 /* do nothing */
298 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100299 }
300
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100301 vcpu->regs.r[0] = ret;
302
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100303 return NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100304}