git: 5bc997b6f5b1 - main - amd64: do not allow to set IOPL to 3 when FRED is used

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Tue, 27 Oct 2026 12:01:18 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=5bc997b6f5b1265ff28d11405eb980bf471c3db4

commit 5bc997b6f5b1265ff28d11405eb980bf471c3db4
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-10-27 09:10:09 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-10-27 11:58:48 +0000

    amd64: do not allow to set IOPL to 3 when FRED is used
    
    SDM requires that IOPL is zero when returning to usermode with ERETU.
    
    Do not set rflags.iopl for FreeBSD io(4) open.
    This might be changed to disallow io(4) open for FRED at all with a
    knob, if there is a real need.
    
    Return ENOSYS from linux iopl(2), apparently Linux kernel can be
    configured without iopl(2) support, so linux programs should be prepared
    for this error.
    
    Return EINVAL from vt(4)/syscons(4) ioctl(KDENABIO).
    
    Fixes:  6e93f5e4d693 ("amd64: FRED support")
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
---
 sys/amd64/amd64/io.c                | 16 ++++++++++++----
 sys/amd64/linux/linux_machdep.c     |  3 +++
 sys/amd64/linux32/linux32_machdep.c |  3 +++
 sys/dev/syscons/syscons.c           |  3 +++
 sys/dev/vt/vt_core.c                |  3 +++
 5 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/sys/amd64/amd64/io.c b/sys/amd64/amd64/io.c
index 6c36d5fb20e1..ba34675c0cc5 100644
--- a/sys/amd64/amd64/io.c
+++ b/sys/amd64/amd64/io.c
@@ -31,21 +31,29 @@
 
 #include <machine/frame.h>
 #include <machine/iodev.h>
+#include <machine/md_var.h>
 #include <machine/psl.h>
 
 int
 iodev_open(struct thread *td)
 {
-
-	td->td_frame->tf_rflags |= PSL_IOPL;
+	/*
+	 * SDM rev. 93 vol.3 8.4.2.1 Loading and Checking the Return State:
+	 * Checking IOPL ensures that, when FRED transitions are
+	 * enabled, IOPL is always 0 when CPL = 3.
+	 *
+	 * As result, io(4) cannot enable the IOPL for usermode on FRED.
+	 */
+	if (!fred)
+		td->td_frame->tf_rflags |= PSL_IOPL;
 	return (0);
 }
 
 int
 iodev_close(struct thread *td)
 {
-
-	td->td_frame->tf_rflags &= ~PSL_IOPL;
+	if (!fred)
+		td->td_frame->tf_rflags &= ~PSL_IOPL;
 	return (0);
 }
 
diff --git a/sys/amd64/linux/linux_machdep.c b/sys/amd64/linux/linux_machdep.c
index dc7f55f6f198..e22a3ed22c31 100644
--- a/sys/amd64/linux/linux_machdep.c
+++ b/sys/amd64/linux/linux_machdep.c
@@ -88,6 +88,9 @@ linux_iopl(struct thread *td, struct linux_iopl_args *args)
 		return (error);
 	if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
 		return (error);
+	/* See comment in sys/amd64/amd64/io.c. */
+	if (fred && args->level != 0)
+		return (ENOSYS);
 	td->td_frame->tf_rflags = (td->td_frame->tf_rflags & ~PSL_IOPL) |
 	    (args->level * (PSL_IOPL / 3));
 
diff --git a/sys/amd64/linux32/linux32_machdep.c b/sys/amd64/linux32/linux32_machdep.c
index a9517b42a4d9..a62965308c33 100644
--- a/sys/amd64/linux32/linux32_machdep.c
+++ b/sys/amd64/linux32/linux32_machdep.c
@@ -332,6 +332,9 @@ linux_iopl(struct thread *td, struct linux_iopl_args *args)
 		return (error);
 	if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
 		return (error);
+	/* See comment in sys/amd64/amd64/io.c. */
+	if (fred && args->level != 0)
+		return (ENOSYS);
 	td->td_frame->tf_rflags = (td->td_frame->tf_rflags & ~PSL_IOPL) |
 	    (args->level * (PSL_IOPL / 3));
 
diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c
index 9167da23efd8..06e3c498ddd6 100644
--- a/sys/dev/syscons/syscons.c
+++ b/sys/dev/syscons/syscons.c
@@ -72,6 +72,7 @@
 #if defined(__i386__) || defined(__amd64__)
 #include <machine/psl.h>
 #include <machine/frame.h>
+#include <machine/md_var.h>
 #endif
 
 #if defined(__amd64__) || defined(__i386__)
@@ -1338,6 +1339,8 @@ sctty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
 #ifdef __i386__
 		td->td_frame->tf_eflags |= PSL_IOPL;
 #elif defined(__amd64__)
+		if (fred)
+			return (EINVAL);
 		td->td_frame->tf_rflags |= PSL_IOPL;
 #endif
 		return 0;
diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c
index 0ea2ec58f4cc..909ee3b8689d 100644
--- a/sys/dev/vt/vt_core.c
+++ b/sys/dev/vt/vt_core.c
@@ -61,6 +61,7 @@
 #if defined(__i386__) || defined(__amd64__)
 #include <machine/psl.h>
 #include <machine/frame.h>
+#include <machine/md_var.h>
 #endif
 
 static int vtterm_cngrab_noswitch(struct vt_device *, struct vt_window *);
@@ -2971,6 +2972,8 @@ skip_thunk:
 #if defined(__i386__)
 		td->td_frame->tf_eflags |= PSL_IOPL;
 #elif defined(__amd64__)
+		if (fred)
+			return (EINVAL);
 		td->td_frame->tf_rflags |= PSL_IOPL;
 #endif
 		return (0);