From fa9bcabe2387bb230ef82d62827ad6f93b8a1e61 Mon Sep 17 00:00:00 2001
From: Frederic Konrad <fkonrad@amd.com>
Date: Wed, 17 Jan 2024 18:15:06 +0000
Subject: [PATCH 1/2] linux-user/*: workaround for missing MAP_FIXED_NOREPLACE

QEMU v8.1.0 recently requires MAP_FIXED_NOREPLACE flags implementation for mmap.

This is missing from ubuntu 18.04, thus this patch catches the mmap calls which
could use that new flag and forwards them to mmap when MAP_FIXED_NOREPLACE
flag isn't set or emulates them by checking the returned address w.r.t the
requested address.

Signed-off-by: Frederic Konrad <fkonrad@amd.com>
Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com>

Upstream-Status: Inappropriate [OE specific]

The upstream only supports the last two major releases of an OS.  The ones
they have declared all have kernel 4.17 or newer.

See:
https://xilinx.slack.com/archives/D04G2647CTV/p1705074697942019

https://www.qemu.org/docs/master/about/build-platforms.html

 The project aims to support the most recent major version at all times for up
 to five years after its initial release. Support for the previous major
 version will be dropped 2 years after the new major version is released or
 when the vendor itself drops support, whichever comes first.

Signed-off-by: Mark Hatle <mark.hatle@amd.com>
---
 linux-user/elfload.c    |  7 +++--
 linux-user/meson.build  |  1 +
 linux-user/mmap-fixed.c | 63 +++++++++++++++++++++++++++++++++++++++++
 linux-user/mmap-fixed.h | 39 +++++++++++++++++++++++++
 linux-user/mmap.c       | 31 +++++++++++---------
 linux-user/syscall.c    |  1 +
 6 files changed, 125 insertions(+), 17 deletions(-)
 create mode 100644 linux-user/mmap-fixed.c
 create mode 100644 linux-user/mmap-fixed.h

Index: qemu-8.2.1/linux-user/elfload.c
===================================================================
--- qemu-8.2.1.orig/linux-user/elfload.c
+++ qemu-8.2.1/linux-user/elfload.c
@@ -22,6 +22,7 @@
 #include "qemu/error-report.h"
 #include "target_signal.h"
 #include "accel/tcg/debuginfo.h"
+#include "mmap-fixed.h"
 
 #ifdef TARGET_ARM
 #include "target/arm/cpu-features.h"
@@ -2765,9 +2766,9 @@ static abi_ulong create_elf_tables(abi_u
 static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep)
 {
     size_t size = addr_last - addr + 1;
-    void *p = mmap((void *)addr, size, PROT_NONE,
-                   MAP_ANONYMOUS | MAP_PRIVATE |
-                   MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
+    void *p = mmap_fixed_noreplace((void *)addr, size, PROT_NONE,
+                                   MAP_ANONYMOUS | MAP_PRIVATE |
+                                   MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
     int ret;
 
     if (p == MAP_FAILED) {
Index: qemu-8.2.1/linux-user/meson.build
===================================================================
--- qemu-8.2.1.orig/linux-user/meson.build
+++ qemu-8.2.1/linux-user/meson.build
@@ -14,6 +14,7 @@ linux_user_ss.add(files(
   'linuxload.c',
   'main.c',
   'mmap.c',
+  'mmap-fixed.c',
   'signal.c',
   'strace.c',
   'syscall.c',
Index: qemu-8.2.1/linux-user/mmap-fixed.c
===================================================================
--- /dev/null
+++ qemu-8.2.1/linux-user/mmap-fixed.c
@@ -0,0 +1,63 @@
+/*
+ * Workaround for MAP_FIXED_NOREPLACE
+ *
+ * Copyright (c) 2024, Advanced Micro Devices, Inc.
+ * Developed by Fred Konrad <fkonrad@amd.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <sys/mman.h>
+#include <errno.h>
+
+#ifndef MAP_FIXED_NOREPLACE
+#include "mmap-fixed.h"
+
+void *mmap_fixed_noreplace(void *addr, size_t len, int prot, int flags,
+                                  int fd, off_t offset)
+{
+    void *retaddr;
+
+    if (!(flags & MAP_FIXED_NOREPLACE)) {
+        /* General case, use the regular mmap.  */
+        return mmap(addr, len, prot, flags, fd, offset);
+    }
+
+    /* Since MAP_FIXED_NOREPLACE is not implemented, try to emulate it.  */
+    flags = flags & ~(MAP_FIXED_NOREPLACE | MAP_FIXED);
+    retaddr = mmap(addr, len, prot, flags, fd, offset);
+    if ((retaddr == addr) || (retaddr == MAP_FAILED)) {
+        /*
+         * Either the map worked and we get the good address so it can be
+         * returned, or it failed and would have failed the same with
+         * MAP_FIXED*, in which case return MAP_FAILED.
+         */
+        return retaddr;
+    } else {
+        /*
+         * Page has been mapped but not at the requested address.. unmap it and
+         * return EEXIST.
+         */
+        munmap(retaddr, len);
+        errno = EEXIST;
+        return MAP_FAILED;
+    }
+}
+
+#endif
Index: qemu-8.2.1/linux-user/mmap-fixed.h
===================================================================
--- /dev/null
+++ qemu-8.2.1/linux-user/mmap-fixed.h
@@ -0,0 +1,39 @@
+/*
+ * Workaround for MAP_FIXED_NOREPLACE
+ *
+ * Copyright (c) 2024, Advanced Micro Devices, Inc.
+ * Developed by Fred Konrad <fkonrad@amd.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MMAP_FIXED_H
+#define MMAP_FIXED_H
+
+#ifndef MAP_FIXED_NOREPLACE
+#define MAP_FIXED_NOREPLACE 0x100000
+
+void *mmap_fixed_noreplace(void *addr, size_t len, int prot, int flags,
+                           int fd, off_t offset);
+
+#else /* MAP_FIXED_NOREPLACE */
+#define mmap_fixed_noreplace mmap
+#endif /* MAP_FIXED_NOREPLACE */
+
+#endif /* MMAP_FIXED_H */
Index: qemu-8.2.1/linux-user/mmap.c
===================================================================
--- qemu-8.2.1.orig/linux-user/mmap.c
+++ qemu-8.2.1/linux-user/mmap.c
@@ -25,6 +25,7 @@
 #include "user-mmap.h"
 #include "target_mman.h"
 #include "qemu/interval-tree.h"
