162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * iopl.c - Test case for a Linux on Xen 64-bit bug 462306a36Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define _GNU_SOURCE 862306a36Sopenharmony_ci#include <err.h> 962306a36Sopenharmony_ci#include <stdio.h> 1062306a36Sopenharmony_ci#include <stdint.h> 1162306a36Sopenharmony_ci#include <signal.h> 1262306a36Sopenharmony_ci#include <setjmp.h> 1362306a36Sopenharmony_ci#include <stdlib.h> 1462306a36Sopenharmony_ci#include <string.h> 1562306a36Sopenharmony_ci#include <errno.h> 1662306a36Sopenharmony_ci#include <unistd.h> 1762306a36Sopenharmony_ci#include <sys/types.h> 1862306a36Sopenharmony_ci#include <sys/wait.h> 1962306a36Sopenharmony_ci#include <stdbool.h> 2062306a36Sopenharmony_ci#include <sched.h> 2162306a36Sopenharmony_ci#include <sys/io.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int nerrs = 0; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 2662306a36Sopenharmony_ci int flags) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct sigaction sa; 2962306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 3062306a36Sopenharmony_ci sa.sa_sigaction = handler; 3162306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 3262306a36Sopenharmony_ci sigemptyset(&sa.sa_mask); 3362306a36Sopenharmony_ci if (sigaction(sig, &sa, 0)) 3462306a36Sopenharmony_ci err(1, "sigaction"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void clearhandler(int sig) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct sigaction sa; 4162306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 4262306a36Sopenharmony_ci sa.sa_handler = SIG_DFL; 4362306a36Sopenharmony_ci sigemptyset(&sa.sa_mask); 4462306a36Sopenharmony_ci if (sigaction(sig, &sa, 0)) 4562306a36Sopenharmony_ci err(1, "sigaction"); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic jmp_buf jmpbuf; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void sigsegv(int sig, siginfo_t *si, void *ctx_void) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci siglongjmp(jmpbuf, 1); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic bool try_outb(unsigned short port) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 5862306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) != 0) { 5962306a36Sopenharmony_ci return false; 6062306a36Sopenharmony_ci } else { 6162306a36Sopenharmony_ci asm volatile ("outb %%al, %w[port]" 6262306a36Sopenharmony_ci : : [port] "Nd" (port), "a" (0)); 6362306a36Sopenharmony_ci return true; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci clearhandler(SIGSEGV); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void expect_ok_outb(unsigned short port) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci if (!try_outb(port)) { 7162306a36Sopenharmony_ci printf("[FAIL]\toutb to 0x%02hx failed\n", port); 7262306a36Sopenharmony_ci exit(1); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci printf("[OK]\toutb to 0x%02hx worked\n", port); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void expect_gp_outb(unsigned short port) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (try_outb(port)) { 8162306a36Sopenharmony_ci printf("[FAIL]\toutb to 0x%02hx worked\n", port); 8262306a36Sopenharmony_ci nerrs++; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci printf("[OK]\toutb to 0x%02hx failed\n", port); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define RET_FAULTED 0 8962306a36Sopenharmony_ci#define RET_FAIL 1 9062306a36Sopenharmony_ci#define RET_EMUL 2 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int try_cli(void) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci unsigned long flags; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 9762306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) != 0) { 9862306a36Sopenharmony_ci return RET_FAULTED; 9962306a36Sopenharmony_ci } else { 10062306a36Sopenharmony_ci asm volatile("cli; pushf; pop %[flags]" 10162306a36Sopenharmony_ci : [flags] "=rm" (flags)); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* X86_FLAGS_IF */ 10462306a36Sopenharmony_ci if (!(flags & (1 << 9))) 10562306a36Sopenharmony_ci return RET_FAIL; 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci return RET_EMUL; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci clearhandler(SIGSEGV); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int try_sti(bool irqs_off) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci unsigned long flags; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 11762306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) != 0) { 11862306a36Sopenharmony_ci return RET_FAULTED; 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci asm volatile("sti; pushf; pop %[flags]" 12162306a36Sopenharmony_ci : [flags] "=rm" (flags)); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* X86_FLAGS_IF */ 12462306a36Sopenharmony_ci if (irqs_off && (flags & (1 << 9))) 12562306a36Sopenharmony_ci return RET_FAIL; 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci return RET_EMUL; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci clearhandler(SIGSEGV); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void expect_gp_sti(bool irqs_off) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int ret = try_sti(irqs_off); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci switch (ret) { 13762306a36Sopenharmony_ci case RET_FAULTED: 13862306a36Sopenharmony_ci printf("[OK]\tSTI faulted\n"); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case RET_EMUL: 14162306a36Sopenharmony_ci printf("[OK]\tSTI NOPped\n"); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci default: 14462306a36Sopenharmony_ci printf("[FAIL]\tSTI worked\n"); 14562306a36Sopenharmony_ci nerrs++; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * Returns whether it managed to disable interrupts. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic bool test_cli(void) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int ret = try_cli(); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci switch (ret) { 15762306a36Sopenharmony_ci case RET_FAULTED: 15862306a36Sopenharmony_ci printf("[OK]\tCLI faulted\n"); 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci case RET_EMUL: 16162306a36Sopenharmony_ci printf("[OK]\tCLI NOPped\n"); 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci default: 16462306a36Sopenharmony_ci printf("[FAIL]\tCLI worked\n"); 16562306a36Sopenharmony_ci nerrs++; 16662306a36Sopenharmony_ci return true; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return false; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciint main(void) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci cpu_set_t cpuset; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci CPU_ZERO(&cpuset); 17762306a36Sopenharmony_ci CPU_SET(0, &cpuset); 17862306a36Sopenharmony_ci if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 17962306a36Sopenharmony_ci err(1, "sched_setaffinity to CPU 0"); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Probe for iopl support. Note that iopl(0) works even as nonroot. */ 18262306a36Sopenharmony_ci switch(iopl(3)) { 18362306a36Sopenharmony_ci case 0: 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case -ENOSYS: 18662306a36Sopenharmony_ci printf("[OK]\tiopl() nor supported\n"); 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci default: 18962306a36Sopenharmony_ci printf("[OK]\tiopl(3) failed (%d) -- try running as root\n", 19062306a36Sopenharmony_ci errno); 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Make sure that CLI/STI are blocked even with IOPL level 3 */ 19562306a36Sopenharmony_ci expect_gp_sti(test_cli()); 19662306a36Sopenharmony_ci expect_ok_outb(0x80); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Establish an I/O bitmap to test the restore */ 19962306a36Sopenharmony_ci if (ioperm(0x80, 1, 1) != 0) 20062306a36Sopenharmony_ci err(1, "ioperm(0x80, 1, 1) failed\n"); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Restore our original state prior to starting the fork test. */ 20362306a36Sopenharmony_ci if (iopl(0) != 0) 20462306a36Sopenharmony_ci err(1, "iopl(0)"); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Verify that IOPL emulation is disabled and the I/O bitmap still 20862306a36Sopenharmony_ci * works. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci expect_ok_outb(0x80); 21162306a36Sopenharmony_ci expect_gp_outb(0xed); 21262306a36Sopenharmony_ci /* Drop the I/O bitmap */ 21362306a36Sopenharmony_ci if (ioperm(0x80, 1, 0) != 0) 21462306a36Sopenharmony_ci err(1, "ioperm(0x80, 1, 0) failed\n"); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci pid_t child = fork(); 21762306a36Sopenharmony_ci if (child == -1) 21862306a36Sopenharmony_ci err(1, "fork"); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (child == 0) { 22162306a36Sopenharmony_ci printf("\tchild: set IOPL to 3\n"); 22262306a36Sopenharmony_ci if (iopl(3) != 0) 22362306a36Sopenharmony_ci err(1, "iopl"); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci printf("[RUN]\tchild: write to 0x80\n"); 22662306a36Sopenharmony_ci asm volatile ("outb %%al, $0x80" : : "a" (0)); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci int status; 23162306a36Sopenharmony_ci if (waitpid(child, &status, 0) != child || 23262306a36Sopenharmony_ci !WIFEXITED(status)) { 23362306a36Sopenharmony_ci printf("[FAIL]\tChild died\n"); 23462306a36Sopenharmony_ci nerrs++; 23562306a36Sopenharmony_ci } else if (WEXITSTATUS(status) != 0) { 23662306a36Sopenharmony_ci printf("[FAIL]\tChild failed\n"); 23762306a36Sopenharmony_ci nerrs++; 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci printf("[OK]\tChild succeeded\n"); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci printf("[RUN]\tparent: write to 0x80 (should fail)\n"); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci expect_gp_outb(0x80); 24662306a36Sopenharmony_ci expect_gp_sti(test_cli()); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Test the capability checks. */ 24962306a36Sopenharmony_ci printf("\tiopl(3)\n"); 25062306a36Sopenharmony_ci if (iopl(3) != 0) 25162306a36Sopenharmony_ci err(1, "iopl(3)"); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci printf("\tDrop privileges\n"); 25462306a36Sopenharmony_ci if (setresuid(1, 1, 1) != 0) { 25562306a36Sopenharmony_ci printf("[WARN]\tDropping privileges failed\n"); 25662306a36Sopenharmony_ci goto done; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n"); 26062306a36Sopenharmony_ci if (iopl(3) != 0) { 26162306a36Sopenharmony_ci printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n"); 26262306a36Sopenharmony_ci nerrs++; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci printf("[RUN]\tiopl(0) unprivileged\n"); 26662306a36Sopenharmony_ci if (iopl(0) != 0) { 26762306a36Sopenharmony_ci printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n"); 26862306a36Sopenharmony_ci nerrs++; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci printf("[RUN]\tiopl(3) unprivileged\n"); 27262306a36Sopenharmony_ci if (iopl(3) == 0) { 27362306a36Sopenharmony_ci printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n"); 27462306a36Sopenharmony_ci nerrs++; 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci printf("[OK]\tFailed as expected\n"); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cidone: 28062306a36Sopenharmony_ci return nerrs ? 1 : 0; 28162306a36Sopenharmony_ci} 282