blob: 180d9d393d0fb6425d63c66629831b74002db562 [file]
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <vka/cspacepath_t.h>
#include <sel4vm/sel4_arch/processor.h>
typedef struct vm_vcpu vm_vcpu_t;
typedef struct fault fault_t;
enum fault_width {
WIDTH_DOUBLEWORD,
WIDTH_WORD,
WIDTH_HALFWORD,
WIDTH_BYTE
};
typedef enum {
DATA,
PREFETCH,
VCPU
} fault_type_t;
#define CPSR_THUMB BIT(5)
#define CPSR_IS_THUMB(x) ((x) & CPSR_THUMB)
/**
* Data structure representating a fault
*/
struct fault {
/// The vcpu associated with the fault
vm_vcpu_t *vcpu;
/// Reply capability to the faulting TCB
cspacepath_t reply_cap;
/// VM registers at the time of the fault
seL4_UserContext regs;
/// The IPA address of the fault
seL4_Word base_addr;
/// The IPA address of the fault at the current stage
seL4_Word addr;
/// The IPA of the instruction which caused the fault
seL4_Word ip;
/// The data which was to be written, or the data to return to the VM
seL4_Word data;
/// Fault status register (IL and ISS fields of HSR cp15 register)
seL4_Word fsr;
/// type of fault
fault_type_t type;
/// For multiple str/ldr and 32 bit access, the fault is handled in stages
int stage;
/// If the instruction requires fetching, cache it here
seL4_Word instruction;
/// The width of the fault
enum fault_width width;
/// The mode of the processor
processor_mode_t pmode;
/// The active content within the fault structure to allow lazy loading
int content;
};
typedef struct fault fault_t;
/**
* Initialise a fault structure.
* The structure will be bound to a VM and a reply cap slot
* will be reserved.
* @param[in] vm The VM that the fault structure should be bound to
* @return An initialised fault structure handle or NULL on failure
*/
fault_t* fault_init(vm_vcpu_t* vcpu);
/**
* Populate an initialised fault structure with fault data obtained from
* a pending VCPU fault message. The reply cap to the faulting TCB will
* also be saved
* @param[in] fault A handle to a fault structure
* @return 0 on success;
*/
int new_vcpu_fault(fault_t *fault, uint32_t hsr);
/**
* Populate an initialised fault structure with fault data obtained from
* a pending virtual memory fault IPC message. The reply cap to the faulting
* TCB will also be saved.
* @param[in] fault A handle to a fault structure
* @return 0 on success;
*/
int new_memory_fault(fault_t *fault);
/**
* Abandon the fault.
* Performs any necessary clean up of the fault structure once a fault
* has been serviced. The VM will not be restarted by a call to this function.
* @param[in] fault A handle to a fault structure
* @return 0 on success;
*/
int abandon_fault(fault_t *fault);
/**
* Restart the fault
* Return execution to the VM without modifying registers or advancing the
* program counter. This is useful when the suitable response to the fault
* is to map a frame.
* @param[in] fault A handle to a fault structure
* @return 0 on success;
*/
int restart_fault(fault_t *fault);
/**
* Ignore the fault.
* Advances the PC without modifying registers. Useful when VM reads and
* writes to invalid memory can be ignored.
* @param[in] fault A handle to a fault structure
* @return 0 on success;
*/
int ignore_fault(fault_t *fault);
/**
* Update register contents and return from a fault.
* Updates user registers based on the data field of the fault struction
* and replies to the faulting TCB to resume execution.
* @param[in] fault A handle to a fault structure
* @param[in] data The data word that the VM was attempting to access
* @return 0 on success;
*/
int advance_fault(fault_t *fault);
/**
* Emulates the faulting instruction of the VM on the provided data.
* This function does not modify the state of the fault, it only returns
* an updated representation of the given data based on the write operation
* that the VM was attempting to perform.
* @param[in] fault A handle to a fault structure
* @param[in] data The data word that the VM was attempting to access
* @return The updated data based on the operation that the VM
* was attempting to perform.
*/
seL4_Word fault_emulate(fault_t *fault, seL4_Word data);
/**
* Determine if a fault has been handled. This is useful for multi-stage faults
* @param[in] fault A handle to the fault
* @return 0 if the fault has not yet been handled, otherwise, further
* action is required
*/
int fault_handled(fault_t *fault);
/**
* Retrieve the data that the faulting thread is trying to write or the data
* that will be returned to the thread in case of a read fault.
* The fault must be a data fault.
* @param[in] fault A handle to the fault
* @return If it is a read fault, returns the data that will be returned
* to the thread when the fault is advanced. Otherwise, returns
* the data that the thread was attempting to write.
*/
seL4_Word fault_get_data(fault_t *fault);
/**
* Set the data that will be returned to the thread when the fault is advanced.
* The fault must be a data fault.
* @param[in] fault A handle to the fault
* @param[in] data The data to return to the thread.
*/
void fault_set_data(fault_t *fault, seL4_Word data);
/**
* Retrieve a mask for the data within the aligned word that the fault is
* attempting to access
* @param[in] fault A handle to the fault
* @return A mask for the data within the aligned word that the fault is
* attempting to access
*/
seL4_Word fault_get_data_mask(fault_t *fault);
/**
* Get the faulting address
* @param[in] fault A handle to the fault
* @return The address that the faulting thread was attempting to access
*/
seL4_Word fault_get_address(fault_t *fault);
/**
* Get the access width of the fault
* The fault must be a data fault.
* @param[in] fault A handle to the fault
* @return The access width of the fault
*/
enum fault_width fault_get_width(fault_t *f);
/**
* Get the access width size of the fault
* The fault must be a data fault.
* @param[in] fault A handle to the fault
* @return The access width size of the fault
*/
size_t fault_get_width_size(fault_t* f);
/**
* Get the context of a fault
* @param[in] fault A handle to the fault
* @return A handle to the fault context
*/
seL4_UserContext *fault_get_ctx(fault_t *fault);
/**
* Set the context of a fault
* @param[in] fault A handle to the fault
* @param[in] ctx A handle to the fault context
*/
void fault_set_ctx(fault_t *f, seL4_UserContext *ctx);
/**
* Get the fault status register of a fault
* @param[in] fault A handle to the fault
* @return the ARM HSR register associated with this fault. The EC
* field will be masked out.
*/
seL4_Word fault_get_fsr(fault_t *fault);
/**
* Determine if a fault is a prefetch fault
* @param[in] fault A handle to the fault
* @return 1 if the fault is a prefetch fault, otherwise 0
*/
int fault_is_prefetch(fault_t *fault);
/**
* Determine if we should wait for an interrupt before
* resuming from the fault
* @param[in] fault A handle to the fault
*/
int fault_is_wfi(fault_t *fault);
/**
* Determine if a fault is a vcpu fault
* @param[in] fault A handle to the fault
* @return 1 if the fault is a vcpu fault, otherwise 0
*/
int fault_is_vcpu(fault_t *f);
/**
* Determine if a fault was caused by a 32 bit instruction
* @param[in] fault A handle to the fault
* @return 0 if it is a 16 bit instruction, otherwise, it is 32bit
*/
int fault_is_32bit_instruction(fault_t *f);
/****************
*** Helpers ***
****************/
static inline int fault_is_16bit_instruction(fault_t *f)
{
return !fault_is_32bit_instruction(f);
}
static inline int fault_is_data(fault_t *f)
{
return f->type == DATA;
}
static inline int fault_is_write(fault_t *f)
{
return (fault_get_fsr(f) & (1U << 6));
}
static inline int fault_is_read(fault_t *f)
{
return !fault_is_write(f);
}
static inline seL4_Word fault_get_addr_word(fault_t *f)
{
return fault_get_address(f) & ~(0x3U);
}
seL4_Word *decode_rt(int reg, seL4_UserContext *c);
int decode_vcpu_reg(int rt, fault_t *f);
void fault_print_data(fault_t *fault);
bool fault_is_thumb(fault_t *f);
/***************
*** Debug ***
***************/
/**
* Prints a fault to the console
* @param[in] fault A handle to a fault structure
*/
void print_fault(fault_t *fault);
/**
* Prints contents of context registers to the console
* @param[in] regs A handle to the VM registers to print
*/
void print_ctx_regs(seL4_UserContext *regs);