aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-09-04 09:52:57 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-09-04 09:52:57 -0700
commitb0c79f49c343cda8954b3322984c32f258ca4ccb (patch)
treedd823d13683b7e6b0caebcaf3964df6150aee294 /tools
parentMerge branch 'sched-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip (diff)
parentobjtool: Handle GCC stack pointer adjustment bug (diff)
downloadlinux-dev-b0c79f49c343cda8954b3322984c32f258ca4ccb.tar.xz
linux-dev-b0c79f49c343cda8954b3322984c32f258ca4ccb.zip
Merge branch 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 asm updates from Ingo Molnar: - Introduce the ORC unwinder, which can be enabled via CONFIG_ORC_UNWINDER=y. The ORC unwinder is a lightweight, Linux kernel specific debuginfo implementation, which aims to be DWARF done right for unwinding. Objtool is used to generate the ORC unwinder tables during build, so the data format is flexible and kernel internal: there's no dependency on debuginfo created by an external toolchain. The ORC unwinder is almost two orders of magnitude faster than the (out of tree) DWARF unwinder - which is important for perf call graph profiling. It is also significantly simpler and is coded defensively: there has not been a single ORC related kernel crash so far, even with early versions. (knock on wood!) But the main advantage is that enabling the ORC unwinder allows CONFIG_FRAME_POINTERS to be turned off - which speeds up the kernel measurably: With frame pointers disabled, GCC does not have to add frame pointer instrumentation code to every function in the kernel. The kernel's .text size decreases by about 3.2%, resulting in better cache utilization and fewer instructions executed, resulting in a broad kernel-wide speedup. Average speedup of system calls should be roughly in the 1-3% range - measurements by Mel Gorman [1] have shown a speedup of 5-10% for some function execution intense workloads. The main cost of the unwinder is that the unwinder data has to be stored in RAM: the memory cost is 2-4MB of RAM, depending on kernel config - which is a modest cost on modern x86 systems. Given how young the ORC unwinder code is it's not enabled by default - but given the performance advantages the plan is to eventually make it the default unwinder on x86. See Documentation/x86/orc-unwinder.txt for more details. - Remove lguest support: its intended role was that of a temporary proof of concept for virtualization, plus its removal will enable the reduction (removal) of the paravirt API as well, so Rusty agreed to its removal. (Juergen Gross) - Clean up and fix FSGS related functionality (Andy Lutomirski) - Clean up IO access APIs (Andy Shevchenko) - Enhance the symbol namespace (Jiri Slaby) * 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (47 commits) objtool: Handle GCC stack pointer adjustment bug x86/entry/64: Use ENTRY() instead of ALIGN+GLOBAL for stub32_clone() x86/fpu/math-emu: Add ENDPROC to functions x86/boot/64: Extract efi_pe_entry() from startup_64() x86/boot/32: Extract efi_pe_entry() from startup_32() x86/lguest: Remove lguest support x86/paravirt/xen: Remove xen_patch() objtool: Fix objtool fallthrough detection with function padding x86/xen/64: Fix the reported SS and CS in SYSCALL objtool: Track DRAP separately from callee-saved registers objtool: Fix validate_branch() return codes x86: Clarify/fix no-op barriers for text_poke_bp() x86/switch_to/64: Rewrite FS/GS switching yet again to fix AMD CPUs selftests/x86/fsgsbase: Test selectors 1, 2, and 3 x86/fsgsbase/64: Report FSBASE and GSBASE correctly in core dumps x86/fsgsbase/64: Fully initialize FS and GS state in start_thread_common x86/asm: Fix UNWIND_HINT_REGS macro for older binutils x86/asm/32: Fix regs_get_register() on segment registers x86/xen/64: Rearrange the SYSCALL entries x86/asm/32: Remove a bunch of '& 0xffff' from pt_regs segment reads ...
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile11
-rw-r--r--tools/lguest/.gitignore2
-rw-r--r--tools/lguest/Makefile14
-rw-r--r--tools/lguest/extract58
-rw-r--r--tools/lguest/lguest.c3420
-rw-r--r--tools/lguest/lguest.txt125
-rw-r--r--tools/objtool/Build3
-rw-r--r--tools/objtool/Documentation/stack-validation.txt56
-rw-r--r--tools/objtool/Makefile6
-rw-r--r--tools/objtool/arch.h5
-rw-r--r--tools/objtool/arch/x86/decode.c111
-rw-r--r--tools/objtool/builtin-check.c7
-rw-r--r--tools/objtool/builtin-orc.c70
-rw-r--r--tools/objtool/builtin.h1
-rw-r--r--tools/objtool/cfi.h2
-rw-r--r--tools/objtool/check.c498
-rw-r--r--tools/objtool/check.h22
-rw-r--r--tools/objtool/elf.c212
-rw-r--r--tools/objtool/elf.h15
-rw-r--r--tools/objtool/objtool.c3
-rw-r--r--tools/objtool/orc.h30
-rw-r--r--tools/objtool/orc_dump.c212
-rw-r--r--tools/objtool/orc_gen.c214
-rw-r--r--tools/objtool/orc_types.h107
-rw-r--r--tools/testing/selftests/x86/fsgsbase.c41
25 files changed, 1377 insertions, 3868 deletions
diff --git a/tools/Makefile b/tools/Makefile
index 221e1ce78b06..a19b176b914b 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -18,7 +18,6 @@ help:
@echo ' iio - IIO tools'
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
@echo ' leds - LEDs tools'
- @echo ' lguest - a minimal 32-bit x86 hypervisor'
@echo ' liblockdep - user-space wrapper for kernel locking-validator'
@echo ' net - misc networking tools'
@echo ' perf - Linux performance measurement and analysis tool'
@@ -90,7 +89,7 @@ freefall: FORCE
kvm_stat: FORCE
$(call descend,kvm/$@)
-all: acpi cgroup cpupower gpio hv firewire lguest liblockdep \
+all: acpi cgroup cpupower gpio hv firewire liblockdep \
perf selftests turbostat usb \
virtio vm net x86_energy_perf_policy \
tmon freefall objtool kvm_stat
@@ -101,7 +100,7 @@ acpi_install:
cpupower_install:
$(call descend,power/$(@:_install=),install)
-cgroup_install firewire_install gpio_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install:
+cgroup_install firewire_install gpio_install hv_install perf_install usb_install virtio_install vm_install net_install objtool_install:
$(call descend,$(@:_install=),install)
liblockdep_install:
@@ -123,7 +122,7 @@ kvm_stat_install:
$(call descend,kvm/$(@:_install=),install)
install: acpi_install cgroup_install cpupower_install gpio_install \
- hv_install firewire_install lguest_install liblockdep_install \
+ hv_install firewire_install liblockdep_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install net_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install
@@ -134,7 +133,7 @@ acpi_clean:
cpupower_clean:
$(call descend,power/cpupower,clean)
-cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
+cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
$(call descend,$(@:_clean=),clean)
liblockdep_clean:
@@ -168,7 +167,7 @@ freefall_clean:
build_clean:
$(call descend,build,clean)
-clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
+clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
diff --git a/tools/lguest/.gitignore b/tools/lguest/.gitignore
deleted file mode 100644
index 8d9a8383a52e..000000000000
--- a/tools/lguest/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-lguest
-include
diff --git a/tools/lguest/Makefile b/tools/lguest/Makefile
deleted file mode 100644
index d04599a79802..000000000000
--- a/tools/lguest/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-# This creates the demonstration utility "lguest" which runs a Linux guest.
-CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE -Iinclude
-
-all: lguest
-
-include/linux/virtio_types.h: ../../include/uapi/linux/virtio_types.h
- mkdir -p include/linux 2>&1 || true
- ln -sf ../../../../include/uapi/linux/virtio_types.h $@
-
-lguest: include/linux/virtio_types.h
-
-clean:
- rm -f lguest
- rm -rf include
diff --git a/tools/lguest/extract b/tools/lguest/extract
deleted file mode 100644
index 7730bb6e4b94..000000000000
--- a/tools/lguest/extract
+++ /dev/null
@@ -1,58 +0,0 @@
-#! /bin/sh
-
-set -e
-
-PREFIX=$1
-shift
-
-trap 'rm -r $TMPDIR' 0
-TMPDIR=`mktemp -d`
-
-exec 3>/dev/null
-for f; do
- while IFS="
-" read -r LINE; do
- case "$LINE" in
- *$PREFIX:[0-9]*:\**)
- NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"`
- if [ -f $TMPDIR/$NUM ]; then
- echo "$TMPDIR/$NUM already exits prior to $f"
- exit 1
- fi
- exec 3>>$TMPDIR/$NUM
- echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM
- /bin/echo "$LINE" | sed -e "s/$PREFIX:[0-9]*//" -e "s/:\*/*/" >&3
- ;;
- *$PREFIX:[0-9]*)
- NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"`
- if [ -f $TMPDIR/$NUM ]; then
- echo "$TMPDIR/$NUM already exits prior to $f"
- exit 1
- fi
- exec 3>>$TMPDIR/$NUM
- echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM
- /bin/echo "$LINE" | sed "s/$PREFIX:[0-9]*//" >&3
- ;;
- *:\**)
- /bin/echo "$LINE" | sed -e "s/:\*/*/" -e "s,/\*\*/,," >&3
- echo >&3
- exec 3>/dev/null
- ;;
- *)
- /bin/echo "$LINE" >&3
- ;;
- esac
- done < $f
- echo >&3
- exec 3>/dev/null
-done
-
-LASTFILE=""
-for f in $TMPDIR/*; do
- if [ "$LASTFILE" != $(cat $TMPDIR/.$(basename $f) ) ]; then
- LASTFILE=$(cat $TMPDIR/.$(basename $f) )
- echo "[ $LASTFILE ]"
- fi
- cat $f
-done
-
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c
deleted file mode 100644
index 897cd6f3f687..000000000000
--- a/tools/lguest/lguest.c
+++ /dev/null
@@ -1,3420 +0,0 @@
-/*P:100
- * This is the Launcher code, a simple program which lays out the "physical"
- * memory for the new Guest by mapping the kernel image and the virtual
- * devices, then opens /dev/lguest to tell the kernel about the Guest and
- * control it.
-:*/
-#define _LARGEFILE64_SOURCE
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <err.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <elf.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/eventfd.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <ctype.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <time.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <linux/sockios.h>
-#include <linux/if_tun.h>
-#include <sys/uio.h>
-#include <termios.h>
-#include <getopt.h>
-#include <assert.h>
-#include <sched.h>
-#include <limits.h>
-#include <stddef.h>
-#include <signal.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/user.h>
-#include <linux/pci_regs.h>
-
-#ifndef VIRTIO_F_ANY_LAYOUT
-#define VIRTIO_F_ANY_LAYOUT 27
-#endif
-
-/*L:110
- * We can ignore the 43 include files we need for this program, but I do want
- * to draw attention to the use of kernel-style types.
- *
- * As Linus said, "C is a Spartan language, and so should your naming be." I
- * like these abbreviations, so we define them here. Note that u64 is always
- * unsigned long long, which works on all Linux systems: this means that we can
- * use %llu in printf for any u64.
- */
-typedef unsigned long long u64;
-typedef uint32_t u32;
-typedef uint16_t u16;
-typedef uint8_t u8;
-/*:*/
-
-#define VIRTIO_CONFIG_NO_LEGACY
-#define VIRTIO_PCI_NO_LEGACY
-#define VIRTIO_BLK_NO_LEGACY
-#define VIRTIO_NET_NO_LEGACY
-
-/* Use in-kernel ones, which defines VIRTIO_F_VERSION_1 */
-#include "../../include/uapi/linux/virtio_config.h"
-#include "../../include/uapi/linux/virtio_net.h"
-#include "../../include/uapi/linux/virtio_blk.h"
-#include "../../include/uapi/linux/virtio_console.h"
-#include "../../include/uapi/linux/virtio_rng.h"
-#include <linux/virtio_ring.h>
-#include "../../include/uapi/linux/virtio_pci.h"
-#include <asm/bootparam.h>
-#include "../../include/linux/lguest_launcher.h"
-
-#define BRIDGE_PFX "bridge:"
-#ifndef SIOCBRADDIF
-#define SIOCBRADDIF 0x89a2 /* add interface to bridge */
-#endif
-/* We can have up to 256 pages for devices. */
-#define DEVICE_PAGES 256
-/* This will occupy 3 pages: it must be a power of 2. */
-#define VIRTQUEUE_NUM 256
-
-/*L:120
- * verbose is both a global flag and a macro. The C preprocessor allows
- * this, and although I wouldn't recommend it, it works quite nicely here.
- */
-static bool verbose;
-#define verbose(args...) \
- do { if (verbose) printf(args); } while(0)
-/*:*/
-
-/* The pointer to the start of guest memory. */
-static void *guest_base;
-/* The maximum guest physical address allowed, and maximum possible. */
-static unsigned long guest_limit, guest_max, guest_mmio;
-/* The /dev/lguest file descriptor. */
-static int lguest_fd;
-
-/* a per-cpu variable indicating whose vcpu is currently running */
-static unsigned int __thread cpu_id;
-
-/* 5 bit device number in the PCI_CONFIG_ADDR => 32 only */
-#define MAX_PCI_DEVICES 32
-
-/* This is our list of devices. */
-struct device_list {
- /* Counter to assign interrupt numbers. */
- unsigned int next_irq;
-
- /* Counter to print out convenient device numbers. */
- unsigned int device_num;
-
- /* PCI devices. */
- struct device *pci[MAX_PCI_DEVICES];
-};
-
-/* The list of Guest devices, based on command line arguments. */
-static struct device_list devices;
-
-/*
- * Just like struct virtio_pci_cfg_cap in uapi/linux/virtio_pci.h,
- * but uses a u32 explicitly for the data.
- */
-struct virtio_pci_cfg_cap_u32 {
- struct virtio_pci_cap cap;
- u32 pci_cfg_data; /* Data for BAR access. */
-};
-
-struct virtio_pci_mmio {
- struct virtio_pci_common_cfg cfg;
- u16 notify;
- u8 isr;
- u8 padding;
- /* Device-specific configuration follows this. */
-};
-
-/* This is the layout (little-endian) of the PCI config space. */
-struct pci_config {
- u16 vendor_id, device_id;
- u16 command, status;
- u8 revid, prog_if, subclass, class;
- u8 cacheline_size, lat_timer, header_type, bist;
- u32 bar[6];
- u32 cardbus_cis_ptr;
- u16 subsystem_vendor_id, subsystem_device_id;
- u32 expansion_rom_addr;
- u8 capabilities, reserved1[3];
- u32 reserved2;
- u8 irq_line, irq_pin, min_grant, max_latency;
-
- /* Now, this is the linked capability list. */
- struct virtio_pci_cap common;
- struct virtio_pci_notify_cap notify;
- struct virtio_pci_cap isr;
- struct virtio_pci_cap device;
- struct virtio_pci_cfg_cap_u32 cfg_access;
-};
-
-/* The device structure describes a single device. */
-struct device {
- /* The name of this device, for --verbose. */
- const char *name;
-
- /* Any queues attached to this device */
- struct virtqueue *vq;
-
- /* Is it operational */
- bool running;
-
- /* Has it written FEATURES_OK but not re-checked it? */
- bool wrote_features_ok;
-
- /* PCI configuration */
- union {
- struct pci_config config;
- u32 config_words[sizeof(struct pci_config) / sizeof(u32)];
- };
-
- /* Features we offer, and those accepted. */
- u64 features, features_accepted;
-
- /* Device-specific config hangs off the end of this. */
- struct virtio_pci_mmio *mmio;
-
- /* PCI MMIO resources (all in BAR0) */
- size_t mmio_size;
- u32 mmio_addr;
-
- /* Device-specific data. */
- void *priv;
-};
-
-/* The virtqueue structure describes a queue attached to a device. */
-struct virtqueue {
- struct virtqueue *next;
-
- /* Which device owns me. */
- struct device *dev;
-
- /* Name for printing errors. */
- const char *name;
-
- /* The actual ring of buffers. */
- struct vring vring;
-
- /* The information about this virtqueue (we only use queue_size on) */
- struct virtio_pci_common_cfg pci_config;
-
- /* Last available index we saw. */
- u16 last_avail_idx;
-
- /* How many are used since we sent last irq? */
- unsigned int pending_used;
-
- /* Eventfd where Guest notifications arrive. */
- int eventfd;
-
- /* Function for the thread which is servicing this virtqueue. */
- void (*service)(struct virtqueue *vq);
- pid_t thread;
-};
-
-/* Remember the arguments to the program so we can "reboot" */
-static char **main_args;
-
-/* The original tty settings to restore on exit. */
-static struct termios orig_term;
-
-/*
- * We have to be careful with barriers: our devices are all run in separate
- * threads and so we need to make sure that changes visible to the Guest happen
- * in precise order.
- */
-#define wmb() __asm__ __volatile__("" : : : "memory")
-#define rmb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory")
-#define mb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory")
-
-/* Wrapper for the last available index. Makes it easier to change. */
-#define lg_last_avail(vq) ((vq)->last_avail_idx)
-
-/*
- * The virtio configuration space is defined to be little-endian. x86 is
- * little-endian too, but it's nice to be explicit so we have these helpers.
- */
-#define cpu_to_le16(v16) (v16)
-#define cpu_to_le32(v32) (v32)
-#define cpu_to_le64(v64) (v64)
-#define le16_to_cpu(v16) (v16)
-#define le32_to_cpu(v32) (v32)
-#define le64_to_cpu(v64) (v64)
-
-/*
- * A real device would ignore weird/non-compliant driver behaviour. We
- * stop and flag it, to help debugging Linux problems.
- */
-#define bad_driver(d, fmt, ...) \
- errx(1, "%s: bad driver: " fmt, (d)->name, ## __VA_ARGS__)
-#define bad_driver_vq(vq, fmt, ...) \
- errx(1, "%s vq %s: bad driver: " fmt, (vq)->dev->name, \
- vq->name, ## __VA_ARGS__)
-
-/* Is this iovec empty? */
-static bool iov_empty(const struct iovec iov[], unsigned int num_iov)
-{
- unsigned int i;
-
- for (i = 0; i < num_iov; i++)
- if (iov[i].iov_len)
- return false;
- return true;
-}
-
-/* Take len bytes from the front of this iovec. */
-static void iov_consume(struct device *d,
- struct iovec iov[], unsigned num_iov,
- void *dest, unsigned len)
-{
- unsigned int i;
-
- for (i = 0; i < num_iov; i++) {
- unsigned int used;
-
- used = iov[i].iov_len < len ? iov[i].iov_len : len;
- if (dest) {
- memcpy(dest, iov[i].iov_base, used);
- dest += used;
- }
- iov[i].iov_base += used;
- iov[i].iov_len -= used;
- len -= used;
- }
- if (len != 0)
- bad_driver(d, "iovec too short!");
-}
-
-/*L:100
- * The Launcher code itself takes us out into userspace, that scary place where
- * pointers run wild and free! Unfortunately, like most userspace programs,
- * it's quite boring (which is why everyone likes to hack on the kernel!).
- * Perhaps if you make up an Lguest Drinking Game at this point, it will get
- * you through this section. Or, maybe not.
- *
- * The Launcher sets up a big chunk of memory to be the Guest's "physical"
- * memory and stores it in "guest_base". In other words, Guest physical ==
- * Launcher virtual with an offset.
- *
- * This can be tough to get your head around, but usually it just means that we
- * use these trivial conversion functions when the Guest gives us its
- * "physical" addresses:
- */
-static void *from_guest_phys(unsigned long addr)
-{
- return guest_base + addr;
-}
-
-static unsigned long to_guest_phys(const void *addr)
-{
- return (addr - guest_base);
-}
-
-/*L:130
- * Loading the Kernel.
- *
- * We start with couple of simple helper routines. open_or_die() avoids
- * error-checking code cluttering the callers:
- */
-static int open_or_die(const char *name, int flags)
-{
- int fd = open(name, flags);
- if (fd < 0)
- err(1, "Failed to open %s", name);
- return fd;
-}
-
-/* map_zeroed_pages() takes a number of pages. */
-static void *map_zeroed_pages(unsigned int num)
-{
- int fd = open_or_die("/dev/zero", O_RDONLY);
- void *addr;
-
- /*
- * We use a private mapping (ie. if we write to the page, it will be
- * copied). We allocate an extra two pages PROT_NONE to act as guard
- * pages against read/write attempts that exceed allocated space.
- */
- addr = mmap(NULL, getpagesize() * (num+2),
- PROT_NONE, MAP_PRIVATE, fd, 0);
-
- if (addr == MAP_FAILED)
- err(1, "Mmapping %u pages of /dev/zero", num);
-
- if (mprotect(addr + getpagesize(), getpagesize() * num,
- PROT_READ|PROT_WRITE) == -1)
- err(1, "mprotect rw %u pages failed", num);
-
- /*
- * One neat mmap feature is that you can close the fd, and it
- * stays mapped.
- */
- close(fd);
-
- /* Return address after PROT_NONE page */
- return addr + getpagesize();
-}
-
-/* Get some bytes which won't be mapped into the guest. */
-static unsigned long get_mmio_region(size_t size)
-{
- unsigned long addr = guest_mmio;
- size_t i;
-
- if (!size)
- return addr;
-
- /* Size has to be a power of 2 (and multiple of 16) */
- for (i = 1; i < size; i <<= 1);
-
- guest_mmio += i;
-
- return addr;
-}
-
-/*
- * This routine is used to load the kernel or initrd. It tries mmap, but if
- * that fails (Plan 9's kernel file isn't nicely aligned on page boundaries),
- * it falls back to reading the memory in.
- */
-static void map_at(int fd, void *addr, unsigned long offset, unsigned long len)
-{
- ssize_t r;
-
- /*
- * We map writable even though for some segments are marked read-only.
- * The kernel really wants to be writable: it patches its own
- * instructions.
- *
- * MAP_PRIVATE means that the page won't be copied until a write is
- * done to it. This allows us to share untouched memory between
- * Guests.
- */
- if (mmap(addr, len, PROT_READ|PROT_WRITE,
- MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED)
- return;
-
- /* pread does a seek and a read in one shot: saves a few lines. */
- r = pread(fd, addr, len, offset);
- if (r != len)
- err(1, "Reading offset %lu len %lu gave %zi", offset, len, r);
-}
-
-/*
- * This routine takes an open vmlinux image, which is in ELF, and maps it into
- * the Guest memory. ELF = Embedded Linking Format, which is the format used
- * by all modern binaries on Linux including the kernel.
- *
- * The ELF headers give *two* addresses: a physical address, and a virtual
- * address. We use the physical address; the Guest will map itself to the
- * virtual address.
- *
- * We return the starting address.
- */
-static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr)
-{
- Elf32_Phdr phdr[ehdr->e_phnum];
- unsigned int i;
-
- /*
- * Sanity checks on the main ELF header: an x86 executable with a
- * reasonable number of correctly-sized program headers.
- */
- if (ehdr->e_type != ET_EXEC
- || ehdr->e_machine != EM_386
- || ehdr->e_phentsize != sizeof(Elf32_Phdr)
- || ehdr->e_phnum < 1 || ehdr->e_phnum > 65536U/sizeof(Elf32_Phdr))
- errx(1, "Malformed elf header");
-
- /*
- * An ELF executable contains an ELF header and a number of "program"
- * headers which indicate which parts ("segments") of the program to
- * load where.
- */
-
- /* We read in all the program headers at once: */
- if (lseek(elf_fd, ehdr->e_phoff, SEEK_SET) < 0)
- err(1, "Seeking to program headers");
- if (read(elf_fd, phdr, sizeof(phdr)) != sizeof(phdr))
- err(1, "Reading program headers");
-
- /*
- * Try all the headers: there are usually only three. A read-only one,
- * a read-write one, and a "note" section which we don't load.
- */
- for (i = 0; i < ehdr->e_phnum; i++) {
- /* If this isn't a loadable segment, we ignore it */
- if (phdr[i].p_type != PT_LOAD)
- continue;
-
- verbose("Section %i: size %i addr %p\n",
- i, phdr[i].p_memsz, (void *)phdr[i].p_paddr);
-
- /* We map this section of the file at its physical address. */
- map_at(elf_fd, from_guest_phys(phdr[i].p_paddr),
- phdr[i].p_offset, phdr[i].p_filesz);
- }
-
- /* The entry point is given in the ELF header. */
- return ehdr->e_entry;
-}
-
-/*L:150
- * A bzImage, unlike an ELF file, is not meant to be loaded. You're supposed
- * to jump into it and it will unpack itself. We used to have to perform some
- * hairy magic because the unpacking code scared me.
- *
- * Fortunately, Jeremy Fitzhardinge convinced me it wasn't that hard and wrote
- * a small patch to jump over the tricky bits in the Guest, so now we just read
- * the funky header so we know where in the file to load, and away we go!
- */
-static unsigned long load_bzimage(int fd)
-{
- struct boot_params boot;
- int r;
- /* Modern bzImages get loaded at 1M. */
- void *p = from_guest_phys(0x100000);
-
- /*
- * Go back to the start of the file and read the header. It should be
- * a Linux boot header (see Documentation/x86/boot.txt)
- */
- lseek(fd, 0, SEEK_SET);
- read(fd, &boot, sizeof(boot));
-
- /* Inside the setup_hdr, we expect the magic "HdrS" */
- if (memcmp(&boot.hdr.header, "HdrS", 4) != 0)
- errx(1, "This doesn't look like a bzImage to me");
-
- /* Skip over the extra sectors of the header. */
- lseek(fd, (boot.hdr.setup_sects+1) * 512, SEEK_SET);
-
- /* Now read everything into memory. in nice big chunks. */
- while ((r = read(fd, p, 65536)) > 0)
- p += r;
-
- /* Finally, code32_start tells us where to enter the kernel. */
- return boot.hdr.code32_start;
-}
-
-/*L:140
- * Loading the kernel is easy when it's a "vmlinux", but most kernels
- * come wrapped up in the self-decompressing "bzImage" format. With a little
- * work, we can load those, too.
- */
-static unsigned long load_kernel(int fd)
-{
- Elf32_Ehdr hdr;
-
- /* Read in the first few bytes. */
- if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
- err(1, "Reading kernel");
-
- /* If it's an ELF file, it starts with "\177ELF" */
- if (memcmp(hdr.e_ident, ELFMAG, SELFMAG) == 0)
- return map_elf(fd, &hdr);
-
- /* Otherwise we assume it's a bzImage, and try to load it. */
- return load_bzimage(fd);
-}
-
-/*
- * This is a trivial little helper to align pages. Andi Kleen hated it because
- * it calls getpagesize() twice: "it's dumb code."
- *
- * Kernel guys get really het up about optimization, even when it's not
- * necessary. I leave this code as a reaction against that.
- */
-static inline unsigned long page_align(unsigned long addr)
-{
- /* Add upwards and truncate downwards. */
- return ((addr + getpagesize()-1) & ~(getpagesize()-1));
-}
-
-/*L:180
- * An "initial ram disk" is a disk image loaded into memory along with the
- * kernel which the kernel can use to boot from without needing any drivers.
- * Most distributions now use this as standard: the initrd contains the code to
- * load the appropriate driver modules for the current machine.
- *
- * Importantly, James Morris works for RedHat, and Fedora uses initrds for its
- * kernels. He sent me this (and tells me when I break it).
- */
-static unsigned long load_initrd(const char *name, unsigned long mem)
-{
- int ifd;
- struct stat st;
- unsigned long len;
-
- ifd = open_or_die(name, O_RDONLY);
- /* fstat() is needed to get the file size. */
- if (fstat(ifd, &st) < 0)
- err(1, "fstat() on initrd '%s'", name);
-
- /*
- * We map the initrd at the top of memory, but mmap wants it to be
- * page-aligned, so we round the size up for that.
- */
- len = page_align(st.st_size);
- map_at(ifd, from_guest_phys(mem - len), 0, st.st_size);
- /*
- * Once a file is mapped, you can close the file descriptor. It's a
- * little odd, but quite useful.
- */
- close(ifd);
- verbose("mapped initrd %s size=%lu @ %p\n", name, len, (void*)mem-len);
-
- /* We return the initrd size. */
- return len;
-}
-/*:*/
-
-/*
- * Simple routine to roll all the commandline arguments together with spaces
- * between them.
- */
-static void concat(char *dst, char *args[])
-{
- unsigned int i, len = 0;
-
- for (i = 0; args[i]; i++) {
- if (i) {
- strcat(dst+len, " ");
- len++;
- }
- strcpy(dst+len, args[i]);
- len += strlen(args[i]);
- }
- /* In case it's empty. */
- dst[len] = '\0';
-}
-
-/*L:185
- * This is where we actually tell the kernel to initialize the Guest. We
- * saw the arguments it expects when we looked at initialize() in lguest_user.c:
- * the base of Guest "physical" memory, the top physical page to allow and the
- * entry point for the Guest.
- */
-static void tell_kernel(unsigned long start)
-{
- unsigned long args[] = { LHREQ_INITIALIZE,
- (unsigned long)guest_base,
- guest_limit / getpagesize(), start,
- (guest_mmio+getpagesize()-1) / getpagesize() };
- verbose("Guest: %p - %p (%#lx, MMIO %#lx)\n",
- guest_base, guest_base + guest_limit,
- guest_limit, guest_mmio);
- lguest_fd = open_or_die("/dev/lguest", O_RDWR);
- if (write(lguest_fd, args, sizeof(args)) < 0)
- err(1, "Writing to /dev/lguest");
-}
-/*:*/
-
-/*L:200
- * Device Handling.
- *
- * When the Guest gives us a buffer, it sends an array of addresses and sizes.
- * We need to make sure it's not trying to reach into the Launcher itself, so
- * we have a convenient routine which checks it and exits with an error message
- * if something funny is going on:
- */
-static void *_check_pointer(struct device *d,
- unsigned long addr, unsigned int size,
- unsigned int line)
-{
- /*
- * Check if the requested address and size exceeds the allocated memory,
- * or addr + size wraps around.
- */
- if ((addr + size) > guest_limit || (addr + size) < addr)
- bad_driver(d, "%s:%i: Invalid address %#lx",
- __FILE__, line, addr);
- /*
- * We return a pointer for the caller's convenience, now we know it's
- * safe to use.
- */
- return from_guest_phys(addr);
-}
-/* A macro which transparently hands the line number to the real function. */
-#define check_pointer(d,addr,size) _check_pointer(d, addr, size, __LINE__)
-
-/*
- * Each buffer in the virtqueues is actually a chain of descriptors. This
- * function returns the next descriptor in the chain, or vq->vring.num if we're
- * at the end.
- */
-static unsigned next_desc(struct device *d, struct vring_desc *desc,
- unsigned int i, unsigned int max)
-{
- unsigned int next;
-
- /* If this descriptor says it doesn't chain, we're done. */
- if (!(desc[i].flags & VRING_DESC_F_NEXT))
- return max;
-
- /* Check they're not leading us off end of descriptors. */
- next = desc[i].next;
- /* Make sure compiler knows to grab that: we don't want it changing! */
- wmb();
-
- if (next >= max)
- bad_driver(d, "Desc next is %u", next);
-
- return next;
-}
-
-/*
- * This actually sends the interrupt for this virtqueue, if we've used a
- * buffer.
- */
-static void trigger_irq(struct virtqueue *vq)
-{
- unsigned long buf[] = { LHREQ_IRQ, vq->dev->config.irq_line };
-
- /* Don't inform them if nothing used. */
- if (!vq->pending_used)
- return;
- vq->pending_used = 0;
-
- /*
- * 2.4.7.1:
- *
- * If the VIRTIO_F_EVENT_IDX feature bit is not negotiated:
- * The driver MUST set flags to 0 or 1.
- */
- if (vq->vring.avail->flags > 1)
- bad_driver_vq(vq, "avail->flags = %u\n", vq->vring.avail->flags);
-
- /*
- * 2.4.7.2:
- *
- * If the VIRTIO_F_EVENT_IDX feature bit is not negotiated:
- *
- * - The device MUST ignore the used_event value.
- * - After the device writes a descriptor index into the used ring:
- * - If flags is 1, the device SHOULD NOT send an interrupt.
- * - If flags is 0, the device MUST send an interrupt.
- */
- if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
- return;
- }
-
- /*
- * 4.1.4.5.1:
- *
- * If MSI-X capability is disabled, the device MUST set the Queue
- * Interrupt bit in ISR status before sending a virtqueue notification
- * to the driver.
- */
- vq->dev->mmio->isr = 0x1;
-
- /* Send the Guest an interrupt tell them we used something up. */
- if (write(lguest_fd, buf, sizeof(buf)) != 0)
- err(1, "Triggering irq %i", vq->dev->config.irq_line);
-}
-
-/*
- * This looks in the virtqueue for the first available buffer, and converts
- * it to an iovec for convenient access. Since descriptors consist of some
- * number of output then some number of input descriptors, it's actually two
- * iovecs, but we pack them into one and note how many of each there were.
- *
- * This function waits if necessary, and returns the descriptor number found.
- */
-static unsigned wait_for_vq_desc(struct virtqueue *vq,
- struct iovec iov[],
- unsigned int *out_num, unsigned int *in_num)
-{
- unsigned int i, head, max;
- struct vring_desc *desc;
- u16 last_avail = lg_last_avail(vq);
-
- /*
- * 2.4.7.1:
- *
- * The driver MUST handle spurious interrupts from the device.
- *
- * That's why this is a while loop.
- */
-
- /* There's nothing available? */
- while (last_avail == vq->vring.avail->idx) {
- u64 event;
-
- /*
- * Since we're about to sleep, now is a good time to tell the
- * Guest about what we've used up to now.
- */
- trigger_irq(vq);
-
- /* OK, now we need to know about added descriptors. */
- vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
-
- /*
- * They could have slipped one in as we were doing that: make
- * sure it's written, then check again.
- */
- mb();
- if (last_avail != vq->vring.avail->idx) {
- vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
- break;
- }
-
- /* Nothing new? Wait for eventfd to tell us they refilled. */
- if (read(vq->eventfd, &event, sizeof(event)) != sizeof(event))
- errx(1, "Event read failed?");
-
- /* We don't need to be notified again. */
- vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
- }
-
- /* Check it isn't doing very strange things with descriptor numbers. */
- if ((u16)(vq->vring.avail->idx - last_avail) > vq->vring.num)
- bad_driver_vq(vq, "Guest moved used index from %u to %u",
- last_avail, vq->vring.avail->idx);
-
- /*
- * Make sure we read the descriptor number *after* we read the ring
- * update; don't let the cpu or compiler change the order.
- */
- rmb();
-
- /*
- * Grab the next descriptor number they're advertising, and increment
- * the index we've seen.
- */
- head = vq->vring.avail->ring[last_avail % vq->vring.num];
- lg_last_avail(vq)++;
-
- /* If their number is silly, that's a fatal mistake. */
- if (head >= vq->vring.num)
- bad_driver_vq(vq, "Guest says index %u is available", head);
-
- /* When we start there are none of either input nor output. */
- *out_num = *in_num = 0;
-
- max = vq->vring.num;
- desc = vq->vring.desc;
- i = head;
-
- /*
- * We have to read the descriptor after we read the descriptor number,
- * but there's a data dependency there so the CPU shouldn't reorder
- * that: no rmb() required.
- */
-
- do {
- /*
- * If this is an indirect entry, then this buffer contains a
- * descriptor table which we handle as if it's any normal
- * descriptor chain.
- */
- if (desc[i].flags & VRING_DESC_F_INDIRECT) {
- /* 2.4.5.3.1:
- *
- * The driver MUST NOT set the VIRTQ_DESC_F_INDIRECT
- * flag unless the VIRTIO_F_INDIRECT_DESC feature was
- * negotiated.
- */
- if (!(vq->dev->features_accepted &
- (1<<VIRTIO_RING_F_INDIRECT_DESC)))
- bad_driver_vq(vq, "vq indirect not negotiated");
-
- /*
- * 2.4.5.3.1:
- *
- * The driver MUST NOT set the VIRTQ_DESC_F_INDIRECT
- * flag within an indirect descriptor (ie. only one
- * table per descriptor).
- */
- if (desc != vq->vring.desc)
- bad_driver_vq(vq, "Indirect within indirect");
-
- /*
- * Proposed update VIRTIO-134 spells this out:
- *
- * A driver MUST NOT set both VIRTQ_DESC_F_INDIRECT
- * and VIRTQ_DESC_F_NEXT in flags.
- */
- if (desc[i].flags & VRING_DESC_F_NEXT)
- bad_driver_vq(vq, "indirect and next together");
-
- if (desc[i].len % sizeof(struct vring_desc))
- bad_driver_vq(vq,
- "Invalid size for indirect table");
- /*
- * 2.4.5.3.2:
- *
- * The device MUST ignore the write-only flag
- * (flags&VIRTQ_DESC_F_WRITE) in the descriptor that
- * refers to an indirect table.
- *
- * We ignore it here: :)
- */
-
- max = desc[i].len / sizeof(struct vring_desc);
- desc = check_pointer(vq->dev, desc[i].addr, desc[i].len);
- i = 0;
-
- /* 2.4.5.3.1:
- *
- * A driver MUST NOT create a descriptor chain longer
- * than the Queue Size of the device.
- */
- if (max > vq->pci_config.queue_size)
- bad_driver_vq(vq,
- "indirect has too many entries");
- }
-
- /* Grab the first descriptor, and check it's OK. */
- iov[*out_num + *in_num].iov_len = desc[i].len;
- iov[*out_num + *in_num].iov_base
- = check_pointer(vq->dev, desc[i].addr, desc[i].len);
- /* If this is an input descriptor, increment that count. */
- if (desc[i].flags & VRING_DESC_F_WRITE)
- (*in_num)++;
- else {
- /*
- * If it's an output descriptor, they're all supposed
- * to come before any input descriptors.
- */
- if (*in_num)
- bad_driver_vq(vq,
- "Descriptor has out after in");
- (*out_num)++;
- }
-
- /* If we've got too many, that implies a descriptor loop. */
- if (*out_num + *in_num > max)
- bad_driver_vq(vq, "Looped descriptor");
- } while ((i = next_desc(vq->dev, desc, i, max)) != max);
-
- return head;
-}
-
-/*
- * After we've used one of their buffers, we tell the Guest about it. Sometime
- * later we'll want to send them an interrupt using trigger_irq(); note that
- * wait_for_vq_desc() does that for us if it has to wait.
- */
-static void add_used(struct virtqueue *vq, unsigned int head, int len)
-{
- struct vring_used_elem *used;
-
- /*
- * The virtqueue contains a ring of used buffers. Get a pointer to the
- * next entry in that used ring.
- */
- used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num];
- used->id = head;
- used->len = len;
- /* Make sure buffer is written before we update index. */
- wmb();
- vq->vring.used->idx++;
- vq->pending_used++;
-}
-
-/* And here's the combo meal deal. Supersize me! */
-static void add_used_and_trigger(struct virtqueue *vq, unsigned head, int len)
-{
- add_used(vq, head, len);
- trigger_irq(vq);
-}
-
-/*
- * The Console
- *
- * We associate some data with the console for our exit hack.
- */
-struct console_abort {
- /* How many times have they hit ^C? */
- int count;
- /* When did they start? */
- struct timeval start;
-};
-
-/* This is the routine which handles console input (ie. stdin). */
-static void console_input(struct virtqueue *vq)
-{
- int len;
- unsigned int head, in_num, out_num;
- struct console_abort *abort = vq->dev->priv;
- struct iovec iov[vq->vring.num];
-
- /* Make sure there's a descriptor available. */
- head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
- if (out_num)
- bad_driver_vq(vq, "Output buffers in console in queue?");
-
- /* Read into it. This is where we usually wait. */
- len = readv(STDIN_FILENO, iov, in_num);
- if (len <= 0) {
- /* Ran out of input? */
- warnx("Failed to get console input, ignoring console.");
- /*
- * For simplicity, dying threads kill the whole Launcher. So
- * just nap here.
- */
- for (;;)
- pause();
- }
-
- /* Tell the Guest we used a buffer. */
- add_used_and_trigger(vq, head, len);
-
- /*
- * Three ^C within one second? Exit.
- *
- * This is such a hack, but works surprisingly well. Each ^C has to
- * be in a buffer by itself, so they can't be too fast. But we check
- * that we get three within about a second, so they can't be too
- * slow.
- */
- if (len != 1 || ((char *)iov[0].iov_base)[0] != 3) {
- abort->count = 0;
- return;
- }
-
- abort->count++;
- if (abort->count == 1)
- gettimeofday(&abort->start, NULL);
- else if (abort->count == 3) {
- struct timeval now;
- gettimeofday(&now, NULL);
- /* Kill all Launcher processes with SIGINT, like normal ^C */
- if (now.tv_sec <= abort->start.tv_sec+1)
- kill(0, SIGINT);
- abort->count = 0;
- }
-}
-
-/* This is the routine which handles console output (ie. stdout). */
-static void console_output(struct virtqueue *vq)
-{
- unsigned int head, out, in;
- struct iovec iov[vq->vring.num];
-
- /* We usually wait in here, for the Guest to give us something. */
- head = wait_for_vq_desc(vq, iov, &out, &in);
- if (in)
- bad_driver_vq(vq, "Input buffers in console output queue?");
-
- /* writev can return a partial write, so we loop here. */
- while (!iov_empty(iov, out)) {
- int len = writev(STDOUT_FILENO, iov, out);
- if (len <= 0) {
- warn("Write to stdout gave %i (%d)", len, errno);
- break;
- }
- iov_consume(vq->dev, iov, out, NULL, len);
- }
-
- /*
- * We're finished with that buffer: if we're going to sleep,
- * wait_for_vq_desc() will prod the Guest with an interrupt.
- */
- add_used(vq, head, 0);
-}
-
-/*
- * The Network
- *
- * Handling output for network is also simple: we get all the output buffers
- * and write them to /dev/net/tun.
- */
-struct net_info {
- int tunfd;
-};
-
-static void net_output(struct virtqueue *vq)
-{
- struct net_info *net_info = vq->dev->priv;
- unsigned int head, out, in;
- struct iovec iov[vq->vring.num];
-
- /* We usually wait in here for the Guest to give us a packet. */
- head = wait_for_vq_desc(vq, iov, &out, &in);
- if (in)
- bad_driver_vq(vq, "Input buffers in net output queue?");
- /*
- * Send the whole thing through to /dev/net/tun. It expects the exact
- * same format: what a coincidence!
- */
- if (writev(net_info->tunfd, iov, out) < 0)
- warnx("Write to tun failed (%d)?", errno);
-
- /*
- * Done with that one; wait_for_vq_desc() will send the interrupt if
- * all packets are processed.
- */
- add_used(vq, head, 0);
-}
-
-/*
- * Handling network input is a bit trickier, because I've tried to optimize it.
- *
- * First we have a helper routine which tells is if from this file descriptor
- * (ie. the /dev/net/tun device) will block:
- */
-static bool will_block(int fd)
-{
- fd_set fdset;
- struct timeval zero = { 0, 0 };
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- return select(fd+1, &fdset, NULL, NULL, &zero) != 1;
-}
-
-/*
- * This handles packets coming in from the tun device to our Guest. Like all
- * service routines, it gets called again as soon as it returns, so you don't
- * see a while(1) loop here.
- */
-static void net_input(struct virtqueue *vq)
-{
- int len;
- unsigned int head, out, in;
- struct iovec iov[vq->vring.num];
- struct net_info *net_info = vq->dev->priv;
-
- /*
- * Get a descriptor to write an incoming packet into. This will also
- * send an interrupt if they're out of descriptors.
- */
- head = wait_for_vq_desc(vq, iov, &out, &in);
- if (out)
- bad_driver_vq(vq, "Output buffers in net input queue?");
-
- /*
- * If it looks like we'll block reading from the tun device, send them
- * an interrupt.
- */
- if (vq->pending_used && will_block(net_info->tunfd))
- trigger_irq(vq);
-
- /*
- * Read in the packet. This is where we normally wait (when there's no
- * incoming network traffic).
- */
- len = readv(net_info->tunfd, iov, in);
- if (len <= 0)
- warn("Failed to read from tun (%d).", errno);
-
- /*
- * Mark that packet buffer as used, but don't interrupt here. We want
- * to wait until we've done as much work as we can.
- */
- add_used(vq, head, len);
-}
-/*:*/
-
-/* This is the helper to create threads: run the service routine in a loop. */
-static int do_thread(void *_vq)
-{
- struct virtqueue *vq = _vq;
-
- for (;;)
- vq->service(vq);
- return 0;
-}
-
-/*
- * When a child dies, we kill our entire process group with SIGTERM. This
- * also has the side effect that the shell restores the console for us!
- */
-static void kill_launcher(int signal)
-{
- kill(0, SIGTERM);
-}
-
-static void reset_vq_pci_config(struct virtqueue *vq)
-{
- vq->pci_config.queue_size = VIRTQUEUE_NUM;
- vq->pci_config.queue_enable = 0;
-}
-
-static void reset_device(struct device *dev)
-{
- struct virtqueue *vq;
-
- verbose("Resetting device %s\n", dev->name);
-
- /* Clear any features they've acked. */
- dev->features_accepted = 0;
-
- /* We're going to be explicitly killing threads, so ignore them. */
- signal(SIGCHLD, SIG_IGN);
-
- /*
- * 4.1.4.3.1:
- *
- * The device MUST present a 0 in queue_enable on reset.
- *
- * This means we set it here, and reset the saved ones in every vq.
- */
- dev->mmio->cfg.queue_enable = 0;
-
- /* Get rid of the virtqueue threads */
- for (vq = dev->vq; vq; vq = vq->next) {
- vq->last_avail_idx = 0;
- reset_vq_pci_config(vq);
- if (vq->thread != (pid_t)-1) {
- kill(vq->thread, SIGTERM);
- waitpid(vq->thread, NULL, 0);
- vq->thread = (pid_t)-1;
- }
- }
- dev->running = false;
- dev->wrote_features_ok = false;
-
- /* Now we care if threads die. */
- signal(SIGCHLD, (void *)kill_launcher);
-}
-
-static void cleanup_devices(void)
-{
- unsigned int i;
-
- for (i = 1; i < MAX_PCI_DEVICES; i++) {
- struct device *d = devices.pci[i];
- if (!d)
- continue;
- reset_device(d);
- }
-
- /* If we saved off the original terminal settings, restore them now. */
- if (orig_term.c_lflag & (ISIG|ICANON|ECHO))
- tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
-}
-
-/*L:217
- * We do PCI. This is mainly done to let us test the kernel virtio PCI
- * code.
- */
-
-/* Linux expects a PCI host bridge: ours is a dummy, and first on the bus. */
-static struct device pci_host_bridge;
-
-static void init_pci_host_bridge(void)
-{
- pci_host_bridge.name = "PCI Host Bridge";
- pci_host_bridge.config.class = 0x06; /* bridge */
- pci_host_bridge.config.subclass = 0; /* host bridge */
- devices.pci[0] = &pci_host_bridge;
-}
-
-/* The IO ports used to read the PCI config space. */
-#define PCI_CONFIG_ADDR 0xCF8
-#define PCI_CONFIG_DATA 0xCFC
-
-/*
- * Not really portable, but does help readability: this is what the Guest
- * writes to the PCI_CONFIG_ADDR IO port.
- */
-union pci_config_addr {
- struct {
- unsigned mbz: 2;
- unsigned offset: 6;
- unsigned funcnum: 3;
- unsigned devnum: 5;
- unsigned busnum: 8;
- unsigned reserved: 7;
- unsigned enabled : 1;
- } bits;
- u32 val;
-};
-
-/*
- * We cache what they wrote to the address port, so we know what they're
- * talking about when they access the data port.
- */
-static union pci_config_addr pci_config_addr;
-
-static struct device *find_pci_device(unsigned int index)
-{
- return devices.pci[index];
-}
-
-/* PCI can do 1, 2 and 4 byte reads; we handle that here. */
-static void ioread(u16 off, u32 v, u32 mask, u32 *val)
-{
- assert(off < 4);
- assert(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF);
- *val = (v >> (off * 8)) & mask;
-}
-
-/* PCI can do 1, 2 and 4 byte writes; we handle that here. */
-static void iowrite(u16 off, u32 v, u32 mask, u32 *dst)
-{
- assert(off < 4);
- assert(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF);
- *dst &= ~(mask << (off * 8));
- *dst |= (v & mask) << (off * 8);
-}
-
-/*
- * Where PCI_CONFIG_DATA accesses depends on the previous write to
- * PCI_CONFIG_ADDR.
- */
-static struct device *dev_and_reg(u32 *reg)
-{
- if (!pci_config_addr.bits.enabled)
- return NULL;
-
- if (pci_config_addr.bits.funcnum != 0)
- return NULL;
-
- if (pci_config_addr.bits.busnum != 0)
- return NULL;
-
- if (pci_config_addr.bits.offset * 4 >= sizeof(struct pci_config))
- return NULL;
-
- *reg = pci_config_addr.bits.offset;
- return find_pci_device(pci_config_addr.bits.devnum);
-}
-
-/*
- * We can get invalid combinations of values while they're writing, so we
- * only fault if they try to write with some invalid bar/offset/length.
- */
-static bool valid_bar_access(struct device *d,
- struct virtio_pci_cfg_cap_u32 *cfg_access)
-{
- /* We only have 1 bar (BAR0) */
- if (cfg_access->cap.bar != 0)
- return false;
-
- /* Check it's within BAR0. */
- if (cfg_access->cap.offset >= d->mmio_size
- || cfg_access->cap.offset + cfg_access->cap.length > d->mmio_size)
- return false;
-
- /* Check length is 1, 2 or 4. */
- if (cfg_access->cap.length != 1
- && cfg_access->cap.length != 2
- && cfg_access->cap.length != 4)
- return false;
-
- /*
- * 4.1.4.7.2:
- *
- * The driver MUST NOT write a cap.offset which is not a multiple of
- * cap.length (ie. all accesses MUST be aligned).
- */
- if (cfg_access->cap.offset % cfg_access->cap.length != 0)
- return false;
-
- /* Return pointer into word in BAR0. */
- return true;
-}
-
-/* Is this accessing the PCI config address port?. */
-static bool is_pci_addr_port(u16 port)
-{
- return port >= PCI_CONFIG_ADDR && port < PCI_CONFIG_ADDR + 4;
-}
-
-static bool pci_addr_iowrite(u16 port, u32 mask, u32 val)
-{
- iowrite(port - PCI_CONFIG_ADDR, val, mask,
- &pci_config_addr.val);
- verbose("PCI%s: %#x/%x: bus %u dev %u func %u reg %u\n",
- pci_config_addr.bits.enabled ? "" : " DISABLED",
- val, mask,
- pci_config_addr.bits.busnum,
- pci_config_addr.bits.devnum,
- pci_config_addr.bits.funcnum,
- pci_config_addr.bits.offset);
- return true;
-}
-
-static void pci_addr_ioread(u16 port, u32 mask, u32 *val)
-{
- ioread(port - PCI_CONFIG_ADDR, pci_config_addr.val, mask, val);
-}
-
-/* Is this accessing the PCI config data port?. */
-static bool is_pci_data_port(u16 port)
-{
- return port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + 4;
-}
-
-static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask);
-
-static bool pci_data_iowrite(u16 port, u32 mask, u32 val)
-{
- u32 reg, portoff;
- struct device *d = dev_and_reg(&reg);
-
- /* Complain if they don't belong to a device. */
- if (!d)
- return false;
-
- /* They can do 1 byte writes, etc. */
- portoff = port - PCI_CONFIG_DATA;
-
- /*
- * PCI uses a weird way to determine the BAR size: the OS
- * writes all 1's, and sees which ones stick.
- */
- if (&d->config_words[reg] == &d->config.bar[0]) {
- int i;
-
- iowrite(portoff, val, mask, &d->config.bar[0]);
- for (i = 0; (1 << i) < d->mmio_size; i++)
- d->config.bar[0] &= ~(1 << i);
- return true;
- } else if ((&d->config_words[reg] > &d->config.bar[0]
- && &d->config_words[reg] <= &d->config.bar[6])
- || &d->config_words[reg] == &d->config.expansion_rom_addr) {
- /* Allow writing to any other BAR, or expansion ROM */
- iowrite(portoff, val, mask, &d->config_words[reg]);
- return true;
- /* We let them override latency timer and cacheline size */
- } else if (&d->config_words[reg] == (void *)&d->config.cacheline_size) {
- /* Only let them change the first two fields. */
- if (mask == 0xFFFFFFFF)
- mask = 0xFFFF;
- iowrite(portoff, val, mask, &d->config_words[reg]);
- return true;
- } else if (&d->config_words[reg] == (void *)&d->config.command
- && mask == 0xFFFF) {
- /* Ignore command writes. */
- return true;
- } else if (&d->config_words[reg]
- == (void *)&d->config.cfg_access.cap.bar
- || &d->config_words[reg]
- == &d->config.cfg_access.cap.length
- || &d->config_words[reg]
- == &d->config.cfg_access.cap.offset) {
-
- /*
- * The VIRTIO_PCI_CAP_PCI_CFG capability
- * provides a backdoor to access the MMIO
- * regions without mapping them. Weird, but
- * useful.
- */
- iowrite(portoff, val, mask, &d->config_words[reg]);
- return true;
- } else if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) {
- u32 write_mask;
-
- /*
- * 4.1.4.7.1:
- *
- * Upon detecting driver write access to pci_cfg_data, the
- * device MUST execute a write access at offset cap.offset at
- * BAR selected by cap.bar using the first cap.length bytes
- * from pci_cfg_data.
- */
-
- /* Must be bar 0 */
- if (!valid_bar_access(d, &d->config.cfg_access))
- return false;
-
- iowrite(portoff, val, mask, &d->config.cfg_access.pci_cfg_data);
-
- /*
- * Now emulate a write. The mask we use is set by
- * len, *not* this write!
- */
- write_mask = (1ULL<<(8*d->config.cfg_access.cap.length)) - 1;
- verbose("Window writing %#x/%#x to bar %u, offset %u len %u\n",
- d->config.cfg_access.pci_cfg_data, write_mask,
- d->config.cfg_access.cap.bar,
- d->config.cfg_access.cap.offset,
- d->config.cfg_access.cap.length);
-
- emulate_mmio_write(d, d->config.cfg_access.cap.offset,
- d->config.cfg_access.pci_cfg_data,
- write_mask);
- return true;
- }
-
- /*
- * 4.1.4.1:
- *
- * The driver MUST NOT write into any field of the capability
- * structure, with the exception of those with cap_type
- * VIRTIO_PCI_CAP_PCI_CFG...
- */
- return false;
-}
-
-static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask);
-
-static void pci_data_ioread(u16 port, u32 mask, u32 *val)
-{
- u32 reg;
- struct device *d = dev_and_reg(&reg);
-
- if (!d)
- return;
-
- /* Read through the PCI MMIO access window is special */
- if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) {
- u32 read_mask;
-
- /*
- * 4.1.4.7.1:
- *
- * Upon detecting driver read access to pci_cfg_data, the
- * device MUST execute a read access of length cap.length at
- * offset cap.offset at BAR selected by cap.bar and store the
- * first cap.length bytes in pci_cfg_data.
- */
- /* Must be bar 0 */
- if (!valid_bar_access(d, &d->config.cfg_access))
- bad_driver(d,
- "Invalid cfg_access to bar%u, offset %u len %u",
- d->config.cfg_access.cap.bar,
- d->config.cfg_access.cap.offset,
- d->config.cfg_access.cap.length);
-
- /*
- * Read into the window. The mask we use is set by
- * len, *not* this read!
- */
- read_mask = (1ULL<<(8*d->config.cfg_access.cap.length))-1;
- d->config.cfg_access.pci_cfg_data
- = emulate_mmio_read(d,
- d->config.cfg_access.cap.offset,
- read_mask);
- verbose("Window read %#x/%#x from bar %u, offset %u len %u\n",
- d->config.cfg_access.pci_cfg_data, read_mask,
- d->config.cfg_access.cap.bar,
- d->config.cfg_access.cap.offset,
- d->config.cfg_access.cap.length);
- }
- ioread(port - PCI_CONFIG_DATA, d->config_words[reg], mask, val);
-}
-
-/*L:216
- * This is where we emulate a handful of Guest instructions. It's ugly
- * and we used to do it in the kernel but it grew over time.
- */
-
-/*
- * We use the ptrace syscall's pt_regs struct to talk about registers
- * to lguest: these macros convert the names to the offsets.
- */
-#define getreg(name) getreg_off(offsetof(struct user_regs_struct, name))
-#define setreg(name, val) \
- setreg_off(offsetof(struct user_regs_struct, name), (val))
-
-static u32 getreg_off(size_t offset)
-{
- u32 r;
- unsigned long args[] = { LHREQ_GETREG, offset };
-
- if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0)
- err(1, "Getting register %u", offset);
- if (pread(lguest_fd, &r, sizeof(r), cpu_id) != sizeof(r))
- err(1, "Reading register %u", offset);
-
- return r;
-}
-
-static void setreg_off(size_t offset, u32 val)
-{
- unsigned long args[] = { LHREQ_SETREG, offset, val };
-
- if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0)
- err(1, "Setting register %u", offset);
-}
-
-/* Get register by instruction encoding */
-static u32 getreg_num(unsigned regnum, u32 mask)
-{
- /* 8 bit ops use regnums 4-7 for high parts of word */
- if (mask == 0xFF && (regnum & 0x4))
- return getreg_num(regnum & 0x3, 0xFFFF) >> 8;
-
- switch (regnum) {
- case 0: return getreg(eax) & mask;
- case 1: return getreg(ecx) & mask;
- case 2: return getreg(edx) & mask;
- case 3: return getreg(ebx) & mask;
- case 4: return getreg(esp) & mask;
- case 5: return getreg(ebp) & mask;
- case 6: return getreg(esi) & mask;
- case 7: return getreg(edi) & mask;
- }
- abort();
-}
-
-/* Set register by instruction encoding */
-static void setreg_num(unsigned regnum, u32 val, u32 mask)
-{
- /* Don't try to set bits out of range */
- assert(~(val & ~mask));
-
- /* 8 bit ops use regnums 4-7 for high parts of word */
- if (mask == 0xFF && (regnum & 0x4)) {
- /* Construct the 16 bits we want. */
- val = (val << 8) | getreg_num(regnum & 0x3, 0xFF);
- setreg_num(regnum & 0x3, val, 0xFFFF);
- return;
- }
-
- switch (regnum) {
- case 0: setreg(eax, val | (getreg(eax) & ~mask)); return;
- case 1: setreg(ecx, val | (getreg(ecx) & ~mask)); return;
- case 2: setreg(edx, val | (getreg(edx) & ~mask)); return;
- case 3: setreg(ebx, val | (getreg(ebx) & ~mask)); return;
- case 4: setreg(esp, val | (getreg(esp) & ~mask)); return;
- case 5: setreg(ebp, val | (getreg(ebp) & ~mask)); return;
- case 6: setreg(esi, val | (getreg(esi) & ~mask)); return;
- case 7: setreg(edi, val | (getreg(edi) & ~mask)); return;
- }
- abort();
-}
-
-/* Get bytes of displacement appended to instruction, from r/m encoding */
-static u32 insn_displacement_len(u8 mod_reg_rm)
-{
- /* Switch on the mod bits */
- switch (mod_reg_rm >> 6) {
- case 0:
- /* If mod == 0, and r/m == 101, 16-bit displacement follows */
- if ((mod_reg_rm & 0x7) == 0x5)
- return 2;
- /* Normally, mod == 0 means no literal displacement */
- return 0;
- case 1:
- /* One byte displacement */
- return 1;
- case 2:
- /* Four byte displacement */
- return 4;
- case 3:
- /* Register mode */
- return 0;
- }
- abort();
-}
-
-static void emulate_insn(const u8 insn[])
-{
- unsigned long args[] = { LHREQ_TRAP, 13 };
- unsigned int insnlen = 0, in = 0, small_operand = 0, byte_access;
- unsigned int eax, port, mask;
- /*
- * Default is to return all-ones on IO port reads, which traditionally
- * means "there's nothing there".
- */
- u32 val = 0xFFFFFFFF;
-
- /*
- * This must be the Guest kernel trying to do something, not userspace!
- * The bottom two bits of the CS segment register are the privilege
- * level.
- */
- if ((getreg(xcs) & 3) != 0x1)
- goto no_emulate;
-
- /* Decoding x86 instructions is icky. */
-
- /*
- * Around 2.6.33, the kernel started using an emulation for the
- * cmpxchg8b instruction in early boot on many configurations. This
- * code isn't paravirtualized, and it tries to disable interrupts.
- * Ignore it, which will Mostly Work.
- */
- if (insn[insnlen] == 0xfa) {
- /* "cli", or Clear Interrupt Enable instruction. Skip it. */
- insnlen = 1;
- goto skip_insn;
- }
-
- /*
- * 0x66 is an "operand prefix". It means a 16, not 32 bit in/out.
- */
- if (insn[insnlen] == 0x66) {
- small_operand = 1;
- /* The instruction is 1 byte so far, read the next byte. */
- insnlen = 1;
- }
-
- /* If the lower bit isn't set, it's a single byte access */
- byte_access = !(insn[insnlen] & 1);
-
- /*
- * Now we can ignore the lower bit and decode the 4 opcodes
- * we need to emulate.
- */
- switch (insn[insnlen] & 0xFE) {
- case 0xE4: /* in <next byte>,%al */
- port = insn[insnlen+1];
- insnlen += 2;
- in = 1;
- break;
- case 0xEC: /* in (%dx),%al */
- port = getreg(edx) & 0xFFFF;
- insnlen += 1;
- in = 1;
- break;
- case 0xE6: /* out %al,<next byte> */
- port = insn[insnlen+1];
- insnlen += 2;
- break;
- case 0xEE: /* out %al,(%dx) */
- port = getreg(edx) & 0xFFFF;
- insnlen += 1;
- break;
- default:
- /* OK, we don't know what this is, can't emulate. */
- goto no_emulate;
- }
-
- /* Set a mask of the 1, 2 or 4 bytes, depending on size of IO */
- if (byte_access)
- mask = 0xFF;
- else if (small_operand)
- mask = 0xFFFF;
- else
- mask = 0xFFFFFFFF;
-
- /*
- * If it was an "IN" instruction, they expect the result to be read
- * into %eax, so we change %eax.
- */
- eax = getreg(eax);
-
- if (in) {
- /* This is the PS/2 keyboard status; 1 means ready for output */
- if (port == 0x64)
- val = 1;
- else if (is_pci_addr_port(port))
- pci_addr_ioread(port, mask, &val);
- else if (is_pci_data_port(port))
- pci_data_ioread(port, mask, &val);
-
- /* Clear the bits we're about to read */
- eax &= ~mask;
- /* Copy bits in from val. */
- eax |= val & mask;
- /* Now update the register. */
- setreg(eax, eax);
- } else {
- if (is_pci_addr_port(port)) {
- if (!pci_addr_iowrite(port, mask, eax))
- goto bad_io;
- } else if (is_pci_data_port(port)) {
- if (!pci_data_iowrite(port, mask, eax))
- goto bad_io;
- }
- /* There are many other ports, eg. CMOS clock, serial
- * and parallel ports, so we ignore them all. */
- }
-
- verbose("IO %s of %x to %u: %#08x\n",
- in ? "IN" : "OUT", mask, port, eax);
-skip_insn:
- /* Finally, we've "done" the instruction, so move past it. */
- setreg(eip, getreg(eip) + insnlen);
- return;
-
-bad_io:
- warnx("Attempt to %s port %u (%#x mask)",
- in ? "read from" : "write to", port, mask);
-
-no_emulate:
- /* Inject trap into Guest. */
- if (write(lguest_fd, args, sizeof(args)) < 0)
- err(1, "Reinjecting trap 13 for fault at %#x", getreg(eip));
-}
-
-static struct device *find_mmio_region(unsigned long paddr, u32 *off)
-{
- unsigned int i;
-
- for (i = 1; i < MAX_PCI_DEVICES; i++) {
- struct device *d = devices.pci[i];
-
- if (!d)
- continue;
- if (paddr < d->mmio_addr)
- continue;
- if (paddr >= d->mmio_addr + d->mmio_size)
- continue;
- *off = paddr - d->mmio_addr;
- return d;
- }
- return NULL;
-}
-
-/* FIXME: Use vq array. */
-static struct virtqueue *vq_by_num(struct device *d, u32 num)
-{
- struct virtqueue *vq = d->vq;
-
- while (num-- && vq)
- vq = vq->next;
-
- return vq;
-}
-
-static void save_vq_config(const struct virtio_pci_common_cfg *cfg,
- struct virtqueue *vq)
-{
- vq->pci_config = *cfg;
-}
-
-static void restore_vq_config(struct virtio_pci_common_cfg *cfg,
- struct virtqueue *vq)
-{
- /* Only restore the per-vq part */
- size_t off = offsetof(struct virtio_pci_common_cfg, queue_size);
-
- memcpy((void *)cfg + off, (void *)&vq->pci_config + off,
- sizeof(*cfg) - off);
-}
-
-/*
- * 4.1.4.3.2:
- *
- * The driver MUST configure the other virtqueue fields before
- * enabling the virtqueue with queue_enable.
- *
- * When they enable the virtqueue, we check that their setup is valid.
- */
-static void check_virtqueue(struct device *d, struct virtqueue *vq)
-{
- /* Because lguest is 32 bit, all the descriptor high bits must be 0 */
- if (vq->pci_config.queue_desc_hi
- || vq->pci_config.queue_avail_hi
- || vq->pci_config.queue_used_hi)
- bad_driver_vq(vq, "invalid 64-bit queue address");
-
- /*
- * 2.4.1:
- *
- * The driver MUST ensure that the physical address of the first byte
- * of each virtqueue part is a multiple of the specified alignment
- * value in the above table.
- */
- if (vq->pci_config.queue_desc_lo % 16
- || vq->pci_config.queue_avail_lo % 2
- || vq->pci_config.queue_used_lo % 4)
- bad_driver_vq(vq, "invalid alignment in queue addresses");
-
- /* Initialize the virtqueue and check they're all in range. */
- vq->vring.num = vq->pci_config.queue_size;
- vq->vring.desc = check_pointer(vq->dev,
- vq->pci_config.queue_desc_lo,
- sizeof(*vq->vring.desc) * vq->vring.num);
- vq->vring.avail = check_pointer(vq->dev,
- vq->pci_config.queue_avail_lo,
- sizeof(*vq->vring.avail)
- + (sizeof(vq->vring.avail->ring[0])
- * vq->vring.num));
- vq->vring.used = check_pointer(vq->dev,
- vq->pci_config.queue_used_lo,
- sizeof(*vq->vring.used)
- + (sizeof(vq->vring.used->ring[0])
- * vq->vring.num));
-
- /*
- * 2.4.9.1:
- *
- * The driver MUST initialize flags in the used ring to 0
- * when allocating the used ring.
- */
- if (vq->vring.used->flags != 0)
- bad_driver_vq(vq, "invalid initial used.flags %#x",
- vq->vring.used->flags);
-}
-
-static void start_virtqueue(struct virtqueue *vq)
-{
- /*
- * Create stack for thread. Since the stack grows upwards, we point
- * the stack pointer to the end of this region.
- */
- char *stack = malloc(32768);
-
- /* Create a zero-initialized eventfd. */
- vq->eventfd = eventfd(0, 0);
- if (vq->eventfd < 0)
- err(1, "Creating eventfd");
-
- /*
- * CLONE_VM: because it has to access the Guest memory, and SIGCHLD so
- * we get a signal if it dies.
- */
- vq->thread = clone(do_thread, stack + 32768, CLONE_VM | SIGCHLD, vq);
- if (vq->thread == (pid_t)-1)
- err(1, "Creating clone");
-}
-
-static void start_virtqueues(struct device *d)
-{
- struct virtqueue *vq;
-
- for (vq = d->vq; vq; vq = vq->next) {
- if (vq->pci_config.queue_enable)
- start_virtqueue(vq);
- }
-}
-
-static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask)
-{
- struct virtqueue *vq;
-
- switch (off) {
- case offsetof(struct virtio_pci_mmio, cfg.device_feature_select):
- /*
- * 4.1.4.3.1:
- *
- * The device MUST present the feature bits it is offering in
- * device_feature, starting at bit device_feature_select ∗ 32
- * for any device_feature_select written by the driver
- */
- if (val == 0)
- d->mmio->cfg.device_feature = d->features;
- else if (val == 1)
- d->mmio->cfg.device_feature = (d->features >> 32);
- else
- d->mmio->cfg.device_feature = 0;
- goto feature_write_through32;
- case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select):
- if (val > 1)
- bad_driver(d, "Unexpected driver select %u", val);
- goto feature_write_through32;
- case offsetof(struct virtio_pci_mmio, cfg.guest_feature):
- if (d->mmio->cfg.guest_feature_select == 0) {
- d->features_accepted &= ~((u64)0xFFFFFFFF);
- d->features_accepted |= val;
- } else {
- assert(d->mmio->cfg.guest_feature_select == 1);
- d->features_accepted &= 0xFFFFFFFF;
- d->features_accepted |= ((u64)val) << 32;
- }
- /*
- * 2.2.1:
- *
- * The driver MUST NOT accept a feature which the device did
- * not offer
- */
- if (d->features_accepted & ~d->features)
- bad_driver(d, "over-accepted features %#llx of %#llx",
- d->features_accepted, d->features);
- goto feature_write_through32;
- case offsetof(struct virtio_pci_mmio, cfg.device_status): {
- u8 prev;
-
- verbose("%s: device status -> %#x\n", d->name, val);
- /*
- * 4.1.4.3.1:
- *
- * The device MUST reset when 0 is written to device_status,
- * and present a 0 in device_status once that is done.
- */
- if (val == 0) {
- reset_device(d);
- goto write_through8;
- }
-
- /* 2.1.1: The driver MUST NOT clear a device status bit. */
- if (d->mmio->cfg.device_status & ~val)
- bad_driver(d, "unset of device status bit %#x -> %#x",
- d->mmio->cfg.device_status, val);
-
- /*
- * 2.1.2:
- *
- * The device MUST NOT consume buffers or notify the driver
- * before DRIVER_OK.
- */
- if (val & VIRTIO_CONFIG_S_DRIVER_OK
- && !(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK))
- start_virtqueues(d);
-
- /*
- * 3.1.1:
- *
- * The driver MUST follow this sequence to initialize a device:
- * - Reset the device.
- * - Set the ACKNOWLEDGE status bit: the guest OS has
- * notice the device.
- * - Set the DRIVER status bit: the guest OS knows how
- * to drive the device.
- * - Read device feature bits, and write the subset
- * of feature bits understood by the OS and driver
- * to the device. During this step the driver MAY
- * read (but MUST NOT write) the device-specific
- * configuration fields to check that it can
- * support the device before accepting it.
- * - Set the FEATURES_OK status bit. The driver
- * MUST not accept new feature bits after this
- * step.
- * - Re-read device status to ensure the FEATURES_OK
- * bit is still set: otherwise, the device does
- * not support our subset of features and the
- * device is unusable.
- * - Perform device-specific setup, including
- * discovery of virtqueues for the device,
- * optional per-bus setup, reading and possibly
- * writing the device’s virtio configuration
- * space, and population of virtqueues.
- * - Set the DRIVER_OK status bit. At this point the
- * device is “live”.
- */
- prev = 0;
- switch (val & ~d->mmio->cfg.device_status) {
- case VIRTIO_CONFIG_S_DRIVER_OK:
- prev |= VIRTIO_CONFIG_S_FEATURES_OK; /* fall thru */
- case VIRTIO_CONFIG_S_FEATURES_OK:
- prev |= VIRTIO_CONFIG_S_DRIVER; /* fall thru */
- case VIRTIO_CONFIG_S_DRIVER:
- prev |= VIRTIO_CONFIG_S_ACKNOWLEDGE; /* fall thru */
- case VIRTIO_CONFIG_S_ACKNOWLEDGE:
- break;
- default:
- bad_driver(d, "unknown device status bit %#x -> %#x",
- d->mmio->cfg.device_status, val);
- }
- if (d->mmio->cfg.device_status != prev)
- bad_driver(d, "unexpected status transition %#x -> %#x",
- d->mmio->cfg.device_status, val);
-
- /* If they just wrote FEATURES_OK, we make sure they read */
- switch (val & ~d->mmio->cfg.device_status) {
- case VIRTIO_CONFIG_S_FEATURES_OK:
- d->wrote_features_ok = true;
- break;
- case VIRTIO_CONFIG_S_DRIVER_OK:
- if (d->wrote_features_ok)
- bad_driver(d, "did not re-read FEATURES_OK");
- break;
- }
- goto write_through8;
- }
- case offsetof(struct virtio_pci_mmio, cfg.queue_select):
- vq = vq_by_num(d, val);
- /*
- * 4.1.4.3.1:
- *
- * The device MUST present a 0 in queue_size if the virtqueue
- * corresponding to the current queue_select is unavailable.
- */
- if (!vq) {
- d->mmio->cfg.queue_size = 0;
- goto write_through16;
- }
- /* Save registers for old vq, if it was a valid vq */
- if (d->mmio->cfg.queue_size)
- save_vq_config(&d->mmio->cfg,
- vq_by_num(d, d->mmio->cfg.queue_select));
- /* Restore the registers for the queue they asked for */
- restore_vq_config(&d->mmio->cfg, vq);
- goto write_through16;
- case offsetof(struct virtio_pci_mmio, cfg.queue_size):
- /*
- * 4.1.4.3.2:
- *
- * The driver MUST NOT write a value which is not a power of 2
- * to queue_size.
- */
- if (val & (val-1))
- bad_driver(d, "invalid queue size %u", val);
- if (d->mmio->cfg.queue_enable)
- bad_driver(d, "changing queue size on live device");
- goto write_through16;
- case offsetof(struct virtio_pci_mmio, cfg.queue_msix_vector):
- bad_driver(d, "attempt to set MSIX vector to %u", val);
- case offsetof(struct virtio_pci_mmio, cfg.queue_enable): {
- struct virtqueue *vq = vq_by_num(d, d->mmio->cfg.queue_select);
-
- /*
- * 4.1.4.3.2:
- *
- * The driver MUST NOT write a 0 to queue_enable.
- */
- if (val != 1)
- bad_driver(d, "setting queue_enable to %u", val);
-
- /*
- * 3.1.1:
- *
- * 7. Perform device-specific setup, including discovery of
- * virtqueues for the device, optional per-bus setup,
- * reading and possibly writing the device’s virtio
- * configuration space, and population of virtqueues.
- * 8. Set the DRIVER_OK status bit.
- *
- * All our devices require all virtqueues to be enabled, so
- * they should have done that before setting DRIVER_OK.
- */
- if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)
- bad_driver(d, "enabling vq after DRIVER_OK");
-
- d->mmio->cfg.queue_enable = val;
- save_vq_config(&d->mmio->cfg, vq);
- check_virtqueue(d, vq);
- goto write_through16;
- }
- case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off):
- bad_driver(d, "attempt to write to queue_notify_off");
- case offsetof(struct virtio_pci_mmio, cfg.queue_desc_lo):
- case offsetof(struct virtio_pci_mmio, cfg.queue_desc_hi):
- case offsetof(struct virtio_pci_mmio, cfg.queue_avail_lo):
- case offsetof(struct virtio_pci_mmio, cfg.queue_avail_hi):
- case offsetof(struct virtio_pci_mmio, cfg.queue_used_lo):
- case offsetof(struct virtio_pci_mmio, cfg.queue_used_hi):
- /*
- * 4.1.4.3.2:
- *
- * The driver MUST configure the other virtqueue fields before
- * enabling the virtqueue with queue_enable.
- */
- if (d->mmio->cfg.queue_enable)
- bad_driver(d, "changing queue on live device");
-
- /*
- * 3.1.1:
- *
- * The driver MUST follow this sequence to initialize a device:
- *...
- * 5. Set the FEATURES_OK status bit. The driver MUST not
- * accept new feature bits after this step.
- */
- if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK))
- bad_driver(d, "setting up vq before FEATURES_OK");
-
- /*
- * 6. Re-read device status to ensure the FEATURES_OK bit is
- * still set...
- */
- if (d->wrote_features_ok)
- bad_driver(d, "didn't re-read FEATURES_OK before setup");
-
- goto write_through32;
- case offsetof(struct virtio_pci_mmio, notify):
- vq = vq_by_num(d, val);
- if (!vq)
- bad_driver(d, "Invalid vq notification on %u", val);
- /* Notify the process handling this vq by adding 1 to eventfd */
- write(vq->eventfd, "\1\0\0\0\0\0\0\0", 8);
- goto write_through16;
- case offsetof(struct virtio_pci_mmio, isr):
- bad_driver(d, "Unexpected write to isr");
- /* Weird corner case: write to emerg_wr of console */
- case sizeof(struct virtio_pci_mmio)
- + offsetof(struct virtio_console_config, emerg_wr):
- if (strcmp(d->name, "console") == 0) {
- char c = val;
- write(STDOUT_FILENO, &c, 1);
- goto write_through32;
- }
- /* Fall through... */
- default:
- /*
- * 4.1.4.3.2:
- *
- * The driver MUST NOT write to device_feature, num_queues,
- * config_generation or queue_notify_off.
- */
- bad_driver(d, "Unexpected write to offset %u", off);
- }
-
-feature_write_through32:
- /*
- * 3.1.1:
- *
- * The driver MUST follow this sequence to initialize a device:
- *...
- * - Set the DRIVER status bit: the guest OS knows how
- * to drive the device.
- * - Read device feature bits, and write the subset
- * of feature bits understood by the OS and driver
- * to the device.
- *...
- * - Set the FEATURES_OK status bit. The driver MUST not
- * accept new feature bits after this step.
- */
- if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER))
- bad_driver(d, "feature write before VIRTIO_CONFIG_S_DRIVER");
- if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK)
- bad_driver(d, "feature write after VIRTIO_CONFIG_S_FEATURES_OK");
-
- /*
- * 4.1.3.1:
- *
- * The driver MUST access each field using the “natural” access
- * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for
- * 16-bit fields and 8-bit accesses for 8-bit fields.
- */
-write_through32:
- if (mask != 0xFFFFFFFF) {
- bad_driver(d, "non-32-bit write to offset %u (%#x)",
- off, getreg(eip));
- return;
- }
- memcpy((char *)d->mmio + off, &val, 4);
- return;
-
-write_through16:
- if (mask != 0xFFFF)
- bad_driver(d, "non-16-bit write to offset %u (%#x)",
- off, getreg(eip));
- memcpy((char *)d->mmio + off, &val, 2);
- return;
-
-write_through8:
- if (mask != 0xFF)
- bad_driver(d, "non-8-bit write to offset %u (%#x)",
- off, getreg(eip));
- memcpy((char *)d->mmio + off, &val, 1);
- return;
-}
-
-static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask)
-{
- u8 isr;
- u32 val = 0;
-
- switch (off) {
- case offsetof(struct virtio_pci_mmio, cfg.device_feature_select):
- case offsetof(struct virtio_pci_mmio, cfg.device_feature):
- case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select):
- case offsetof(struct virtio_pci_mmio, cfg.guest_feature):
- /*
- * 3.1.1:
- *
- * The driver MUST follow this sequence to initialize a device:
- *...
- * - Set the DRIVER status bit: the guest OS knows how
- * to drive the device.
- * - Read device feature bits, and write the subset
- * of feature bits understood by the OS and driver
- * to the device.
- */
- if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER))
- bad_driver(d,
- "feature read before VIRTIO_CONFIG_S_DRIVER");
- goto read_through32;
- case offsetof(struct virtio_pci_mmio, cfg.msix_config):
- bad_driver(d, "read of msix_config");
- case offsetof(struct virtio_pci_mmio, cfg.num_queues):
- goto read_through16;
- case offsetof(struct virtio_pci_mmio, cfg.device_status):
- /* As they did read, any write of FEATURES_OK is now fine. */
- d->wrote_features_ok = false;
- goto read_through8;
- case offsetof(struct virtio_pci_mmio, cfg.config_generation):
- /*
- * 4.1.4.3.1:
- *
- * The device MUST present a changed config_generation after
- * the driver has read a device-specific configuration value
- * which has changed since any part of the device-specific
- * configuration was last read.
- *
- * This is simple: none of our devices change config, so this
- * is always 0.
- */
- goto read_through8;
- case offsetof(struct virtio_pci_mmio, notify):
- /*
- * 3.1.1:
- *
- * The driver MUST NOT notify the device before setting
- * DRIVER_OK.
- */
- if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK))
- bad_driver(d, "notify before VIRTIO_CONFIG_S_DRIVER_OK");
- goto read_through16;
- case offsetof(struct virtio_pci_mmio, isr):
- if (mask != 0xFF)
- bad_driver(d, "non-8-bit read from offset %u (%#x)",
- off, getreg(eip));
- isr = d->mmio->isr;
- /*
- * 4.1.4.5.1:
- *
- * The device MUST reset ISR status to 0 on driver read.
- */
- d->mmio->isr = 0;
- return isr;
- case offsetof(struct virtio_pci_mmio, padding):
- bad_driver(d, "read from padding (%#x)", getreg(eip));
- default:
- /* Read from device config space, beware unaligned overflow */
- if (off > d->mmio_size - 4)
- bad_driver(d, "read past end (%#x)", getreg(eip));
-
- /*
- * 3.1.1:
- * The driver MUST follow this sequence to initialize a device:
- *...
- * 3. Set the DRIVER status bit: the guest OS knows how to
- * drive the device.
- * 4. Read device feature bits, and write the subset of
- * feature bits understood by the OS and driver to the
- * device. During this step the driver MAY read (but MUST NOT
- * write) the device-specific configuration fields to check
- * that it can support the device before accepting it.
- */
- if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER))
- bad_driver(d,
- "config read before VIRTIO_CONFIG_S_DRIVER");
-
- if (mask == 0xFFFFFFFF)
- goto read_through32;
- else if (mask == 0xFFFF)
- goto read_through16;
- else
- goto read_through8;
- }
-
- /*
- * 4.1.3.1:
- *
- * The driver MUST access each field using the “natural” access
- * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for
- * 16-bit fields and 8-bit accesses for 8-bit fields.
- */
-read_through32:
- if (mask != 0xFFFFFFFF)
- bad_driver(d, "non-32-bit read to offset %u (%#x)",
- off, getreg(eip));
- memcpy(&val, (char *)d->mmio + off, 4);
- return val;
-
-read_through16:
- if (mask != 0xFFFF)
- bad_driver(d, "non-16-bit read to offset %u (%#x)",
- off, getreg(eip));
- memcpy(&val, (char *)d->mmio + off, 2);
- return val;
-
-read_through8:
- if (mask != 0xFF)
- bad_driver(d, "non-8-bit read to offset %u (%#x)",
- off, getreg(eip));
- memcpy(&val, (char *)d->mmio + off, 1);
- return val;
-}
-
-static void emulate_mmio(unsigned long paddr, const u8 *insn)
-{
- u32 val, off, mask = 0xFFFFFFFF, insnlen = 0;
- struct device *d = find_mmio_region(paddr, &off);
- unsigned long args[] = { LHREQ_TRAP, 14 };
-
- if (!d) {
- warnx("MMIO touching %#08lx (not a device)", paddr);
- goto reinject;
- }
-
- /* Prefix makes it a 16 bit op */
- if (insn[0] == 0x66) {
- mask = 0xFFFF;
- insnlen++;
- }
-
- /* iowrite */
- if (insn[insnlen] == 0x89) {
- /* Next byte is r/m byte: bits 3-5 are register. */
- val = getreg_num((insn[insnlen+1] >> 3) & 0x7, mask);
- emulate_mmio_write(d, off, val, mask);
- insnlen += 2 + insn_displacement_len(insn[insnlen+1]);
- } else if (insn[insnlen] == 0x8b) { /* ioread */
- /* Next byte is r/m byte: bits 3-5 are register. */
- val = emulate_mmio_read(d, off, mask);
- setreg_num((insn[insnlen+1] >> 3) & 0x7, val, mask);
- insnlen += 2 + insn_displacement_len(insn[insnlen+1]);
- } else if (insn[0] == 0x88) { /* 8-bit iowrite */
- mask = 0xff;
- /* Next byte is r/m byte: bits 3-5 are register. */
- val = getreg_num((insn[1] >> 3) & 0x7, mask);
- emulate_mmio_write(d, off, val, mask);
- insnlen = 2 + insn_displacement_len(insn[1]);
- } else if (insn[0] == 0x8a) { /* 8-bit ioread */
- mask = 0xff;
- val = emulate_mmio_read(d, off, mask);
- setreg_num((insn[1] >> 3) & 0x7, val, mask);
- insnlen = 2 + insn_displacement_len(insn[1]);
- } else {
- warnx("Unknown MMIO instruction touching %#08lx:"
- " %02x %02x %02x %02x at %u",
- paddr, insn[0], insn[1], insn[2], insn[3], getreg(eip));
- reinject:
- /* Inject trap into Guest. */
- if (write(lguest_fd, args, sizeof(args)) < 0)
- err(1, "Reinjecting trap 14 for fault at %#x",
- getreg(eip));
- return;
- }
-
- /* Finally, we've "done" the instruction, so move past it. */
- setreg(eip, getreg(eip) + insnlen);
-}
-
-/*L:190
- * Device Setup
- *
- * All devices need a descriptor so the Guest knows it exists, and a "struct
- * device" so the Launcher can keep track of it. We have common helper
- * routines to allocate and manage them.
- */
-static void add_pci_virtqueue(struct device *dev,
- void (*service)(struct virtqueue *),
- const char *name)
-{
- struct virtqueue **i, *vq = malloc(sizeof(*vq));
-
- /* Initialize the virtqueue */
- vq->next = NULL;
- vq->last_avail_idx = 0;
- vq->dev = dev;
- vq->name = name;
-
- /*
- * This is the routine the service thread will run, and its Process ID
- * once it's running.
- */
- vq->service = service;
- vq->thread = (pid_t)-1;
-
- /* Initialize the configuration. */
- reset_vq_pci_config(vq);
- vq->pci_config.queue_notify_off = 0;
-
- /* Add one to the number of queues */
- vq->dev->mmio->cfg.num_queues++;
-
- /*
- * Add to tail of list, so dev->vq is first vq, dev->vq->next is
- * second.
- */
- for (i = &dev->vq; *i; i = &(*i)->next);
- *i = vq;
-}
-
-/* The Guest accesses the feature bits via the PCI common config MMIO region */
-static void add_pci_feature(struct device *dev, unsigned bit)
-{
- dev->features |= (1ULL << bit);
-}
-
-/* For devices with no config. */
-static void no_device_config(struct device *dev)
-{
- dev->mmio_addr = get_mmio_region(dev->mmio_size);
-
- dev->config.bar[0] = dev->mmio_addr;
- /* Bottom 4 bits must be zero */
- assert(~(dev->config.bar[0] & 0xF));
-}
-
-/* This puts the device config into BAR0 */
-static void set_device_config(struct device *dev, const void *conf, size_t len)
-{
- /* Set up BAR 0 */
- dev->mmio_size += len;
- dev->mmio = realloc(dev->mmio, dev->mmio_size);
- memcpy(dev->mmio + 1, conf, len);
-
- /*
- * 4.1.4.6:
- *
- * The device MUST present at least one VIRTIO_PCI_CAP_DEVICE_CFG
- * capability for any device type which has a device-specific
- * configuration.
- */
- /* Hook up device cfg */
- dev->config.cfg_access.cap.cap_next
- = offsetof(struct pci_config, device);
-
- /*
- * 4.1.4.6.1:
- *
- * The offset for the device-specific configuration MUST be 4-byte
- * aligned.
- */
- assert(dev->config.cfg_access.cap.cap_next % 4 == 0);
-
- /* Fix up device cfg field length. */
- dev->config.device.length = len;
-
- /* The rest is the same as the no-config case */
- no_device_config(dev);
-}
-
-static void init_cap(struct virtio_pci_cap *cap, size_t caplen, int type,
- size_t bar_offset, size_t bar_bytes, u8 next)
-{
- cap->cap_vndr = PCI_CAP_ID_VNDR;
- cap->cap_next = next;
- cap->cap_len = caplen;
- cap->cfg_type = type;
- cap->bar = 0;
- memset(cap->padding, 0, sizeof(cap->padding));
- cap->offset = bar_offset;
- cap->length = bar_bytes;
-}
-
-/*
- * This sets up the pci_config structure, as defined in the virtio 1.0
- * standard (and PCI standard).
- */
-static void init_pci_config(struct pci_config *pci, u16 type,
- u8 class, u8 subclass)
-{
- size_t bar_offset, bar_len;
-
- /*
- * 4.1.4.4.1:
- *
- * The device MUST either present notify_off_multiplier as an even
- * power of 2, or present notify_off_multiplier as 0.
- *
- * 2.1.2:
- *
- * The device MUST initialize device status to 0 upon reset.
- */
- memset(pci, 0, sizeof(*pci));
-
- /* 4.1.2.1: Devices MUST have the PCI Vendor ID 0x1AF4 */
- pci->vendor_id = 0x1AF4;
- /* 4.1.2.1: ... PCI Device ID calculated by adding 0x1040 ... */
- pci->device_id = 0x1040 + type;
-
- /*
- * PCI have specific codes for different types of devices.
- * Linux doesn't care, but it's a good clue for people looking
- * at the device.
- */
- pci->class = class;
- pci->subclass = subclass;
-
- /*
- * 4.1.2.1:
- *
- * Non-transitional devices SHOULD have a PCI Revision ID of 1 or
- * higher
- */
- pci->revid = 1;
-
- /*
- * 4.1.2.1:
- *
- * Non-transitional devices SHOULD have a PCI Subsystem Device ID of
- * 0x40 or higher.
- */
- pci->subsystem_device_id = 0x40;
-
- /* We use our dummy interrupt controller, and irq_line is the irq */
- pci->irq_line = devices.next_irq++;
- pci->irq_pin = 0;
-
- /* Support for extended capabilities. */
- pci->status = (1 << 4);
-
- /* Link them in. */
- /*
- * 4.1.4.3.1:
- *
- * The device MUST present at least one common configuration
- * capability.
- */
- pci->capabilities = offsetof(struct pci_config, common);
-
- /* 4.1.4.3.1 ... offset MUST be 4-byte aligned. */
- assert(pci->capabilities % 4 == 0);
-
- bar_offset = offsetof(struct virtio_pci_mmio, cfg);
- bar_len = sizeof(((struct virtio_pci_mmio *)0)->cfg);
- init_cap(&pci->common, sizeof(pci->common), VIRTIO_PCI_CAP_COMMON_CFG,
- bar_offset, bar_len,
- offsetof(struct pci_config, notify));
-
- /*
- * 4.1.4.4.1:
- *
- * The device MUST present at least one notification capability.
- */
- bar_offset += bar_len;
- bar_len = sizeof(((struct virtio_pci_mmio *)0)->notify);
-
- /*
- * 4.1.4.4.1:
- *
- * The cap.offset MUST be 2-byte aligned.
- */
- assert(pci->common.cap_next % 2 == 0);
-
- /* FIXME: Use a non-zero notify_off, for per-queue notification? */
- /*
- * 4.1.4.4.1:
- *
- * The value cap.length presented by the device MUST be at least 2 and
- * MUST be large enough to support queue notification offsets for all
- * supported queues in all possible configurations.
- */
- assert(bar_len >= 2);
-
- init_cap(&pci->notify.cap, sizeof(pci->notify),
- VIRTIO_PCI_CAP_NOTIFY_CFG,
- bar_offset, bar_len,
- offsetof(struct pci_config, isr));
-
- bar_offset += bar_len;
- bar_len = sizeof(((struct virtio_pci_mmio *)0)->isr);
- /*
- * 4.1.4.5.1:
- *
- * The device MUST present at least one VIRTIO_PCI_CAP_ISR_CFG
- * capability.
- */
- init_cap(&pci->isr, sizeof(pci->isr),
- VIRTIO_PCI_CAP_ISR_CFG,
- bar_offset, bar_len,
- offsetof(struct pci_config, cfg_access));
-
- /*
- * 4.1.4.7.1:
- *
- * The device MUST present at least one VIRTIO_PCI_CAP_PCI_CFG
- * capability.
- */
- /* This doesn't have any presence in the BAR */
- init_cap(&pci->cfg_access.cap, sizeof(pci->cfg_access),
- VIRTIO_PCI_CAP_PCI_CFG,
- 0, 0, 0);
-
- bar_offset += bar_len + sizeof(((struct virtio_pci_mmio *)0)->padding);
- assert(bar_offset == sizeof(struct virtio_pci_mmio));
-
- /*
- * This gets sewn in and length set in set_device_config().
- * Some devices don't have a device configuration interface, so
- * we never expose this if we don't call set_device_config().
- */
- init_cap(&pci->device, sizeof(pci->device), VIRTIO_PCI_CAP_DEVICE_CFG,
- bar_offset, 0, 0);
-}
-
-/*
- * This routine does all the creation and setup of a new device, but we don't
- * actually place the MMIO region until we know the size (if any) of the
- * device-specific config. And we don't actually start the service threads
- * until later.
- *
- * See what I mean about userspace being boring?
- */
-static struct device *new_pci_device(const char *name, u16 type,
- u8 class, u8 subclass)
-{
- struct device *dev = malloc(sizeof(*dev));
-
- /* Now we populate the fields one at a time. */
- dev->name = name;
- dev->vq = NULL;
- dev->running = false;
- dev->wrote_features_ok = false;
- dev->mmio_size = sizeof(struct virtio_pci_mmio);
- dev->mmio = calloc(1, dev->mmio_size);
- dev->features = (u64)1 << VIRTIO_F_VERSION_1;
- dev->features_accepted = 0;
-
- if (devices.device_num + 1 >= MAX_PCI_DEVICES)
- errx(1, "Can only handle 31 PCI devices");
-
- init_pci_config(&dev->config, type, class, subclass);
- assert(!devices.pci[devices.device_num+1]);
- devices.pci[++devices.device_num] = dev;
-
- return dev;
-}
-
-/*
- * Our first setup routine is the console. It's a fairly simple device, but
- * UNIX tty handling makes it uglier than it could be.
- */
-static void setup_console(void)
-{
- struct device *dev;
- struct virtio_console_config conf;
-
- /* If we can save the initial standard input settings... */
- if (tcgetattr(STDIN_FILENO, &orig_term) == 0) {
- struct termios term = orig_term;
- /*
- * Then we turn off echo, line buffering and ^C etc: We want a
- * raw input stream to the Guest.
- */
- term.c_lflag &= ~(ISIG|ICANON|ECHO);
- tcsetattr(STDIN_FILENO, TCSANOW, &term);
- }
-
- dev = new_pci_device("console", VIRTIO_ID_CONSOLE, 0x07, 0x00);
-
- /* We store the console state in dev->priv, and initialize it. */
- dev->priv = malloc(sizeof(struct console_abort));
- ((struct console_abort *)dev->priv)->count = 0;
-
- /*
- * The console needs two virtqueues: the input then the output. When
- * they put something the input queue, we make sure we're listening to
- * stdin. When they put something in the output queue, we write it to
- * stdout.
- */
- add_pci_virtqueue(dev, console_input, "input");
- add_pci_virtqueue(dev, console_output, "output");
-
- /* We need a configuration area for the emerg_wr early writes. */
- add_pci_feature(dev, VIRTIO_CONSOLE_F_EMERG_WRITE);
- set_device_config(dev, &conf, sizeof(conf));
-
- verbose("device %u: console\n", devices.device_num);
-}
-/*:*/
-
-/*M:010
- * Inter-guest networking is an interesting area. Simplest is to have a
- * --sharenet=<name> option which opens or creates a named pipe. This can be
- * used to send packets to another guest in a 1:1 manner.
- *
- * More sophisticated is to use one of the tools developed for project like UML
- * to do networking.
- *
- * Faster is to do virtio bonding in kernel. Doing this 1:1 would be
- * completely generic ("here's my vring, attach to your vring") and would work
- * for any traffic. Of course, namespace and permissions issues need to be
- * dealt with. A more sophisticated "multi-channel" virtio_net.c could hide
- * multiple inter-guest channels behind one interface, although it would
- * require some manner of hotplugging new virtio channels.
- *
- * Finally, we could use a virtio network switch in the kernel, ie. vhost.
-:*/
-
-static u32 str2ip(const char *ipaddr)
-{
- unsigned int b[4];
-
- if (sscanf(ipaddr, "%u.%u.%u.%u", &b[0], &b[1], &b[2], &b[3]) != 4)
- errx(1, "Failed to parse IP address '%s'", ipaddr);
- return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
-}
-
-static void str2mac(const char *macaddr, unsigned char mac[6])
-{
- unsigned int m[6];
- if (sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
- &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) != 6)
- errx(1, "Failed to parse mac address '%s'", macaddr);
- mac[0] = m[0];
- mac[1] = m[1];
- mac[2] = m[2];
- mac[3] = m[3];
- mac[4] = m[4];
- mac[5] = m[5];
-}
-
-/*
- * This code is "adapted" from libbridge: it attaches the Host end of the
- * network device to the bridge device specified by the command line.
- *
- * This is yet another James Morris contribution (I'm an IP-level guy, so I
- * dislike bridging), and I just try not to break it.
- */
-static void add_to_bridge(int fd, const char *if_name, const char *br_name)
-{
- int ifidx;
- struct ifreq ifr;
-
- if (!*br_name)
- errx(1, "must specify bridge name");
-
- ifidx = if_nametoindex(if_name);
- if (!ifidx)
- errx(1, "interface %s does not exist!", if_name);
-
- strncpy(ifr.ifr_name, br_name, IFNAMSIZ);
- ifr.ifr_name[IFNAMSIZ-1] = '\0';
- ifr.ifr_ifindex = ifidx;
- if (ioctl(fd, SIOCBRADDIF, &ifr) < 0)
- err(1, "can't add %s to bridge %s", if_name, br_name);
-}
-
-/*
- * This sets up the Host end of the network device with an IP address, brings
- * it up so packets will flow, the copies the MAC address into the hwaddr
- * pointer.
- */
-static void configure_device(int fd, const char *tapif, u32 ipaddr)
-{
- struct ifreq ifr;
- struct sockaddr_in sin;
-
- memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, tapif);
-
- /* Don't read these incantations. Just cut & paste them like I did! */
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(ipaddr);
- memcpy(&ifr.ifr_addr, &sin, sizeof(sin));
- if (ioctl(fd, SIOCSIFADDR, &ifr) != 0)
- err(1, "Setting %s interface address", tapif);
- ifr.ifr_flags = IFF_UP;
- if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0)
- err(1, "Bringing interface %s up", tapif);
-}
-
-static int get_tun_device(char tapif[IFNAMSIZ])
-{
- struct ifreq ifr;
- int vnet_hdr_sz;
- int netfd;
-
- /* Start with this zeroed. Messy but sure. */
- memset(&ifr, 0, sizeof(ifr));
-
- /*
- * We open the /dev/net/tun device and tell it we want a tap device. A
- * tap device is like a tun device, only somehow different. To tell
- * the truth, I completely blundered my way through this code, but it
- * works now!
- */
- netfd = open_or_die("/dev/net/tun", O_RDWR);
- ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
- strcpy(ifr.ifr_name, "tap%d");
- if (ioctl(netfd, TUNSETIFF, &ifr) != 0)
- err(1, "configuring /dev/net/tun");
-
- if (ioctl(netfd, TUNSETOFFLOAD,
- TUN_F_CSUM|TUN_F_TSO4|TUN_F_TSO6|TUN_F_TSO_ECN) != 0)
- err(1, "Could not set features for tun device");
-
- /*
- * We don't need checksums calculated for packets coming in this
- * device: trust us!
- */
- ioctl(netfd, TUNSETNOCSUM, 1);
-
- /*
- * In virtio before 1.0 (aka legacy virtio), we added a 16-bit
- * field at the end of the network header iff
- * VIRTIO_NET_F_MRG_RXBUF was negotiated. For virtio 1.0,
- * that became the norm, but we need to tell the tun device
- * about our expanded header (which is called
- * virtio_net_hdr_mrg_rxbuf in the legacy system).
- */
- vnet_hdr_sz = sizeof(struct virtio_net_hdr_v1);
- if (ioctl(netfd, TUNSETVNETHDRSZ, &vnet_hdr_sz) != 0)
- err(1, "Setting tun header size to %u", vnet_hdr_sz);
-
- memcpy(tapif, ifr.ifr_name, IFNAMSIZ);
- return netfd;
-}
-
-/*L:195
- * Our network is a Host<->Guest network. This can either use bridging or
- * routing, but the principle is the same: it uses the "tun" device to inject
- * packets into the Host as if they came in from a normal network card. We
- * just shunt packets between the Guest and the tun device.
- */
-static void setup_tun_net(char *arg)
-{
- struct device *dev;
- struct net_info *net_info = malloc(sizeof(*net_info));
- int ipfd;
- u32 ip = INADDR_ANY;
- bool bridging = false;
- char tapif[IFNAMSIZ], *p;
- struct virtio_net_config conf;
-
- net_info->tunfd = get_tun_device(tapif);
-
- /* First we create a new network device. */
- dev = new_pci_device("net", VIRTIO_ID_NET, 0x02, 0x00);
- dev->priv = net_info;
-
- /* Network devices need a recv and a send queue, just like console. */
- add_pci_virtqueue(dev, net_input, "rx");
- add_pci_virtqueue(dev, net_output, "tx");
-
- /*
- * We need a socket to perform the magic network ioctls to bring up the
- * tap interface, connect to the bridge etc. Any socket will do!
- */
- ipfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
- if (ipfd < 0)
- err(1, "opening IP socket");
-
- /* If the command line was --tunnet=bridge:<name> do bridging. */
- if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) {
- arg += strlen(BRIDGE_PFX);
- bridging = true;
- }
-
- /* A mac address may follow the bridge name or IP address */
- p = strchr(arg, ':');
- if (p) {
- str2mac(p+1, conf.mac);
- add_pci_feature(dev, VIRTIO_NET_F_MAC);
- *p = '\0';
- }
-
- /* arg is now either an IP address or a bridge name */
- if (bridging)
- add_to_bridge(ipfd, tapif, arg);
- else
- ip = str2ip(arg);
-
- /* Set up the tun device. */
- configure_device(ipfd, tapif, ip);
-
- /* Expect Guest to handle everything except UFO */
- add_pci_feature(dev, VIRTIO_NET_F_CSUM);
- add_pci_feature(dev, VIRTIO_NET_F_GUEST_CSUM);
- add_pci_feature(dev, VIRTIO_NET_F_GUEST_TSO4);
- add_pci_feature(dev, VIRTIO_NET_F_GUEST_TSO6);
- add_pci_feature(dev, VIRTIO_NET_F_GUEST_ECN);
- add_pci_feature(dev, VIRTIO_NET_F_HOST_TSO4);
- add_pci_feature(dev, VIRTIO_NET_F_HOST_TSO6);
- add_pci_feature(dev, VIRTIO_NET_F_HOST_ECN);
- /* We handle indirect ring entries */
- add_pci_feature(dev, VIRTIO_RING_F_INDIRECT_DESC);
- set_device_config(dev, &conf, sizeof(conf));
-
- /* We don't need the socket any more; setup is done. */
- close(ipfd);
-
- if (bridging)
- verbose("device %u: tun %s attached to bridge: %s\n",
- devices.device_num, tapif, arg);
- else
- verbose("device %u: tun %s: %s\n",
- devices.device_num, tapif, arg);
-}
-/*:*/
-
-/* This hangs off device->priv. */
-struct vblk_info {
- /* The size of the file. */
- off64_t len;
-
- /* The file descriptor for the file. */
- int fd;
-
-};
-
-/*L:210
- * The Disk
- *
- * The disk only has one virtqueue, so it only has one thread. It is really
- * simple: the Guest asks for a block number and we read or write that position
- * in the file.
- *
- * Before we serviced each virtqueue in a separate thread, that was unacceptably
- * slow: the Guest waits until the read is finished before running anything
- * else, even if it could have been doing useful work.
- *
- * We could have used async I/O, except it's reputed to suck so hard that
- * characters actually go missing from your code when you try to use it.
- */
-static void blk_request(struct virtqueue *vq)
-{
- struct vblk_info *vblk = vq->dev->priv;
- unsigned int head, out_num, in_num, wlen;
- int ret, i;
- u8 *in;
- struct virtio_blk_outhdr out;
- struct iovec iov[vq->vring.num];
- off64_t off;
-
- /*
- * Get the next request, where we normally wait. It triggers the
- * interrupt to acknowledge previously serviced requests (if any).
- */
- head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
-
- /* Copy the output header from the front of the iov (adjusts iov) */
- iov_consume(vq->dev, iov, out_num, &out, sizeof(out));
-
- /* Find and trim end of iov input array, for our status byte. */
- in = NULL;
- for (i = out_num + in_num - 1; i >= out_num; i--) {
- if (iov[i].iov_len > 0) {
- in = iov[i].iov_base + iov[i].iov_len - 1;
- iov[i].iov_len--;
- break;
- }
- }
- if (!in)
- bad_driver_vq(vq, "Bad virtblk cmd with no room for status");
-
- /*
- * For historical reasons, block operations are expressed in 512 byte
- * "sectors".
- */
- off = out.sector * 512;
-
- if (out.type & VIRTIO_BLK_T_OUT) {
- /*
- * Write
- *
- * Move to the right location in the block file. This can fail
- * if they try to write past end.
- */
- if (lseek64(vblk->fd, off, SEEK_SET) != off)
- err(1, "Bad seek to sector %llu", out.sector);
-
- ret = writev(vblk->fd, iov, out_num);
- verbose("WRITE to sector %llu: %i\n", out.sector, ret);
-
- /*
- * Grr... Now we know how long the descriptor they sent was, we
- * make sure they didn't try to write over the end of the block
- * file (possibly extending it).
- */
- if (ret > 0 && off + ret > vblk->len) {
- /* Trim it back to the correct length */
- ftruncate64(vblk->fd, vblk->len);
- /* Die, bad Guest, die. */
- bad_driver_vq(vq, "Write past end %llu+%u", off, ret);
- }
-
- wlen = sizeof(*in);
- *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR);
- } else if (out.type & VIRTIO_BLK_T_FLUSH) {
- /* Flush */
- ret = fdatasync(vblk->fd);
- verbose("FLUSH fdatasync: %i\n", ret);
- wlen = sizeof(*in);
- *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR);
- } else {
- /*
- * Read
- *
- * Move to the right location in the block file. This can fail
- * if they try to read past end.
- */
- if (lseek64(vblk->fd, off, SEEK_SET) != off)
- err(1, "Bad seek to sector %llu", out.sector);
-
- ret = readv(vblk->fd, iov + out_num, in_num);
- if (ret >= 0) {
- wlen = sizeof(*in) + ret;
- *in = VIRTIO_BLK_S_OK;
- } else {
- wlen = sizeof(*in);
- *in = VIRTIO_BLK_S_IOERR;
- }
- }
-
- /* Finished that request. */
- add_used(vq, head, wlen);
-}
-
-/*L:198 This actually sets up a virtual block device. */
-static void setup_block_file(const char *filename)
-{
- struct device *dev;
- struct vblk_info *vblk;
- struct virtio_blk_config conf;
-
- /* Create the device. */
- dev = new_pci_device("block", VIRTIO_ID_BLOCK, 0x01, 0x80);
-
- /* The device has one virtqueue, where the Guest places requests. */
- add_pci_virtqueue(dev, blk_request, "request");
-
- /* Allocate the room for our own bookkeeping */
- vblk = dev->priv = malloc(sizeof(*vblk));
-
- /* First we open the file and store the length. */
- vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE);
- vblk->len = lseek64(vblk->fd, 0, SEEK_END);
-
- /* Tell Guest how many sectors this device has. */
- conf.capacity = cpu_to_le64(vblk->len / 512);
-
- /*
- * Tell Guest not to put in too many descriptors at once: two are used
- * for the in and out elements.
- */
- add_pci_feature(dev, VIRTIO_BLK_F_SEG_MAX);
- conf.seg_max = cpu_to_le32(VIRTQUEUE_NUM - 2);
-
- set_device_config(dev, &conf, sizeof(struct virtio_blk_config));
-
- verbose("device %u: virtblock %llu sectors\n",
- devices.device_num, le64_to_cpu(conf.capacity));
-}
-
-/*L:211
- * Our random number generator device reads from /dev/urandom into the Guest's
- * input buffers. The usual case is that the Guest doesn't want random numbers
- * and so has no buffers although /dev/urandom is still readable, whereas
- * console is the reverse.
- *
- * The same logic applies, however.
- */
-struct rng_info {
- int rfd;
-};
-
-static void rng_input(struct virtqueue *vq)
-{
- int len;
- unsigned int head, in_num, out_num, totlen = 0;
- struct rng_info *rng_info = vq->dev->priv;
- struct iovec iov[vq->vring.num];
-
- /* First we need a buffer from the Guests's virtqueue. */
- head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
- if (out_num)
- bad_driver_vq(vq, "Output buffers in rng?");
-
- /*
- * Just like the console write, we loop to cover the whole iovec.
- * In this case, short reads actually happen quite a bit.
- */
- while (!iov_empty(iov, in_num)) {
- len = readv(rng_info->rfd, iov, in_num);
- if (len <= 0)
- err(1, "Read from /dev/urandom gave %i", len);
- iov_consume(vq->dev, iov, in_num, NULL, len);
- totlen += len;
- }
-
- /* Tell the Guest about the new input. */
- add_used(vq, head, totlen);
-}
-
-/*L:199
- * This creates a "hardware" random number device for the Guest.
- */
-static void setup_rng(void)
-{
- struct device *dev;
- struct rng_info *rng_info = malloc(sizeof(*rng_info));
-
- /* Our device's private info simply contains the /dev/urandom fd. */
- rng_info->rfd = open_or_die("/dev/urandom", O_RDONLY);
-
- /* Create the new device. */
- dev = new_pci_device("rng", VIRTIO_ID_RNG, 0xff, 0);
- dev->priv = rng_info;
-
- /* The device has one virtqueue, where the Guest places inbufs. */
- add_pci_virtqueue(dev, rng_input, "input");
-
- /* We don't have any configuration space */
- no_device_config(dev);
-
- verbose("device %u: rng\n", devices.device_num);
-}
-/* That's the end of device setup. */
-
-/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */
-static void __attribute__((noreturn)) restart_guest(void)
-{
- unsigned int i;
-
- /*
- * Since we don't track all open fds, we simply close everything beyond
- * stderr.
- */
- for (i = 3; i < FD_SETSIZE; i++)
- close(i);
-
- /* Reset all the devices (kills all threads). */
- cleanup_devices();
-
- execv(main_args[0], main_args);
- err(1, "Could not exec %s", main_args[0]);
-}
-
-/*L:220
- * Finally we reach the core of the Launcher which runs the Guest, serves
- * its input and output, and finally, lays it to rest.
- */
-static void __attribute__((noreturn)) run_guest(void)
-{
- for (;;) {
- struct lguest_pending notify;
- int readval;
-
- /* We read from the /dev/lguest device to run the Guest. */
- readval = pread(lguest_fd, &notify, sizeof(notify), cpu_id);
- if (readval == sizeof(notify)) {
- if (notify.trap == 13) {
- verbose("Emulating instruction at %#x\n",
- getreg(eip));
- emulate_insn(notify.insn);
- } else if (notify.trap == 14) {
- verbose("Emulating MMIO at %#x\n",
- getreg(eip));
- emulate_mmio(notify.addr, notify.insn);
- } else
- errx(1, "Unknown trap %i addr %#08x\n",
- notify.trap, notify.addr);
- /* ENOENT means the Guest died. Reading tells us why. */
- } else if (errno == ENOENT) {
- char reason[1024] = { 0 };
- pread(lguest_fd, reason, sizeof(reason)-1, cpu_id);
- errx(1, "%s", reason);
- /* ERESTART means that we need to reboot the guest */
- } else if (errno == ERESTART) {
- restart_guest();
- /* Anything else means a bug or incompatible change. */
- } else
- err(1, "Running guest failed");
- }
-}
-/*L:240
- * This is the end of the Launcher. The good news: we are over halfway
- * through! The bad news: the most fiendish part of the code still lies ahead
- * of us.
- *
- * Are you ready? Take a deep breath and join me in the core of the Host, in
- * "make Host".
-:*/
-
-static struct option opts[] = {
- { "verbose", 0, NULL, 'v' },
- { "tunnet", 1, NULL, 't' },
- { "block", 1, NULL, 'b' },
- { "rng", 0, NULL, 'r' },
- { "initrd", 1, NULL, 'i' },
- { "username", 1, NULL, 'u' },
- { "chroot", 1, NULL, 'c' },
- { NULL },
-};
-static void usage(void)
-{
- errx(1, "Usage: lguest [--verbose] "
- "[--tunnet=(<ipaddr>:<macaddr>|bridge:<bridgename>:<macaddr>)\n"
- "|--block=<filename>|--initrd=<filename>]...\n"
- "<mem-in-mb> vmlinux [args...]");
-}
-
-/*L:105 The main routine is where the real work begins: */
-int main(int argc, char *argv[])
-{
- /* Memory, code startpoint and size of the (optional) initrd. */
- unsigned long mem = 0, start, initrd_size = 0;
- /* Two temporaries. */
- int i, c;
- /* The boot information for the Guest. */
- struct boot_params *boot;
- /* If they specify an initrd file to load. */
- const char *initrd_name = NULL;
-
- /* Password structure for initgroups/setres[gu]id */
- struct passwd *user_details = NULL;
-
- /* Directory to chroot to */
- char *chroot_path = NULL;
-
- /* Save the args: we "reboot" by execing ourselves again. */
- main_args = argv;
-
- /*
- * First we initialize the device list. We remember next interrupt
- * number to use for devices (1: remember that 0 is used by the timer).
- */
- devices.next_irq = 1;
-
- /* We're CPU 0. In fact, that's the only CPU possible right now. */
- cpu_id = 0;
-
- /*
- * We need to know how much memory so we can set up the device
- * descriptor and memory pages for the devices as we parse the command
- * line. So we quickly look through the arguments to find the amount
- * of memory now.
- */
- for (i = 1; i < argc; i++) {
- if (argv[i][0] != '-') {
- mem = atoi(argv[i]) * 1024 * 1024;
- /*
- * We start by mapping anonymous pages over all of
- * guest-physical memory range. This fills it with 0,
- * and ensures that the Guest won't be killed when it
- * tries to access it.
- */
- guest_base = map_zeroed_pages(mem / getpagesize()
- + DEVICE_PAGES);
- guest_limit = mem;
- guest_max = guest_mmio = mem + DEVICE_PAGES*getpagesize();
- break;
- }
- }
-
- /* If we exit via err(), this kills all the threads, restores tty. */
- atexit(cleanup_devices);
-
- /* We always have a console device, and it's always device 1. */
- setup_console();
-
- /* The options are fairly straight-forward */
- while ((c = getopt_long(argc, argv, "v", opts, NULL)) != EOF) {
- switch (c) {
- case 'v':
- verbose = true;
- break;
- case 't':
- setup_tun_net(optarg);
- break;
- case 'b':
- setup_block_file(optarg);
- break;
- case 'r':
- setup_rng();
- break;
- case 'i':
- initrd_name = optarg;
- break;
- case 'u':
- user_details = getpwnam(optarg);
- if (!user_details)
- err(1, "getpwnam failed, incorrect username?");
- break;
- case 'c':
- chroot_path = optarg;
- break;
- default:
- warnx("Unknown argument %s", argv[optind]);
- usage();
- }
- }
- /*
- * After the other arguments we expect memory and kernel image name,
- * followed by command line arguments for the kernel.
- */
- if (optind + 2 > argc)
- usage();
-
- verbose("Guest base is at %p\n", guest_base);
-
- /* Initialize the (fake) PCI host bridge device. */
- init_pci_host_bridge();
-
- /* Now we load the kernel */
- start = load_kernel(open_or_die(argv[optind+1], O_RDONLY));
-
- /* Boot information is stashed at physical address 0 */
- boot = from_guest_phys(0);
-
- /* Map the initrd image if requested (at top of physical memory) */
- if (initrd_name) {
- initrd_size = load_initrd(initrd_name, mem);
- /*
- * These are the location in the Linux boot header where the
- * start and size of the initrd are expected to be found.
- */
- boot->hdr.ramdisk_image = mem - initrd_size;
- boot->hdr.ramdisk_size = initrd_size;
- /* The bootloader type 0xFF means "unknown"; that's OK. */
- boot->hdr.type_of_loader = 0xFF;
- }
-
- /*
- * The Linux boot header contains an "E820" memory map: ours is a
- * simple, single region.
- */
- boot->e820_entries = 1;
- boot->e820_table[0] = ((struct e820_entry) { 0, mem, E820_TYPE_RAM });
- /*
- * The boot header contains a command line pointer: we put the command
- * line after the boot header.
- */
- boot->hdr.cmd_line_ptr = to_guest_phys(boot + 1);
- /* We use a simple helper to copy the arguments separated by spaces. */
- concat((char *)(boot + 1), argv+optind+2);
-
- /* Set kernel alignment to 16M (CONFIG_PHYSICAL_ALIGN) */
- boot->hdr.kernel_alignment = 0x1000000;
-
- /* Boot protocol version: 2.07 supports the fields for lguest. */
- boot->hdr.version = 0x207;
-
- /* X86_SUBARCH_LGUEST tells the Guest it's an lguest. */
- boot->hdr.hardware_subarch = X86_SUBARCH_LGUEST;
-
- /* Tell the entry path not to try to reload segment registers. */
- boot->hdr.loadflags |= KEEP_SEGMENTS;
-
- /* We don't support tboot: */
- boot->tboot_addr = 0;
-
- /* Ensure this is 0 to prevent APM from loading: */
- boot->apm_bios_info.version = 0;
-
- /* We tell the kernel to initialize the Guest. */
- tell_kernel(start);
-
- /* Ensure that we terminate if a device-servicing child dies. */
- signal(SIGCHLD, kill_launcher);
-
- /* If requested, chroot to a directory */
- if (chroot_path) {
- if (chroot(chroot_path) != 0)
- err(1, "chroot(\"%s\") failed", chroot_path);
-
- if (chdir("/") != 0)
- err(1, "chdir(\"/\") failed");
-
- verbose("chroot done\n");
- }
-
- /* If requested, drop privileges */
- if (user_details) {
- uid_t u;
- gid_t g;
-
- u = user_details->pw_uid;
- g = user_details->pw_gid;
-
- if (initgroups(user_details->pw_name, g) != 0)
- err(1, "initgroups failed");
-
- if (setresgid(g, g, g) != 0)
- err(1, "setresgid failed");
-
- if (setresuid(u, u, u) != 0)
- err(1, "setresuid failed");
-
- verbose("Dropping privileges completed\n");
- }
-
- /* Finally, run the Guest. This doesn't return. */
- run_guest();
-}
-/*:*/
-
-/*M:999
- * Mastery is done: you now know everything I do.
- *
- * But surely you have seen code, features and bugs in your wanderings which
- * you now yearn to attack? That is the real game, and I look forward to you
- * patching and forking lguest into the Your-Name-Here-visor.
- *
- * Farewell, and good coding!
- * Rusty Russell.
- */
diff --git a/tools/lguest/lguest.txt b/tools/lguest/lguest.txt
deleted file mode 100644
index 06e1f4649511..000000000000
--- a/tools/lguest/lguest.txt
+++ /dev/null
@@ -1,125 +0,0 @@
- __
- (___()'`; Rusty's Remarkably Unreliable Guide to Lguest
- /, /` - or, A Young Coder's Illustrated Hypervisor
- \\"--\\ http://lguest.ozlabs.org
-
-Lguest is designed to be a minimal 32-bit x86 hypervisor for the Linux kernel,
-for Linux developers and users to experiment with virtualization with the
-minimum of complexity. Nonetheless, it should have sufficient features to
-make it useful for specific tasks, and, of course, you are encouraged to fork
-and enhance it (see drivers/lguest/README).
-
-Features:
-
-- Kernel module which runs in a normal kernel.
-- Simple I/O model for communication.
-- Simple program to create new guests.
-- Logo contains cute puppies: http://lguest.ozlabs.org
-
-Developer features:
-
-- Fun to hack on.
-- No ABI: being tied to a specific kernel anyway, you can change anything.
-- Many opportunities for improvement or feature implementation.
-
-Running Lguest:
-
-- The easiest way to run lguest is to use same kernel as guest and host.
- You can configure them differently, but usually it's easiest not to.
-
- You will need to configure your kernel with the following options:
-
- "Processor type and features":
- "Paravirtualized guest support" = Y
- "Lguest guest support" = Y
- "High Memory Support" = off/4GB
- "Alignment value to which kernel should be aligned" = 0x100000
- (CONFIG_PARAVIRT=y, CONFIG_LGUEST_GUEST=y, CONFIG_HIGHMEM64G=n and
- CONFIG_PHYSICAL_ALIGN=0x100000)
-
- "Device Drivers":
- "Block devices"
- "Virtio block driver" = M/Y
- "Network device support"
- "Universal TUN/TAP device driver support" = M/Y
- "Virtio network driver" = M/Y
- (CONFIG_VIRTIO_BLK=m, CONFIG_VIRTIO_NET=m and CONFIG_TUN=m)
-
- "Virtualization"
- "Linux hypervisor example code" = M/Y
- (CONFIG_LGUEST=m)
-
-- A tool called "lguest" is available in this directory: type "make"
- to build it. If you didn't build your kernel in-tree, use "make
- O=<builddir>".
-
-- Create or find a root disk image. There are several useful ones
- around, such as the xm-test tiny root image at
- http://xm-test.xensource.com/ramdisks/initrd-1.1-i386.img
-
- For more serious work, I usually use a distribution ISO image and
- install it under qemu, then make multiple copies:
-
- dd if=/dev/zero of=rootfile bs=1M count=2048
- qemu -cdrom image.iso -hda rootfile -net user -net nic -boot d
-
- Make sure that you install a getty on /dev/hvc0 if you want to log in on the
- console!
-
-- "modprobe lg" if you built it as a module.
-
-- Run an lguest as root:
-
- tools/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 \
- --block=rootfile root=/dev/vda
-
- Explanation:
- 64: the amount of memory to use, in MB.
-
- vmlinux: the kernel image found in the top of your build directory. You
- can also use a standard bzImage.
-
- --tunnet=192.168.19.1: configures a "tap" device for networking with this
- IP address.
-
- --block=rootfile: a file or block device which becomes /dev/vda
- inside the guest.
-
- root=/dev/vda: this (and anything else on the command line) are
- kernel boot parameters.
-
-- Configuring networking. I usually have the host masquerade, using
- "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE" and "echo 1 >
- /proc/sys/net/ipv4/ip_forward". In this example, I would configure
- eth0 inside the guest at 192.168.19.2.
-
- Another method is to bridge the tap device to an external interface
- using --tunnet=bridge:<bridgename>, and perhaps run dhcp on the guest
- to obtain an IP address. The bridge needs to be configured first:
- this option simply adds the tap interface to it.
-
- A simple example on my system:
-
- ifconfig eth0 0.0.0.0
- brctl addbr lg0
- ifconfig lg0 up
- brctl addif lg0 eth0
- dhclient lg0
-
- Then use --tunnet=bridge:lg0 when launching the guest.
-
- See:
-
- http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge
-
- for general information on how to get bridging to work.
-
-- Random number generation. Using the --rng option will provide a
- /dev/hwrng in the guest that will read from the host's /dev/random.
- Use this option in conjunction with rng-tools (see ../hw_random.txt)
- to provide entropy to the guest kernel's /dev/random.
-
-There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest
-
-Good luck!
-Rusty Russell rusty@rustcorp.com.au.
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 6f2e1987c4d9..749becdf5b90 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -1,6 +1,9 @@
objtool-y += arch/$(SRCARCH)/
objtool-y += builtin-check.o
+objtool-y += builtin-orc.o
objtool-y += check.o
+objtool-y += orc_gen.o
+objtool-y += orc_dump.o
objtool-y += elf.o
objtool-y += special.o
objtool-y += objtool.o
diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt
index 17c1195f11f4..6a1af43862df 100644
--- a/tools/objtool/Documentation/stack-validation.txt
+++ b/tools/objtool/Documentation/stack-validation.txt
@@ -11,9 +11,6 @@ analyzes every .o file and ensures the validity of its stack metadata.
It enforces a set of rules on asm code and C inline assembly code so
that stack traces can be reliable.
-Currently it only checks frame pointer usage, but there are plans to add
-CFI validation for C files and CFI generation for asm files.
-
For each function, it recursively follows all possible code paths and
validates the correct frame pointer state at each instruction.
@@ -23,6 +20,10 @@ alternative execution paths to a given instruction (or set of
instructions). Similarly, it knows how to follow switch statements, for
which gcc sometimes uses jump tables.
+(Objtool also has an 'orc generate' subcommand which generates debuginfo
+for the ORC unwinder. See Documentation/x86/orc-unwinder.txt in the
+kernel tree for more details.)
+
Why do we need stack metadata validation?
-----------------------------------------
@@ -93,37 +94,14 @@ a) More reliable stack traces for frame pointer enabled kernels
or at the very end of the function after the stack frame has been
destroyed. This is an inherent limitation of frame pointers.
-b) 100% reliable stack traces for DWARF enabled kernels
-
- (NOTE: This is not yet implemented)
-
- As an alternative to frame pointers, DWARF Call Frame Information
- (CFI) metadata can be used to walk the stack. Unlike frame pointers,
- CFI metadata is out of band. So it doesn't affect runtime
- performance and it can be reliable even when interrupts or exceptions
- are involved.
-
- For C code, gcc automatically generates DWARF CFI metadata. But for
- asm code, generating CFI is a tedious manual approach which requires
- manually placed .cfi assembler macros to be scattered throughout the
- code. It's clumsy and very easy to get wrong, and it makes the real
- code harder to read.
-
- Stacktool will improve this situation in several ways. For code
- which already has CFI annotations, it will validate them. For code
- which doesn't have CFI annotations, it will generate them. So an
- architecture can opt to strip out all the manual .cfi annotations
- from their asm code and have objtool generate them instead.
+b) ORC (Oops Rewind Capability) unwind table generation
- We might also add a runtime stack validation debug option where we
- periodically walk the stack from schedule() and/or an NMI to ensure
- that the stack metadata is sane and that we reach the bottom of the
- stack.
+ An alternative to frame pointers and DWARF, ORC unwind data can be
+ used to walk the stack. Unlike frame pointers, ORC data is out of
+ band. So it doesn't affect runtime performance and it can be
+ reliable even when interrupts or exceptions are involved.
- So the benefit of objtool here will be that external tooling should
- always show perfect stack traces. And the same will be true for
- kernel warning/oops traces if the architecture has a runtime DWARF
- unwinder.
+ For more details, see Documentation/x86/orc-unwinder.txt.
c) Higher live patching compatibility rate
@@ -211,7 +189,7 @@ they mean, and suggestions for how to fix them.
function, add proper frame pointer logic using the FRAME_BEGIN and
FRAME_END macros. Otherwise, if it's not a callable function, remove
its ELF function annotation by changing ENDPROC to END, and instead
- use the manual CFI hint macros in asm/undwarf.h.
+ use the manual unwind hint macros in asm/unwind_hints.h.
If it's a GCC-compiled .c file, the error may be because the function
uses an inline asm() statement which has a "call" instruction. An
@@ -231,8 +209,8 @@ they mean, and suggestions for how to fix them.
If the error is for an asm file, and the instruction is inside (or
reachable from) a callable function, the function should be annotated
with the ENTRY/ENDPROC macros (ENDPROC is the important one).
- Otherwise, the code should probably be annotated with the CFI hint
- macros in asm/undwarf.h so objtool and the unwinder can know the
+ Otherwise, the code should probably be annotated with the unwind hint
+ macros in asm/unwind_hints.h so objtool and the unwinder can know the
stack state associated with the code.
If you're 100% sure the code won't affect stack traces, or if you're
@@ -258,7 +236,7 @@ they mean, and suggestions for how to fix them.
instructions aren't allowed in a callable function, and are most
likely part of the kernel entry code. They should usually not have
the callable function annotation (ENDPROC) and should always be
- annotated with the CFI hint macros in asm/undwarf.h.
+ annotated with the unwind hint macros in asm/unwind_hints.h.
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
@@ -272,7 +250,7 @@ they mean, and suggestions for how to fix them.
If the instruction is not actually in a callable function (e.g.
kernel entry code), change ENDPROC to END and annotate manually with
- the CFI hint macros in asm/undwarf.h.
+ the unwind hint macros in asm/unwind_hints.h.
7. file: warning: objtool: func()+0x5c: stack state mismatch
@@ -288,8 +266,8 @@ they mean, and suggestions for how to fix them.
Another possibility is that the code has some asm or inline asm which
does some unusual things to the stack or the frame pointer. In such
- cases it's probably appropriate to use the CFI hint macros in
- asm/undwarf.h.
+ cases it's probably appropriate to use the unwind hint macros in
+ asm/unwind_hints.h.
8. file.o: warning: objtool: funcA() falls through to next function funcB()
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 0e2765e243c0..6976c73e60c4 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -25,7 +25,8 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
all: $(OBJTOOL)
INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
-CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES)
+WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
+CFLAGS += -Wall -Werror $(WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
LDFLAGS += -lelf $(LIBSUBCMD)
# Allow old libelf to be used:
@@ -52,6 +53,9 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
|| echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true
+ @(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
+ diff ../../arch/x86/include/asm/orc_types.h orc_types.h >/dev/null) \
+ || echo "warning: objtool: orc_types.h differs from kernel" >&2 )) || true
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index 21aeca874edb..b0d7dc3d71b5 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -31,8 +31,9 @@
#define INSN_RETURN 6
#define INSN_CONTEXT_SWITCH 7
#define INSN_STACK 8
-#define INSN_NOP 9
-#define INSN_OTHER 10
+#define INSN_BUG 9
+#define INSN_NOP 10
+#define INSN_OTHER 11
#define INSN_LAST INSN_OTHER
enum op_dest_type {
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 4559a21a8de2..0e8c8ec4fd4e 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -86,8 +86,8 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
struct insn insn;
int x86_64, sign;
unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0,
- modrm = 0, modrm_mod = 0, modrm_rm = 0, modrm_reg = 0,
- sib = 0;
+ rex_x = 0, modrm = 0, modrm_mod = 0, modrm_rm = 0,
+ modrm_reg = 0, sib = 0;
x86_64 = is_x86_64(elf);
if (x86_64 == -1)
@@ -114,6 +114,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
rex = insn.rex_prefix.bytes[0];
rex_w = X86_REX_W(rex) >> 3;
rex_r = X86_REX_R(rex) >> 2;
+ rex_x = X86_REX_X(rex) >> 1;
rex_b = X86_REX_B(rex);
}
@@ -217,6 +218,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
op->dest.reg = CFI_BP;
break;
}
+
+ if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
+
+ /* mov reg, %rsp */
+ *type = INSN_STACK;
+ op->src.type = OP_SRC_REG;
+ op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
+ op->dest.type = OP_DEST_REG;
+ op->dest.reg = CFI_SP;
+ break;
+ }
+
/* fallthrough */
case 0x88:
if (!rex_b &&
@@ -269,80 +282,28 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
break;
case 0x8d:
- if (rex == 0x48 && modrm == 0x65) {
+ if (sib == 0x24 && rex_w && !rex_b && !rex_x) {
- /* lea disp(%rbp), %rsp */
+ /* lea disp(%rsp), reg */
*type = INSN_STACK;
op->src.type = OP_SRC_ADD;
- op->src.reg = CFI_BP;
+ op->src.reg = CFI_SP;
op->src.offset = insn.displacement.value;
op->dest.type = OP_DEST_REG;
- op->dest.reg = CFI_SP;
- break;
- }
+ op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
- if (rex == 0x48 && (modrm == 0xa4 || modrm == 0x64) &&
- sib == 0x24) {
+ } else if (rex == 0x48 && modrm == 0x65) {
- /* lea disp(%rsp), %rsp */
+ /* lea disp(%rbp), %rsp */
*type = INSN_STACK;
op->src.type = OP_SRC_ADD;
- op->src.reg = CFI_SP;
+ op->src.reg = CFI_BP;
op->src.offset = insn.displacement.value;
op->dest.type = OP_DEST_REG;
op->dest.reg = CFI_SP;
- break;
- }
-
- if (rex == 0x48 && modrm == 0x2c && sib == 0x24) {
- /* lea (%rsp), %rbp */
- *type = INSN_STACK;
- op->src.type = OP_SRC_REG;
- op->src.reg = CFI_SP;
- op->dest.type = OP_DEST_REG;
- op->dest.reg = CFI_BP;
- break;
- }
-
- if (rex == 0x4c && modrm == 0x54 && sib == 0x24 &&
- insn.displacement.value == 8) {
-
- /*
- * lea 0x8(%rsp), %r10
- *
- * Here r10 is the "drap" pointer, used as a stack
- * pointer helper when the stack gets realigned.
- */
- *type = INSN_STACK;
- op->src.type = OP_SRC_ADD;
- op->src.reg = CFI_SP;
- op->src.offset = 8;
- op->dest.type = OP_DEST_REG;
- op->dest.reg = CFI_R10;
- break;
- }
-
- if (rex == 0x4c && modrm == 0x6c && sib == 0x24 &&
- insn.displacement.value == 16) {
-
- /*
- * lea 0x10(%rsp), %r13
- *
- * Here r13 is the "drap" pointer, used as a stack
- * pointer helper when the stack gets realigned.
- */
- *type = INSN_STACK;
- op->src.type = OP_SRC_ADD;
- op->src.reg = CFI_SP;
- op->src.offset = 16;
- op->dest.type = OP_DEST_REG;
- op->dest.reg = CFI_R13;
- break;
- }
-
- if (rex == 0x49 && modrm == 0x62 &&
- insn.displacement.value == -8) {
+ } else if (rex == 0x49 && modrm == 0x62 &&
+ insn.displacement.value == -8) {
/*
* lea -0x8(%r10), %rsp
@@ -356,11 +317,9 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
op->src.offset = -8;
op->dest.type = OP_DEST_REG;
op->dest.reg = CFI_SP;
- break;
- }
- if (rex == 0x49 && modrm == 0x65 &&
- insn.displacement.value == -16) {
+ } else if (rex == 0x49 && modrm == 0x65 &&
+ insn.displacement.value == -16) {
/*
* lea -0x10(%r13), %rsp
@@ -374,7 +333,6 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
op->src.offset = -16;
op->dest.type = OP_DEST_REG;
op->dest.reg = CFI_SP;
- break;
}
break;
@@ -406,20 +364,27 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
case 0x0f:
- if (op2 >= 0x80 && op2 <= 0x8f)
+ if (op2 >= 0x80 && op2 <= 0x8f) {
+
*type = INSN_JUMP_CONDITIONAL;
- else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
- op2 == 0x35)
+
+ } else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
+ op2 == 0x35) {
/* sysenter, sysret */
*type = INSN_CONTEXT_SWITCH;
- else if (op2 == 0x0d || op2 == 0x1f)
+ } else if (op2 == 0x0b || op2 == 0xb9) {
+
+ /* ud2 */
+ *type = INSN_BUG;
+
+ } else if (op2 == 0x0d || op2 == 0x1f) {
/* nopl/nopw */
*type = INSN_NOP;
- else if (op2 == 0xa0 || op2 == 0xa8) {
+ } else if (op2 == 0xa0 || op2 == 0xa8) {
/* push fs/gs */
*type = INSN_STACK;
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 365c34ecab26..57254f5b2779 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -29,7 +29,7 @@
#include "builtin.h"
#include "check.h"
-bool nofp;
+bool no_fp, no_unreachable;
static const char * const check_usage[] = {
"objtool check [<options>] file.o",
@@ -37,7 +37,8 @@ static const char * const check_usage[] = {
};
const struct option check_options[] = {
- OPT_BOOLEAN('f', "no-fp", &nofp, "Skip frame pointer validation"),
+ OPT_BOOLEAN('f', "no-fp", &no_fp, "Skip frame pointer validation"),
+ OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"),
OPT_END(),
};
@@ -52,5 +53,5 @@ int cmd_check(int argc, const char **argv)
objname = argv[0];
- return check(objname, nofp);
+ return check(objname, no_fp, no_unreachable, false);
}
diff --git a/tools/objtool/builtin-orc.c b/tools/objtool/builtin-orc.c
new file mode 100644
index 000000000000..4c6b5c9ef073
--- /dev/null
+++ b/tools/objtool/builtin-orc.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * objtool orc:
+ *
+ * This command analyzes a .o file and adds .orc_unwind and .orc_unwind_ip
+ * sections to it, which is used by the in-kernel ORC unwinder.
+ *
+ * This command is a superset of "objtool check".
+ */
+
+#include <string.h>
+#include <subcmd/parse-options.h>
+#include "builtin.h"
+#include "check.h"
+
+
+static const char *orc_usage[] = {
+ "objtool orc generate [<options>] file.o",
+ "objtool orc dump file.o",
+ NULL,
+};
+
+extern const struct option check_options[];
+extern bool no_fp, no_unreachable;
+
+int cmd_orc(int argc, const char **argv)
+{
+ const char *objname;
+
+ argc--; argv++;
+ if (!strncmp(argv[0], "gen", 3)) {
+ argc = parse_options(argc, argv, check_options, orc_usage, 0);
+ if (argc != 1)
+ usage_with_options(orc_usage, check_options);
+
+ objname = argv[0];
+
+ return check(objname, no_fp, no_unreachable, true);
+
+ }
+
+ if (!strcmp(argv[0], "dump")) {
+ if (argc != 2)
+ usage_with_options(orc_usage, check_options);
+
+ objname = argv[1];
+
+ return orc_dump(objname);
+ }
+
+ usage_with_options(orc_usage, check_options);
+
+ return 0;
+}
diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h
index 34d2ba78a616..dd526067fed5 100644
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -18,5 +18,6 @@
#define _BUILTIN_H
extern int cmd_check(int argc, const char **argv);
+extern int cmd_orc(int argc, const char **argv);
#endif /* _BUILTIN_H */
diff --git a/tools/objtool/cfi.h b/tools/objtool/cfi.h
index 443ab2c69992..2fe883c665c7 100644
--- a/tools/objtool/cfi.h
+++ b/tools/objtool/cfi.h
@@ -40,7 +40,7 @@
#define CFI_R14 14
#define CFI_R15 15
#define CFI_RA 16
-#define CFI_NUM_REGS 17
+#define CFI_NUM_REGS 17
struct cfi_reg {
int base;
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 2c6d74880403..f744617c9946 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -33,11 +33,11 @@ struct alternative {
};
const char *objname;
-static bool nofp;
+static bool no_fp;
struct cfi_state initial_func_cfi;
-static struct instruction *find_insn(struct objtool_file *file,
- struct section *sec, unsigned long offset)
+struct instruction *find_insn(struct objtool_file *file,
+ struct section *sec, unsigned long offset)
{
struct instruction *insn;
@@ -59,19 +59,6 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
return next;
}
-static bool gcov_enabled(struct objtool_file *file)
-{
- struct section *sec;
- struct symbol *sym;
-
- for_each_sec(file, sec)
- list_for_each_entry(sym, &sec->symbol_list, list)
- if (!strncmp(sym->name, "__gcov_.", 8))
- return true;
-
- return false;
-}
-
#define func_for_each_insn(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \
insn && &insn->list != &file->insn_list && \
@@ -100,7 +87,6 @@ static bool gcov_enabled(struct objtool_file *file)
static bool ignore_func(struct objtool_file *file, struct symbol *func)
{
struct rela *rela;
- struct instruction *insn;
/* check for STACK_FRAME_NON_STANDARD */
if (file->whitelist && file->whitelist->rela)
@@ -113,11 +99,6 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)
return true;
}
- /* check if it has a context switching instruction */
- func_for_each_insn(file, func, insn)
- if (insn->type == INSN_CONTEXT_SWITCH)
- return true;
-
return false;
}
@@ -237,9 +218,12 @@ static void clear_insn_state(struct insn_state *state)
memset(state, 0, sizeof(*state));
state->cfa.base = CFI_UNDEFINED;
- for (i = 0; i < CFI_NUM_REGS; i++)
+ for (i = 0; i < CFI_NUM_REGS; i++) {
state->regs[i].base = CFI_UNDEFINED;
+ state->vals[i].base = CFI_UNDEFINED;
+ }
state->drap_reg = CFI_UNDEFINED;
+ state->drap_offset = -1;
}
/*
@@ -259,6 +243,11 @@ static int decode_instructions(struct objtool_file *file)
if (!(sec->sh.sh_flags & SHF_EXECINSTR))
continue;
+ if (strcmp(sec->name, ".altinstr_replacement") &&
+ strcmp(sec->name, ".altinstr_aux") &&
+ strncmp(sec->name, ".discard.", 9))
+ sec->text = true;
+
for (offset = 0; offset < sec->len; offset += insn->len) {
insn = malloc(sizeof(*insn));
if (!insn) {
@@ -310,7 +299,7 @@ static int decode_instructions(struct objtool_file *file)
}
/*
- * Find all uses of the unreachable() macro, which are code path dead ends.
+ * Mark "ud2" instructions and manually annotated dead ends.
*/
static int add_dead_ends(struct objtool_file *file)
{
@@ -319,9 +308,20 @@ static int add_dead_ends(struct objtool_file *file)
struct instruction *insn;
bool found;
+ /*
+ * By default, "ud2" is a dead end unless otherwise annotated, because
+ * GCC 7 inserts it for certain divide-by-zero cases.
+ */
+ for_each_insn(file, insn)
+ if (insn->type == INSN_BUG)
+ insn->dead_end = true;
+
+ /*
+ * Check for manually annotated dead ends.
+ */
sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
if (!sec)
- return 0;
+ goto reachable;
list_for_each_entry(rela, &sec->rela_list, list) {
if (rela->sym->type != STT_SECTION) {
@@ -354,6 +354,48 @@ static int add_dead_ends(struct objtool_file *file)
insn->dead_end = true;
}
+reachable:
+ /*
+ * These manually annotated reachable checks are needed for GCC 4.4,
+ * where the Linux unreachable() macro isn't supported. In that case
+ * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
+ * not a dead end.
+ */
+ sec = find_section_by_name(file->elf, ".rela.discard.reachable");
+ if (!sec)
+ return 0;
+
+ list_for_each_entry(rela, &sec->rela_list, list) {
+ if (rela->sym->type != STT_SECTION) {
+ WARN("unexpected relocation symbol type in %s", sec->name);
+ return -1;
+ }
+ insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (insn)
+ insn = list_prev_entry(insn, list);
+ else if (rela->addend == rela->sym->sec->len) {
+ found = false;
+ list_for_each_entry_reverse(insn, &file->insn_list, list) {
+ if (insn->sec == rela->sym->sec) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ WARN("can't find reachable insn at %s+0x%x",
+ rela->sym->sec->name, rela->addend);
+ return -1;
+ }
+ } else {
+ WARN("can't find reachable insn at %s+0x%x",
+ rela->sym->sec->name, rela->addend);
+ return -1;
+ }
+
+ insn->dead_end = false;
+ }
+
return 0;
}
@@ -874,6 +916,99 @@ static int add_switch_table_alts(struct objtool_file *file)
return 0;
}
+static int read_unwind_hints(struct objtool_file *file)
+{
+ struct section *sec, *relasec;
+ struct rela *rela;
+ struct unwind_hint *hint;
+ struct instruction *insn;
+ struct cfi_reg *cfa;
+ int i;
+
+ sec = find_section_by_name(file->elf, ".discard.unwind_hints");
+ if (!sec)
+ return 0;
+
+ relasec = sec->rela;
+ if (!relasec) {
+ WARN("missing .rela.discard.unwind_hints section");
+ return -1;
+ }
+
+ if (sec->len % sizeof(struct unwind_hint)) {
+ WARN("struct unwind_hint size mismatch");
+ return -1;
+ }
+
+ file->hints = true;
+
+ for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
+ hint = (struct unwind_hint *)sec->data->d_buf + i;
+
+ rela = find_rela_by_dest(sec, i * sizeof(*hint));
+ if (!rela) {
+ WARN("can't find rela for unwind_hints[%d]", i);
+ return -1;
+ }
+
+ insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!insn) {
+ WARN("can't find insn for unwind_hints[%d]", i);
+ return -1;
+ }
+
+ cfa = &insn->state.cfa;
+
+ if (hint->type == UNWIND_HINT_TYPE_SAVE) {
+ insn->save = true;
+ continue;
+
+ } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
+ insn->restore = true;
+ insn->hint = true;
+ continue;
+ }
+
+ insn->hint = true;
+
+ switch (hint->sp_reg) {
+ case ORC_REG_UNDEFINED:
+ cfa->base = CFI_UNDEFINED;
+ break;
+ case ORC_REG_SP:
+ cfa->base = CFI_SP;
+ break;
+ case ORC_REG_BP:
+ cfa->base = CFI_BP;
+ break;
+ case ORC_REG_SP_INDIRECT:
+ cfa->base = CFI_SP_INDIRECT;
+ break;
+ case ORC_REG_R10:
+ cfa->base = CFI_R10;
+ break;
+ case ORC_REG_R13:
+ cfa->base = CFI_R13;
+ break;
+ case ORC_REG_DI:
+ cfa->base = CFI_DI;
+ break;
+ case ORC_REG_DX:
+ cfa->base = CFI_DX;
+ break;
+ default:
+ WARN_FUNC("unsupported unwind_hint sp base reg %d",
+ insn->sec, insn->offset, hint->sp_reg);
+ return -1;
+ }
+
+ cfa->offset = hint->sp_offset;
+ insn->state.type = hint->type;
+ }
+
+ return 0;
+}
+
static int decode_sections(struct objtool_file *file)
{
int ret;
@@ -904,6 +1039,10 @@ static int decode_sections(struct objtool_file *file)
if (ret)
return ret;
+ ret = read_unwind_hints(file);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -947,11 +1086,34 @@ static bool has_valid_stack_frame(struct insn_state *state)
return false;
}
+static int update_insn_state_regs(struct instruction *insn, struct insn_state *state)
+{
+ struct cfi_reg *cfa = &state->cfa;
+ struct stack_op *op = &insn->stack_op;
+
+ if (cfa->base != CFI_SP)
+ return 0;
+
+ /* push */
+ if (op->dest.type == OP_DEST_PUSH)
+ cfa->offset += 8;
+
+ /* pop */
+ if (op->src.type == OP_SRC_POP)
+ cfa->offset -= 8;
+
+ /* add immediate to sp */
+ if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
+ op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
+ cfa->offset -= op->src.offset;
+
+ return 0;
+}
+
static void save_reg(struct insn_state *state, unsigned char reg, int base,
int offset)
{
- if ((arch_callee_saved_reg(reg) ||
- (state->drap && reg == state->drap_reg)) &&
+ if (arch_callee_saved_reg(reg) &&
state->regs[reg].base == CFI_UNDEFINED) {
state->regs[reg].base = base;
state->regs[reg].offset = offset;
@@ -1032,30 +1194,56 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
return 0;
}
+ if (state->type == ORC_TYPE_REGS || state->type == ORC_TYPE_REGS_IRET)
+ return update_insn_state_regs(insn, state);
+
switch (op->dest.type) {
case OP_DEST_REG:
switch (op->src.type) {
case OP_SRC_REG:
- if (cfa->base == op->src.reg && cfa->base == CFI_SP &&
- op->dest.reg == CFI_BP && regs[CFI_BP].base == CFI_CFA &&
- regs[CFI_BP].offset == -cfa->offset) {
-
- /* mov %rsp, %rbp */
- cfa->base = op->dest.reg;
- state->bp_scratch = false;
- } else if (state->drap) {
-
- /* drap: mov %rsp, %rbp */
- regs[CFI_BP].base = CFI_BP;
- regs[CFI_BP].offset = -state->stack_size;
- state->bp_scratch = false;
- } else if (!nofp) {
-
- WARN_FUNC("unknown stack-related register move",
- insn->sec, insn->offset);
- return -1;
+ if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP) {
+
+ if (cfa->base == CFI_SP &&
+ regs[CFI_BP].base == CFI_CFA &&
+ regs[CFI_BP].offset == -cfa->offset) {
+
+ /* mov %rsp, %rbp */
+ cfa->base = op->dest.reg;
+ state->bp_scratch = false;
+ }
+
+ else if (state->drap) {
+
+ /* drap: mov %rsp, %rbp */
+ regs[CFI_BP].base = CFI_BP;
+ regs[CFI_BP].offset = -state->stack_size;
+ state->bp_scratch = false;
+ }
+ }
+
+ else if (op->dest.reg == cfa->base) {
+
+ /* mov %reg, %rsp */
+ if (cfa->base == CFI_SP &&
+ state->vals[op->src.reg].base == CFI_CFA) {
+
+ /*
+ * This is needed for the rare case
+ * where GCC does something dumb like:
+ *
+ * lea 0x8(%rsp), %rcx
+ * ...
+ * mov %rcx, %rsp
+ */
+ cfa->offset = -state->vals[op->src.reg].offset;
+ state->stack_size = cfa->offset;
+
+ } else {
+ cfa->base = CFI_UNDEFINED;
+ cfa->offset = 0;
+ }
}
break;
@@ -1077,11 +1265,25 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
break;
}
- if (op->dest.reg != CFI_BP && op->src.reg == CFI_SP &&
- cfa->base == CFI_SP) {
+ if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
/* drap: lea disp(%rsp), %drap */
state->drap_reg = op->dest.reg;
+
+ /*
+ * lea disp(%rsp), %reg
+ *
+ * This is needed for the rare case where GCC
+ * does something dumb like:
+ *
+ * lea 0x8(%rsp), %rcx
+ * ...
+ * mov %rcx, %rsp
+ */
+ state->vals[op->dest.reg].base = CFI_CFA;
+ state->vals[op->dest.reg].offset = \
+ -state->stack_size + op->src.offset;
+
break;
}
@@ -1118,7 +1320,6 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
cfa->base = state->drap_reg;
cfa->offset = state->stack_size = 0;
state->drap = true;
-
}
/*
@@ -1136,17 +1337,19 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
cfa->base = CFI_SP;
}
- if (regs[op->dest.reg].offset == -state->stack_size) {
+ if (state->drap && cfa->base == CFI_BP_INDIRECT &&
+ op->dest.type == OP_DEST_REG &&
+ op->dest.reg == state->drap_reg &&
+ state->drap_offset == -state->stack_size) {
- if (state->drap && cfa->base == CFI_BP_INDIRECT &&
- op->dest.type == OP_DEST_REG &&
- op->dest.reg == state->drap_reg) {
+ /* drap: pop %drap */
+ cfa->base = state->drap_reg;
+ cfa->offset = 0;
+ state->drap_offset = -1;
- /* drap: pop %drap */
- cfa->base = state->drap_reg;
- cfa->offset = 0;
- }
+ } else if (regs[op->dest.reg].offset == -state->stack_size) {
+ /* pop %reg */
restore_reg(state, op->dest.reg);
}
@@ -1158,14 +1361,18 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
case OP_SRC_REG_INDIRECT:
if (state->drap && op->src.reg == CFI_BP &&
+ op->src.offset == state->drap_offset) {
+
+ /* drap: mov disp(%rbp), %drap */
+ cfa->base = state->drap_reg;
+ cfa->offset = 0;
+ state->drap_offset = -1;
+ }
+
+ if (state->drap && op->src.reg == CFI_BP &&
op->src.offset == regs[op->dest.reg].offset) {
/* drap: mov disp(%rbp), %reg */
- if (op->dest.reg == state->drap_reg) {
- cfa->base = state->drap_reg;
- cfa->offset = 0;
- }
-
restore_reg(state, op->dest.reg);
} else if (op->src.reg == cfa->base &&
@@ -1201,8 +1408,8 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
cfa->base = CFI_BP_INDIRECT;
cfa->offset = -state->stack_size;
- /* save drap so we know when to undefine it */
- save_reg(state, op->src.reg, CFI_CFA, -state->stack_size);
+ /* save drap so we know when to restore it */
+ state->drap_offset = -state->stack_size;
} else if (op->src.reg == CFI_BP && cfa->base == state->drap_reg) {
@@ -1222,7 +1429,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
}
/* detect when asm code uses rbp as a scratch register */
- if (!nofp && insn->func && op->src.reg == CFI_BP &&
+ if (!no_fp && insn->func && op->src.reg == CFI_BP &&
cfa->base != CFI_BP)
state->bp_scratch = true;
break;
@@ -1236,8 +1443,8 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
cfa->base = CFI_BP_INDIRECT;
cfa->offset = op->dest.offset;
- /* save drap so we know when to undefine it */
- save_reg(state, op->src.reg, CFI_CFA, op->dest.offset);
+ /* save drap offset so we know when to restore it */
+ state->drap_offset = op->dest.offset;
}
else if (regs[op->src.reg].base == CFI_UNDEFINED) {
@@ -1323,12 +1530,17 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state)
break;
}
+ } else if (state1->type != state2->type) {
+ WARN_FUNC("stack state mismatch: type1=%d type2=%d",
+ insn->sec, insn->offset, state1->type, state2->type);
+
} else if (state1->drap != state2->drap ||
- (state1->drap && state1->drap_reg != state2->drap_reg)) {
- WARN_FUNC("stack state mismatch: drap1=%d(%d) drap2=%d(%d)",
+ (state1->drap && state1->drap_reg != state2->drap_reg) ||
+ (state1->drap && state1->drap_offset != state2->drap_offset)) {
+ WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
insn->sec, insn->offset,
- state1->drap, state1->drap_reg,
- state2->drap, state2->drap_reg);
+ state1->drap, state1->drap_reg, state1->drap_offset,
+ state2->drap, state2->drap_reg, state2->drap_offset);
} else
return true;
@@ -1346,7 +1558,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
struct insn_state state)
{
struct alternative *alt;
- struct instruction *insn;
+ struct instruction *insn, *next_insn;
struct section *sec;
struct symbol *func = NULL;
int ret;
@@ -1357,34 +1569,77 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (insn->alt_group && list_empty(&insn->alts)) {
WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
sec, insn->offset);
- return -1;
+ return 1;
}
while (1) {
- if (file->c_file && insn->func) {
- if (func && func != insn->func) {
- WARN("%s() falls through to next function %s()",
- func->name, insn->func->name);
- return 1;
- }
+ next_insn = next_insn_same_sec(file, insn);
+
+
+ if (file->c_file && func && insn->func && func != insn->func) {
+ WARN("%s() falls through to next function %s()",
+ func->name, insn->func->name);
+ return 1;
}
- func = insn->func;
+ if (insn->func)
+ func = insn->func;
if (func && insn->ignore) {
WARN_FUNC("BUG: why am I validating an ignored function?",
sec, insn->offset);
- return -1;
+ return 1;
}
if (insn->visited) {
- if (!!insn_state_match(insn, &state))
+ if (!insn->hint && !insn_state_match(insn, &state))
return 1;
return 0;
}
- insn->state = state;
+ if (insn->hint) {
+ if (insn->restore) {
+ struct instruction *save_insn, *i;
+
+ i = insn;
+ save_insn = NULL;
+ func_for_each_insn_continue_reverse(file, func, i) {
+ if (i->save) {
+ save_insn = i;
+ break;
+ }
+ }
+
+ if (!save_insn) {
+ WARN_FUNC("no corresponding CFI save for CFI restore",
+ sec, insn->offset);
+ return 1;
+ }
+
+ if (!save_insn->visited) {
+ /*
+ * Oops, no state to copy yet.
+ * Hopefully we can reach this
+ * instruction from another branch
+ * after the save insn has been
+ * visited.
+ */
+ if (insn == first)
+ return 0;
+
+ WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
+ sec, insn->offset);
+ return 1;
+ }
+
+ insn->state = save_insn->state;
+ }
+
+ state = insn->state;
+
+ } else
+ insn->state = state;
insn->visited = true;
@@ -1423,7 +1678,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
/* fallthrough */
case INSN_CALL_DYNAMIC:
- if (!nofp && func && !has_valid_stack_frame(&state)) {
+ if (!no_fp && func && !has_valid_stack_frame(&state)) {
WARN_FUNC("call without frame pointer save/setup",
sec, insn->offset);
return 1;
@@ -1461,9 +1716,17 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
return 0;
+ case INSN_CONTEXT_SWITCH:
+ if (func && (!next_insn || !next_insn->hint)) {
+ WARN_FUNC("unsupported instruction in callable function",
+ sec, insn->offset);
+ return 1;
+ }
+ return 0;
+
case INSN_STACK:
if (update_insn_state(insn, &state))
- return -1;
+ return 1;
break;
@@ -1474,7 +1737,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (insn->dead_end)
return 0;
- insn = next_insn_same_sec(file, insn);
+ insn = next_insn;
if (!insn) {
WARN("%s: unexpected end of section", sec->name);
return 1;
@@ -1484,6 +1747,27 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
return 0;
}
+static int validate_unwind_hints(struct objtool_file *file)
+{
+ struct instruction *insn;
+ int ret, warnings = 0;
+ struct insn_state state;
+
+ if (!file->hints)
+ return 0;
+
+ clear_insn_state(&state);
+
+ for_each_insn(file, insn) {
+ if (insn->hint && !insn->visited) {
+ ret = validate_branch(file, insn, state);
+ warnings += ret;
+ }
+ }
+
+ return warnings;
+}
+
static bool is_kasan_insn(struct instruction *insn)
{
return (insn->type == INSN_CALL &&
@@ -1507,8 +1791,13 @@ static bool ignore_unreachable_insn(struct instruction *insn)
/*
* Ignore any unused exceptions. This can happen when a whitelisted
* function has an exception table entry.
+ *
+ * Also ignore alternative replacement instructions. This can happen
+ * when a whitelisted function uses one of the ALTERNATIVE macros.
*/
- if (!strcmp(insn->sec->name, ".fixup"))
+ if (!strcmp(insn->sec->name, ".fixup") ||
+ !strcmp(insn->sec->name, ".altinstr_replacement") ||
+ !strcmp(insn->sec->name, ".altinstr_aux"))
return true;
/*
@@ -1580,15 +1869,6 @@ static int validate_reachable_instructions(struct objtool_file *file)
if (insn->visited || ignore_unreachable_insn(insn))
continue;
- /*
- * gcov produces a lot of unreachable instructions. If we get
- * an unreachable warning and the file has gcov enabled, just
- * ignore it, and all other such warnings for the file. Do
- * this here because this is an expensive function.
- */
- if (gcov_enabled(file))
- return 0;
-
WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
return 1;
}
@@ -1613,15 +1893,15 @@ static void cleanup(struct objtool_file *file)
elf_close(file->elf);
}
-int check(const char *_objname, bool _nofp)
+int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc)
{
struct objtool_file file;
int ret, warnings = 0;
objname = _objname;
- nofp = _nofp;
+ no_fp = _no_fp;
- file.elf = elf_open(objname);
+ file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY);
if (!file.elf)
return 1;
@@ -1629,8 +1909,9 @@ int check(const char *_objname, bool _nofp)
hash_init(file.insn_hash);
file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
file.rodata = find_section_by_name(file.elf, ".rodata");
- file.ignore_unreachables = false;
file.c_file = find_section_by_name(file.elf, ".comment");
+ file.ignore_unreachables = no_unreachable;
+ file.hints = false;
arch_initial_func_cfi_state(&initial_func_cfi);
@@ -1647,6 +1928,11 @@ int check(const char *_objname, bool _nofp)
goto out;
warnings += ret;
+ ret = validate_unwind_hints(&file);
+ if (ret < 0)
+ goto out;
+ warnings += ret;
+
if (!warnings) {
ret = validate_reachable_instructions(&file);
if (ret < 0)
@@ -1654,6 +1940,20 @@ int check(const char *_objname, bool _nofp)
warnings += ret;
}
+ if (orc) {
+ ret = create_orc(&file);
+ if (ret < 0)
+ goto out;
+
+ ret = create_orc_sections(&file);
+ if (ret < 0)
+ goto out;
+
+ ret = elf_write(file.elf);
+ if (ret < 0)
+ goto out;
+ }
+
out:
cleanup(&file);
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index da85f5b00ec6..47d9ea70a83d 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -22,15 +22,18 @@
#include "elf.h"
#include "cfi.h"
#include "arch.h"
+#include "orc.h"
#include <linux/hashtable.h>
struct insn_state {
struct cfi_reg cfa;
struct cfi_reg regs[CFI_NUM_REGS];
int stack_size;
+ unsigned char type;
bool bp_scratch;
bool drap;
- int drap_reg;
+ int drap_reg, drap_offset;
+ struct cfi_reg vals[CFI_NUM_REGS];
};
struct instruction {
@@ -41,13 +44,14 @@ struct instruction {
unsigned int len;
unsigned char type;
unsigned long immediate;
- bool alt_group, visited, dead_end, ignore;
+ bool alt_group, visited, dead_end, ignore, hint, save, restore;
struct symbol *call_dest;
struct instruction *jump_dest;
struct list_head alts;
struct symbol *func;
struct stack_op stack_op;
struct insn_state state;
+ struct orc_entry orc;
};
struct objtool_file {
@@ -55,12 +59,22 @@ struct objtool_file {
struct list_head insn_list;
DECLARE_HASHTABLE(insn_hash, 16);
struct section *rodata, *whitelist;
- bool ignore_unreachables, c_file;
+ bool ignore_unreachables, c_file, hints;
};
-int check(const char *objname, bool nofp);
+int check(const char *objname, bool no_fp, bool no_unreachable, bool orc);
+
+struct instruction *find_insn(struct objtool_file *file,
+ struct section *sec, unsigned long offset);
#define for_each_insn(file, insn) \
list_for_each_entry(insn, &file->insn_list, list)
+#define sec_for_each_insn(file, sec, insn) \
+ for (insn = find_insn(file, sec, 0); \
+ insn && &insn->list != &file->insn_list && \
+ insn->sec == sec; \
+ insn = list_next_entry(insn, list))
+
+
#endif /* _CHECK_H */
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 1a7e8aa2af58..6e9f980a7d26 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -30,16 +30,6 @@
#include "elf.h"
#include "warn.h"
-/*
- * Fallback for systems without this "read, mmaping if possible" cmd.
- */
-#ifndef ELF_C_READ_MMAP
-#define ELF_C_READ_MMAP ELF_C_READ
-#endif
-
-#define WARN_ELF(format, ...) \
- WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
-
struct section *find_section_by_name(struct elf *elf, const char *name)
{
struct section *sec;
@@ -349,9 +339,10 @@ static int read_relas(struct elf *elf)
return 0;
}
-struct elf *elf_open(const char *name)
+struct elf *elf_open(const char *name, int flags)
{
struct elf *elf;
+ Elf_Cmd cmd;
elf_version(EV_CURRENT);
@@ -364,13 +355,20 @@ struct elf *elf_open(const char *name)
INIT_LIST_HEAD(&elf->sections);
- elf->fd = open(name, O_RDONLY);
+ elf->fd = open(name, flags);
if (elf->fd == -1) {
perror("open");
goto err;
}
- elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL);
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ cmd = ELF_C_READ_MMAP;
+ else if ((flags & O_ACCMODE) == O_RDWR)
+ cmd = ELF_C_RDWR;
+ else /* O_WRONLY */
+ cmd = ELF_C_WRITE;
+
+ elf->elf = elf_begin(elf->fd, cmd, NULL);
if (!elf->elf) {
WARN_ELF("elf_begin");
goto err;
@@ -397,6 +395,194 @@ err:
return NULL;
}
+struct section *elf_create_section(struct elf *elf, const char *name,
+ size_t entsize, int nr)
+{
+ struct section *sec, *shstrtab;
+ size_t size = entsize * nr;
+ struct Elf_Scn *s;
+ Elf_Data *data;
+
+ sec = malloc(sizeof(*sec));
+ if (!sec) {
+ perror("malloc");
+ return NULL;
+ }
+ memset(sec, 0, sizeof(*sec));
+
+ INIT_LIST_HEAD(&sec->symbol_list);
+ INIT_LIST_HEAD(&sec->rela_list);
+ hash_init(sec->rela_hash);
+ hash_init(sec->symbol_hash);
+
+ list_add_tail(&sec->list, &elf->sections);
+
+ s = elf_newscn(elf->elf);
+ if (!s) {
+ WARN_ELF("elf_newscn");
+ return NULL;
+ }
+
+ sec->name = strdup(name);
+ if (!sec->name) {
+ perror("strdup");
+ return NULL;
+ }
+
+ sec->idx = elf_ndxscn(s);
+ sec->len = size;
+ sec->changed = true;
+
+ sec->data = elf_newdata(s);
+ if (!sec->data) {
+ WARN_ELF("elf_newdata");
+ return NULL;
+ }
+
+ sec->data->d_size = size;
+ sec->data->d_align = 1;
+
+ if (size) {
+ sec->data->d_buf = malloc(size);
+ if (!sec->data->d_buf) {
+ perror("malloc");
+ return NULL;
+ }
+ memset(sec->data->d_buf, 0, size);
+ }
+
+ if (!gelf_getshdr(s, &sec->sh)) {
+ WARN_ELF("gelf_getshdr");
+ return NULL;
+ }
+
+ sec->sh.sh_size = size;
+ sec->sh.sh_entsize = entsize;
+ sec->sh.sh_type = SHT_PROGBITS;
+ sec->sh.sh_addralign = 1;
+ sec->sh.sh_flags = SHF_ALLOC;
+
+
+ /* Add section name to .shstrtab */
+ shstrtab = find_section_by_name(elf, ".shstrtab");
+ if (!shstrtab) {
+ WARN("can't find .shstrtab section");
+ return NULL;
+ }
+
+ s = elf_getscn(elf->elf, shstrtab->idx);
+ if (!s) {
+ WARN_ELF("elf_getscn");
+ return NULL;
+ }
+
+ data = elf_newdata(s);
+ if (!data) {
+ WARN_ELF("elf_newdata");
+ return NULL;
+ }
+
+ data->d_buf = sec->name;
+ data->d_size = strlen(name) + 1;
+ data->d_align = 1;
+
+ sec->sh.sh_name = shstrtab->len;
+
+ shstrtab->len += strlen(name) + 1;
+ shstrtab->changed = true;
+
+ return sec;
+}
+
+struct section *elf_create_rela_section(struct elf *elf, struct section *base)
+{
+ char *relaname;
+ struct section *sec;
+
+ relaname = malloc(strlen(base->name) + strlen(".rela") + 1);
+ if (!relaname) {
+ perror("malloc");
+ return NULL;
+ }
+ strcpy(relaname, ".rela");
+ strcat(relaname, base->name);
+
+ sec = elf_create_section(elf, relaname, sizeof(GElf_Rela), 0);
+ if (!sec)
+ return NULL;
+
+ base->rela = sec;
+ sec->base = base;
+
+ sec->sh.sh_type = SHT_RELA;
+ sec->sh.sh_addralign = 8;
+ sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
+ sec->sh.sh_info = base->idx;
+ sec->sh.sh_flags = SHF_INFO_LINK;
+
+ return sec;
+}
+
+int elf_rebuild_rela_section(struct section *sec)
+{
+ struct rela *rela;
+ int nr, idx = 0, size;
+ GElf_Rela *relas;
+
+ nr = 0;
+ list_for_each_entry(rela, &sec->rela_list, list)
+ nr++;
+
+ size = nr * sizeof(*relas);
+ relas = malloc(size);
+ if (!relas) {
+ perror("malloc");
+ return -1;
+ }
+
+ sec->data->d_buf = relas;
+ sec->data->d_size = size;
+
+ sec->sh.sh_size = size;
+
+ idx = 0;
+ list_for_each_entry(rela, &sec->rela_list, list) {
+ relas[idx].r_offset = rela->offset;
+ relas[idx].r_addend = rela->addend;
+ relas[idx].r_info = GELF_R_INFO(rela->sym->idx, rela->type);
+ idx++;
+ }
+
+ return 0;
+}
+
+int elf_write(struct elf *elf)
+{
+ struct section *sec;
+ Elf_Scn *s;
+
+ list_for_each_entry(sec, &elf->sections, list) {
+ if (sec->changed) {
+ s = elf_getscn(elf->elf, sec->idx);
+ if (!s) {
+ WARN_ELF("elf_getscn");
+ return -1;
+ }
+ if (!gelf_update_shdr (s, &sec->sh)) {
+ WARN_ELF("gelf_update_shdr");
+ return -1;
+ }
+ }
+ }
+
+ if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
+ WARN_ELF("elf_update");
+ return -1;
+ }
+
+ return 0;
+}
+
void elf_close(struct elf *elf)
{
struct section *sec, *tmpsec;
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index 343968b778cb..d86e2ff14466 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -28,6 +28,13 @@
# define elf_getshdrstrndx elf_getshstrndx
#endif
+/*
+ * Fallback for systems without this "read, mmaping if possible" cmd.
+ */
+#ifndef ELF_C_READ_MMAP
+#define ELF_C_READ_MMAP ELF_C_READ
+#endif
+
struct section {
struct list_head list;
GElf_Shdr sh;
@@ -41,6 +48,7 @@ struct section {
char *name;
int idx;
unsigned int len;
+ bool changed, text;
};
struct symbol {
@@ -75,7 +83,7 @@ struct elf {
};
-struct elf *elf_open(const char *name);
+struct elf *elf_open(const char *name, int flags);
struct section *find_section_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
@@ -83,6 +91,11 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
unsigned int len);
struct symbol *find_containing_func(struct section *sec, unsigned long offset);
+struct section *elf_create_section(struct elf *elf, const char *name, size_t
+ entsize, int nr);
+struct section *elf_create_rela_section(struct elf *elf, struct section *base);
+int elf_rebuild_rela_section(struct section *sec);
+int elf_write(struct elf *elf);
void elf_close(struct elf *elf);
#define for_each_sec(file, sec) \
diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c
index ecc5b1b5d15d..31e0f9143840 100644
--- a/tools/objtool/objtool.c
+++ b/tools/objtool/objtool.c
@@ -42,10 +42,11 @@ struct cmd_struct {
};
static const char objtool_usage_string[] =
- "objtool [OPTIONS] COMMAND [ARGS]";
+ "objtool COMMAND [ARGS]";
static struct cmd_struct objtool_cmds[] = {
{"check", cmd_check, "Perform stack metadata validation on an object file" },
+ {"orc", cmd_orc, "Generate in-place ORC unwind tables for an object file" },
};
bool help;
diff --git a/tools/objtool/orc.h b/tools/objtool/orc.h
new file mode 100644
index 000000000000..a4139e386ef3
--- /dev/null
+++ b/tools/objtool/orc.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ORC_H
+#define _ORC_H
+
+#include "orc_types.h"
+
+struct objtool_file;
+
+int create_orc(struct objtool_file *file);
+int create_orc_sections(struct objtool_file *file);
+
+int orc_dump(const char *objname);
+
+#endif /* _ORC_H */
diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
new file mode 100644
index 000000000000..36c5bf6a2675
--- /dev/null
+++ b/tools/objtool/orc_dump.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include "orc.h"
+#include "warn.h"
+
+static const char *reg_name(unsigned int reg)
+{
+ switch (reg) {
+ case ORC_REG_PREV_SP:
+ return "prevsp";
+ case ORC_REG_DX:
+ return "dx";
+ case ORC_REG_DI:
+ return "di";
+ case ORC_REG_BP:
+ return "bp";
+ case ORC_REG_SP:
+ return "sp";
+ case ORC_REG_R10:
+ return "r10";
+ case ORC_REG_R13:
+ return "r13";
+ case ORC_REG_BP_INDIRECT:
+ return "bp(ind)";
+ case ORC_REG_SP_INDIRECT:
+ return "sp(ind)";
+ default:
+ return "?";
+ }
+}
+
+static const char *orc_type_name(unsigned int type)
+{
+ switch (type) {
+ case ORC_TYPE_CALL:
+ return "call";
+ case ORC_TYPE_REGS:
+ return "regs";
+ case ORC_TYPE_REGS_IRET:
+ return "iret";
+ default:
+ return "?";
+ }
+}
+
+static void print_reg(unsigned int reg, int offset)
+{
+ if (reg == ORC_REG_BP_INDIRECT)
+ printf("(bp%+d)", offset);
+ else if (reg == ORC_REG_SP_INDIRECT)
+ printf("(sp%+d)", offset);
+ else if (reg == ORC_REG_UNDEFINED)
+ printf("(und)");
+ else
+ printf("%s%+d", reg_name(reg), offset);
+}
+
+int orc_dump(const char *_objname)
+{
+ int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
+ struct orc_entry *orc = NULL;
+ char *name;
+ unsigned long nr_sections, orc_ip_addr = 0;
+ size_t shstrtab_idx;
+ Elf *elf;
+ Elf_Scn *scn;
+ GElf_Shdr sh;
+ GElf_Rela rela;
+ GElf_Sym sym;
+ Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
+
+
+ objname = _objname;
+
+ elf_version(EV_CURRENT);
+
+ fd = open(objname, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ return -1;
+ }
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (!elf) {
+ WARN_ELF("elf_begin");
+ return -1;
+ }
+
+ if (elf_getshdrnum(elf, &nr_sections)) {
+ WARN_ELF("elf_getshdrnum");
+ return -1;
+ }
+
+ if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
+ WARN_ELF("elf_getshdrstrndx");
+ return -1;
+ }
+
+ for (i = 0; i < nr_sections; i++) {
+ scn = elf_getscn(elf, i);
+ if (!scn) {
+ WARN_ELF("elf_getscn");
+ return -1;
+ }
+
+ if (!gelf_getshdr(scn, &sh)) {
+ WARN_ELF("gelf_getshdr");
+ return -1;
+ }
+
+ name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
+ if (!name) {
+ WARN_ELF("elf_strptr");
+ return -1;
+ }
+
+ data = elf_getdata(scn, NULL);
+ if (!data) {
+ WARN_ELF("elf_getdata");
+ return -1;
+ }
+
+ if (!strcmp(name, ".symtab")) {
+ symtab = data;
+ } else if (!strcmp(name, ".orc_unwind")) {
+ orc = data->d_buf;
+ orc_size = sh.sh_size;
+ } else if (!strcmp(name, ".orc_unwind_ip")) {
+ orc_ip = data->d_buf;
+ orc_ip_addr = sh.sh_addr;
+ } else if (!strcmp(name, ".rela.orc_unwind_ip")) {
+ rela_orc_ip = data;
+ }
+ }
+
+ if (!symtab || !orc || !orc_ip)
+ return 0;
+
+ if (orc_size % sizeof(*orc) != 0) {
+ WARN("bad .orc_unwind section size");
+ return -1;
+ }
+
+ nr_entries = orc_size / sizeof(*orc);
+ for (i = 0; i < nr_entries; i++) {
+ if (rela_orc_ip) {
+ if (!gelf_getrela(rela_orc_ip, i, &rela)) {
+ WARN_ELF("gelf_getrela");
+ return -1;
+ }
+
+ if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
+ WARN_ELF("gelf_getsym");
+ return -1;
+ }
+
+ scn = elf_getscn(elf, sym.st_shndx);
+ if (!scn) {
+ WARN_ELF("elf_getscn");
+ return -1;
+ }
+
+ if (!gelf_getshdr(scn, &sh)) {
+ WARN_ELF("gelf_getshdr");
+ return -1;
+ }
+
+ name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
+ if (!name || !*name) {
+ WARN_ELF("elf_strptr");
+ return -1;
+ }
+
+ printf("%s+%lx:", name, rela.r_addend);
+
+ } else {
+ printf("%lx:", orc_ip_addr + (i * sizeof(int)) + orc_ip[i]);
+ }
+
+
+ printf(" sp:");
+
+ print_reg(orc[i].sp_reg, orc[i].sp_offset);
+
+ printf(" bp:");
+
+ print_reg(orc[i].bp_reg, orc[i].bp_offset);
+
+ printf(" type:%s\n", orc_type_name(orc[i].type));
+ }
+
+ elf_end(elf);
+ close(fd);
+
+ return 0;
+}
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
new file mode 100644
index 000000000000..e5ca31429c9b
--- /dev/null
+++ b/tools/objtool/orc_gen.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "orc.h"
+#include "check.h"
+#include "warn.h"
+
+int create_orc(struct objtool_file *file)
+{
+ struct instruction *insn;
+
+ for_each_insn(file, insn) {
+ struct orc_entry *orc = &insn->orc;
+ struct cfi_reg *cfa = &insn->state.cfa;
+ struct cfi_reg *bp = &insn->state.regs[CFI_BP];
+
+ if (cfa->base == CFI_UNDEFINED) {
+ orc->sp_reg = ORC_REG_UNDEFINED;
+ continue;
+ }
+
+ switch (cfa->base) {
+ case CFI_SP:
+ orc->sp_reg = ORC_REG_SP;
+ break;
+ case CFI_SP_INDIRECT:
+ orc->sp_reg = ORC_REG_SP_INDIRECT;
+ break;
+ case CFI_BP:
+ orc->sp_reg = ORC_REG_BP;
+ break;
+ case CFI_BP_INDIRECT:
+ orc->sp_reg = ORC_REG_BP_INDIRECT;
+ break;
+ case CFI_R10:
+ orc->sp_reg = ORC_REG_R10;
+ break;
+ case CFI_R13:
+ orc->sp_reg = ORC_REG_R13;
+ break;
+ case CFI_DI:
+ orc->sp_reg = ORC_REG_DI;
+ break;
+ case CFI_DX:
+ orc->sp_reg = ORC_REG_DX;
+ break;
+ default:
+ WARN_FUNC("unknown CFA base reg %d",
+ insn->sec, insn->offset, cfa->base);
+ return -1;
+ }
+
+ switch(bp->base) {
+ case CFI_UNDEFINED:
+ orc->bp_reg = ORC_REG_UNDEFINED;
+ break;
+ case CFI_CFA:
+ orc->bp_reg = ORC_REG_PREV_SP;
+ break;
+ case CFI_BP:
+ orc->bp_reg = ORC_REG_BP;
+ break;
+ default:
+ WARN_FUNC("unknown BP base reg %d",
+ insn->sec, insn->offset, bp->base);
+ return -1;
+ }
+
+ orc->sp_offset = cfa->offset;
+ orc->bp_offset = bp->offset;
+ orc->type = insn->state.type;
+ }
+
+ return 0;
+}
+
+static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
+ unsigned int idx, struct section *insn_sec,
+ unsigned long insn_off, struct orc_entry *o)
+{
+ struct orc_entry *orc;
+ struct rela *rela;
+
+ /* populate ORC data */
+ orc = (struct orc_entry *)u_sec->data->d_buf + idx;
+ memcpy(orc, o, sizeof(*orc));
+
+ /* populate rela for ip */
+ rela = malloc(sizeof(*rela));
+ if (!rela) {
+ perror("malloc");
+ return -1;
+ }
+ memset(rela, 0, sizeof(*rela));
+
+ rela->sym = insn_sec->sym;
+ rela->addend = insn_off;
+ rela->type = R_X86_64_PC32;
+ rela->offset = idx * sizeof(int);
+
+ list_add_tail(&rela->list, &ip_relasec->rela_list);
+ hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
+
+ return 0;
+}
+
+int create_orc_sections(struct objtool_file *file)
+{
+ struct instruction *insn, *prev_insn;
+ struct section *sec, *u_sec, *ip_relasec;
+ unsigned int idx;
+
+ struct orc_entry empty = {
+ .sp_reg = ORC_REG_UNDEFINED,
+ .bp_reg = ORC_REG_UNDEFINED,
+ .type = ORC_TYPE_CALL,
+ };
+
+ sec = find_section_by_name(file->elf, ".orc_unwind");
+ if (sec) {
+ WARN("file already has .orc_unwind section, skipping");
+ return -1;
+ }
+
+ /* count the number of needed orcs */
+ idx = 0;
+ for_each_sec(file, sec) {
+ if (!sec->text)
+ continue;
+
+ prev_insn = NULL;
+ sec_for_each_insn(file, sec, insn) {
+ if (!prev_insn ||
+ memcmp(&insn->orc, &prev_insn->orc,
+ sizeof(struct orc_entry))) {
+ idx++;
+ }
+ prev_insn = insn;
+ }
+
+ /* section terminator */
+ if (prev_insn)
+ idx++;
+ }
+ if (!idx)
+ return -1;
+
+
+ /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
+ sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
+
+ ip_relasec = elf_create_rela_section(file->elf, sec);
+ if (!ip_relasec)
+ return -1;
+
+ /* create .orc_unwind section */
+ u_sec = elf_create_section(file->elf, ".orc_unwind",
+ sizeof(struct orc_entry), idx);
+
+ /* populate sections */
+ idx = 0;
+ for_each_sec(file, sec) {
+ if (!sec->text)
+ continue;
+
+ prev_insn = NULL;
+ sec_for_each_insn(file, sec, insn) {
+ if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
+ sizeof(struct orc_entry))) {
+
+ if (create_orc_entry(u_sec, ip_relasec, idx,
+ insn->sec, insn->offset,
+ &insn->orc))
+ return -1;
+
+ idx++;
+ }
+ prev_insn = insn;
+ }
+
+ /* section terminator */
+ if (prev_insn) {
+ if (create_orc_entry(u_sec, ip_relasec, idx,
+ prev_insn->sec,
+ prev_insn->offset + prev_insn->len,
+ &empty))
+ return -1;
+
+ idx++;
+ }
+ }
+
+ if (elf_rebuild_rela_section(ip_relasec))
+ return -1;
+
+ return 0;
+}
diff --git a/tools/objtool/orc_types.h b/tools/objtool/orc_types.h
new file mode 100644
index 000000000000..9c9dc579bd7d
--- /dev/null
+++ b/tools/objtool/orc_types.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ORC_TYPES_H
+#define _ORC_TYPES_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+
+/*
+ * The ORC_REG_* registers are base registers which are used to find other
+ * registers on the stack.
+ *
+ * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
+ * address of the previous frame: the caller's SP before it called the current
+ * function.
+ *
+ * ORC_REG_UNDEFINED means the corresponding register's value didn't change in
+ * the current frame.
+ *
+ * The most commonly used base registers are SP and BP -- which the previous SP
+ * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
+ * usually based on.
+ *
+ * The rest of the base registers are needed for special cases like entry code
+ * and GCC realigned stacks.
+ */
+#define ORC_REG_UNDEFINED 0
+#define ORC_REG_PREV_SP 1
+#define ORC_REG_DX 2
+#define ORC_REG_DI 3
+#define ORC_REG_BP 4
+#define ORC_REG_SP 5
+#define ORC_REG_R10 6
+#define ORC_REG_R13 7
+#define ORC_REG_BP_INDIRECT 8
+#define ORC_REG_SP_INDIRECT 9
+#define ORC_REG_MAX 15
+
+/*
+ * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
+ * caller's SP right before it made the call). Used for all callable
+ * functions, i.e. all C code and all callable asm functions.
+ *
+ * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
+ * to a fully populated pt_regs from a syscall, interrupt, or exception.
+ *
+ * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
+ * points to the iret return frame.
+ *
+ * The UNWIND_HINT macros are used only for the unwind_hint struct. They
+ * aren't used in struct orc_entry due to size and complexity constraints.
+ * Objtool converts them to real types when it converts the hints to orc
+ * entries.
+ */
+#define ORC_TYPE_CALL 0
+#define ORC_TYPE_REGS 1
+#define ORC_TYPE_REGS_IRET 2
+#define UNWIND_HINT_TYPE_SAVE 3
+#define UNWIND_HINT_TYPE_RESTORE 4
+
+#ifndef __ASSEMBLY__
+/*
+ * This struct is more or less a vastly simplified version of the DWARF Call
+ * Frame Information standard. It contains only the necessary parts of DWARF
+ * CFI, simplified for ease of access by the in-kernel unwinder. It tells the
+ * unwinder how to find the previous SP and BP (and sometimes entry regs) on
+ * the stack for a given code address. Each instance of the struct corresponds
+ * to one or more code locations.
+ */
+struct orc_entry {
+ s16 sp_offset;
+ s16 bp_offset;
+ unsigned sp_reg:4;
+ unsigned bp_reg:4;
+ unsigned type:2;
+} __packed;
+
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack for the ORC unwinder.
+ *
+ * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
+ */
+struct unwind_hint {
+ u32 ip;
+ s16 sp_offset;
+ u8 sp_reg;
+ u8 type;
+};
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ORC_TYPES_H */
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c
index b4967d875236..f249e042b3b5 100644
--- a/tools/testing/selftests/x86/fsgsbase.c
+++ b/tools/testing/selftests/x86/fsgsbase.c
@@ -285,9 +285,12 @@ static void *threadproc(void *ctx)
}
}
-static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
+static void set_gs_and_switch_to(unsigned long local,
+ unsigned short force_sel,
+ unsigned long remote)
{
unsigned long base;
+ unsigned short sel_pre_sched, sel_post_sched;
bool hard_zero = false;
if (local == HARD_ZERO) {
@@ -297,6 +300,8 @@ static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
printf("[RUN]\tARCH_SET_GS(0x%lx)%s, then schedule to 0x%lx\n",
local, hard_zero ? " and clear gs" : "", remote);
+ if (force_sel)
+ printf("\tBefore schedule, set selector to 0x%hx\n", force_sel);
if (syscall(SYS_arch_prctl, ARCH_SET_GS, local) != 0)
err(1, "ARCH_SET_GS");
if (hard_zero)
@@ -307,18 +312,35 @@ static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
printf("[FAIL]\tGSBASE wasn't set as expected\n");
}
+ if (force_sel) {
+ asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
+ sel_pre_sched = force_sel;
+ local = read_base(GS);
+
+ /*
+ * Signal delivery seems to mess up weird selectors. Put it
+ * back.
+ */
+ asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
+ } else {
+ asm volatile ("mov %%gs, %0" : "=rm" (sel_pre_sched));
+ }
+
remote_base = remote;
ftx = 1;
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
while (ftx != 0)
syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
+ asm volatile ("mov %%gs, %0" : "=rm" (sel_post_sched));
base = read_base(GS);
- if (base == local) {
- printf("[OK]\tGSBASE remained 0x%lx\n", local);
+ if (base == local && sel_pre_sched == sel_post_sched) {
+ printf("[OK]\tGS/BASE remained 0x%hx/0x%lx\n",
+ sel_pre_sched, local);
} else {
nerrs++;
- printf("[FAIL]\tGSBASE changed to 0x%lx\n", base);
+ printf("[FAIL]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx\n",
+ sel_pre_sched, local, sel_post_sched, base);
}
}
@@ -381,8 +403,15 @@ int main()
for (int local = 0; local < 4; local++) {
for (int remote = 0; remote < 4; remote++) {
- set_gs_and_switch_to(bases_with_hard_zero[local],
- bases_with_hard_zero[remote]);
+ for (unsigned short s = 0; s < 5; s++) {
+ unsigned short sel = s;
+ if (s == 4)
+ asm ("mov %%ss, %0" : "=rm" (sel));
+ set_gs_and_switch_to(
+ bases_with_hard_zero[local],
+ sel,
+ bases_with_hard_zero[remote]);
+ }
}
}