blob: 82ae46147bf25b1ed03c24746eaa08800484b060 [file] [log] [blame]
Andrew Scull18c78fc2018-08-20 12:57:41 +01001#include "hf/api.h"
2#include "hf/cpu.h"
3#include "hf/dlog.h"
4#include "hf/vm.h"
5
Andrew Scullf35a5c92018-08-07 18:09:46 +01006#include "vmapi/hf/call.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01007
8#include "msr.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +01009#include "psci.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010010
11struct hvc_handler_return {
Wedson Almeida Filho87009642018-07-02 10:20:07 +010012 long user_ret;
13 struct vcpu *new;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010014};
15
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010016int32_t smc(size_t arg0, size_t arg1, size_t arg2, size_t arg3);
17void cpu_entry(struct cpu *c);
18
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010019void irq_current(void)
20{
21 dlog("IRQ from current\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010022 for (;;) {
23 /* do nothing */
24 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025}
26
27void sync_current_exception(uint64_t esr, uint64_t elr)
28{
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010029 switch (esr >> 26) {
30 case 0x25: /* EC = 100101, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +010031 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", elr, esr,
32 esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010033 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +010034 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
35 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +010036 } else {
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010037 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +010038 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010039
40 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +010041 for (;;) {
42 /* do nothing */
43 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010044
45 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +010046 dlog("Unknown current sync exception pc=0x%x, esr=0x%x, "
47 "ec=0x%x\n",
48 elr, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +010049 for (;;) {
50 /* do nothing */
51 }
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010052 }
Andrew Scull7364a8e2018-07-19 15:39:29 +010053 for (;;) {
54 /* do nothing */
55 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010056}
57
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +010058/**
59 * Handles PSCI requests received via HVC or SMC instructions from the primary
60 * VM only.
61 *
62 * Returns true if the request was a PSCI one, false otherwise.
63 */
64static bool psci_handler(uint32_t func, size_t arg0, size_t arg1, size_t arg2,
65 long *ret)
66{
67 struct cpu *c;
68 int32_t sret;
69
70 switch (func & ~PSCI_CONVENTION_MASK) {
71 case PSCI_VERSION:
72 /* Version is 0.2. */
73 *ret = 2;
74 break;
75
76 case PSCI_MIGRATE_INFO_TYPE:
77 /* Trusted OS does not require migration. */
78 *ret = 2;
79 break;
80
81 case PSCI_SYSTEM_OFF:
82 smc(PSCI_SYSTEM_OFF, 0, 0, 0);
83 for (;;) {
84 }
85 break;
86
87 case PSCI_SYSTEM_RESET:
88 smc(PSCI_SYSTEM_RESET, 0, 0, 0);
89 for (;;) {
90 }
91 break;
92
93 case PSCI_AFFINITY_INFO:
94 c = cpu_find(arg0);
95 if (!c) {
96 *ret = PSCI_RETURN_INVALID_PARAMETERS;
97 break;
98 }
99
100 if (arg1 != 0) {
101 *ret = PSCI_RETURN_NOT_SUPPORTED;
102 break;
103 }
104
105 sl_lock(&c->lock);
106 if (c->is_on) {
107 *ret = 0; /* ON */
108 } else {
109 *ret = 1; /* OFF */
110 }
111 sl_unlock(&c->lock);
112 break;
113
114 case PSCI_CPU_OFF:
115 cpu_off(cpu());
116 smc(PSCI_CPU_OFF, 0, 0, 0);
117 for (;;) {
118 }
119 break;
120
121 case PSCI_CPU_ON:
122 c = cpu_find(arg0);
123 if (!c) {
124 *ret = PSCI_RETURN_INVALID_PARAMETERS;
125 break;
126 }
127
Andrew Scull1b8d0442018-08-06 15:47:04 +0100128 if (cpu_on(c, ipa_init(arg1), arg2)) {
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100129 *ret = PSCI_RETURN_ALREADY_ON;
130 break;
131 }
132
133 /*
134 * There's a race when turning a CPU on when it's in the
135 * process of turning off. We need to loop here while it is
136 * reported that the CPU is on (because it's about to turn
137 * itself off).
138 */
139 do {
140 sret = smc(PSCI_CPU_ON, arg0, (size_t)&cpu_entry,
141 (size_t)c);
142 } while (sret == PSCI_RETURN_ALREADY_ON);
143
144 if (sret == PSCI_RETURN_SUCCESS) {
145 *ret = PSCI_RETURN_SUCCESS;
146 } else {
147 dlog("Unexpected return from PSCI_CPU_ON: 0x%x\n",
148 sret);
149 *ret = PSCI_RETURN_INTERNAL_FAILURE;
150 }
151 break;
152
153 default:
154 return false;
155 }
156
157 return true;
158}
159
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100160struct hvc_handler_return hvc_handler(size_t arg0, size_t arg1, size_t arg2,
161 size_t arg3)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100162{
163 struct hvc_handler_return ret;
164
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100165 ret.new = NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100166
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100167 if (cpu()->current->vm == &primary_vm &&
168 psci_handler(arg0, arg1, arg2, arg3, &ret.user_ret)) {
169 return ret;
170 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100171
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100172 switch ((uint32_t)arg0 & ~PSCI_CONVENTION_MASK) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100173 case HF_VM_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100174 ret.user_ret = api_vm_get_count();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100175 break;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100176
177 case HF_VCPU_GET_COUNT:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100178 ret.user_ret = api_vcpu_get_count(arg1);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100179 break;
180
181 case HF_VCPU_RUN:
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100182 ret.user_ret = api_vcpu_run(arg1, arg2, &ret.new);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100183 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100184
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100185 case HF_VM_CONFIGURE:
Andrew Scull265ada92018-07-30 15:19:01 +0100186 ret.user_ret = api_vm_configure(ipa_init(arg1), ipa_init(arg2));
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100187 break;
188
189 case HF_RPC_REQUEST:
190 ret.user_ret = api_rpc_request(arg1, arg2);
191 break;
192
193 case HF_RPC_READ_REQUEST:
194 ret.user_ret = api_rpc_read_request(arg1, &ret.new);
195 break;
196
197 case HF_RPC_ACK:
198 ret.user_ret = api_rpc_ack();
199 break;
200
201 case HF_RPC_REPLY:
202 ret.user_ret = api_rpc_reply(arg1, arg2, &ret.new);
203 break;
204
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100205 default:
206 ret.user_ret = -1;
207 }
208
209 return ret;
210}
211
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100212struct vcpu *irq_lower(void)
213{
214 /* TODO: Only switch if we know the interrupt was not for the secondary
215 * VM. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100216 /* Switch back to primary VM, interrupts will be handled there. */
Andrew Scull13652af2018-09-17 14:49:08 +0100217 return api_switch_to_primary(HF_VCPU_RUN_RESPONSE(HF_VCPU_RUN_YIELD, 0),
218 vcpu_state_ready);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100219}
220
221struct vcpu *sync_lower_exception(uint64_t esr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100222{
223 struct cpu *c = cpu();
224 struct vcpu *vcpu = c->current;
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100225 long ret;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100226
227 switch (esr >> 26) {
228 case 0x01: /* EC = 000001, WFI or WFE. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100229 /* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100230 if (esr & 1) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100231 return NULL;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100232 }
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100233 return api_wait_for_interrupt();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100234
235 case 0x24: /* EC = 100100, Data abort. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100236 dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", vcpu->regs.pc,
237 esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100238 if (!(esr & (1u << 10))) { /* Check FnV bit. */
Andrew Scull4f170f52018-07-19 12:58:20 +0100239 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
240 read_msr(hpfar_el2) << 8);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100241 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100242 dlog(", far=invalid");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100243 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100244
245 dlog("\n");
Andrew Scull7364a8e2018-07-19 15:39:29 +0100246 for (;;) {
247 /* do nothing */
248 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100249
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100250 case 0x20: /* EC = 100000, Instruction abort. */
251 dlog("Instruction abort: pc=0x%x, esr=0x%x, ec=0x%x",
252 vcpu->regs.pc, esr, esr >> 26);
253 if (!(esr & (1u << 10))) { /* Check FnV bit. */
254 dlog(", far=0x%x, hpfar=0x%x", read_msr(far_el2),
255 read_msr(hpfar_el2) << 8);
256 } else {
257 dlog(", far=invalid");
258 }
259
260 dlog(", vttbr_el2=0x%x", read_msr(vttbr_el2));
261 dlog("\n");
262 for (;;) {
263 /* do nothing */
264 }
265
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100266 case 0x17: /* EC = 010111, SMC instruction. */
267 if (vcpu->vm != &primary_vm ||
268 !psci_handler(vcpu->regs.r[0], vcpu->regs.r[1],
269 vcpu->regs.r[2], vcpu->regs.r[3], &ret)) {
270 dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
271 ret = -1;
272 }
273
274 /* Skip the SMC instruction. */
275 vcpu->regs.pc += (esr & (1u << 25)) ? 4 : 2;
276 break;
277
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100278 default:
Wedson Almeida Filho2f94ec12018-07-26 16:00:48 +0100279 dlog("Unknown lower sync exception pc=0x%x, esr=0x%x, "
280 "ec=0x%x\n",
Andrew Scull4f170f52018-07-19 12:58:20 +0100281 vcpu->regs.pc, esr, esr >> 26);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100282 for (;;) {
283 /* do nothing */
284 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100285 }
286
Wedson Almeida Filho03e767a2018-07-30 15:32:03 +0100287 vcpu->regs.r[0] = ret;
288
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100289 return NULL;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100290}