mirror of
https://github.com/hlandau/tags-active-demo.git
synced 2025-05-09 21:07:23 +00:00
603 lines
12 KiB
C
603 lines
12 KiB
C
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdnoreturn.h>
|
|
|
|
/* PowerPC AS - Tags Active Demonstration Program
|
|
* for POWER9 PowerNV Systems (Talos, Blackbird, etc.) {{{1
|
|
* ===================================================
|
|
*/
|
|
|
|
#define NL "\n"
|
|
|
|
/* Hypercalls {{{2
|
|
* ==========
|
|
*/
|
|
|
|
/* Definitions {{{3
|
|
* -----------
|
|
*/
|
|
|
|
// Hypercalls
|
|
#define H_GET_TERM_CHAR 0x54
|
|
#define H_PUT_TERM_CHAR 0x58
|
|
|
|
// Hypercall Return Codes
|
|
#define H_SUCCESS 0
|
|
#define H_BUSY 1
|
|
|
|
typedef int64_t HResult;
|
|
|
|
/* HPutTermChar {{{3
|
|
* ------------
|
|
* Hypercall to write character to console.
|
|
*/
|
|
static HResult HPutTermChar(int64_t termno, char c)
|
|
{
|
|
/* H_PUT_TERM_CHAR(int64_t termno, int64_t len,
|
|
* uint64_t char0_7, uint64_t char8_15)
|
|
* → return_code
|
|
*/
|
|
register uint64_t r3 __asm__("r3") = H_PUT_TERM_CHAR; /* in: hypercall opcode, out: return code */
|
|
register uint64_t r4 __asm__("r4") = termno;
|
|
register uint64_t r5 __asm__("r5") = 1; /* len */
|
|
register uint64_t r6 __asm__("r6") = ((uint64_t)c) << 56;
|
|
|
|
__asm__ __volatile__ (
|
|
"sc 1"
|
|
:"=r"(r3)
|
|
:"r"(r3), "r"(r4), "r"(r5), "r"(r6)
|
|
);
|
|
|
|
return (HResult)r3;
|
|
}
|
|
|
|
/* HGetTermChar {{{3
|
|
* ------------
|
|
* Hypercall to get characters from console.
|
|
*/
|
|
static HResult HGetTermChar(int64_t termno, char *buf, uint64_t *buf_len)
|
|
{
|
|
register uint64_t r3 __asm__("r3") = H_GET_TERM_CHAR;
|
|
register uint64_t r4 __asm__("r4") = termno; /* in: termno, out: num bytes */
|
|
register uint64_t r5 __asm__("r5");
|
|
register uint64_t r6 __asm__("r6");
|
|
|
|
__asm__ __volatile__ (
|
|
"sc 1"
|
|
:"=r"(r3), "=r"(r4), "=r"(r5), "=r"(r6)
|
|
:"r"(r3), "r"(r4)
|
|
);
|
|
|
|
if (r3 == H_SUCCESS) {
|
|
for (uint64_t i = 0; i < r4; ++i)
|
|
buf[i] = ((i >= 8 ? r6 : r5) >> (56 - ((i % 8) * 8))) & 0xFF;
|
|
|
|
*buf_len = r4;
|
|
}
|
|
|
|
return (HResult)r3;
|
|
}
|
|
|
|
/* Architecture {{{2
|
|
* ============
|
|
*/
|
|
|
|
/* Definitions {{{3
|
|
* ===========
|
|
*/
|
|
#define BIT(N) (1UL<<(N)) /* Conventional bit numbering */
|
|
#define BBT(N) BIT(63-(N)) /* IBM bit numbering */
|
|
|
|
#define REG_MSR__SF BBT(0) /* 64-Bit Mode */
|
|
#define REG_MSR__TA BBT(1) /* Tags Active */
|
|
|
|
#define REG_XER__T02 BBT(41)
|
|
#define REG_XER__T07 BBT(42)
|
|
#define REG_XER__TAG BBT(43)
|
|
|
|
/* SETTAG instruction. Sets XER.TAG=1. Takes no operands. */
|
|
#define SETTAG ".long 0x7C0103E6\n"
|
|
|
|
/* Utility Functions {{{3
|
|
* =================
|
|
*/
|
|
|
|
/* MsrGet {{{4
|
|
* ------
|
|
* Get value of Power ISA MSR register.
|
|
*/
|
|
static inline uint64_t MsrGet(void)
|
|
{
|
|
register uint64_t r3 __asm__("r3");
|
|
|
|
__asm__ __volatile__ (
|
|
"mfmsr %0"
|
|
:"=r"(r3)
|
|
);
|
|
|
|
return r3;
|
|
}
|
|
|
|
/* MsrSet {{{4
|
|
* ------
|
|
* Set value of Power ISA MSR register.
|
|
*/
|
|
static inline void MsrSet(uint64_t msr)
|
|
{
|
|
__asm__ __volatile__ (
|
|
"mtmsrd %0"
|
|
::"r"(msr)
|
|
);
|
|
}
|
|
|
|
/* MsrMaskOr {{{4
|
|
* ---------
|
|
* Masks and ORs bits in the Power ISA MSR register.
|
|
*/
|
|
static inline void MsrMaskOr(uint64_t mask, uint64_t or_)
|
|
{
|
|
MsrSet((MsrGet() & ~mask) | or_);
|
|
}
|
|
|
|
/* XerGet {{{4
|
|
* ------
|
|
* Get value of Power ISA XER register.
|
|
*/
|
|
static inline uint64_t XerGet(void)
|
|
{
|
|
register uint64_t r3 __asm__("r3");
|
|
|
|
__asm__ __volatile__ (
|
|
"mfxer %0"
|
|
:"=r"(r3)
|
|
);
|
|
|
|
return r3;
|
|
}
|
|
|
|
/* Fat Pointer Management {{{2
|
|
* ======================
|
|
*/
|
|
|
|
/* Definitions {{{3
|
|
* -----------
|
|
*/
|
|
|
|
/* Structure to represent a 128-bit pointer. By convention,
|
|
* the 64-bit memory address (the actual pointer) goes in w[1];
|
|
* w[0] is reserved for metadata. Must be 16-byte aligned. */
|
|
typedef struct __attribute__((__aligned__(16))) {
|
|
uint64_t w[2];
|
|
} Ptr;
|
|
|
|
/* Ptr structure which never has tag bits set on it. Used to quickly
|
|
* clear XER.TAG. */
|
|
Ptr g_ptrDummy;
|
|
|
|
/* PPC AS-Specific Architecture Utilities {{{3
|
|
* ======================================
|
|
*/
|
|
|
|
/* XerTagSet {{{4
|
|
* ---------
|
|
* Sets XER.TAG=1 (XER bbt 43).
|
|
*/
|
|
static inline void XerTagSet(void)
|
|
{
|
|
__asm__ __volatile__ (
|
|
SETTAG
|
|
);
|
|
}
|
|
|
|
/* XerTagClear {{{4
|
|
* -----------
|
|
* Sets XER.TAG=0 (XER bbt 43).
|
|
*/
|
|
static inline void XerTagClear(void)
|
|
{
|
|
register uint64_t w0 __asm__("r4");
|
|
register uint64_t w1 __asm__("r5");
|
|
|
|
g_ptrDummy.w[0] = 0;
|
|
|
|
__asm__ __volatile__ (
|
|
"lq %0, 0(%1)"
|
|
:"=r"(w0), "=r"(w1)
|
|
:"r"(&g_ptrDummy)
|
|
);
|
|
}
|
|
|
|
/* TagsOn {{{4
|
|
* ------
|
|
* Sets MSR.TA=1 (MSR bbt 1).
|
|
*/
|
|
static inline void TagsOn(void)
|
|
{
|
|
MsrMaskOr(0, REG_MSR__TA);
|
|
}
|
|
|
|
/* TagsOff {{{4
|
|
* -------
|
|
* Sets MSR.TA=0 (MSR bbt 1).
|
|
*/
|
|
static inline void TagsOff(void)
|
|
{
|
|
MsrMaskOr(REG_MSR__TA, 0);
|
|
}
|
|
|
|
|
|
/* PtrBless {{{3
|
|
* --------
|
|
* Sets tag bit on the quadword at *p.
|
|
*
|
|
* Note: This would not be safe to use if the tag bit is being used to enforce
|
|
* a security invariant and concurrent access to *p by untrusted code is
|
|
* possible, due to race conditions. See PtrStoreBless for a more secure
|
|
* construction.
|
|
*/
|
|
static inline void PtrBless(Ptr *p)
|
|
{
|
|
register uint64_t w0 __asm__("r4");
|
|
register uint64_t w1 __asm__("r5");
|
|
|
|
__asm__ __volatile__ (
|
|
NL "lq %0, 0(%1)"
|
|
NL SETTAG
|
|
NL "stq %0, 0(%1)"
|
|
:"=&r"(w0), "=&r"(w1)
|
|
:"r"(p)
|
|
);
|
|
}
|
|
|
|
/* PtrUnbless {{{3
|
|
* ----------
|
|
* Clears tag bit on the quadword at *p.
|
|
*/
|
|
static inline void PtrUnbless(Ptr *p)
|
|
{
|
|
register uint64_t w0 __asm__("r4");
|
|
register uint64_t w1 __asm__("r5");
|
|
register uint64_t w2 __asm__("r6");
|
|
register uint64_t w3 __asm__("r7");
|
|
|
|
// Could also be implemented as:
|
|
// *(volatile uint8_t *)p |= 0;
|
|
// as any write clears the tag bits.
|
|
|
|
__asm__ __volatile__ (
|
|
NL "lq %0, 0(%4)"
|
|
NL "lq %2, 0(%5)"
|
|
NL "stq %0, 0(%4)"
|
|
:"=&r"(w0), "=&r"(w1), "=&r"(w2), "=&r"(w3)
|
|
:"r"(p), "r"(&g_ptrDummy)
|
|
);
|
|
}
|
|
|
|
/* PtrValid {{{3
|
|
* --------
|
|
* Returns true if tag bit is set on *p.
|
|
*/
|
|
static inline bool PtrValid(const Ptr *p)
|
|
{
|
|
register uint64_t w0 __asm__("r4");
|
|
register uint64_t w1 __asm__("r5");
|
|
uint64_t xer;
|
|
|
|
__asm__ __volatile__ (
|
|
NL "lq %0, 0(%3)"
|
|
NL "mfxer %2"
|
|
NL "lq %0, 0(%4)"
|
|
:"=&r"(w0), "=&r"(w1), "=&r"(xer)
|
|
:"r"(p), "r"(&g_ptrDummy)
|
|
);
|
|
|
|
return !!(xer & REG_XER__TAG);
|
|
}
|
|
|
|
/* PtrSel {{{3
|
|
* ------
|
|
* Demonstrates use of SELII instruction. Returns ifValid if tag bit is set on
|
|
* *p, ifInvalid otherwise.
|
|
*/
|
|
static inline uint64_t PtrSel(const Ptr *p, uint64_t ifValid, uint64_t ifInvalid)
|
|
{
|
|
register uint64_t r __asm__("r3");
|
|
register uint64_t w0 __asm__("r4");
|
|
register uint64_t w1 __asm__("r5");
|
|
register uint64_t ifValid_ __asm__("r6") = ifValid;
|
|
register uint64_t ifInvalid_ __asm__("r7") = ifInvalid;
|
|
|
|
__asm__ __volatile__ (
|
|
NL "lq %0, 0(%2)"
|
|
// selrr %r3, %r6, %r7, TAG // (TAG = 43-32 = 11)
|
|
NL ".long 0x78c33d9e"
|
|
NL "lq %0, 0(%3)"
|
|
:"=&r"(w0), "=&r"(w1)
|
|
:"r"(p), "r"(&g_ptrDummy), "r"(ifValid_), "r"(ifInvalid_)
|
|
);
|
|
|
|
return r;
|
|
}
|
|
|
|
/* PtrValidAlt {{{3
|
|
* -----------
|
|
* Demonstration alternate implementation of PtrValid(). Identical semantics.
|
|
*/
|
|
static inline bool PtrValidAlt(const Ptr *p)
|
|
{
|
|
return PtrSel(p, 1, 0);
|
|
}
|
|
|
|
/* PtrStoreBless {{{3
|
|
* -------------
|
|
* Store *src to *dst, blessing it in the process. Safer than PtrBless.
|
|
*
|
|
* Note: If using tag bits to enforce a security invariant, *src must be a
|
|
* trusted location in memory which cannot be concurrently accessed by
|
|
* untrusted code.
|
|
*/
|
|
static inline void PtrStoreBless(Ptr *dst, const Ptr *src)
|
|
{
|
|
__asm__ __volatile__ (
|
|
NL "lq %%r4, 0(%1)"
|
|
NL SETTAG
|
|
NL "stq %%r4, 0(%0)"
|
|
// Clear tag in XER
|
|
NL "lq %%r4, 0(%2)"
|
|
:: "r"(dst), "r"(src), "r"(&g_ptrDummy)
|
|
: "r4", "r5"
|
|
);
|
|
}
|
|
|
|
/* PtrCopy {{{3
|
|
* -------
|
|
* Copies *src to *dst. Any tag bits are preserved, if set.
|
|
*/
|
|
static inline void PtrCopy(Ptr *dst, const Ptr *src)
|
|
{
|
|
__asm__ __volatile__ (
|
|
NL "lq %%r4, 0(%1)"
|
|
NL "stq %%r4, 0(%0)"
|
|
// Clear tag in XER
|
|
NL "lq %%r4, 0(%2)"
|
|
:: "r"(dst), "r"(src), "r"(&g_ptrDummy)
|
|
: "r4", "r5"
|
|
);
|
|
}
|
|
|
|
/* PtrLoad {{{3
|
|
* -------
|
|
* Loads a pointer from *p to *dst. Returns true if the tag bit was set.
|
|
*/
|
|
static inline bool PtrLoad(const Ptr *p, Ptr *dst)
|
|
{
|
|
register uint64_t w0 __asm__("r4");
|
|
register uint64_t w1 __asm__("r5");
|
|
uint64_t xer;
|
|
|
|
__asm__ __volatile__ (
|
|
NL "lq %0, 0(%3)"
|
|
NL "mfxer %2"
|
|
:"=&r"(w0), "=&r"(w1), "=&r"(xer)
|
|
:"r"(p)
|
|
);
|
|
|
|
if (!(xer & REG_MSR__TA)) {
|
|
dst->w[0] = 0;
|
|
dst->w[1] = 0;
|
|
return false;
|
|
}
|
|
|
|
dst->w[0] = w0;
|
|
dst->w[1] = w1;
|
|
return true;
|
|
}
|
|
|
|
/* PtrLoadW1 {{{3
|
|
* ---------
|
|
* Loads the second half of *p, returning it if the tag bit is set and
|
|
* returning NULL otherwise. Uses the LTPTR instruction which was probably
|
|
* added in POWER7. Won't work on earlier machines. (See US patent
|
|
* US20090198967A1.)
|
|
*/
|
|
static inline void *PtrLoadW1(const Ptr *p)
|
|
{
|
|
register uint64_t w1 __asm__("r3") = (uint64_t)p;
|
|
|
|
// ltptr RT,DQ(RA),EPT
|
|
//
|
|
// 57 | RT | RA | DQ | EPT | 1 |
|
|
// 0:7 | 8:10 | 11:17 | 18:27 | 28:29 | 30:31 | fake (as shown in patent)
|
|
// 0:5 | 6:10 | 11:15 | 16:27 | 28:29 | 30:31 | real (guessed)
|
|
__asm__ __volatile__ (
|
|
// ltptr %r3, 0(%r3), 3
|
|
NL ".long 0xe463000d"
|
|
:"=r"(w1)
|
|
:"r"(w1)
|
|
);
|
|
|
|
return (void *)w1;
|
|
}
|
|
|
|
/* I/O Utilities {{{2
|
|
* =============
|
|
*/
|
|
|
|
/* PutChar {{{3
|
|
* -------
|
|
* Writes a character to the console.
|
|
*/
|
|
static void PutChar(char c)
|
|
{
|
|
while (HPutTermChar(0, c) == H_BUSY);
|
|
}
|
|
|
|
/* Print {{{3
|
|
* -----
|
|
*/
|
|
static void Print(const char *s)
|
|
{
|
|
while (*s)
|
|
PutChar(*s++);
|
|
}
|
|
|
|
/* Printf {{{3
|
|
* ------
|
|
*/
|
|
static char _HexChar(uint8_t x)
|
|
{
|
|
if (x >= 10)
|
|
return 'a' + x - 10;
|
|
return '0' + x;
|
|
}
|
|
|
|
static void PrintHex(uint64_t x)
|
|
{
|
|
for (int i = 60; i >= 0; i -= 4)
|
|
PutChar(_HexChar((x >> i) & 0xf));
|
|
}
|
|
|
|
static void PrintUInt(uint64_t x)
|
|
{
|
|
char buf[32], *p = buf + 31;
|
|
|
|
if (!x) {
|
|
PutChar('0');
|
|
return;
|
|
}
|
|
|
|
*p-- = '\0';
|
|
for (; x; x /= 10)
|
|
*p-- = '0' + x % 10;
|
|
|
|
Print(p + 1);
|
|
}
|
|
|
|
static void VPrintf(const char *fmt, va_list args)
|
|
{
|
|
char c;
|
|
bool esc = false;
|
|
|
|
for (;; ++fmt) {
|
|
if (esc) {
|
|
esc = false;
|
|
switch (c = *fmt) {
|
|
case '%':
|
|
PutChar('%');
|
|
break;
|
|
case 's':
|
|
Print(va_arg(args, const char *));
|
|
break;
|
|
case 'u':
|
|
PrintUInt(va_arg(args, uint64_t));
|
|
break;
|
|
case 'x':
|
|
PrintHex(va_arg(args, uint64_t));
|
|
break;
|
|
case 'c':
|
|
PutChar(va_arg(args, int));
|
|
break;
|
|
case '\0':
|
|
return;
|
|
default:
|
|
PutChar(c);
|
|
break;
|
|
}
|
|
} else switch (c = *fmt) {
|
|
case '%':
|
|
esc = true;
|
|
break;
|
|
case '\0':
|
|
return;
|
|
default:
|
|
PutChar(c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Printf(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
VPrintf(fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* Halt {{{3
|
|
* ----
|
|
* Halt and do not return.
|
|
*/
|
|
noreturn void Halt(void)
|
|
{
|
|
for (;;);
|
|
}
|
|
|
|
/* Initialization {{{2
|
|
* ==============
|
|
*/
|
|
|
|
/* BssInit {{{3
|
|
* -------
|
|
* Zeroes all of the BSS space.
|
|
*/
|
|
extern char _bss_start[];
|
|
extern char _bss_end[];
|
|
|
|
static void BssInit(void)
|
|
{
|
|
uint64_t *p = (uint64_t *)_bss_start,
|
|
*end = (uint64_t *)_bss_end;
|
|
|
|
while (p < end)
|
|
*p++ = 0;
|
|
}
|
|
|
|
/* Test Program {{{2
|
|
* ============
|
|
*/
|
|
Ptr test_ptr;
|
|
|
|
int main(void)
|
|
{
|
|
BssInit();
|
|
|
|
Print("\n\n\n============================================\n");
|
|
Print("TA Test Utility\n");
|
|
Printf("MSR: 0x%x SF=%u TA=%u\n", MsrGet(), !!(MsrGet() & REG_MSR__SF), !!(MsrGet() & REG_MSR__TA));
|
|
TagsOn();
|
|
Printf("MSR: 0x%x SF=%u TA=%u\n", MsrGet(), !!(MsrGet() & REG_MSR__SF), !!(MsrGet() & REG_MSR__TA));
|
|
if (!(MsrGet() & REG_MSR__TA)) {
|
|
Printf("ERROR: Cannot set Tags Active mode, environment not supported.\n");
|
|
Halt();
|
|
} else
|
|
Printf("SUCCESS: Successfully activated Tags Active mode.\n");
|
|
|
|
// Create an unblessed pointer.
|
|
Ptr p;
|
|
p.w[0] = 42;
|
|
p.w[1] = 69;
|
|
PtrCopy(&test_ptr, &p);
|
|
Printf("Test Pointer (pre-bless): Valid=%u Ptr=%x\n", PtrValid(&test_ptr), PtrLoadW1(&test_ptr));
|
|
if (PtrValid(&test_ptr) || PtrValidAlt(&test_ptr))
|
|
Printf("ERROR: Pointer should not be valid\n");
|
|
|
|
// Bless the pointer.
|
|
PtrStoreBless(&test_ptr, &p);
|
|
Printf("Test Pointer (post-bless): Valid=%u Ptr=%x\n", PtrValid(&test_ptr), PtrLoadW1(&test_ptr));
|
|
if (!PtrValid(&test_ptr) || !PtrValidAlt(&test_ptr))
|
|
Printf("ERROR: Pointer should be valid\n");
|
|
|
|
// Invalidate the pointer by modifying it.
|
|
test_ptr.w[1] = 69;
|
|
Printf("Test Pointer (post-write): Valid=%u Ptr=%x\n", PtrValid(&test_ptr), PtrLoadW1(&test_ptr));
|
|
if (PtrValid(&test_ptr) || PtrValidAlt(&test_ptr))
|
|
Printf("ERROR: Pointer should be valid\n");
|
|
|
|
Printf("Test program complete.\n\n\n");
|
|
Halt();
|
|
return 0;
|
|
}
|