1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
// SPDX-License-Identifier: GPL-2.0+
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "dexcr.h"
#include "reg.h"
#include "utils.h"
static jmp_buf generic_signal_jump_buf;
static void generic_signal_handler(int signum, siginfo_t *info, void *context)
{
longjmp(generic_signal_jump_buf, 0);
}
bool dexcr_exists(void)
{
struct sigaction old;
volatile bool exists;
old = push_signal_handler(SIGILL, generic_signal_handler);
if (setjmp(generic_signal_jump_buf))
goto out;
/*
* If the SPR is not recognised by the hardware it triggers
* a hypervisor emulation interrupt. If the kernel does not
* recognise/try to emulate it, we receive a SIGILL signal.
*
* If we do not receive a signal, assume we have the SPR or the
* kernel is trying to emulate it correctly.
*/
exists = false;
mfspr(SPRN_DEXCR_RO);
exists = true;
out:
pop_signal_handler(SIGILL, old);
return exists;
}
unsigned int pr_which_to_aspect(unsigned long which)
{
switch (which) {
case PR_PPC_DEXCR_SBHE:
return DEXCR_PR_SBHE;
case PR_PPC_DEXCR_IBRTPD:
return DEXCR_PR_IBRTPD;
case PR_PPC_DEXCR_SRAPD:
return DEXCR_PR_SRAPD;
case PR_PPC_DEXCR_NPHIE:
return DEXCR_PR_NPHIE;
default:
FAIL_IF_EXIT_MSG(true, "unknown PR aspect");
}
}
int pr_get_dexcr(unsigned long which)
{
return prctl(PR_PPC_GET_DEXCR, which, 0UL, 0UL, 0UL);
}
int pr_set_dexcr(unsigned long which, unsigned long ctrl)
{
return prctl(PR_PPC_SET_DEXCR, which, ctrl, 0UL, 0UL);
}
bool pr_dexcr_aspect_supported(unsigned long which)
{
if (pr_get_dexcr(which) == -1)
return errno == ENODEV;
return true;
}
bool pr_dexcr_aspect_editable(unsigned long which)
{
return pr_get_dexcr(which) & PR_PPC_DEXCR_CTRL_EDITABLE;
}
/*
* Just test if a bad hashchk triggers a signal, without checking
* for support or if the NPHIE aspect is enabled.
*/
bool hashchk_triggers(void)
{
struct sigaction old;
volatile bool triggers;
old = push_signal_handler(SIGILL, generic_signal_handler);
if (setjmp(generic_signal_jump_buf))
goto out;
triggers = true;
do_bad_hashchk();
triggers = false;
out:
pop_signal_handler(SIGILL, old);
return triggers;
}
unsigned int get_dexcr(enum dexcr_source source)
{
switch (source) {
case DEXCR:
return mfspr(SPRN_DEXCR_RO);
case HDEXCR:
return mfspr(SPRN_HDEXCR_RO);
case EFFECTIVE:
return mfspr(SPRN_DEXCR_RO) | mfspr(SPRN_HDEXCR_RO);
default:
FAIL_IF_EXIT_MSG(true, "bad enum dexcr_source");
}
}
void await_child_success(pid_t pid)
{
int wstatus;
FAIL_IF_EXIT_MSG(pid == -1, "fork failed");
FAIL_IF_EXIT_MSG(waitpid(pid, &wstatus, 0) == -1, "wait failed");
FAIL_IF_EXIT_MSG(!WIFEXITED(wstatus), "child did not exit cleanly");
FAIL_IF_EXIT_MSG(WEXITSTATUS(wstatus) != 0, "child exit error");
}
/*
* Perform a hashst instruction. The following components determine the result
*
* 1. The LR value (any register technically)
* 2. The SP value (also any register, but it must be a valid address)
* 3. A secret key managed by the kernel
*
* The result is stored to the address held in SP.
*/
void hashst(unsigned long lr, void *sp)
{
asm volatile ("addi 31, %0, 0;" /* set r31 (pretend LR) to lr */
"addi 30, %1, 8;" /* set r30 (pretend SP) to sp + 8 */
PPC_RAW_HASHST(31, -8, 30) /* compute hash into stack location */
: : "r" (lr), "r" (sp) : "r31", "r30", "memory");
}
/*
* Perform a hashchk instruction. A hash is computed as per hashst(),
* however the result is not stored to memory. Instead the existing
* value is read and compared against the computed hash.
*
* If they match, execution continues.
* If they differ, an interrupt triggers.
*/
void hashchk(unsigned long lr, void *sp)
{
asm volatile ("addi 31, %0, 0;" /* set r31 (pretend LR) to lr */
"addi 30, %1, 8;" /* set r30 (pretend SP) to sp + 8 */
PPC_RAW_HASHCHK(31, -8, 30) /* check hash at stack location */
: : "r" (lr), "r" (sp) : "r31", "r30", "memory");
}
void do_bad_hashchk(void)
{
unsigned long hash = 0;
hashst(0, &hash);
hash += 1;
hashchk(0, &hash);
}
|