- 264 license table entries with exact download URLs (224/264 resolved) - Complete sources/ directory with all BitBake recipes - Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl) - Full traceability for Softwarefreigabeantrag - GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4 - License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
237 lines
8.0 KiB
Diff
237 lines
8.0 KiB
Diff
From cb48d5d1592e63ebd0d4a3e300ef98e38e6306d7 Mon Sep 17 00:00:00 2001
|
|
From: Richard Henderson <richard.henderson@linaro.org>
|
|
Date: Wed, 28 Feb 2024 10:25:17 -1000
|
|
Subject: [PATCH 4/5] linux-user: Rewrite target_shmat
|
|
|
|
Handle combined host and guest alignment requirements.
|
|
Handle host and guest page size differences.
|
|
Handle SHM_EXEC.
|
|
|
|
Upstream-Status: Submitted [https://www.mail-archive.com/qemu-devel@nongnu.org/msg1026793.html]
|
|
|
|
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/115
|
|
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
|
|
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
|
|
---
|
|
linux-user/mmap.c | 166 +++++++++++++++++++++++++++++++++++++---------
|
|
1 file changed, 133 insertions(+), 33 deletions(-)
|
|
|
|
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
|
|
index 18fb3aaf7..6a2f649bb 100644
|
|
--- a/linux-user/mmap.c
|
|
+++ b/linux-user/mmap.c
|
|
@@ -1062,69 +1062,161 @@ static inline abi_ulong target_shmlba(CPUArchState *cpu_env)
|
|
}
|
|
#endif
|
|
|
|
+#if defined(__arm__) || defined(__mips__) || defined(__sparc__)
|
|
+#define HOST_FORCE_SHMLBA 1
|
|
+#else
|
|
+#define HOST_FORCE_SHMLBA 0
|
|
+#endif
|
|
+
|
|
abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
|
|
abi_ulong shmaddr, int shmflg)
|
|
{
|
|
CPUState *cpu = env_cpu(cpu_env);
|
|
- abi_ulong raddr;
|
|
struct shmid_ds shm_info;
|
|
int ret;
|
|
- abi_ulong shmlba;
|
|
+ int h_pagesize;
|
|
+ int t_shmlba, h_shmlba, m_shmlba;
|
|
+ size_t t_len, h_len, m_len;
|
|
|
|
/* shmat pointers are always untagged */
|
|
|
|
- /* find out the length of the shared memory segment */
|
|
+ /*
|
|
+ * Because we can't use host shmat() unless the address is sufficiently
|
|
+ * aligned for the host, we'll need to check both.
|
|
+ * TODO: Could be fixed with softmmu.
|
|
+ */
|
|
+ t_shmlba = target_shmlba(cpu_env);
|
|
+ h_pagesize = qemu_real_host_page_size();
|
|
+ h_shmlba = (HOST_FORCE_SHMLBA ? SHMLBA : h_pagesize);
|
|
+ m_shmlba = MAX(t_shmlba, h_shmlba);
|
|
+
|
|
+ if (shmaddr) {
|
|
+ if (shmaddr & (m_shmlba - 1)) {
|
|
+ if (shmflg & SHM_RND) {
|
|
+ /*
|
|
+ * The guest is allowing the kernel to round the address.
|
|
+ * Assume that the guest is ok with us rounding to the
|
|
+ * host required alignment too. Anyway if we don't, we'll
|
|
+ * get an error from the kernel.
|
|
+ */
|
|
+ shmaddr &= ~(m_shmlba - 1);
|
|
+ if (shmaddr == 0 && (shmflg & SHM_REMAP)) {
|
|
+ return -TARGET_EINVAL;
|
|
+ }
|
|
+ } else {
|
|
+ int require = TARGET_PAGE_SIZE;
|
|
+#ifdef TARGET_FORCE_SHMLBA
|
|
+ require = t_shmlba;
|
|
+#endif
|
|
+ /*
|
|
+ * Include host required alignment, as otherwise we cannot
|
|
+ * use host shmat at all.
|
|
+ */
|
|
+ require = MAX(require, h_shmlba);
|
|
+ if (shmaddr & (require - 1)) {
|
|
+ return -TARGET_EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (shmflg & SHM_REMAP) {
|
|
+ return -TARGET_EINVAL;
|
|
+ }
|
|
+ }
|
|
+ /* All rounding now manually concluded. */
|
|
+ shmflg &= ~SHM_RND;
|
|
+
|
|
+ /* Find out the length of the shared memory segment. */
|
|
ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info));
|
|
if (is_error(ret)) {
|
|
/* can't get length, bail out */
|
|
return ret;
|
|
}
|
|
+ t_len = TARGET_PAGE_ALIGN(shm_info.shm_segsz);
|
|
+ h_len = ROUND_UP(shm_info.shm_segsz, h_pagesize);
|
|
+ m_len = MAX(t_len, h_len);
|
|
|
|
- shmlba = target_shmlba(cpu_env);
|
|
-
|
|
- if (shmaddr & (shmlba - 1)) {
|
|
- if (shmflg & SHM_RND) {
|
|
- shmaddr &= ~(shmlba - 1);
|
|
- } else {
|
|
- return -TARGET_EINVAL;
|
|
- }
|
|
- }
|
|
- if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) {
|
|
+ if (!guest_range_valid_untagged(shmaddr, m_len)) {
|
|
return -TARGET_EINVAL;
|
|
}
|
|
|
|
WITH_MMAP_LOCK_GUARD() {
|
|
- void *host_raddr;
|
|
+ bool mapped = false;
|
|
+ void *want, *test;
|
|
abi_ulong last;
|
|
|
|
- if (shmaddr) {
|
|
- host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg);
|
|
+ if (!shmaddr) {
|
|
+ shmaddr = mmap_find_vma(0, m_len, m_shmlba);
|
|
+ if (shmaddr == -1) {
|
|
+ return -TARGET_ENOMEM;
|
|
+ }
|
|
+ mapped = !reserved_va;
|
|
+ } else if (shmflg & SHM_REMAP) {
|
|
+ /*
|
|
+ * If host page size > target page size, the host shmat may map
|
|
+ * more memory than the guest expects. Reject a mapping that
|
|
+ * would replace memory in the unexpected gap.
|
|
+ * TODO: Could be fixed with softmmu.
|
|
+ */
|
|
+ if (t_len < h_len &&
|
|
+ !page_check_range_empty(shmaddr + t_len,
|
|
+ shmaddr + h_len - 1)) {
|
|
+ return -TARGET_EINVAL;
|
|
+ }
|
|
} else {
|
|
- abi_ulong mmap_start;
|
|
+ if (!page_check_range_empty(shmaddr, shmaddr + m_len - 1)) {
|
|
+ return -TARGET_EINVAL;
|
|
+ }
|
|
+ }
|
|
|
|
- /* In order to use the host shmat, we need to honor host SHMLBA. */
|
|
- mmap_start = mmap_find_vma(0, shm_info.shm_segsz,
|
|
- MAX(SHMLBA, shmlba));
|
|
+ /* All placement is now complete. */
|
|
+ want = (void *)g2h_untagged(shmaddr);
|
|
|
|
- if (mmap_start == -1) {
|
|
- return -TARGET_ENOMEM;
|
|
+ /*
|
|
+ * Map anonymous pages across the entire range, then remap with
|
|
+ * the shared memory. This is required for a number of corner
|
|
+ * cases for which host and guest page sizes differ.
|
|
+ */
|
|
+ if (h_len != t_len) {
|
|
+ int mmap_p = PROT_READ | (shmflg & SHM_RDONLY ? 0 : PROT_WRITE);
|
|
+ int mmap_f = MAP_PRIVATE | MAP_ANONYMOUS
|
|
+ | (reserved_va || (shmflg & SHM_REMAP)
|
|
+ ? MAP_FIXED : MAP_FIXED_NOREPLACE);
|
|
+
|
|
+ test = mmap(want, m_len, mmap_p, mmap_f, -1, 0);
|
|
+ if (unlikely(test != want)) {
|
|
+ /* shmat returns EINVAL not EEXIST like mmap. */
|
|
+ ret = (test == MAP_FAILED && errno != EEXIST
|
|
+ ? get_errno(-1) : -TARGET_EINVAL);
|
|
+ if (mapped) {
|
|
+ do_munmap(want, m_len);
|
|
+ }
|
|
+ return ret;
|
|
}
|
|
- host_raddr = shmat(shmid, g2h_untagged(mmap_start),
|
|
- shmflg | SHM_REMAP);
|
|
+ mapped = true;
|
|
}
|
|
|
|
- if (host_raddr == (void *)-1) {
|
|
- return get_errno(-1);
|
|
+ if (reserved_va || mapped) {
|
|
+ shmflg |= SHM_REMAP;
|
|
+ }
|
|
+ test = shmat(shmid, want, shmflg);
|
|
+ if (test == MAP_FAILED) {
|
|
+ ret = get_errno(-1);
|
|
+ if (mapped) {
|
|
+ do_munmap(want, m_len);
|
|
+ }
|
|
+ return ret;
|
|
}
|
|
- raddr = h2g(host_raddr);
|
|
- last = raddr + shm_info.shm_segsz - 1;
|
|
+ assert(test == want);
|
|
|
|
- page_set_flags(raddr, last,
|
|
+ last = shmaddr + m_len - 1;
|
|
+ page_set_flags(shmaddr, last,
|
|
PAGE_VALID | PAGE_RESET | PAGE_READ |
|
|
- (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE));
|
|
+ (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE) |
|
|
+ (shmflg & SHM_EXEC ? PAGE_EXEC : 0));
|
|
|
|
- shm_region_rm_complete(raddr, last);
|
|
- shm_region_add(raddr, last);
|
|
+ shm_region_rm_complete(shmaddr, last);
|
|
+ shm_region_add(shmaddr, last);
|
|
}
|
|
|
|
/*
|
|
@@ -1138,7 +1230,15 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
|
|
tb_flush(cpu);
|
|
}
|
|
|
|
- return raddr;
|
|
+ if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
|
|
+ FILE *f = qemu_log_trylock();
|
|
+ if (f) {
|
|
+ fprintf(f, "page layout changed following shmat\n");
|
|
+ page_dump(f);
|
|
+ qemu_log_unlock(f);
|
|
+ }
|
|
+ }
|
|
+ return shmaddr;
|
|
}
|
|
|
|
abi_long target_shmdt(abi_ulong shmaddr)
|
|
--
|
|
2.34.1
|
|
|