#include <sys/param.h>
#include <sys/systm.h>
#include <machine/codepatch.h>
#include <machine/cpu.h>
#include <uvm/uvm_extern.h>
#ifdef CODEPATCH_DEBUG
#define DBGPRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
#else
#define DBGPRINT(fmt, args...) do {} while (0)
#endif
struct codepatch {
vaddr_t addr;
uint16_t len;
uint16_t tag;
};
extern struct codepatch codepatch_begin;
extern struct codepatch codepatch_end;
void
codepatch_fill_nop(void *caddr, uint16_t len)
{
unsigned char *addr = caddr;
uint16_t nop_len;
if ((strcmp(cpu_vendor, "GenuineIntel") != 0) &&
(strcmp(cpu_vendor, "AuthenticAMD") != 0)) {
goto singlebyte;
}
switch (cpu_id & 0xf00) {
case 0x600:
case 0xf00:
break;
default:
goto singlebyte;
}
while (len > 0) {
nop_len = len < 127 ? len : 127;
switch (nop_len) {
case 1:
addr[0] = 0x90;
break;
case 2:
addr[0] = 0x66;
addr[1] = 0x90;
break;
default:
addr[0] = 0xEB;
addr[1] = nop_len - 2;
memset(addr + 2, 0xCC, nop_len - 2);
break;
}
addr += nop_len;
len -= nop_len;
}
return;
singlebyte:
memset(caddr, 0x90, len);
}
void *
codepatch_maprw(vaddr_t *nva, vaddr_t dest)
{
paddr_t kva = trunc_page((paddr_t)dest);
paddr_t po = (paddr_t)dest & PAGE_MASK;
paddr_t pa1, pa2;
if (*nva == 0)
*nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none,
&kd_waitok);
pmap_extract(pmap_kernel(), kva, &pa1);
pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2);
pmap_kenter_pa(*nva, pa1, PROT_READ | PROT_WRITE);
pmap_kenter_pa(*nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE);
pmap_update(pmap_kernel());
return (void *)(*nva + po);
}
void
codepatch_unmaprw(vaddr_t nva)
{
if (nva == 0)
return;
pmap_kremove(nva, 2 * PAGE_SIZE);
km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none);
}
void
codepatch_nop(uint16_t tag)
{
struct codepatch *patch;
unsigned char *rwaddr;
vaddr_t rwmap = 0;
int i = 0;
DBGPRINT("patching tag %u", tag);
for (patch = &codepatch_begin; patch < &codepatch_end; patch++) {
if (patch->tag != tag)
continue;
rwaddr = codepatch_maprw(&rwmap, patch->addr);
codepatch_fill_nop(rwaddr, patch->len);
i++;
}
codepatch_unmaprw(rwmap);
DBGPRINT("patched %d places", i);
}
void
codepatch_replace(uint16_t tag, const void *code, size_t len)
{
struct codepatch *patch;
unsigned char *rwaddr;
vaddr_t rwmap = 0;
int i = 0;
DBGPRINT("patching tag %u with %p", tag, code);
for (patch = &codepatch_begin; patch < &codepatch_end; patch++) {
if (patch->tag != tag)
continue;
if (len > patch->len) {
panic("%s: can't replace len %u with %zu at %#lx",
__func__, patch->len, len, patch->addr);
}
rwaddr = codepatch_maprw(&rwmap, patch->addr);
memcpy(rwaddr, code, len);
codepatch_fill_nop(rwaddr + len, patch->len - len);
i++;
}
codepatch_unmaprw(rwmap);
DBGPRINT("patched %d places", i);
}
void
codepatch_call(uint16_t tag, void *func)
{
struct codepatch *patch;
unsigned char *rwaddr;
int32_t offset;
int i = 0;
vaddr_t rwmap = 0;
DBGPRINT("patching tag %u with call %p", tag, func);
for (patch = &codepatch_begin; patch < &codepatch_end; patch++) {
if (patch->tag != tag)
continue;
if (patch->len < 5)
panic("%s: can't replace len %u with call at %#lx",
__func__, patch->len, patch->addr);
offset = (vaddr_t)func - (patch->addr + 5);
rwaddr = codepatch_maprw(&rwmap, patch->addr);
rwaddr[0] = 0xe8;
memcpy(rwaddr + 1, &offset, sizeof(offset));
codepatch_fill_nop(rwaddr + 5, patch->len - 5);
i++;
}
codepatch_unmaprw(rwmap);
DBGPRINT("patched %d places", i);
}