+#include "mmap-fixed.h"
 
 #ifdef TARGET_ARM
 #include "target/arm/cpu-features.h"
@@ -273,7 +274,7 @@ int target_mprotect(abi_ulong start, abi
 static int do_munmap(void *addr, size_t len)
 {
     if (reserved_va) {
-        void *ptr = mmap(addr, len, PROT_NONE,
+        void *ptr =  mmap_fixed_noreplace(addr, len, PROT_NONE,
                          MAP_FIXED | MAP_ANONYMOUS
                          | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
         return ptr == addr ? 0 : -1;
@@ -319,9 +320,9 @@ static bool mmap_frag(abi_ulong real_sta
          * outside of the fragment we need to map.  Allocate a new host
          * page to cover, discarding whatever else may have been present.
          */
-        void *p = mmap(host_start, qemu_host_page_size,
-                       target_to_host_prot(prot),
-                       flags | MAP_ANONYMOUS, -1, 0);
+        void *p = mmap_fixed_noreplace(host_start, qemu_host_page_size,
+                                       target_to_host_prot(prot),
+                                       flags | MAP_ANONYMOUS, -1, 0);
         if (p != host_start) {
             if (p != MAP_FAILED) {
                 munmap(p, qemu_host_page_size);
@@ -420,8 +421,9 @@ abi_ulong mmap_find_vma(abi_ulong start,
          *  - mremap() with MREMAP_FIXED flag
          *  - shmat() with SHM_REMAP flag
          */
-        ptr = mmap(g2h_untagged(addr), size, PROT_NONE,
-                   MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
+        ptr = mmap_fixed_noreplace(g2h_untagged(addr), size, PROT_NONE,
+                                   MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
+                                   -1, 0);
 
         /* ENOMEM, if host address space has no memory */
         if (ptr == MAP_FAILED) {
@@ -615,16 +617,16 @@ abi_long target_mmap(abi_ulong start, ab
          * especially important if qemu_host_page_size >
          * qemu_real_host_page_size.
          */
-        p = mmap(g2h_untagged(start), host_len, host_prot,
-                 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
+        p = mmap_fixed_noreplace(g2h_untagged(start), host_len, host_prot,
+                                 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
         if (p == MAP_FAILED) {
             goto fail;
         }
         /* update start so that it points to the file position at 'offset' */
         host_start = (uintptr_t)p;
         if (!(flags & MAP_ANONYMOUS)) {
-            p = mmap(g2h_untagged(start), len, host_prot,
-                     flags | MAP_FIXED, fd, host_offset);
+            p = mmap_fixed_noreplace(g2h_untagged(start), len, host_prot,
+                                     flags | MAP_FIXED, fd, host_offset);
             if (p == MAP_FAILED) {
                 munmap(g2h_untagged(start), host_len);
                 goto fail;
@@ -749,8 +751,9 @@ abi_long target_mmap(abi_ulong start, ab
             len1 = real_last - real_start + 1;
             want_p = g2h_untagged(real_start);
 
-            p = mmap(want_p, len1, target_to_host_prot(target_prot),
-                     flags, fd, offset1);
+            p = mmap_fixed_noreplace(want_p, len1,
+                                     target_to_host_prot(target_prot),
+                                     flags, fd, offset1);
             if (p != want_p) {
                 if (p != MAP_FAILED) {
                     munmap(p, len1);
Index: qemu-8.2.1/linux-user/syscall.c
===================================================================
--- qemu-8.2.1.orig/linux-user/syscall.c
+++ qemu-8.2.1/linux-user/syscall.c
@@ -145,6 +145,7 @@
 #include "qapi/error.h"
 #include "fd-trans.h"
 #include "cpu_loop-common.h"
+#include "mmap-fixed.h"
 
 #ifndef CLONE_IO
 #define CLONE_IO                0x80000000      /* Clone io context */
