diff --git a/meta/recipes-devtools/qemu/qemu.inc b/meta/recipes-devtools/qemu/qemu.inc
index 748a32215e0..54644dd9241 100644
--- a/meta/recipes-devtools/qemu/qemu.inc
+++ b/meta/recipes-devtools/qemu/qemu.inc
@@ -43,6 +43,8 @@ SRC_URI = "https://download.qemu.org/${BPN}-${PV}.tar.xz \
            file://qemu-guest-agent.udev \
            file://CVE-2024-8354.patch \
            file://CVE-2025-12464.patch \
+           file://0001-python-backport-Remove-deprecated-get_event_loop-cal.patch \
+           file://0002-python-backport-avoid-creating-additional-event-loop.patch \
            "
 UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar"
 
diff --git a/meta/recipes-devtools/qemu/qemu/0001-python-backport-Remove-deprecated-get_event_loop-cal.patch b/meta/recipes-devtools/qemu/qemu/0001-python-backport-Remove-deprecated-get_event_loop-cal.patch
new file mode 100644
index 00000000000..7a564513c03
--- /dev/null
+++ b/meta/recipes-devtools/qemu/qemu/0001-python-backport-Remove-deprecated-get_event_loop-cal.patch
@@ -0,0 +1,92 @@
+From 120d060528d02e24b68ac06b44de34fb206b4319 Mon Sep 17 00:00:00 2001
+From: John Snow <jsnow@redhat.com>
+Date: Tue, 13 Aug 2024 09:35:30 -0400
+Subject: [PATCH] python: backport 'Remove deprecated get_event_loop calls'
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This method was deprecated in 3.12 because it ordinarily should not be
+used from coroutines; if there is not a currently running event loop,
+this automatically creates a new event loop - which is usually not what
+you want from code that would ever run in the bottom half.
+
+In our case, we do want this behavior in two places:
+
+(1) The synchronous shim, for convenience: this allows fully sync
+programs to use QEMUMonitorProtocol() without needing to set up an event
+loop beforehand. This is intentional to fully box in the async
+complexities into the legacy sync shim.
+
+(2) The qmp_tui shell; instead of relying on asyncio.run to create and
+run an asyncio program, we need to be able to pass the current asyncio
+loop to urwid setup functions. For convenience, again, we create one if
+one is not present to simplify the creation of the TUI appliance.
+
+The remaining user of get_event_loop() was in fact one of the erroneous
+users that should not have been using this function: if there's no
+running event loop inside of a coroutine, you're in big trouble :)
+
+Signed-off-by: John Snow <jsnow@redhat.com>
+cherry picked from commit python-qemu-qmp@aa1ff9907603a3033296027e1bd021133df86ef1
+Signed-off-by: John Snow <jsnow@redhat.com>
+Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
+Upstream-Status: Backport [https://gitlab.com/qemu-project/qemu/-/commit/5d99044d09db0fa8c2b3294e301927118f9effc9]
+Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
+---
+ python/qemu/qmp/legacy.py  | 9 ++++++++-
+ python/qemu/qmp/qmp_tui.py | 7 ++++++-
+ python/tests/protocol.py   | 2 +-
+ 3 files changed, 15 insertions(+), 3 deletions(-)
+
+diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
+index 22a2b5616ef..ea9b8032c3b 100644
+--- a/python/qemu/qmp/legacy.py
++++ b/python/qemu/qmp/legacy.py
+@@ -86,7 +86,14 @@ def __init__(self,
+                 "server argument should be False when passing a socket")
+ 
+         self._qmp = QMPClient(nickname)
+-        self._aloop = asyncio.get_event_loop()
++
++        try:
++            self._aloop = asyncio.get_running_loop()
++        except RuntimeError:
++            # No running loop; since this is a sync shim likely to be
++            # used in fully sync programs, create one if neccessary.
++            self._aloop = asyncio.get_event_loop_policy().get_event_loop()
++
+         self._address = address
+         self._timeout: Optional[float] = None
+ 
+diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py
+index 2d9ebbd20bc..d11b9fc547b 100644
+--- a/python/qemu/qmp/qmp_tui.py
++++ b/python/qemu/qmp/qmp_tui.py
+@@ -377,7 +377,12 @@ def run(self, debug: bool = False) -> None:
+         screen = urwid.raw_display.Screen()
+         screen.set_terminal_properties(256)
+ 
+-        self.aloop = asyncio.get_event_loop()
++        try:
++            self.aloop = asyncio.get_running_loop()
++        except RuntimeError:
++            # No running asyncio event loop. Create one if necessary.
++            self.aloop = asyncio.get_event_loop_policy().get_event_loop()
++
+         self.aloop.set_debug(debug)
+ 
+         # Gracefully handle SIGTERM and SIGINT signals
+diff --git a/python/tests/protocol.py b/python/tests/protocol.py
+index 56c4d441f9c..8dcef573b6c 100644
+--- a/python/tests/protocol.py
++++ b/python/tests/protocol.py
+@@ -228,7 +228,7 @@ def async_test(async_test_method):
+         Decorator; adds SetUp and TearDown to async tests.
+         """
+         async def _wrapper(self, *args, **kwargs):
+-            loop = asyncio.get_event_loop()
++            loop = asyncio.get_running_loop()
+             loop.set_debug(True)
+ 
+             await self._asyncSetUp()
diff --git a/meta/recipes-devtools/qemu/qemu/0002-python-backport-avoid-creating-additional-event-loop.patch b/meta/recipes-devtools/qemu/qemu/0002-python-backport-avoid-creating-additional-event-loop.patch
new file mode 100644
index 00000000000..d893c10c420
--- /dev/null
+++ b/meta/recipes-devtools/qemu/qemu/0002-python-backport-avoid-creating-additional-event-loop.patch
@@ -0,0 +1,199 @@
+From f25eb62190a6fa170db24584fe6225cd0dcd64ad Mon Sep 17 00:00:00 2001
+From: John Snow <jsnow@redhat.com>
+Date: Wed, 3 Sep 2025 01:06:30 -0400
+Subject: [PATCH] python: backport 'avoid creating additional event loops per
+ thread'
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This commit is two backports squashed into one to avoid regressions.
+
+python: *really* remove get_event_loop
+
+A prior commit, aa1ff990, switched away from using get_event_loop *by
+default*, but this is not good enough to avoid deprecation warnings as
+`asyncio.get_event_loop_policy().get_event_loop()` is *also*
+deprecated. Replace this mechanism with explicit calls to
+asyncio.get_new_loop() and revise the cleanup mechanisms in __del__ to
+match.
+
+python: avoid creating additional event loops per thread
+
+"Too hasty by far!", commit 21ce2ee4 attempted to avoid deprecated
+behavior altogether by calling new_event_loop() directly if there was no
+loop currently running, but this has the unfortunate side effect of
+potentially creating multiple event loops per thread if tests
+instantiate multiple QMP connections in a single thread. This behavior
+is apparently not well-defined and causes problems in some, but not all,
+combinations of Python interpreter version and platform environment.
+
+Partially revert to Daniel Berrange's original patch, which calls
+get_event_loop and simply suppresses the deprecation warning in
+Python<=3.13. This time, however, additionally register new loops
+created with new_event_loop() so that future calls to get_event_loop()
+will return the loop already created.
+
+Reported-by: Richard W.M. Jones <rjones@redhat.com>
+Reported-by: Daniel P. Berrangé <berrange@redhat.com>
+Signed-off-by: John Snow <jsnow@redhat.com>
+cherry picked from commit python-qemu-qmp@21ce2ee4f2df87efe84a27b9c5112487f4670622
+cherry picked from commit python-qemu-qmp@c08fb82b38212956ccffc03fc6d015c3979f42fe
+Signed-off-by: John Snow <jsnow@redhat.com>
+Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
+Upstream-Status: Backport [https://gitlab.com/qemu-project/qemu/-/commit/85f223e5b031eb8ab63fbca314a4fb296a3a2632]
+Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
+---
+ python/qemu/qmp/legacy.py  | 46 +++++++++++++++++++++++---------------
+ python/qemu/qmp/qmp_tui.py | 10 ++-------
+ python/qemu/qmp/util.py    | 27 ++++++++++++++++++++++
+ 3 files changed, 57 insertions(+), 26 deletions(-)
+
+diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
+index ea9b8032c3b..c732212c048 100644
+--- a/python/qemu/qmp/legacy.py
++++ b/python/qemu/qmp/legacy.py
+@@ -38,6 +38,7 @@
+ from .error import QMPError
+ from .protocol import Runstate, SocketAddrT
+ from .qmp_client import QMPClient
++from .util import get_or_create_event_loop
+ 
+ 
+ #: QMPMessage is an entire QMP message of any kind.
+@@ -86,17 +87,13 @@ def __init__(self,
+                 "server argument should be False when passing a socket")
+ 
+         self._qmp = QMPClient(nickname)
+-
+-        try:
+-            self._aloop = asyncio.get_running_loop()
+-        except RuntimeError:
+-            # No running loop; since this is a sync shim likely to be
+-            # used in fully sync programs, create one if neccessary.
+-            self._aloop = asyncio.get_event_loop_policy().get_event_loop()
+-
+         self._address = address
+         self._timeout: Optional[float] = None
+ 
++        # This is a sync shim intended for use in fully synchronous
++        # programs. Create and set an event loop if necessary.
++        self._aloop = get_or_create_event_loop()
++
+         if server:
+             assert not isinstance(self._address, socket.socket)
+             self._sync(self._qmp.start_server(self._address))
+@@ -310,17 +307,30 @@ def send_fd_scm(self, fd: int) -> None:
+         self._qmp.send_fd_scm(fd)
+ 
+     def __del__(self) -> None:
+-        if self._qmp.runstate == Runstate.IDLE:
+-            return
++        if self._qmp.runstate != Runstate.IDLE:
++            self._qmp.logger.warning(
++                "QEMUMonitorProtocol object garbage collected without a prior "
++                "call to close()"
++            )
+ 
+         if not self._aloop.is_running():
+-            self.close()
+-        else:
+-            # Garbage collection ran while the event loop was running.
+-            # Nothing we can do about it now, but if we don't raise our
+-            # own error, the user will be treated to a lot of traceback
+-            # they might not understand.
++            if self._qmp.runstate != Runstate.IDLE:
++                # If the user neglected to close the QMP session and we
++                # are not currently running in an asyncio context, we
++                # have the opportunity to close the QMP session. If we
++                # do not do this, the error messages presented over
++                # dangling async resources may not make any sense to the
++                # user.
++                self.close()
++
++        if self._qmp.runstate != Runstate.IDLE:
++            # If QMP is still not quiesced, it means that the garbage
++            # collector ran from a context within the event loop and we
++            # are simply too late to take any corrective action. Raise
++            # our own error to give meaningful feedback to the user in
++            # order to prevent pages of asyncio stacktrace jargon.
+             raise QMPError(
+-                "QEMUMonitorProtocol.close()"
+-                " was not called before object was garbage collected"
++                "QEMUMonitorProtocol.close() was not called before object was "
++                "garbage collected, and could not be closed due to GC running "
++                "in the event loop"
+             )
+diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py
+index d11b9fc547b..76e540931c7 100644
+--- a/python/qemu/qmp/qmp_tui.py
++++ b/python/qemu/qmp/qmp_tui.py
+@@ -40,7 +40,7 @@
+ from .message import DeserializationError, Message, UnexpectedTypeError
+ from .protocol import ConnectError, Runstate
+ from .qmp_client import ExecInterruptedError, QMPClient
+-from .util import create_task, pretty_traceback
++from .util import get_or_create_event_loop, create_task, pretty_traceback
+ 
+ 
+ # The name of the signal that is used to update the history list
+@@ -376,13 +376,7 @@ def run(self, debug: bool = False) -> None:
+         """
+         screen = urwid.raw_display.Screen()
+         screen.set_terminal_properties(256)
+-
+-        try:
+-            self.aloop = asyncio.get_running_loop()
+-        except RuntimeError:
+-            # No running asyncio event loop. Create one if necessary.
+-            self.aloop = asyncio.get_event_loop_policy().get_event_loop()
+-
++        self.aloop = get_or_create_event_loop()
+         self.aloop.set_debug(debug)
+ 
+         # Gracefully handle SIGTERM and SIGINT signals
+diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py
+index ca6225e9cda..213f09c6528 100644
+--- a/python/qemu/qmp/util.py
++++ b/python/qemu/qmp/util.py
+@@ -20,6 +20,7 @@
+     TypeVar,
+     cast,
+ )
++import warnings
+ 
+ 
+ T = TypeVar('T')
+@@ -30,6 +31,32 @@
+ # --------------------------
+ 
+ 
++def get_or_create_event_loop() -> asyncio.AbstractEventLoop:
++    """
++    Return this thread's current event loop, or create a new one.
++
++    This function behaves similarly to asyncio.get_event_loop() in
++    Python<=3.13, where if there is no event loop currently associated
++    with the current context, it will create and register one. It should
++    generally not be used in any asyncio-native applications.
++    """
++    try:
++        with warnings.catch_warnings():
++            # Python <= 3.13 will trigger deprecation warnings if no
++            # event loop is set, but will create and set a new loop.
++            warnings.simplefilter("ignore")
++            loop = asyncio.get_event_loop()
++    except RuntimeError:
++        # Python 3.14+: No event loop set for this thread,
++        # create and set one.
++        loop = asyncio.new_event_loop()
++        # Set this loop as the current thread's loop, to be returned
++        # by calls to get_event_loop() in the future.
++        asyncio.set_event_loop(loop)
++
++    return loop
++
++
+ async def flush(writer: asyncio.StreamWriter) -> None:
+     """
+     Utility function to ensure a StreamWriter is *fully* drained.
