diff --git a/sys/arm64/arm64/db_interface.c b/sys/arm64/arm64/db_interface.c --- a/sys/arm64/arm64/db_interface.c +++ b/sys/arm64/arm64/db_interface.c @@ -153,39 +153,24 @@ jmp_buf jb; void *prev_jb; char *dst; + size_t i; int ret; - uint64_t tmp64; - uint32_t tmp32; - uint16_t tmp16; prev_jb = kdb_jmpbuf(jb); ret = setjmp(jb); if (ret == 0) { - if (size == 8 && (addr & 7) == 0) { - dst = (char *)&tmp64; - while (size-- > 0) - *dst++ = *data++; - *((uint64_t *)addr) = tmp64; - } else if (size == 4 && (addr & 3) == 0) { - dst = (char *)&tmp32; - while (size-- > 0) - *dst++ = *data++; - *((uint32_t *)addr) = tmp32; - } else if (size == 2 && (addr & 1) == 0) { - dst = (char *)&tmp16; - while (size-- > 0) - *dst++ = *data++; - *((uint32_t *)addr) = tmp16; + if (!arm64_get_writable_addr(addr, &addr)) { + ret = 1; } else { dst = (char *)addr; - while (size-- > 0) + for (i = 0; i < size; i++) *dst++ = *data++; - } - dsb(ish); + dsb(ish); - /* Clean D-cache and invalidate I-cache */ - cpu_dcache_wb_range(addr, (vm_size_t)size); - cpu_icache_sync_range(addr, (vm_size_t)size); + /* Clean D-cache and invalidate I-cache */ + cpu_dcache_wb_range(addr, (vm_size_t)size); + cpu_icache_sync_range(addr, (vm_size_t)size); + } } (void)kdb_jmpbuf(prev_jb); diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -942,6 +942,42 @@ serror_enable(); } +/* + * Get an address to be used to write to kernel data that may be mapped + * read-only, e.g. to patch kernel code. + */ +bool +arm64_get_writable_addr(vm_offset_t addr, vm_offset_t *out) +{ + vm_paddr_t pa; + + /* Check if the page is writable */ + if (PAR_SUCCESS(arm64_address_translate_s1e1w(addr))) { + *out = addr; + return (true); + } + + /* + * Find the physical address of the given page. + */ + if (!pmap_klookup(addr, &pa)) { + return (false); + } + + /* + * If it is within the DMAP region and is writable use that. + */ + if (PHYS_IN_DMAP(pa)) { + addr = PHYS_TO_DMAP(pa); + if (PAR_SUCCESS(arm64_address_translate_s1e1w(addr))) { + *out = addr; + return (true); + } + } + + return (false); +} + typedef struct { uint32_t type; uint64_t phys_start; diff --git a/sys/arm64/include/cpufunc.h b/sys/arm64/include/cpufunc.h --- a/sys/arm64/include/cpufunc.h +++ b/sys/arm64/include/cpufunc.h @@ -246,6 +246,7 @@ void arm64_dcache_wbinv_range(vm_offset_t, vm_size_t); void arm64_dcache_inv_range(vm_offset_t, vm_size_t); void arm64_dcache_wb_range(vm_offset_t, vm_size_t); +bool arm64_get_writable_addr(vm_offset_t, vm_offset_t *); #endif /* _KERNEL */ #endif /* _MACHINE_CPUFUNC_H_ */ diff --git a/sys/cddl/dev/fbt/aarch64/fbt_isa.c b/sys/cddl/dev/fbt/aarch64/fbt_isa.c --- a/sys/cddl/dev/fbt/aarch64/fbt_isa.c +++ b/sys/cddl/dev/fbt/aarch64/fbt_isa.c @@ -74,8 +74,12 @@ void fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) { + vm_offset_t addr; - *fbt->fbtp_patchpoint = val; + if (!arm64_get_writable_addr((vm_offset_t)fbt->fbtp_patchpoint, &addr)) + panic("%s: Unable to write new instruction", __func__); + + *(fbt_patchval_t *)addr = val; cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4); }