| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| /* Access guest memory in blocks. */ |
| |
| #include "qemu/osdep.h" |
| #include "cpu.h" |
| #include "exec/cpu_ldst.h" |
| #include "exec/exec-all.h" |
| #include "access.h" |
| |
| |
| void access_prepare_mmu(X86Access *ret, CPUX86State *env, |
| vaddr vaddr, unsigned size, |
| MMUAccessType type, int mmu_idx, uintptr_t ra) |
| { |
| int size1, size2; |
| void *haddr1, *haddr2; |
| |
| assert(size > 0 && size <= TARGET_PAGE_SIZE); |
| |
| size1 = MIN(size, -(vaddr | TARGET_PAGE_MASK)), |
| size2 = size - size1; |
| |
| memset(ret, 0, sizeof(*ret)); |
| ret->vaddr = vaddr; |
| ret->size = size; |
| ret->size1 = size1; |
| ret->mmu_idx = mmu_idx; |
| ret->env = env; |
| ret->ra = ra; |
| |
| haddr1 = probe_access(env, vaddr, size1, type, mmu_idx, ra); |
| ret->haddr1 = haddr1; |
| |
| if (unlikely(size2)) { |
| haddr2 = probe_access(env, vaddr + size1, size2, type, mmu_idx, ra); |
| if (haddr2 == haddr1 + size1) { |
| ret->size1 = size; |
| } else { |
| #ifdef CONFIG_USER_ONLY |
| g_assert_not_reached(); |
| #else |
| ret->haddr2 = haddr2; |
| #endif |
| } |
| } |
| } |
| |
| void access_prepare(X86Access *ret, CPUX86State *env, vaddr vaddr, |
| unsigned size, MMUAccessType type, uintptr_t ra) |
| { |
| int mmu_idx = cpu_mmu_index(env_cpu(env), false); |
| access_prepare_mmu(ret, env, vaddr, size, type, mmu_idx, ra); |
| } |
| |
| static void *access_ptr(X86Access *ac, vaddr addr, unsigned len) |
| { |
| vaddr offset = addr - ac->vaddr; |
| |
| assert(addr >= ac->vaddr); |
| |
| #ifdef CONFIG_USER_ONLY |
| assert(offset <= ac->size1 - len); |
| return ac->haddr1 + offset; |
| #else |
| if (likely(offset <= ac->size1 - len)) { |
| return ac->haddr1 + offset; |
| } |
| assert(offset <= ac->size - len); |
| /* |
| * If the address is not naturally aligned, it might span both pages. |
| * Only return ac->haddr2 if the area is entirely within the second page, |
| * otherwise fall back to slow accesses. |
| */ |
| if (likely(offset >= ac->size1)) { |
| return ac->haddr2 + (offset - ac->size1); |
| } |
| return NULL; |
| #endif |
| } |
| |
| #ifdef CONFIG_USER_ONLY |
| # define test_ptr(p) true |
| #else |
| # define test_ptr(p) likely(p) |
| #endif |
| |
| uint8_t access_ldb(X86Access *ac, vaddr addr) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint8_t)); |
| |
| if (test_ptr(p)) { |
| return ldub_p(p); |
| } |
| return cpu_ldub_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); |
| } |
| |
| uint16_t access_ldw(X86Access *ac, vaddr addr) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint16_t)); |
| |
| if (test_ptr(p)) { |
| return lduw_le_p(p); |
| } |
| return cpu_lduw_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); |
| } |
| |
| uint32_t access_ldl(X86Access *ac, vaddr addr) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint32_t)); |
| |
| if (test_ptr(p)) { |
| return ldl_le_p(p); |
| } |
| return cpu_ldl_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); |
| } |
| |
| uint64_t access_ldq(X86Access *ac, vaddr addr) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint64_t)); |
| |
| if (test_ptr(p)) { |
| return ldq_le_p(p); |
| } |
| return cpu_ldq_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); |
| } |
| |
| void access_stb(X86Access *ac, vaddr addr, uint8_t val) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint8_t)); |
| |
| if (test_ptr(p)) { |
| stb_p(p, val); |
| } else { |
| cpu_stb_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); |
| } |
| } |
| |
| void access_stw(X86Access *ac, vaddr addr, uint16_t val) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint16_t)); |
| |
| if (test_ptr(p)) { |
| stw_le_p(p, val); |
| } else { |
| cpu_stw_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); |
| } |
| } |
| |
| void access_stl(X86Access *ac, vaddr addr, uint32_t val) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint32_t)); |
| |
| if (test_ptr(p)) { |
| stl_le_p(p, val); |
| } else { |
| cpu_stl_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); |
| } |
| } |
| |
| void access_stq(X86Access *ac, vaddr addr, uint64_t val) |
| { |
| void *p = access_ptr(ac, addr, sizeof(uint64_t)); |
| |
| if (test_ptr(p)) { |
| stq_le_p(p, val); |
| } else { |
| cpu_stq_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); |
| } |
| } |