1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6#pragma once
7
8#include <stdint.h>
9#include <stdbool.h>
10#include <vka/cspacepath_t.h>
11
12#include <sel4vm/sel4_arch/processor.h>
13
14typedef struct vm_vcpu vm_vcpu_t;
15typedef struct fault fault_t;
16
17enum fault_width {
18    WIDTH_DOUBLEWORD,
19    WIDTH_WORD,
20    WIDTH_HALFWORD,
21    WIDTH_BYTE
22};
23
24typedef enum {
25    DATA,
26    PREFETCH,
27    VCPU
28} fault_type_t;
29
30#define CPSR_THUMB                 BIT(5)
31#define CPSR_IS_THUMB(x)           ((x) & CPSR_THUMB)
32
33/**
34 * Data structure representating a fault
35 */
36struct fault {
37/// The vcpu associated with the fault
38    vm_vcpu_t *vcpu;
39/// Reply capability to the faulting TCB
40    cspacepath_t reply_cap;
41/// VM registers at the time of the fault
42    seL4_UserContext regs;
43
44/// The IPA address of the fault
45    seL4_Word base_addr;
46/// The IPA address of the fault at the current stage
47    seL4_Word addr;
48/// The IPA of the instruction which caused the fault
49    seL4_Word ip;
50/// The data which was to be written, or the data to return to the VM
51    seL4_Word data;
52/// Fault status register (IL and ISS fields of HSR cp15 register)
53    seL4_Word fsr;
54/// type of fault
55    fault_type_t type;
56/// For multiple str/ldr and 32 bit access, the fault is handled in stages
57    int stage;
58/// If the instruction requires fetching, cache it here
59    seL4_Word instruction;
60/// The width of the fault
61    enum fault_width width;
62/// The mode of the processor
63    processor_mode_t pmode;
64/// The active content within the fault structure to allow lazy loading
65    int content;
66};
67typedef struct fault fault_t;
68
69/**
70 * Initialise a fault structure.
71 * The structure will be bound to a VM and a reply cap slot
72 * will be reserved.
73 * @param[in] vm    The VM that the fault structure should be bound to
74 * @return          An initialised fault structure handle or NULL on failure
75 */
76fault_t *fault_init(vm_vcpu_t *vcpu);
77
78/**
79 * Populate an initialised fault structure with fault data obtained from
80 * a pending VCPU fault message. The reply cap to the faulting TCB will
81 * also be saved
82 * @param[in] fault A handle to a fault structure
83 * @return 0 on success;
84 */
85int new_vcpu_fault(fault_t *fault, uint32_t hsr);
86
87/**
88 * Populate an initialised fault structure with fault data obtained from
89 * a pending virtual memory fault IPC message. The reply cap to the faulting
90 * TCB will also be saved.
91 * @param[in] fault  A handle to a fault structure
92 * @return           0 on success;
93 */
94int new_memory_fault(fault_t *fault);
95
96/**
97 * Abandon the fault.
98 * Performs any necessary clean up of the fault structure once a fault
99 * has been serviced. The VM will not be restarted by a call to this function.
100 * @param[in] fault  A handle to a fault structure
101 * @return           0 on success;
102 */
103int abandon_fault(fault_t *fault);
104
105/**
106 * Restart the fault
107 * Return execution to the VM without modifying registers or advancing the
108 * program counter. This is useful when the suitable response to the fault
109 * is to map a frame.
110 * @param[in] fault  A handle to a fault structure
111 * @return           0 on success;
112 */
113int restart_fault(fault_t *fault);
114
115
116/**
117 * Ignore the fault.
118 * Advances the PC without modifying registers. Useful when VM reads and
119 * writes to invalid memory can be ignored.
120 * @param[in] fault  A handle to a fault structure
121 * @return           0 on success;
122 */
123int ignore_fault(fault_t *fault);
124
125
126/**
127 * Update register contents and return from a fault.
128 * Updates user registers based on the data field of the fault struction
129 * and replies to the faulting TCB to resume execution.
130 * @param[in] fault  A handle to a fault structure
131 * @param[in] data   The data word that the VM was attempting to access
132 * @return           0 on success;
133 */
134int advance_fault(fault_t *fault);
135
136
137/**
138 * Emulates the faulting instruction of the VM on the provided data.
139 * This function does not modify the state of the fault, it only returns
140 * an updated representation of the given data based on the write operation
141 * that the VM was attempting to perform.
142 * @param[in] fault  A handle to a fault structure
143 * @param[in] data   The data word that the VM was attempting to access
144 * @return           The updated data based on the operation that the VM
145 *                   was attempting to perform.
146 */
147seL4_Word fault_emulate(fault_t *fault, seL4_Word data);
148
149/**
150 * Determine if a fault has been handled. This is useful for multi-stage faults
151 * @param[in] fault  A handle to the fault
152 * @return           0 if the fault has not yet been handled, otherwise, further
153 *                   action is required
154 */
155int fault_handled(fault_t *fault);
156
157/**
158 * Retrieve the data that the faulting thread is trying to write or the data
159 * that will be returned to the thread in case of a read fault.
160 * The fault must be a data fault.
161 * @param[in] fault  A handle to the fault
162 * @return           If it is a read fault, returns the data that will be returned
163 *                   to the thread when the fault is advanced. Otherwise, returns
164 *                   the data that the thread was attempting to write.
165 */
166seL4_Word fault_get_data(fault_t *fault);
167
168/**
169 * Set the data that will be returned to the thread when the fault is advanced.
170 * The fault must be a data fault.
171 * @param[in] fault  A handle to the fault
172 * @param[in] data   The data to return to the thread.
173 */
174void fault_set_data(fault_t *fault, seL4_Word data);
175
176/**
177 * Retrieve a mask for the data within the aligned word that the fault is
178 * attempting to access
179 * @param[in] fault  A handle to the fault
180 * @return           A mask for the data within the aligned word that the fault is
181 *                   attempting to access
182 */
183seL4_Word fault_get_data_mask(fault_t *fault);
184
185/**
186 * Get the faulting address
187 * @param[in] fault  A handle to the fault
188 * @return           The address that the faulting thread was attempting to access
189 */
190seL4_Word fault_get_address(fault_t *fault);
191
192/**
193 * Get the access width of the fault
194 * The fault must be a data fault.
195 * @param[in] fault  A handle to the fault
196 * @return           The access width of the fault
197 */
198enum fault_width fault_get_width(fault_t *f);
199
200/**
201 * Get the access width size of the fault
202 * The fault must be a data fault.
203 * @param[in] fault  A handle to the fault
204 * @return           The access width size of the fault
205 */
206size_t fault_get_width_size(fault_t *f);
207
208/**
209 * Get the context of a fault
210 * @param[in] fault  A handle to the fault
211 * @return           A handle to the fault context
212 */
213seL4_UserContext *fault_get_ctx(fault_t *fault);
214
215/**
216 * Set the context of a fault
217 * @param[in] fault  A handle to the fault
218 * @param[in] ctx    A handle to the fault context
219 */
220void fault_set_ctx(fault_t *f, seL4_UserContext *ctx);
221
222/**
223 * Get the fault status register of a fault
224 * @param[in] fault  A handle to the fault
225 * @return           the ARM HSR register associated with this fault. The EC
226 *                   field will be masked out.
227 */
228seL4_Word fault_get_fsr(fault_t *fault);
229
230/**
231 * Determine if a fault is a prefetch fault
232 * @param[in] fault  A handle to the fault
233 * @return           1 if the fault is a prefetch fault, otherwise 0
234 */
235int fault_is_prefetch(fault_t *fault);
236
237/**
238 * Determine if we should wait for an interrupt before
239 * resuming from the fault
240 * @param[in] fault A handle to the fault
241 */
242int fault_is_wfi(fault_t *fault);
243
244/**
245 * Determine if a fault is a vcpu fault
246 * @param[in] fault  A handle to the fault
247 * @return           1 if the fault is a vcpu fault, otherwise 0
248 */
249int fault_is_vcpu(fault_t *f);
250
251/**
252 * Determine if a fault was caused by a 32 bit instruction
253 * @param[in] fault  A handle to the fault
254 * @return           0 if it is a 16 bit instruction, otherwise, it is 32bit
255 */
256int fault_is_32bit_instruction(fault_t *f);
257
258/****************
259 ***  Helpers ***
260 ****************/
261
262static inline int fault_is_16bit_instruction(fault_t *f)
263{
264    return !fault_is_32bit_instruction(f);
265}
266
267static inline int fault_is_data(fault_t *f)
268{
269    return f->type == DATA;
270}
271
272static inline int fault_is_write(fault_t *f)
273{
274    return (fault_get_fsr(f) & (1U << 6));
275}
276
277static inline int fault_is_read(fault_t *f)
278{
279    return !fault_is_write(f);
280}
281
282static inline seL4_Word fault_get_addr_word(fault_t *f)
283{
284    return fault_get_address(f) & ~(0x3U);
285}
286
287seL4_Word *decode_rt(int reg, seL4_UserContext *c);
288int decode_vcpu_reg(int rt, fault_t *f);
289void fault_print_data(fault_t *fault);
290bool fault_is_thumb(fault_t *f);
291
292/***************
293 ***  Debug  ***
294 ***************/
295
296/**
297 * Prints a fault to the console
298 * @param[in] fault  A handle to a fault structure
299 */
300void print_fault(fault_t *fault);
301
302/**
303 * Prints contents of context registers to the console
304 * @param[in] regs   A handle to the VM registers to print
305 */
306void print_ctx_regs(seL4_UserContext *regs);
307