blob: f685aa9f3df761c8ef56a85f3824973195afc31a [file] [log] [blame] [edit]
/*
* Tests guarded storage support.
*
* Copyright 2018 IBM Corp.
*
* Authors:
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Janosch Frank <frankja@de.ibm.com>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#include <libcflat.h>
#include <asm/page.h>
#include <asm/facility.h>
#include <asm/interrupt.h>
#include <asm-generic/barrier.h>
struct gs_cb {
uint64_t reserved;
uint64_t gsd;
uint64_t gssm;
uint64_t gs_epl_a;
};
struct gs_epl {
uint8_t pad1;
union {
uint8_t gs_eam;
struct {
uint8_t : 6;
uint8_t e : 1;
uint8_t b : 1;
};
};
union {
uint8_t gs_eci;
struct {
uint8_t tx : 1;
uint8_t cx : 1;
uint8_t : 5;
uint8_t in : 1;
};
};
union {
uint8_t gs_eai;
struct {
uint8_t : 1;
uint8_t t : 1;
uint8_t as : 2;
uint8_t ar : 4;
};
};
uint32_t pad2;
uint64_t gs_eha;
uint64_t gs_eia;
uint64_t gs_eoa;
uint64_t gs_eir;
uint64_t gs_era;
};
static volatile int guarded = 0;
static struct gs_cb gs_cb;
static struct gs_epl gs_epl;
static unsigned long gs_area = 0x2000000;
void gs_handler(struct gs_cb *this_cb);
static inline void load_gs_cb(struct gs_cb *gs_cb)
{
asm volatile(".insn rxy,0xe3000000004d,0,%0" : : "Q" (*gs_cb));
}
static inline void store_gs_cb(struct gs_cb *gs_cb)
{
asm volatile(".insn rxy,0xe30000000049,0,%0" : : "Q" (*gs_cb));
}
static inline unsigned long load_guarded(unsigned long *p)
{
unsigned long v;
asm(".insn rxy,0xe3000000004c, %0,%1"
: "=d" (v)
: "m" (*p)
: "r14", "memory");
return v;
}
/* guarded-storage event handler and finally it calls gs_handler */
extern void gs_handler_asm(void);
asm(".globl gs_handler_asm\n"
"gs_handler_asm:\n"
" lgr %r14,%r15\n" /* Save current stack address in r14 */
" aghi %r15,-320\n" /* Allocate stack frame */
" stmg %r0,%r13,192(%r15)\n" /* Store regs to save area */
" stg %r14,312(%r15)\n"
" la %r2,160(%r15)\n" /* Store gscb address in this_cb */
" .insn rxy,0xe30000000049,0,160(%r15)\n" /* stgsc */
" lg %r14,24(%r2)\n" /* Get GSEPLA from GSCB*/
" lg %r14,40(%r14)\n" /* Get GSERA from GSEPL*/
" stg %r14,304(%r15)\n" /* Store GSERA in r14 of reg save area */
" brasl %r14,gs_handler\n" /* Jump to gs_handler */
" lmg %r0,%r15,192(%r15)\n" /* Restore regs */
" aghi %r14, 6\n" /* Add lgg instr len to GSERA */
" br %r14\n" /* Jump to next instruction after lgg */
" .size gs_handler_asm,.-gs_handler_asm\n");
void gs_handler(struct gs_cb *this_cb)
{
guarded = 1;
struct gs_epl *gs_epl = (struct gs_epl *) this_cb->gs_epl_a;
printf("gs_handler called for %016lx at %016lx\n",
gs_epl->gs_eir, gs_epl->gs_eia);
}
/* Test if load guarded gets intercepted. */
static void test_load(void)
{
unsigned long v;
guarded = 0;
v = load_guarded(&gs_area);
report(guarded, "load guarded %ld", v);
guarded = 0;
}
/* Test gs instructions without enablement resulting in an exception */
static void test_special(void)
{
report_prefix_push("disabled gs");
report_prefix_push("load gs");
expect_pgm_int();
load_gs_cb(&gs_cb);
check_pgm_int_code(PGM_INT_CODE_SPECIAL_OPERATION);
report_prefix_pop();
report_prefix_push("store gs");
expect_pgm_int();
store_gs_cb(&gs_cb);
check_pgm_int_code(PGM_INT_CODE_SPECIAL_OPERATION);
report_prefix_pop();
report_prefix_pop();
}
static void init(void)
{
/* Enable control bit for gs */
ctl_set_bit(2, 4);
/* Setup gs registers to guard the gs_area */
gs_cb.gsd = gs_area | 25;
/* Check all 512kb slots for events */
gs_cb.gssm = 0xffffffffffffffffULL;
gs_cb.gs_epl_a = (unsigned long) &gs_epl;
/* Register handler */
gs_epl.gs_eha = (unsigned long) gs_handler_asm;
load_gs_cb(&gs_cb);
}
int main(void)
{
bool has_gs = test_facility(133);
report_prefix_push("gs");
if (!has_gs) {
report_skip("Guarded storage is not available");
goto done;
}
test_special();
init();
test_load();
done:
report_prefix_pop();
return report_summary();
}