#include "umem_base.h"
#include <inttypes.h>
#include <strings.h>
#include <umem_impl.h>
#include <atomic.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
const int umem_genasm_supported = 1;
static uintptr_t umem_genasm_mptr = (uintptr_t)&_malloc;
static size_t umem_genasm_msize = 576;
static uintptr_t umem_genasm_fptr = (uintptr_t)&_free;
static size_t umem_genasm_fsize = 576;
static uintptr_t umem_genasm_omptr = (uintptr_t)umem_malloc;
static uintptr_t umem_genasm_ofptr = (uintptr_t)umem_malloc_free;
#define UMEM_GENASM_MAX64 (UINT32_MAX / sizeof (uintptr_t))
#define PTC_JMPADDR(dest, src) (dest - (src + 4))
#define PTC_ROOT_SIZE sizeof (uintptr_t)
#define MULTINOP 0x0000441f0f
#define PTC_MALINIT_JOUT 0x13
#define PTC_MALINIT_MCS 0x1a
#define PTC_MALINIT_JOV 0x20
#define PTC_MALINIT_SOFF 0x30
static const uint8_t malinit[] = {
0x48, 0x8d, 0x77, 0x08,
0x48, 0x83, 0xfe, 0x10,
0x76, 0x04,
0x48, 0x8d, 0x77, 0x10,
0x48, 0x39, 0xfe,
0x0f, 0x82, 0x00, 0x00, 0x00, 0x00,
0x48, 0x81, 0xfe,
0x00, 0x00, 0x00, 0x00,
0x0f, 0x87, 0x00, 0x00, 0x00, 0x00,
0x64, 0x48, 0x8b, 0x0c, 0x25,
0x00, 0x00, 0x00, 0x00,
0x48, 0x81, 0xc1,
0x00, 0x00, 0x00, 0x00,
0x48, 0x8d, 0x51, 0x08,
};
#define PTC_FRINI_JDONE 0x05
#define PTC_FRINI_JFREE 0x25
#define PTC_FRINI_MCS 0x30
#define PTC_FRINI_JOV 0x36
#define PTC_FRINI_SOFF 0x46
static const uint8_t freeinit[] = {
0x48, 0x85, 0xff,
0x0f, 0x84, 0x00, 0x00, 0x00, 0x00,
0x8b, 0x77, 0xf8,
0x8b, 0x47, 0xfc,
0x01, 0xf0,
0x3d, 0x00, 0x70, 0xba, 0x16,
0x75, 0x06,
0x48, 0x8d, 0x47, 0xf0,
0xeb, 0x0f,
0x3d, 0x00, 0xc0, 0x10, 0x3a,
0x0f, 0x85, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8d, 0x47, 0xf8,
0x48, 0x81, 0xfe,
0x00, 0x00, 0x00, 0x00,
0x0f, 0x87, 0x00, 0x00, 0x00, 0x00,
0x64, 0x48, 0x8b, 0x0c, 0x25,
0x00, 0x00, 0x00, 0x00,
0x48, 0x81, 0xc1,
0x00, 0x00, 0x00, 0x00,
0x48, 0x8d, 0x51, 0x08,
};
#define PTC_INICACHE_CMP 0x03
#define PTC_INICACHE_SIZE 0x0c
#define PTC_INICACHE_JMP 0x11
static const uint8_t inicache[] = {
0x48, 0x81, 0xfe,
0x00, 0x00, 0x00, 0x00,
0x77, 0x0c,
0x49, 0xc7, 0xc0,
0x00, 0x00, 0x00, 0x00,
0xe9, 0x00, 0x00, 0x00, 0x00,
};
#define PTC_GENCACHE_CMP 0x03
#define PTC_GENCACHE_SIZE 0x0c
#define PTC_GENCACHE_NUM 0x13
#define PTC_GENCACHE_JMP 0x18
static const uint8_t gencache[] = {
0x48, 0x81, 0xfe,
0x00, 0x00, 0x00, 0x00,
0x77, 0x14,
0x49, 0xc7, 0xc0,
0x00, 0x00, 0x00, 0x00,
0x48, 0x81, 0xc2,
0x00, 0x00, 0x00, 0x00,
0xe9, 0x00, 0x00, 0x00, 0x00
};
#define PTC_FINCACHE_CMP 0x03
#define PTC_FINCACHE_JMP 0x08
#define PTC_FINCACHE_SIZE 0x0c
#define PTC_FINCACHE_NUM 0x13
static const uint8_t fincache[] = {
0x48, 0x81, 0xfe,
0x00, 0x00, 0x00, 0x00,
0x77, 0x00,
0x49, 0xc7, 0xc0,
0x00, 0x00, 0x00, 0x00,
0x48, 0x81, 0xc2,
0x00, 0x00, 0x00, 0x00,
};
#define PTC_MALFINI_ALLABEL 0x00
#define PTC_MALFINI_JMLABEL 0x40
#define PTC_MALFINI_JMADDR 0x41
static const uint8_t malfini[] = {
0x48, 0x8b, 0x02,
0x48, 0x85, 0xc0,
0x74, 0x38,
0x4c, 0x8b, 0x08,
0x4c, 0x89, 0x0a,
0x4c, 0x29, 0x01,
0x48, 0x83, 0xfe, 0x10,
0x76, 0x15,
0x41, 0xb9, 0x00, 0x70, 0xba, 0x16,
0x89, 0x70, 0x08,
0x41, 0x29, 0xf1,
0x44, 0x89, 0x48, 0x0c,
0x48, 0x83, 0xc0, 0x10,
0xc3,
0x41, 0xb9, 0x00, 0xc0, 0x10, 0x3a,
0x89, 0x30,
0x41, 0x29, 0xf1,
0x44, 0x89, 0x48, 0x04,
0x48, 0x83, 0xc0, 0x08,
0xc3,
0xe9, 0x00, 0x00, 0x00, 0x00
};
#define PTC_FRFINI_RBUFLABEL 0x00
#define PTC_FRFINI_CACHEMAX 0x09
#define PTC_FRFINI_DONELABEL 0x1b
#define PTC_FRFINI_JFLABEL 0x1c
#define PTC_FRFINI_JFADDR 0x1d
static const uint8_t freefini[] = {
0x4c, 0x8b, 0x09,
0x4d, 0x01, 0xc1,
0x49, 0x81, 0xf9,
0x00, 0x00, 0x00, 0x00,
0x77, 0x0d,
0x4c, 0x01, 0x01,
0x4c, 0x8b, 0x0a,
0x4c, 0x89, 0x08,
0x48, 0x89, 0x02,
0xc3,
0xe9, 0x00, 0x00, 0x00, 0x00
};
static int
genasm_malinit(uint8_t *bp, uint32_t off, uint32_t ep, uint32_t csize)
{
uint32_t addr;
bcopy(malinit, bp, sizeof (malinit));
addr = PTC_JMPADDR(ep, PTC_MALINIT_JOUT);
bcopy(&addr, bp + PTC_MALINIT_JOUT, sizeof (addr));
bcopy(&csize, bp + PTC_MALINIT_MCS, sizeof (csize));
addr = PTC_JMPADDR(ep, PTC_MALINIT_JOV);
bcopy(&addr, bp + PTC_MALINIT_JOV, sizeof (addr));
bcopy(&off, bp + PTC_MALINIT_SOFF, sizeof (off));
return (sizeof (malinit));
}
static int
genasm_frinit(uint8_t *bp, uint32_t off, uint32_t dp, uint32_t ep, uint32_t mcs)
{
uint32_t addr;
bcopy(freeinit, bp, sizeof (freeinit));
addr = PTC_JMPADDR(dp, PTC_FRINI_JDONE);
bcopy(&addr, bp + PTC_FRINI_JDONE, sizeof (addr));
addr = PTC_JMPADDR(ep, PTC_FRINI_JFREE);
bcopy(&addr, bp + PTC_FRINI_JFREE, sizeof (addr));
bcopy(&mcs, bp + PTC_FRINI_MCS, sizeof (mcs));
addr = PTC_JMPADDR(ep, PTC_FRINI_JOV);
bcopy(&addr, bp + PTC_FRINI_JOV, sizeof (addr));
bcopy(&off, bp + PTC_FRINI_SOFF, sizeof (off));
return (sizeof (freeinit));
}
static int
genasm_firstcache(uint8_t *bp, uint32_t csize, uint32_t ap)
{
uint32_t addr;
bcopy(inicache, bp, sizeof (inicache));
bcopy(&csize, bp + PTC_INICACHE_CMP, sizeof (csize));
bcopy(&csize, bp + PTC_INICACHE_SIZE, sizeof (csize));
addr = PTC_JMPADDR(ap, PTC_INICACHE_JMP);
ASSERT(addr != 0);
bcopy(&addr, bp + PTC_INICACHE_JMP, sizeof (addr));
return (sizeof (inicache));
}
static int
genasm_gencache(uint8_t *bp, int num, uint32_t csize, uint32_t ap)
{
uint32_t addr;
uint32_t coff;
ASSERT(UINT32_MAX / PTC_ROOT_SIZE > num);
ASSERT(num != 0);
bcopy(gencache, bp, sizeof (gencache));
bcopy(&csize, bp + PTC_GENCACHE_CMP, sizeof (csize));
bcopy(&csize, bp + PTC_GENCACHE_SIZE, sizeof (csize));
coff = num * PTC_ROOT_SIZE;
bcopy(&coff, bp + PTC_GENCACHE_NUM, sizeof (coff));
addr = PTC_JMPADDR(ap, PTC_GENCACHE_JMP);
bcopy(&addr, bp + PTC_GENCACHE_JMP, sizeof (addr));
return (sizeof (gencache));
}
static int
genasm_lastcache(uint8_t *bp, int num, uint32_t csize, uint32_t ep)
{
uint8_t eap;
uint32_t coff;
ASSERT(ep <= 0xff && ep > 7);
ASSERT(UINT32_MAX / PTC_ROOT_SIZE > num);
bcopy(fincache, bp, sizeof (fincache));
bcopy(&csize, bp + PTC_FINCACHE_CMP, sizeof (csize));
bcopy(&csize, bp + PTC_FINCACHE_SIZE, sizeof (csize));
coff = num * PTC_ROOT_SIZE;
bcopy(&coff, bp + PTC_FINCACHE_NUM, sizeof (coff));
eap = ep - PTC_FINCACHE_JMP - 1;
bcopy(&eap, bp + PTC_FINCACHE_JMP, sizeof (eap));
return (sizeof (fincache));
}
static int
genasm_malfini(uint8_t *bp, uintptr_t mptr)
{
uint32_t addr;
bcopy(malfini, bp, sizeof (malfini));
addr = PTC_JMPADDR(mptr, ((uintptr_t)bp + PTC_MALFINI_JMADDR));
bcopy(&addr, bp + PTC_MALFINI_JMADDR, sizeof (addr));
return (sizeof (malfini));
}
static int
genasm_frfini(uint8_t *bp, uint32_t maxthr, uintptr_t fptr)
{
uint32_t addr;
bcopy(freefini, bp, sizeof (freefini));
bcopy(&maxthr, bp + PTC_FRFINI_CACHEMAX, sizeof (maxthr));
addr = PTC_JMPADDR(fptr, ((uintptr_t)bp + PTC_FRFINI_JFADDR));
bcopy(&addr, bp + PTC_FRFINI_JFADDR, sizeof (addr));
return (sizeof (freefini));
}
static int
genasm_malloc(void *base, size_t len, int nents, int *umem_alloc_sizes)
{
int ii, off;
uint8_t *bp;
size_t total;
uint32_t allocoff, erroff;
total = sizeof (malinit) + sizeof (malfini) + sizeof (fincache);
if (nents >= 2)
total += sizeof (inicache) + sizeof (gencache) * (nents - 2);
if (total > len)
return (1);
erroff = total - sizeof (malfini) + PTC_MALFINI_JMLABEL;
allocoff = total - sizeof (malfini) + PTC_MALFINI_ALLABEL;
bp = base;
off = genasm_malinit(bp, umem_tmem_off, erroff,
umem_alloc_sizes[nents-1]);
bp += off;
allocoff -= off;
erroff -= off;
if (nents > 1) {
off = genasm_firstcache(bp, umem_alloc_sizes[0], allocoff);
bp += off;
allocoff -= off;
erroff -= off;
}
for (ii = 1; ii < nents - 1; ii++) {
off = genasm_gencache(bp, ii, umem_alloc_sizes[ii], allocoff);
bp += off;
allocoff -= off;
erroff -= off;
}
bp += genasm_lastcache(bp, nents - 1, umem_alloc_sizes[nents - 1],
erroff);
bp += genasm_malfini(bp, umem_genasm_omptr);
ASSERT(((uintptr_t)bp - total) == (uintptr_t)base);
return (0);
}
static int
genasm_free(void *base, size_t len, int nents, int *umem_alloc_sizes)
{
uint8_t *bp;
int ii, off;
size_t total;
uint32_t rbufoff, retoff, erroff;
total = sizeof (freeinit) + sizeof (freefini) + sizeof (fincache);
if (nents >= 2)
total += sizeof (inicache) + sizeof (gencache) * (nents - 2);
if (total > len)
return (1);
erroff = total - (sizeof (freefini) - PTC_FRFINI_JFLABEL);
rbufoff = total - (sizeof (freefini) - PTC_FRFINI_RBUFLABEL);
retoff = total - (sizeof (freefini) - PTC_FRFINI_DONELABEL);
bp = base;
off = genasm_frinit(bp, umem_tmem_off, retoff, erroff,
umem_alloc_sizes[nents - 1]);
bp += off;
erroff -= off;
rbufoff -= off;
if (nents > 1) {
off = genasm_firstcache(bp, umem_alloc_sizes[0], rbufoff);
bp += off;
erroff -= off;
rbufoff -= off;
}
for (ii = 1; ii < nents - 1; ii++) {
off = genasm_gencache(bp, ii, umem_alloc_sizes[ii], rbufoff);
bp += off;
rbufoff -= off;
erroff -= off;
}
bp += genasm_lastcache(bp, nents - 1, umem_alloc_sizes[nents - 1],
erroff);
bp += genasm_frfini(bp, umem_ptc_size, umem_genasm_ofptr);
ASSERT(((uintptr_t)bp - total) == (uintptr_t)base);
return (0);
}
boolean_t
umem_genasm(int *cp, umem_cache_t **caches, int nc)
{
int nents, i;
uint8_t *mptr;
uint8_t *fptr;
uint64_t v, *vptr;
size_t mplen, fplen;
uintptr_t mpbase, fpbase;
boolean_t ret = B_FALSE;
mptr = (void *)((uintptr_t)umem_genasm_mptr + 5);
fptr = (void *)((uintptr_t)umem_genasm_fptr + 5);
if (umem_genasm_mptr == 0 || umem_genasm_msize == 0 ||
umem_genasm_fptr == 0 || umem_genasm_fsize == 0) {
return (B_FALSE);
}
mplen = P2ROUNDUP(umem_genasm_msize, pagesize);
mpbase = P2ALIGN((uintptr_t)umem_genasm_mptr, pagesize);
fplen = P2ROUNDUP(umem_genasm_fsize, pagesize);
fpbase = P2ALIGN((uintptr_t)umem_genasm_mptr, pagesize);
if (P2ALIGN(umem_genasm_msize + (uintptr_t)umem_genasm_mptr,
pagesize) != mpbase) {
mplen += pagesize;
}
if (P2ALIGN(umem_genasm_fsize + (uintptr_t)umem_genasm_fptr,
pagesize) != fpbase) {
fplen += pagesize;
}
if (mprotect((void *)mpbase, mplen, PROT_READ | PROT_WRITE |
PROT_EXEC) != 0) {
return (B_FALSE);
}
if (mprotect((void *)fpbase, fplen, PROT_READ | PROT_WRITE |
PROT_EXEC) != 0) {
if (mprotect((void *)mpbase, mplen, PROT_READ | PROT_EXEC) !=
0) {
umem_panic("genasm failed to restore memory "
"protection: %d", errno);
}
return (B_FALSE);
}
nents = _tmem_get_nentries();
if (UMEM_GENASM_MAX64 < nents)
nents = UMEM_GENASM_MAX64;
if (nc < nents)
nents = nc;
if (nents == 0 || umem_ptc_size == 0) {
goto out;
}
if (genasm_malloc(mptr, umem_genasm_msize, nents, cp) != 0) {
goto out;
}
if (genasm_free(fptr, umem_genasm_fsize, nents, cp) != 0) {
goto out;
}
vptr = (void *)umem_genasm_mptr;
v = MULTINOP;
v |= *vptr & (0xffffffULL << 40);
(void) atomic_swap_64(vptr, v);
vptr = (void *)umem_genasm_fptr;
v = MULTINOP;
v |= *vptr & (0xffffffULL << 40);
(void) atomic_swap_64(vptr, v);
for (i = 0; i < nents; i++)
caches[i]->cache_flags |= UMF_PTC;
ret = B_TRUE;
out:
if (mprotect((void *)mpbase, mplen, PROT_READ | PROT_EXEC) != 0) {
umem_panic("genasm failed to restore memory protection: %d",
errno);
}
if (mprotect((void *)fpbase, fplen, PROT_READ | PROT_EXEC) != 0) {
umem_panic("genasm failed to restore memory protection: %d",
errno);
}
return (ret);
}