blob: 8a633554bee07bda350d7937e202c042f6e6cc0d [file] [log] [blame]
Fuad Tabbaa48d1222019-12-09 15:42:32 +00001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
Fuad Tabbaa48d1222019-12-09 15:42:32 +00007 */
8
9#include "hf/dlog.h"
10
11#include "vmapi/hf/call.h"
12
13#include "../msr.h"
Fuad Tabbab86325a2020-01-10 13:38:15 +000014#include "sysregs.h"
Fuad Tabbaa48d1222019-12-09 15:42:32 +000015#include "test/hftest.h"
16
17/**
18 * Tracks the number of times the exception handler has been invoked.
19 */
Fuad Tabbab0ef2a42019-12-19 11:19:25 +000020static int exception_handler_exception_count = 0;
Fuad Tabbaa48d1222019-12-09 15:42:32 +000021
22/**
Madhukar Pappireddyf054a052022-12-22 11:37:11 -060023 * Tracks the virtual interrupt that was last handled by SP.
24 */
25static uint32_t last_serviced_interrupt = 0;
26
27/**
Fuad Tabbaa48d1222019-12-09 15:42:32 +000028 * Sends the number of exceptions handled to the Primary VM.
29 */
Fuad Tabbab0ef2a42019-12-19 11:19:25 +000030void exception_handler_send_exception_count(void)
Fuad Tabbaa48d1222019-12-09 15:42:32 +000031{
J-Alves8304fb92022-06-24 17:14:08 +010032 struct ffa_partition_msg *exception_msg =
33 (struct ffa_partition_msg *)SERVICE_SEND_BUFFER();
Fuad Tabbaa48d1222019-12-09 15:42:32 +000034
Fuad Tabbab0ef2a42019-12-19 11:19:25 +000035 dlog("Sending exception_count %d to primary VM\n",
36 exception_handler_exception_count);
J-Alves8304fb92022-06-24 17:14:08 +010037
38 /*
39 * TODO: remove use of HF_PRIMARY_VM_ID, replace with a mechanism that
40 * allows to detect the caller to a running test service. This may
41 * eventually become to be another endpoint, different from primary VM.
42 */
43 ffa_rxtx_header_init(hf_vm_get_id(), HF_PRIMARY_VM_ID, sizeof(int),
44 &exception_msg->header);
45 memcpy_s(exception_msg->payload, FFA_MSG_PAYLOAD_MAX,
Fuad Tabbab0ef2a42019-12-19 11:19:25 +000046 (const void *)&exception_handler_exception_count,
47 sizeof(exception_handler_exception_count));
J-Alves8304fb92022-06-24 17:14:08 +010048 EXPECT_EQ(ffa_msg_send2(0).func, FFA_SUCCESS_32);
49 ffa_yield();
Fuad Tabbaa48d1222019-12-09 15:42:32 +000050}
51
52/**
53 * Receives the number of exceptions handled.
54 */
J-Alves8304fb92022-06-24 17:14:08 +010055int exception_handler_receive_exception_count(const void *recv_buf)
Fuad Tabbaa48d1222019-12-09 15:42:32 +000056{
J-Alves8304fb92022-06-24 17:14:08 +010057 struct ffa_partition_msg *exception_msg =
58 (struct ffa_partition_msg *)recv_buf;
59 int exception_count = *((const int *)exception_msg->payload);
60 struct ffa_value ret;
61 ffa_notifications_bitmap_t fwk_notif;
Fuad Tabbaa48d1222019-12-09 15:42:32 +000062
J-Alves8304fb92022-06-24 17:14:08 +010063 ret = ffa_notification_get(hf_vm_get_id(), 0,
64 FFA_NOTIFICATION_FLAG_BITMAP_HYP |
65 FFA_NOTIFICATION_FLAG_BITMAP_SPM);
66
67 fwk_notif = ffa_notification_get_from_framework(ret);
J-Alves8304fb92022-06-24 17:14:08 +010068
J-Alves30e26d92022-09-26 12:04:09 +010069 if (fwk_notif == 0U ||
70 exception_msg->header.size != sizeof(exception_count)) {
71 return 0;
72 }
73
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010074 EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
Fuad Tabbab0ef2a42019-12-19 11:19:25 +000075 return exception_count;
Fuad Tabbaa48d1222019-12-09 15:42:32 +000076}
77
J-Alves30e26d92022-09-26 12:04:09 +010078/*
79 * Returns true if the receiver has been preempted by an exception:
80 * - if the receiver is an EL1 partition, it should have sent the exception
81 * count in a message.
82 * - if the receiver is an EL0 partition, the Hyp/SPMC should return FFA_ERROR
83 * with error code FFA_ABORTED.
84 */
85bool exception_received(struct ffa_value *run_res, const void *recv_buf)
86{
87 return exception_handler_receive_exception_count(recv_buf) == 1 ||
88 (run_res->func == FFA_ERROR_32 &&
89 ffa_error_code(*run_res) == FFA_ABORTED);
90}
91
Fuad Tabbaa48d1222019-12-09 15:42:32 +000092/**
93 * EL1 exception handler to use in unit test VMs.
94 * Skips the instruction that triggered the exception.
95 */
96bool exception_handler_skip_instruction(void)
97{
98 dlog("%s function is triggered!\n", __func__);
Fuad Tabbab0ef2a42019-12-19 11:19:25 +000099 ++exception_handler_exception_count;
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000100
101 /* Skip instruction that triggered the exception. */
102 uint64_t next_pc = read_msr(elr_el1);
103 next_pc += 4UL;
104 write_msr(elr_el1, next_pc);
105
106 /* Indicate that elr_el1 should not be restored. */
107 return true;
108}
109
110/**
111 * EL1 exception handler to use in unit test VMs.
112 * Yields control back to the hypervisor and sends the number of exceptions.
113 */
Fuad Tabbab86325a2020-01-10 13:38:15 +0000114static bool exception_handler_yield(void)
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000115{
116 dlog("%s function is triggered!\n", __func__);
Fuad Tabbab0ef2a42019-12-19 11:19:25 +0000117 ++exception_handler_exception_count;
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000118
Fuad Tabbab0ef2a42019-12-19 11:19:25 +0000119 exception_handler_send_exception_count();
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000120
121 /* Indicate that elr_el1 should not be restored. */
122 return true;
123}
124
125/**
Fuad Tabbab86325a2020-01-10 13:38:15 +0000126 * EL1 exception handler to use in unit test VMs.
127 * Yields control back to the hypervisor and sends the number of exceptions.
128 * Asserts that the Exception Class is Unknown.
129 */
130bool exception_handler_yield_unknown(void)
131{
132 uintreg_t esr_el1 = read_msr(ESR_EL1);
Fuad Tabbac3847c72020-08-11 09:32:25 +0100133 uintreg_t far_el1 = read_msr(FAR_EL1);
134
Fuad Tabbab86325a2020-01-10 13:38:15 +0000135 EXPECT_EQ(GET_ESR_EC(esr_el1), EC_UNKNOWN);
Fuad Tabbac3847c72020-08-11 09:32:25 +0100136
137 /*
138 * For unknown exceptions, the value of far_el1 is UNKNOWN.
139 * Hafnium sets it to 0.
140 */
141 EXPECT_EQ(far_el1, 0);
142
Fuad Tabbab86325a2020-01-10 13:38:15 +0000143 return exception_handler_yield();
144}
145
146/**
147 * EL1 exception handler to use in unit test VMs.
148 * Yields control back to the hypervisor and sends the number of exceptions.
149 * Asserts that the Exception Class is Data Abort (same EL).
150 */
151bool exception_handler_yield_data_abort(void)
152{
153 uintreg_t esr_el1 = read_msr(ESR_EL1);
Fuad Tabbac3847c72020-08-11 09:32:25 +0100154 uintreg_t far_el1 = read_msr(FAR_EL1);
155
Fuad Tabbab86325a2020-01-10 13:38:15 +0000156 EXPECT_EQ(GET_ESR_EC(esr_el1), EC_DATA_ABORT_SAME_EL);
Fuad Tabbac3847c72020-08-11 09:32:25 +0100157 EXPECT_NE(far_el1, 0);
158
Fuad Tabbab86325a2020-01-10 13:38:15 +0000159 return exception_handler_yield();
160}
161
162/**
163 * EL1 exception handler to use in unit test VMs.
164 * Yields control back to the hypervisor and sends the number of exceptions.
165 * Asserts that the Exception Class is Instruction Abort (same EL).
166 */
167bool exception_handler_yield_instruction_abort(void)
168{
169 uintreg_t esr_el1 = read_msr(ESR_EL1);
Fuad Tabbac3847c72020-08-11 09:32:25 +0100170 uintreg_t far_el1 = read_msr(FAR_EL1);
171
Fuad Tabbab86325a2020-01-10 13:38:15 +0000172 EXPECT_EQ(GET_ESR_EC(esr_el1), EC_INSTRUCTION_ABORT_SAME_EL);
Fuad Tabbac3847c72020-08-11 09:32:25 +0100173 EXPECT_NE(far_el1, 0);
174
Fuad Tabbab86325a2020-01-10 13:38:15 +0000175 return exception_handler_yield();
176}
177
178/**
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000179 * Returns the number of times the instruction handler was invoked.
180 */
181int exception_handler_get_num(void)
182{
Fuad Tabbab0ef2a42019-12-19 11:19:25 +0000183 return exception_handler_exception_count;
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000184}
185
186/**
187 * Resets the number of exceptions counter;
188 */
189void exception_handler_reset(void)
190{
Fuad Tabbab0ef2a42019-12-19 11:19:25 +0000191 exception_handler_exception_count = 0;
Fuad Tabbaa48d1222019-12-09 15:42:32 +0000192}
Madhukar Pappireddyf054a052022-12-22 11:37:11 -0600193
194/**
195 * Updates the last serviced virtual interrupt ID.
196 */
197void exception_handler_set_last_interrupt(uint32_t id)
198{
199 last_serviced_interrupt = id;
200}
201
202/**
203 * Returns the last serviced virtual interrupt ID.
204 */
205uint32_t exception_handler_get_last_interrupt(void)
206{
207 return last_serviced_interrupt;
208}