|
| 1 | +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ |
| 2 | +/* |
| 3 | + * Copyright (c) 2016 Los Alamos National Security, LLC. All rights |
| 4 | + * reserved. |
| 5 | + * $COPYRIGHT$ |
| 6 | + * |
| 7 | + * Additional copyrights may follow |
| 8 | + * |
| 9 | + * $HEADER$ |
| 10 | + */ |
| 11 | + |
| 12 | +#include "opal_config.h" |
| 13 | + |
| 14 | +#include "opal/mca/patcher/patcher.h" |
| 15 | +#include "opal/mca/patcher/base/base.h" |
| 16 | +#include "opal/util/sys_limits.h" |
| 17 | +#include "opal/prefetch.h" |
| 18 | +#include <sys/mman.h> |
| 19 | + |
| 20 | +static void mca_patcher_base_patch_construct (mca_patcher_base_patch_t *patch) |
| 21 | +{ |
| 22 | + patch->patch_symbol = NULL; |
| 23 | + patch->patch_data_size = 0; |
| 24 | +} |
| 25 | + |
| 26 | +static void mca_patcher_base_patch_destruct (mca_patcher_base_patch_t *patch) |
| 27 | +{ |
| 28 | + free (patch->patch_symbol); |
| 29 | +} |
| 30 | + |
| 31 | +OBJ_CLASS_INSTANCE(mca_patcher_base_patch_t, opal_list_item_t, |
| 32 | + mca_patcher_base_patch_construct, |
| 33 | + mca_patcher_base_patch_destruct); |
| 34 | + |
| 35 | +#if defined(__PPC__) |
| 36 | + |
| 37 | +// PowerPC instructions used in patching |
| 38 | +// Reference: "PowerPC User Instruction Set Architecture" |
| 39 | +static unsigned int addis(unsigned int RT, unsigned int RS, unsigned int UI) { |
| 40 | + return (15<<26) + (RT<<21) + (RS<<16) + (UI&0xffff); |
| 41 | +} |
| 42 | +static unsigned int ori(unsigned int RT, unsigned int RS, unsigned int UI) { |
| 43 | + return (24<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); |
| 44 | +} |
| 45 | +static unsigned int oris(unsigned int RT, unsigned int RS, unsigned int UI) { |
| 46 | + return (25<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); |
| 47 | +} |
| 48 | +static unsigned int mtspr(unsigned int SPR, unsigned int RS) { |
| 49 | + return (31<<26) + (RS<<21) + ((SPR&0x1f)<<16) + ((SPR>>5)<<11) + (467<<1); |
| 50 | +} |
| 51 | +static unsigned int bcctr(unsigned int BO, unsigned int BI, unsigned int BH) { |
| 52 | + return (19<<26) + (BO<<21) + (BI<<16) + (BH<<11) + (528<<1); |
| 53 | +} |
| 54 | +static unsigned int rldicr(unsigned int RT, unsigned int RS, unsigned int SH, unsigned int MB) |
| 55 | +{ |
| 56 | + return (30<<26) + (RS<<21) + (RT<<16) + ((SH&0x1f)<<11) + ((SH>>5)<<1) |
| 57 | + + ((MB&0x1f)<<6) + ((MB>>5)<<5) + (1<<2); |
| 58 | +} |
| 59 | + |
| 60 | +static int PatchLoadImm (uintptr_t addr, unsigned int reg, size_t value) |
| 61 | +{ |
| 62 | +#if defined(__PPC64__) |
| 63 | + *(unsigned int *) (addr + 0) = addis ( reg, 0, (value >> 48)); |
| 64 | + *(unsigned int *) (addr + 4) = ori ( reg, reg, (value >> 32)); |
| 65 | + *(unsigned int *) (addr + 8) = rldicr( reg, reg, 32, 31); |
| 66 | + *(unsigned int *) (addr +12) = oris ( reg, reg, (value >> 16)); |
| 67 | + *(unsigned int *) (addr +16) = ori ( reg, reg, (value >> 0)); |
| 68 | + return 20; |
| 69 | +#else |
| 70 | + *(unsigned int *) (addr + 0) = addis ( reg, 0, (value >> 16)); |
| 71 | + *(unsigned int *) (addr + 4) = ori ( reg, reg, (value >> 0)); |
| 72 | + return 8; |
| 73 | +#endif |
| 74 | +} |
| 75 | + |
| 76 | +#endif |
| 77 | + |
| 78 | +#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) |
| 79 | + |
| 80 | +static void flush_and_invalidate_cache (unsigned long a) |
| 81 | +{ |
| 82 | +#if defined(__i386__) |
| 83 | + /* does not work with AMD processors */ |
| 84 | + __asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a)); |
| 85 | +#elif defined(__x86_64__) |
| 86 | + __asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a)); |
| 87 | +#elif defined(__ia64__) |
| 88 | + __asm__ volatile ("fc %0;; sync.i;; srlz.i;;" : : "r"(a) : "memory"); |
| 89 | +#endif |
| 90 | +} |
| 91 | +#endif |
| 92 | + |
| 93 | +// modify protection of memory range |
| 94 | +static void ModifyMemoryProtection (uintptr_t addr, size_t length, int prot) |
| 95 | +{ |
| 96 | + long page_size = opal_getpagesize (); |
| 97 | + uintptr_t base = (addr & ~(page_size-1)); |
| 98 | + uintptr_t bound = ((addr + length + page_size-1) & ~(page_size-1)); |
| 99 | + |
| 100 | + length = bound - base; |
| 101 | + |
| 102 | +#if defined(__PPC__) |
| 103 | + /* NTH: is a loop necessary here? */ |
| 104 | + do { |
| 105 | + if (mprotect((void *)base, page_size, prot)) |
| 106 | + perror("MemHook: mprotect failed"); |
| 107 | + base += page_size; |
| 108 | + } while (base < addr + length); |
| 109 | +#else |
| 110 | + if (mprotect((void *) base, length, prot)) { |
| 111 | + perror("MemHook: mprotect failed"); |
| 112 | + } |
| 113 | +#endif |
| 114 | +} |
| 115 | + |
| 116 | +static inline void apply_patch (unsigned char *patch_data, uintptr_t address, size_t data_size) |
| 117 | +{ |
| 118 | + ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ|PROT_WRITE); |
| 119 | + memcpy ((void *) address, patch_data, data_size); |
| 120 | +#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) |
| 121 | + for (size_t i = 0 ; i < data_size ; i += 16) { |
| 122 | + flush_and_invalidate_cache (address + i); |
| 123 | + } |
| 124 | +#endif |
| 125 | + |
| 126 | + ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ); |
| 127 | +} |
| 128 | + |
| 129 | +static void mca_base_patcher_patch_unapply_binary (mca_patcher_base_patch_t *patch) |
| 130 | +{ |
| 131 | + apply_patch (patch->patch_orig_data, patch->patch_orig, patch->patch_data_size); |
| 132 | +} |
| 133 | + |
| 134 | +void mca_base_patcher_patch_apply_binary (mca_patcher_base_patch_t *patch) |
| 135 | +{ |
| 136 | + memcpy (patch->patch_orig_data, (void *) patch->patch_orig, patch->patch_data_size); |
| 137 | + apply_patch (patch->patch_data, patch->patch_orig, patch->patch_data_size); |
| 138 | + patch->patch_restore = mca_base_patcher_patch_unapply_binary; |
| 139 | +} |
| 140 | + |
| 141 | + |
| 142 | +int mca_patcher_base_patch_hook (mca_patcher_base_module_t *module, uintptr_t hook_addr) |
| 143 | +{ |
| 144 | +#if defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__) |
| 145 | + mca_patcher_base_patch_t *hook_patch; |
| 146 | + const unsigned int nop = 0x60000000; |
| 147 | + unsigned int *nop_addr; |
| 148 | + |
| 149 | + fprintf (stderr, "Patching hook @ 0x%lx\n", hook_addr); |
| 150 | + |
| 151 | + hook_patch = OBJ_NEW(mca_patcher_base_patch_t); |
| 152 | + if (OPAL_UNLIKELY(NULL == hook_patch)) { |
| 153 | + return OPAL_ERR_OUT_OF_RESOURCE; |
| 154 | + } |
| 155 | + |
| 156 | + // locate reserved code space in hook function |
| 157 | + for (nop_addr = (unsigned int *)hook_addr ; ; nop_addr++) { |
| 158 | + if (nop_addr[0] == nop && nop_addr[1] == nop && nop_addr[2] == nop |
| 159 | + && nop_addr[3] == nop && nop_addr[4] == nop) { |
| 160 | + break; |
| 161 | + } |
| 162 | + } |
| 163 | + // generate code to restore TOC |
| 164 | + register unsigned long toc asm("r2"); |
| 165 | + hook_patch->patch_orig = (uintptr_t) nop_addr; |
| 166 | + hook_patch->patch_data_size = PatchLoadImm((uintptr_t)hook_patch->patch_data, 2, toc); |
| 167 | + |
| 168 | + /* put the hook patch on the patch list so it will be undone on finalize */ |
| 169 | + opal_list_append (&module->patch_list, &hook_patch->super); |
| 170 | + |
| 171 | + mca_base_patcher_patch_apply_binary (hook_patch); |
| 172 | +#endif |
| 173 | + |
| 174 | + return OPAL_SUCCESS; |
| 175 | +} |
0 commit comments