diff options
260 files changed, 44516 insertions, 180 deletions
diff --git a/.gitignore b/.gitignore index cdd6aad97..bbd4d1706 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,5 @@ pc-bios/bios-pq/status pc-bios/vgabios-pq/status pc-bios/optionrom/multiboot.bin pc-bios/optionrom/multiboot.raw +pc-bios/optionrom/extboot.bin .stgit-* @@ -63,6 +63,18 @@ config-host.h-timestamp: config-host.mak SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) +ifeq ($(KVM_KMOD),yes) + +.PHONEY: kvm-kmod + +all: kvm-kmod + +kvm-kmod: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C kvm/kernel V="$(V)" ) + + +endif + subdir-%: $(GENERATED_HEADERS) $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) @@ -94,6 +106,7 @@ block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o block-obj-y += nbd.o block.o aio.o aes.o osdep.o block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o +block-obj-$(CONFIG_POSIX) += compatfd.o block-nested-y += cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o @@ -283,6 +296,8 @@ pxe-ne2k_pci.bin pxe-pcnet.bin \ pxe-rtl8139.bin pxe-virtio.bin \ bamboo.dtb petalogix-s3adsp1800.dtb \ multiboot.bin linuxboot.bin +BLOBS += extboot.bin +BLOBS += vapic.bin else BLOBS= endif @@ -305,7 +320,12 @@ endif ifneq ($(BLOBS),) $(INSTALL_DIR) "$(DESTDIR)$(datadir)" set -e; for x in $(BLOBS); do \ + if [ -f $(SRC_PATH)/pc-bios/$$x ];then \ $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ + fi \ + ; if [ -f pc-bios/optionrom/$$x ];then \ + $(INSTALL_DATA) pc-bios/optionrom/$$x "$(DESTDIR)$(datadir)"; \ + fi \ done endif $(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps" @@ -315,6 +335,9 @@ endif for d in $(TARGET_DIRS); do \ $(MAKE) -C $$d $@ || exit 1 ; \ done +ifeq ($(KVM_KMOD),yes) + $(MAKE) -C kvm/kernel $@ +endif # various test targets test speed: all @@ -435,6 +458,7 @@ tarbin: $(datadir)/pxe-rtl8139.bin \ $(datadir)/pxe-pcnet.bin \ $(datadir)/pxe-e1000.bin \ + $(datadir)/extboot.bin \ $(docdir)/qemu-doc.html \ $(docdir)/qemu-tech.html \ $(mandir)/man1/qemu.1 \ diff --git a/Makefile.hw b/Makefile.hw index bd252f5ed..6f4dbc40d 100644 --- a/Makefile.hw +++ b/Makefile.hw @@ -25,7 +25,9 @@ obj-$(CONFIG_ESCC) += escc.o # PCI watchdog devices obj-y += wdt_i6300esb.o -obj-y += msix.o +# MSI-X depends on kvm for interrupt injection, +# so moved it from Makefile.hw to Makefile.target for now +# obj-y += msix.o # PCI network cards obj-y += ne2000.o diff --git a/Makefile.target b/Makefile.target index 7c1f30c1a..6037fed49 100644 --- a/Makefile.target +++ b/Makefile.target @@ -30,6 +30,8 @@ LIBS+=-lm kvm.o kvm-all.o: QEMU_CFLAGS+=$(KVM_CFLAGS) +CFLAGS += $(KVM_CFLAGS) + config-target.h: config-target.h-timestamp config-target.h-timestamp: config-target.mak @@ -40,12 +42,18 @@ all: $(PROGS) ######################################################### # cpu emulator library -libobj-y = exec.o translate-all.o cpu-exec.o translate.o -libobj-y += tcg/tcg.o +libobj-y = exec.o cpu-exec.o +libobj-$(CONFIG_NO_CPU_EMULATION) += fake-exec.o +libobj-$(CONFIG_CPU_EMULATION) += translate-all.o translate.o +libobj-$(CONFIG_CPU_EMULATION) += tcg/tcg.o libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o libobj-y += op_helper.o helper.o libobj-$(CONFIG_NEED_MMU) += mmu.o + +libobj-$(CONFIG_KVM) += kvm-tpr-opt.o +libobj-$(CONFIG_KVM) += qemu-kvm-helper.o + libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o libobj-$(TARGET_ALPHA) += alpha_palcode.o @@ -82,6 +90,8 @@ op_helper.o cpu-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) # cpu_signal_handler() in cpu-exec.c. signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS) +qemu-kvm-helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) + ######################################################### # Linux user emulator target @@ -158,6 +168,10 @@ obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o # need to fix this properly obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o +# MSI-X depends on kvm for interrupt injection, +# so moved it from Makefile.hw to Makefile.target for now +obj-y += msix.o + obj-$(CONFIG_ISA_MMIO) += isa_mmio.o LIBS+=-lz @@ -194,12 +208,25 @@ obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o +obj-i386-y += extboot.o obj-i386-y += ne2000-isa.o +obj-i386-y += testdev.o + +obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o +obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o + +# Hardware support +obj-ia64-y += ide.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) +obj-ia64-y += fdc.o mc146818rtc.o serial.o i8259.o ipf.o +obj-ia64-y += cirrus_vga.o parallel.o acpi.o piix_pci.o +obj-ia64-y += usb-uhci.o +obj-ia64-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o # shared objects obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o obj-ppc-y += ide/cmd646.o obj-ppc-y += vga.o vga-pci.o $(sound-obj-y) dma.o openpic.o +obj-ppc-y += cirrus_vga.o # PREP target obj-ppc-y += pckbd.o serial.o i8259.o i8254.o fdc.o mc146818rtc.o obj-ppc-y += prep_pci.o ppc_prep.o ne2000-isa.o @@ -295,6 +322,11 @@ obj-m68k-y += m68k-semi.o dummy_m68k.o obj-s390x-y = s390-virtio-bus.o s390-virtio.o +ifeq ($(TARGET_ARCH), ia64) +firmware.o: firmware.c + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< +endif + main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) diff --git a/block/raw-posix.c b/block/raw-posix.c index 248e34cba..c204cf94e 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -27,6 +27,8 @@ #include "qemu-log.h" #include "block_int.h" #include "module.h" +#include "compatfd.h" +#include <assert.h> #include "block/raw-posix-aio.h" #ifdef CONFIG_COCOA diff --git a/cache-utils.h b/cache-utils.h index b45fde44e..e4f27ef51 100644 --- a/cache-utils.h +++ b/cache-utils.h @@ -34,7 +34,28 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop) asm volatile ("isync" : : : "memory"); } +/* + * Is this correct for PPC? + */ +static inline void dma_flush_range(unsigned long start, unsigned long stop) +{ +} + +#elif defined(__ia64__) +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + while (start < stop) { + asm volatile ("fc %0" :: "r"(start)); + start += 32; + } + asm volatile (";;sync.i;;srlz.i;;"); +} +#define dma_flush_range(start, end) flush_icache_range(start, end) +#define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) #else +static inline void dma_flush_range(unsigned long start, unsigned long stop) +{ +} #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) #endif diff --git a/compat/sys/eventfd.h b/compat/sys/eventfd.h new file mode 100644 index 000000000..f55d96adb --- /dev/null +++ b/compat/sys/eventfd.h @@ -0,0 +1,13 @@ +#ifndef _COMPAT_SYS_EVENTFD +#define _COMPAT_SYS_EVENTFD + +#include <unistd.h> +#include <syscall.h> + + +static inline int eventfd (int count, int flags) +{ + return syscall(SYS_eventfd, count, flags); +} + +#endif diff --git a/compatfd.c b/compatfd.c new file mode 100644 index 000000000..4a00d95a3 --- /dev/null +++ b/compatfd.c @@ -0,0 +1,143 @@ +/* + * signalfd/eventfd compatibility + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "compatfd.h" + +#include <sys/syscall.h> +#include <pthread.h> + +struct sigfd_compat_info +{ + sigset_t mask; + int fd; +}; + +static void *sigwait_compat(void *opaque) +{ + struct sigfd_compat_info *info = opaque; + int err; + sigset_t all; + + sigfillset(&all); + sigprocmask(SIG_BLOCK, &all, NULL); + + do { + siginfo_t siginfo; + + err = sigwaitinfo(&info->mask, &siginfo); + if (err == -1 && errno == EINTR) { + err = 0; + continue; + } + + if (err > 0) { + char buffer[128]; + size_t offset = 0; + + memcpy(buffer, &err, sizeof(err)); + while (offset < sizeof(buffer)) { + ssize_t len; + + len = write(info->fd, buffer + offset, + sizeof(buffer) - offset); + if (len == -1 && errno == EINTR) + continue; + + if (len <= 0) { + err = -1; + break; + } + + offset += len; + } + } + } while (err >= 0); + + return NULL; +} + +static int qemu_signalfd_compat(const sigset_t *mask) +{ + pthread_attr_t attr; + pthread_t tid; + struct sigfd_compat_info *info; + int fds[2]; + + info = malloc(sizeof(*info)); + if (info == NULL) { + errno = ENOMEM; + return -1; + } + + if (pipe(fds) == -1) { + free(info); + return -1; + } + + qemu_set_cloexec(fds[0]); + qemu_set_cloexec(fds[1]); + + memcpy(&info->mask, mask, sizeof(*mask)); + info->fd = fds[1]; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&tid, &attr, sigwait_compat, info); + + pthread_attr_destroy(&attr); + + return fds[0]; +} + +int qemu_signalfd(const sigset_t *mask) +{ +#if defined(CONFIG_SIGNALFD) + int ret; + + ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); + if (ret != -1) { + qemu_set_cloexec(ret); + return ret; + } +#endif + + return qemu_signalfd_compat(mask); +} + +int qemu_eventfd(int *fds) +{ + int ret; + +#if defined(CONFIG_EVENTFD) + ret = syscall(SYS_eventfd, 0); + if (ret >= 0) { + fds[0] = ret; + qemu_set_cloexec(ret); + if ((fds[1] = dup(ret)) == -1) { + close(ret); + return -1; + } + qemu_set_cloexec(fds[1]); + return 0; + } +#endif + + ret = pipe(fds); + if (ret != -1) { + qemu_set_cloexec(fds[0]); + qemu_set_cloexec(fds[1]); + } + return ret; +} diff --git a/compatfd.h b/compatfd.h new file mode 100644 index 000000000..06b0b6ba5 --- /dev/null +++ b/compatfd.h @@ -0,0 +1,45 @@ +/* + * signalfd/eventfd compatibility + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_COMPATFD_H +#define QEMU_COMPATFD_H + +#include <signal.h> + +struct qemu_signalfd_siginfo { + uint32_t ssi_signo; /* Signal number */ + int32_t ssi_errno; /* Error number (unused) */ + int32_t ssi_code; /* Signal code */ + uint32_t ssi_pid; /* PID of sender */ + uint32_t ssi_uid; /* Real UID of sender */ + int32_t ssi_fd; /* File descriptor (SIGIO) */ + uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */ + uint32_t ssi_band; /* Band event (SIGIO) */ + uint32_t ssi_overrun; /* POSIX timer overrun count */ + uint32_t ssi_trapno; /* Trap number that caused signal */ + int32_t ssi_status; /* Exit status or signal (SIGCHLD) */ + int32_t ssi_int; /* Integer sent by sigqueue(2) */ + uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */ + uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */ + uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */ + uint64_t ssi_addr; /* Address that generated signal + (for hardware-generated signals) */ + uint8_t pad[48]; /* Pad size to 128 bytes (allow for + additional fields in the future) */ +}; + +int qemu_signalfd(const sigset_t *mask); + +int qemu_eventfd(int *fds); + +#endif @@ -160,7 +160,7 @@ else cpu=`uname -m` fi -target_list="" +target_list="x86_64-softmmu" case "$cpu" in alpha|cris|ia64|m68k|microblaze|ppc|ppc64|sparc64) cpu="$cpu" @@ -197,6 +197,16 @@ case "$cpu" in ;; esac +kvm_version() { + local fname="$(dirname "$0")/KVM_VERSION" + + if test -f "$fname"; then + cat "$fname" + else + echo "qemu-kvm-devel" + fi +} + # Default value for a variable defining feature "foo". # * foo="no" feature will only be used if --enable-foo arg is given # * foo="" feature will be searched for, and if found, will be used @@ -250,10 +260,15 @@ guest_base="" uname_release="" io_thread="no" mixemu="no" +kvm_trace="no" +kvm_cap_pit="" +kvm_cap_device_assignment="" kerneldir="" aix="no" blobs="yes" -pkgversion="" +pkgversion=" ($(kvm_version))" +cpu_emulation="yes" +kvm_kmod="no" check_utests="no" user_pie="no" zero_malloc="" @@ -389,6 +404,13 @@ AIX) if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then audio_possible_drivers="$audio_possible_drivers fmod" fi + if [ "$cpu" = "ia64" ] ; then + xen="no" + target_list="ia64-softmmu" + cpu_emulation="no" + gdbstub="no" + slirp="no" + fi ;; esac @@ -518,6 +540,14 @@ for opt do ;; --enable-kvm) kvm="yes" ;; + --disable-kvm-cap-pit) kvm_cap_pit="no" + ;; + --enable-kvm-cap-pit) kvm_cap_pit="yes" + ;; + --disable-kvm-cap-device-assignment) kvm_cap_device_assignment="no" + ;; + --enable-kvm-cap-device-assignment) kvm_cap_device_assignment="yes" + ;; --enable-profiler) profiler="yes" ;; --enable-cocoa) @@ -595,12 +625,16 @@ for opt do ;; --kerneldir=*) kerneldir="$optarg" ;; + --with-kvm-trace) kvm_trace="yes" + ;; --with-pkgversion=*) pkgversion=" ($optarg)" ;; --disable-docs) docs="no" ;; --enable-docs) docs="yes" ;; + --disable-cpu-emulation) cpu_emulation="no" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -722,6 +756,10 @@ echo " --disable-bluez disable bluez stack connectivity" echo " --enable-bluez enable bluez stack connectivity" echo " --disable-kvm disable KVM acceleration support" echo " --enable-kvm enable KVM acceleration support" +echo " --disable-cap-kvm-pit disable KVM pit support" +echo " --enable-cap-kvm-pit enable KVM pit support" +echo " --disable-cap-device-assignment disable KVM device assignment support" +echo " --enable-cap-device-assignment enable KVM device assignment support" echo " --disable-nptl disable usermode NPTL support" echo " --enable-nptl enable usermode NPTL support" echo " --enable-system enable all system emulation targets" @@ -753,6 +791,8 @@ echo " --enable-linux-aio enable Linux AIO support" echo " --enable-io-thread enable IO thread" echo " --disable-blobs disable installing provided firmware blobs" echo " --kerneldir=PATH look for kernel includes in PATH" +echo " --with-kvm-trace enable building the KVM module with the kvm trace option" +echo " --disable-cpu-emulation disables use of qemu cpu emulation code" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -1384,8 +1424,22 @@ EOF kvm_cflags="$kvm_cflags -I$kerneldir/arch/$cpu/include" fi else - kvm_cflags="" + case "$cpu" in + i386 | x86_64) + kvm_arch="x86" + ;; + ppc) + kvm_arch="powerpc" + ;; + *) + kvm_arch="$cpu" + ;; + esac + kvm_cflags="-I$source_path/kvm/include" + kvm_cflags="$kvm_cflags -include $source_path/kvm/include/linux/config.h" + kvm_cflags="$kvm_cflags -I$source_path/kvm/include/$kvm_arch" fi + kvm_cflags="$kvm_cflags -idirafter $source_path/compat" if compile_prog "$kvm_cflags" "" ; then kvm=yes else @@ -1408,6 +1462,75 @@ EOF fi ########################################## +# test for KVM_CAP_PIT + +if test "$kvm_cap_pit" != "no" ; then + if test "$kvm" = "no" -a "$kvm_cap_pit" = "yes" ; then + feature_not_found "kvm_cap_pit (kvm is not enabled)" + fi + cat > $TMPC <<EOF +#include <linux/kvm.h> +#ifndef KVM_CAP_PIT +#error "kvm no pit capability" +#endif +int main(void) { return 0; } +EOF + if compile_prog "$kvm_cflags" ""; then + kvm_cap_pit=yes + else + if test "$kvm_cap_pit" = "yes" ; then + feature_not_found "kvm_cap_pit" + fi + kvm_cap_pit=no + fi +fi + +########################################## +# test for KVM_CAP_DEVICE_ASSIGNMENT + +if test "$kvm_cap_device_assignment" != "no" ; then + if test "$kvm" = "no" -a "$kvm_cap_device_assignment" = "yes" ; then + feature_not_found "kvm_cap_device_assignment (kvm is not enabled)" + fi + cat > $TMPC <<EOF +#include <linux/kvm.h> +#ifndef KVM_CAP_DEVICE_ASSIGNMENT +#error "kvm no device assignment capability" +#endif +int main(void) { return 0; } +EOF + if compile_prog "$kvm_cflags" "" ; then + kvm_cap_device_assignment=yes + else + if test "$kvm_cap_device_assignment" = "yes" ; then + feature_not_found "kvm_cap_device_assigment" + fi + kvm_cap_device_assignment=no + fi +fi + +########################################## +# libpci probe for kvm_cap_device_assignment +if test $kvm_cap_device_assignment = "yes" ; then + cat > $TMPC << EOF +#include <pci/pci.h> +#ifndef PCI_VENDOR_ID +#error NO LIBPCI +#endif +int main(void) { struct pci_access a; pci_init(&a); return 0; } +EOF + if compile_prog "" "-lpci -lz" ; then + libs_softmmu="-lpci -lz $libs_softmmu" + else + echo + echo "Error: libpci check failed" + echo "Disable KVM Device Assignment capability." + echo + kvm_cap_device_assignment=no + fi +fi + +########################################## # pthread probe PTHREADLIBS_LIST="-lpthread -lpthreadGC2" @@ -1613,6 +1736,21 @@ if compile_prog "" "" ; then splice=yes fi +########################################## +# signalfd probe +signalfd="no" +cat > $TMPC << EOF +#define _GNU_SOURCE +#include <unistd.h> +#include <sys/syscall.h> +#include <signal.h> +int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); } +EOF + +if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then + signalfd=yes +fi + # check if eventfd is supported eventfd=no cat > $TMPC << EOF @@ -1842,6 +1980,20 @@ else binsuffix="/bin" fi +if test -f kvm/kernel/configure; then + kvm_kmod="yes" + kmod_args="" + if test -n "$kerneldir"; then + kmod_args="--kerneldir=$kerneldir" + fi + if test "$kvm_trace" = "yes"; then + kmod_args="$kmod_args --with-kvm-trace" + fi + # hope there are no spaces in kmod_args; can't use arrays because of + # dash. + (cd kvm/kernel; ./configure $kmod_args) +fi + echo "Install prefix $prefix" echo "BIOS directory $prefix$datasuffix" echo "binary directory $prefix$binsuffix" @@ -1885,6 +2037,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "xen support $xen" +echo "CPU emulation $cpu_emulation" echo "brlapi support $brlapi" echo "bluez support $bluez" echo "Documentation $docs" @@ -1898,6 +2051,9 @@ echo "IO thread $io_thread" echo "Linux AIO support $linux_aio" echo "Install blobs $blobs" echo "KVM support $kvm" +echo "KVM PIT support $kvm_cap_pit" +echo "KVM device assig. $kvm_cap_device_assignment" +echo "KVM trace support $kvm_trace" echo "fdt support $fdt" echo "preadv support $preadv" echo "fdatasync $fdatasync" @@ -2104,6 +2260,9 @@ fi if test "$fdt" = "yes" ; then echo "CONFIG_FDT=y" >> $config_host_mak fi +if test "$signalfd" = "yes" ; then + echo "CONFIG_SIGNALFD=y" >> $config_host_mak +fi if test "$need_offsetof" = "yes" ; then echo "CONFIG_NEED_OFFSETOF=y" >> $config_host_mak fi @@ -2113,6 +2272,11 @@ fi if test "$fdatasync" = "yes" ; then echo "CONFIG_FDATASYNC=y" >> $config_host_mak fi +if test $cpu_emulation = "yes"; then + echo "CONFIG_CPU_EMULATION=y" >> $config_host_mak +else + echo "CONFIG_NO_CPU_EMULATION=y" >> $config_host_mak +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then @@ -2138,6 +2302,8 @@ bsd) ;; esac +echo "KVM_KMOD=$kvm_kmod" >> $config_host_mak + tools= if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" @@ -2288,6 +2454,9 @@ case "$target_arch2" in TARGET_BASE_ARCH=i386 target_phys_bits=64 ;; + ia64) + target_phys_bits=64 + ;; alpha) target_phys_bits=64 ;; @@ -2418,6 +2587,12 @@ case "$target_arch2" in \( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then echo "CONFIG_KVM=y" >> $config_target_mak echo "KVM_CFLAGS=$kvm_cflags" >> $config_target_mak + if test $kvm_cap_pit = "yes" ; then + echo "CONFIG_KVM_PIT=y" >> $config_target_mak + fi + if test $kvm_cap_device_assignment = "yes" ; then + echo "CONFIG_KVM_DEVICE_ASSIGNMENT=y" >> $config_target_mak + fi fi esac echo "TARGET_PHYS_ADDR_BITS=$target_phys_bits" >> $config_target_mak @@ -2626,7 +2801,7 @@ if test "$source_path_used" = "yes" ; then fi # temporary config to build submodules -for rom in seabios vgabios ; do +for rom in seabios vgabios; do config_mak=roms/$rom/config.mak echo "# Automatically generated by configure - do not modify" >> $config_mak echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak @@ -849,6 +849,7 @@ extern int phys_ram_fd; extern uint8_t *phys_ram_dirty; extern ram_addr_t ram_size; extern ram_addr_t last_ram_offset; +extern uint8_t *bios_mem; /* physical memory access */ diff --git a/cpu-common.h b/cpu-common.h index 630237203..5e5956489 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -34,6 +34,7 @@ void qemu_ram_free(ram_addr_t addr); /* This should only be used for ram local to a device. */ void *qemu_get_ram_ptr(ram_addr_t addr); /* This should not be used by devices. */ +int do_qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr); ram_addr_t qemu_ram_addr_from_host(void *ptr); int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, diff --git a/cpu-defs.h b/cpu-defs.h index 95068b530..cf502e992 100644 --- a/cpu-defs.h +++ b/cpu-defs.h @@ -27,6 +27,7 @@ #include <setjmp.h> #include <inttypes.h> #include <signal.h> +#include <pthread.h> #include "osdep.h" #include "qemu-queue.h" #include "targphys.h" @@ -134,6 +135,16 @@ typedef struct CPUWatchpoint { QTAILQ_ENTRY(CPUWatchpoint) entry; } CPUWatchpoint; +/* forward decleration */ +struct qemu_work_item; + +struct KVMCPUState { + pthread_t thread; + int signalled; + struct qemu_work_item *queued_work_first, *queued_work_last; + int regs_modified; +}; + #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ @@ -146,8 +157,6 @@ typedef struct CPUWatchpoint { target_ulong mem_io_vaddr; /* target virtual addr at which the \ memory was accessed */ \ uint32_t halted; /* Nonzero if the CPU is in suspend state */ \ - uint32_t stop; /* Stop request */ \ - uint32_t stopped; /* Artificially stopped */ \ uint32_t interrupt_request; \ volatile sig_atomic_t exit_request; \ /* The meaning of the MMU modes is defined in the target code. */ \ @@ -188,6 +197,7 @@ typedef struct CPUWatchpoint { int nr_cores; /* number of cores within this CPU package */ \ int nr_threads;/* number of threads within this CPU */ \ int running; /* Nonzero if cpu is currently running(usermode). */ \ + int thread_id; \ /* user data */ \ void *opaque; \ \ @@ -197,6 +207,9 @@ typedef struct CPUWatchpoint { const char *cpu_model_str; \ struct KVMState *kvm_state; \ struct kvm_run *kvm_run; \ - int kvm_fd; + int kvm_fd; \ + uint32_t stop; /* Stop request */ \ + uint32_t stopped; /* Artificially stopped */ \ + struct KVMCPUState kvm_cpu_state; #endif diff --git a/cpu-exec.c b/cpu-exec.c index 3246c9e2c..040d4741b 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -19,7 +19,9 @@ #include "config.h" #include "exec.h" #include "disas.h" +#if !defined(TARGET_IA64) #include "tcg.h" +#endif #include "kvm.h" #if !defined(CONFIG_SOFTMMU) @@ -38,6 +40,8 @@ #endif #endif +#include "qemu-kvm.h" + #if defined(__sparc__) && !defined(CONFIG_SOLARIS) // Work around ugly bugs in glibc that mangle global register contents #undef env @@ -252,6 +256,7 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_SH4) #elif defined(TARGET_CRIS) #elif defined(TARGET_S390X) +#elif defined(TARGET_IA64) /* XXXXX */ #else #error unsupported target CPU @@ -319,6 +324,8 @@ int cpu_exec(CPUState *env1) do_interrupt(env); #elif defined(TARGET_M68K) do_interrupt(0); +#elif defined(TARGET_IA64) + do_interrupt(env); #endif #endif } @@ -674,6 +681,7 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) +#elif defined(TARGET_IA64) #elif defined(TARGET_ALPHA) #elif defined(TARGET_CRIS) #elif defined(TARGET_S390X) @@ -218,6 +218,11 @@ void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf) } } +/* + * No dma flushing needed here, as the aio code will call dma_bdrv_cb() + * on completion as well, which will result in a call to + * dma_bdrv_unmap() which will do the flushing .... + */ void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count) { const uint8_t *p = (const uint8_t *)buf; diff --git a/dma-helpers.c b/dma-helpers.c index 712ed897f..d4fc077c0 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -160,6 +160,10 @@ static BlockDriverAIOCB *dma_bdrv_io( dbs->is_write = is_write; dbs->bh = NULL; qemu_iovec_init(&dbs->iov, sg->nsg); + /* + * DMA flushing is handled in dma_bdrv_cb() calling dma_bdrv_unmap() + * so we don't need to do that here. + */ dma_bdrv_cb(dbs, 0); if (!dbs->acb) { qemu_aio_release(dbs); @@ -34,7 +34,13 @@ #include "cpu.h" #include "exec-all.h" #include "qemu-common.h" +#include "cache-utils.h" + +#if !defined(TARGET_IA64) #include "tcg.h" +#endif +#include "qemu-kvm.h" + #include "hw/hw.h" #include "osdep.h" #include "kvm.h" @@ -74,6 +80,8 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 42 #elif defined(TARGET_I386) #define TARGET_PHYS_ADDR_SPACE_BITS 36 +#elif defined(TARGET_IA64) +#define TARGET_PHYS_ADDR_SPACE_BITS 36 #else #define TARGET_PHYS_ADDR_SPACE_BITS 32 #endif @@ -111,6 +119,7 @@ uint8_t *code_gen_ptr; #if !defined(CONFIG_USER_ONLY) int phys_ram_fd; uint8_t *phys_ram_dirty; +uint8_t *bios_mem; static int in_migration; typedef struct RAMBlock { @@ -412,6 +421,9 @@ static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]; static void code_gen_alloc(unsigned long tb_size) { + if (kvm_enabled()) + return; + #ifdef USE_STATIC_CODE_GEN_BUFFER code_gen_buffer = static_code_gen_buffer; code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE; @@ -588,6 +600,11 @@ void cpu_exec_init(CPUState *env) env->numa_node = 0; QTAILQ_INIT(&env->breakpoints); QTAILQ_INIT(&env->watchpoints); +#ifdef __WIN32 + env->thread_id = GetCurrentProcessId(); +#else + env->thread_id = getpid(); +#endif *penv = env; #if defined(CONFIG_USER_ONLY) cpu_list_unlock(); @@ -1553,6 +1570,8 @@ void cpu_interrupt(CPUState *env, int mask) old_mask = env->interrupt_request; env->interrupt_request |= mask; + if (kvm_enabled() && !kvm_irqchip_in_kernel()) + kvm_update_interrupt_request(env); #ifndef CONFIG_USER_ONLY /* @@ -1880,7 +1899,6 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, int cpu_physical_memory_set_dirty_tracking(int enable) { - in_migration = enable; if (kvm_enabled()) { return kvm_set_migration_log(enable); } @@ -2404,6 +2422,113 @@ void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size) kvm_uncoalesce_mmio_region(addr, size); } +#ifdef __linux__ + +#include <sys/vfs.h> + +#define HUGETLBFS_MAGIC 0x958458f6 + +static long gethugepagesize(const char *path) +{ + struct statfs fs; + int ret; + + do { + ret = statfs(path, &fs); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + perror("statfs"); + return 0; + } + + if (fs.f_type != HUGETLBFS_MAGIC) + fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path); + + return fs.f_bsize; +} + +static void *file_ram_alloc(ram_addr_t memory, const char *path) +{ + char *filename; + void *area; + int fd; +#ifdef MAP_POPULATE + int flags; +#endif + unsigned long hpagesize; + extern int mem_prealloc; + + if (!path) { + return NULL; + } + + hpagesize = gethugepagesize(path); + if (!hpagesize) { + return NULL; + } + + if (memory < hpagesize) { + return NULL; + } + + if (kvm_enabled() && !kvm_has_sync_mmu()) { + fprintf(stderr, "host lacks mmu notifiers, disabling --mem-path\n"); + return NULL; + } + + if (asprintf(&filename, "%s/kvm.XXXXXX", path) == -1) { + return NULL; + } + + fd = mkstemp(filename); + if (fd < 0) { + perror("mkstemp"); + free(filename); + return NULL; + } + unlink(filename); + free(filename); + + memory = (memory+hpagesize-1) & ~(hpagesize-1); + + /* + * ftruncate is not supported by hugetlbfs in older + * hosts, so don't bother checking for errors. + * If anything goes wrong with it under other filesystems, + * mmap will fail. + */ + ftruncate(fd, memory); + +#ifdef MAP_POPULATE + /* NB: MAP_POPULATE won't exhaustively alloc all phys pages in the case + * MAP_PRIVATE is requested. For mem_prealloc we mmap as MAP_SHARED + * to sidestep this quirk. + */ + flags = mem_prealloc ? MAP_POPULATE|MAP_SHARED : MAP_PRIVATE; + area = mmap(0, memory, PROT_READ|PROT_WRITE, flags, fd, 0); +#else + area = mmap(0, memory, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + if (area == MAP_FAILED) { + perror("alloc_mem_area: can't mmap hugetlbfs pages"); + close(fd); + return (NULL); + } + return area; +} + +#else + +static void *file_ram_alloc(ram_addr_t memory, const char *path) +{ + return NULL; +} + +#endif + +extern const char *mem_path; + ram_addr_t qemu_ram_alloc(ram_addr_t size) { RAMBlock *new_block; @@ -2411,16 +2536,20 @@ ram_addr_t qemu_ram_alloc(ram_addr_t size) size = TARGET_PAGE_ALIGN(size); new_block = qemu_malloc(sizeof(*new_block)); + new_block->host = file_ram_alloc(size, mem_path); + if (!new_block->host) { #if defined(TARGET_S390X) && defined(CONFIG_KVM) /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */ - new_block->host = mmap((void*)0x1000000, size, PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); + new_block->host = mmap((void*)0x1000000, size, + PROT_EXEC|PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); #else - new_block->host = qemu_vmalloc(size); + new_block->host = qemu_vmalloc(size); #endif #ifdef MADV_MERGEABLE - madvise(new_block->host, size, MADV_MERGEABLE); + madvise(new_block->host, size, MADV_MERGEABLE); #endif + } new_block->offset = last_ram_offset; new_block->length = size; @@ -2482,9 +2611,7 @@ void *qemu_get_ram_ptr(ram_addr_t addr) return block->host + (addr - block->offset); } -/* Some of the softmmu routines need to translate from a host pointer - (typically a TLB entry) back to a ram offset. */ -ram_addr_t qemu_ram_addr_from_host(void *ptr) +int do_qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) { RAMBlock *prev; RAMBlock **prevp; @@ -2501,11 +2628,23 @@ ram_addr_t qemu_ram_addr_from_host(void *ptr) prev = block; block = block->next; } - if (!block) { + if (!block) + return -1; + *ram_addr = block->offset + (host - block->host); + return 0; +} + +/* Some of the softmmu routines need to translate from a host pointer + (typically a TLB entry) back to a ram offset. */ +ram_addr_t qemu_ram_addr_from_host(void *ptr) +{ + ram_addr_t ram_addr; + + if (do_qemu_ram_addr_from_host(ptr, &ram_addr)) { fprintf(stderr, "Bad ram pointer %p\n", ptr); abort(); } - return block->offset + (host - block->host); + return ram_addr; } static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) @@ -3091,6 +3230,11 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= (0xff & ~CODE_DIRTY_FLAG); } + /* qemu doesn't execute guest code directly, but kvm does + therefore flush instruction caches */ + if (kvm_enabled()) + flush_icache_range((unsigned long)ptr, + ((unsigned long)ptr)+l); } } else { if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && @@ -3283,6 +3427,8 @@ void *cpu_physical_memory_map(target_phys_addr_t addr, void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, int is_write, target_phys_addr_t access_len) { + unsigned long flush_len = (unsigned long)access_len; + if (buffer != bounce.buffer) { if (is_write) { ram_addr_t addr1 = qemu_ram_addr_from_host(buffer); @@ -3300,7 +3446,9 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, } addr1 += l; access_len -= l; - } + } + dma_flush_range((unsigned long)buffer, + (unsigned long)buffer + flush_len); } return; } @@ -3668,7 +3816,9 @@ void dump_exec_info(FILE *f, cpu_fprintf(f, "TB flush count %d\n", tb_flush_count); cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count); cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count); +#ifdef CONFIG_PROFILER tcg_dump_info(f, cpu_fprintf); +#endif } #if !defined(CONFIG_USER_ONLY) diff --git a/fpu/softfloat-native.c b/fpu/softfloat-native.c index 8d64f4eff..cb0e97be2 100644 --- a/fpu/softfloat-native.c +++ b/fpu/softfloat-native.c @@ -5,6 +5,7 @@ #if defined(CONFIG_SOLARIS) #include <fenv.h> #endif +#include "config-host.h" void set_float_rounding_mode(int val STATUS_PARAM) { @@ -34,6 +34,7 @@ #include "sysemu.h" #include "gdbstub.h" #endif +#include "qemu-kvm.h" #define MAX_PACKET_LENGTH 4096 @@ -23,6 +23,8 @@ #include "i2c.h" #include "smbus.h" #include "kvm.h" +#include "qemu-kvm.h" +#include "string.h" //#define DEBUG @@ -521,6 +523,13 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, pci_conf[0x40] = 0x01; /* PM io base read only bit */ +#if defined(TARGET_IA64) + pci_conf[0x40] = 0x41; /* PM io base read only bit */ + pci_conf[0x41] = 0x1f; + pm_write_config(s, 0x80, 0x01, 1); /*Set default pm_io_base 0x1f40*/ + s->pmcntrl = SCI_EN; +#endif + register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s); register_ioport_read(0xb2, 2, 1, pm_smi_readb, s); @@ -559,12 +568,14 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, } #define GPE_BASE 0xafe0 +#define PROC_BASE 0xaf00 #define PCI_BASE 0xae00 #define PCI_EJ_BASE 0xae08 struct gpe_regs { uint16_t sts; /* status */ uint16_t en; /* enabled */ + uint8_t cpus_sts[32]; }; struct pci_status { @@ -587,6 +598,10 @@ static uint32_t gpe_readb(void *opaque, uint32_t addr) uint32_t val = 0; struct gpe_regs *g = opaque; switch (addr) { + case PROC_BASE ... PROC_BASE+31: + val = g->cpus_sts[addr - PROC_BASE]; + break; + case GPE_BASE: case GPE_BASE + 1: val = gpe_read_val(g->sts, addr); @@ -629,6 +644,10 @@ static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val) { struct gpe_regs *g = opaque; switch (addr) { + case PROC_BASE ... PROC_BASE + 31: + /* don't allow to change cpus_sts from inside a guest */ + break; + case GPE_BASE: case GPE_BASE + 1: gpe_reset_val(&g->sts, addr, val); @@ -712,22 +731,72 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val) #endif } +static const char *model; + static int piix4_device_hotplug(PCIDevice *dev, int state); -void piix4_acpi_system_hot_add_init(PCIBus *bus) +void piix4_acpi_system_hot_add_init(PCIBus *bus, const char *cpu_model) { + int i = 0, cpus = smp_cpus; + + while (cpus > 0) { + gpe.cpus_sts[i++] = (cpus < 8) ? (1 << cpus) - 1 : 0xff; + cpus -= 8; + } register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, &gpe); register_ioport_read(GPE_BASE, 4, 1, gpe_readb, &gpe); + register_ioport_write(PROC_BASE, 32, 1, gpe_writeb, &gpe); + register_ioport_read(PROC_BASE, 32, 1, gpe_readb, &gpe); + register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, &pci0_status); register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, &pci0_status); register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus); register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus); + model = cpu_model; + pci_bus_hotplug(bus, piix4_device_hotplug); } +#if defined(TARGET_I386) +static void enable_processor(struct gpe_regs *g, int cpu) +{ + g->sts |= 4; + g->cpus_sts[cpu/8] |= (1 << (cpu%8)); +} + +static void disable_processor(struct gpe_regs *g, int cpu) +{ + g->sts |= 4; + g->cpus_sts[cpu/8] &= ~(1 << (cpu%8)); +} + +void qemu_system_cpu_hot_add(int cpu, int state) +{ + CPUState *env; + + if (state && !qemu_get_cpu(cpu)) { + env = pc_new_cpu(model); + if (!env) { + fprintf(stderr, "cpu %d creation failed\n", cpu); + return; + } + env->cpuid_apic_id = cpu; + } + + if (state) + enable_processor(&gpe, cpu); + else + disable_processor(&gpe, cpu); + if (gpe.en & 4) { + qemu_set_irq(pm_state->irq, 1); + qemu_set_irq(pm_state->irq, 0); + } +} +#endif + static void enable_device(struct pci_status *p, struct gpe_regs *g, int slot) { g->sts |= 2; @@ -24,6 +24,8 @@ #include "host-utils.h" #include "kvm.h" +#include "qemu-kvm.h" + //#define DEBUG_APIC /* APIC Local Vector Table */ @@ -299,8 +301,11 @@ void cpu_set_apic_base(CPUState *env, uint64_t val) #endif if (!s) return; - s->apicbase = (val & 0xfffff000) | - (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); + if (kvm_enabled() && kvm_irqchip_in_kernel()) + s->apicbase = val; + else + s->apicbase = (val & 0xfffff000) | + (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); /* if disabled, cannot be enabled again */ if (!(val & MSR_IA32_APICBASE_ENABLE)) { s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; @@ -393,6 +398,11 @@ int apic_get_irq_delivered(void) return apic_irq_delivered; } +void apic_set_irq_delivered(void) +{ + apic_irq_delivered = 1; +} + static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) { apic_irq_delivered += !get_bit(s->irr, vector_num); @@ -478,6 +488,7 @@ void apic_init_reset(CPUState *env) if (!s) return; + cpu_synchronize_state(env); s->tpr = 0; s->spurious_vec = 0xff; s->log_dest = 0; @@ -497,6 +508,13 @@ void apic_init_reset(CPUState *env) s->wait_for_sipi = 1; env->halted = !(s->apicbase & MSR_IA32_APICBASE_BSP); +#ifdef KVM_CAP_MP_STATE + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + env->mp_state + = env->halted ? KVM_MP_STATE_UNINITIALIZED : KVM_MP_STATE_RUNNABLE; + kvm_load_mpstate(env); + } +#endif } static void apic_startup(APICState *s, int vector_num) @@ -864,6 +882,115 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } +#ifdef KVM_CAP_IRQCHIP + +static inline uint32_t kapic_reg(struct kvm_lapic_state *kapic, int reg_id) +{ + return *((uint32_t *) (kapic->regs + (reg_id << 4))); +} + +static inline void kapic_set_reg(struct kvm_lapic_state *kapic, + int reg_id, uint32_t val) +{ + *((uint32_t *) (kapic->regs + (reg_id << 4))) = val; +} + +static void kvm_kernel_lapic_save_to_user(APICState *s) +{ + struct kvm_lapic_state apic; + struct kvm_lapic_state *kapic = &apic; + int i, v; + + kvm_get_lapic(s->cpu_env, kapic); + + s->id = kapic_reg(kapic, 0x2) >> 24; + s->tpr = kapic_reg(kapic, 0x8); + s->arb_id = kapic_reg(kapic, 0x9); + s->log_dest = kapic_reg(kapic, 0xd) >> 24; + s->dest_mode = kapic_reg(kapic, 0xe) >> 28; + s->spurious_vec = kapic_reg(kapic, 0xf); + for (i = 0; i < 8; i++) { + s->isr[i] = kapic_reg(kapic, 0x10 + i); + s->tmr[i] = kapic_reg(kapic, 0x18 + i); + s->irr[i] = kapic_reg(kapic, 0x20 + i); + } + s->esr = kapic_reg(kapic, 0x28); + s->icr[0] = kapic_reg(kapic, 0x30); + s->icr[1] = kapic_reg(kapic, 0x31); + for (i = 0; i < APIC_LVT_NB; i++) + s->lvt[i] = kapic_reg(kapic, 0x32 + i); + s->initial_count = kapic_reg(kapic, 0x38); + s->divide_conf = kapic_reg(kapic, 0x3e); + + v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); + s->count_shift = (v + 1) & 7; + + s->initial_count_load_time = qemu_get_clock(vm_clock); + apic_timer_update(s, s->initial_count_load_time); +} + +static void kvm_kernel_lapic_load_from_user(APICState *s) +{ + struct kvm_lapic_state apic; + struct kvm_lapic_state *klapic = &apic; + int i; + + memset(klapic, 0, sizeof apic); + kapic_set_reg(klapic, 0x2, s->id << 24); + kapic_set_reg(klapic, 0x8, s->tpr); + kapic_set_reg(klapic, 0xd, s->log_dest << 24); + kapic_set_reg(klapic, 0xe, s->dest_mode << 28 | 0x0fffffff); + kapic_set_reg(klapic, 0xf, s->spurious_vec); + for (i = 0; i < 8; i++) { + kapic_set_reg(klapic, 0x10 + i, s->isr[i]); + kapic_set_reg(klapic, 0x18 + i, s->tmr[i]); + kapic_set_reg(klapic, 0x20 + i, s->irr[i]); + } + kapic_set_reg(klapic, 0x28, s->esr); + kapic_set_reg(klapic, 0x30, s->icr[0]); + kapic_set_reg(klapic, 0x31, s->icr[1]); + for (i = 0; i < APIC_LVT_NB; i++) + kapic_set_reg(klapic, 0x32 + i, s->lvt[i]); + kapic_set_reg(klapic, 0x38, s->initial_count); + kapic_set_reg(klapic, 0x3e, s->divide_conf); + + kvm_set_lapic(s->cpu_env, klapic); +} + +#endif + +void qemu_kvm_load_lapic(CPUState *env) +{ +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_vcpu_inited(env) && kvm_irqchip_in_kernel()) { + kvm_kernel_lapic_load_from_user(env->apic_state); + } +#endif +} + +static void apic_pre_save(void *opaque) +{ +#ifdef KVM_CAP_IRQCHIP + APICState *s = (void *)opaque; + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_lapic_save_to_user(s); + } +#endif +} + +static int apic_post_load(void *opaque, int version_id) +{ +#ifdef KVM_CAP_IRQCHIP + APICState *s = opaque; + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_lapic_load_from_user(s); + } +#endif + return 0; +} + /* This function is only used for old state version 1 and 2 */ static int apic_load_old(QEMUFile *f, void *opaque, int version_id) { @@ -900,6 +1027,9 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id) if (version_id >= 2) qemu_get_timer(f, s->timer); + + qemu_kvm_load_lapic(s->cpu_env); + return 0; } @@ -930,7 +1060,9 @@ static const VMStateDescription vmstate_apic = { VMSTATE_INT64(next_time, APICState), VMSTATE_TIMER(timer, APICState), VMSTATE_END_OF_LIST() - } + }, + .pre_save = apic_pre_save, + .post_load = apic_post_load, }; static void apic_reset(void *opaque) @@ -955,6 +1087,7 @@ static void apic_reset(void *opaque) */ s->lvt[APIC_LVT_LINT0] = 0x700; } + qemu_kvm_load_lapic(s->cpu_env); } static CPUReadMemoryFunc * const apic_mem_read[3] = { @@ -998,6 +1131,11 @@ int apic_init(CPUState *env) vmstate_register(s->idx, &vmstate_apic, s); qemu_register_reset(apic_reset, s); + /* apic_reset must be called before the vcpu threads are initialized and load + * registers, in qemu-kvm. + */ + apic_reset(s); + local_apics[s->idx] = s; return 0; } diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 9f61a01d4..571044f64 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -32,6 +32,7 @@ #include "console.h" #include "vga_int.h" #include "kvm.h" +#include "qemu-kvm.h" #include "loader.h" /* @@ -2552,6 +2553,7 @@ static CPUWriteMemoryFunc * const cirrus_linear_bitblt_write[3] = { static void map_linear_vram(CirrusVGAState *s) { + vga_dirty_log_stop(&s->vga); if (!s->vga.map_addr && s->vga.lfb_addr && s->vga.lfb_end) { s->vga.map_addr = s->vga.lfb_addr; s->vga.map_end = s->vga.lfb_end; @@ -2561,13 +2563,19 @@ static void map_linear_vram(CirrusVGAState *s) if (!s->vga.map_addr) return; +#ifndef TARGET_IA64 s->vga.lfb_vram_mapped = 0; + cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, + (s->vga.vram_offset + s->cirrus_bank_base[0]) | IO_MEM_UNASSIGNED); + cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, + (s->vga.vram_offset + s->cirrus_bank_base[1]) | IO_MEM_UNASSIGNED); if (!(s->cirrus_srcptr != s->cirrus_srcptr_end) && !((s->vga.sr[0x07] & 0x01) == 0) && !((s->vga.gr[0x0B] & 0x14) == 0x14) && !(s->vga.gr[0x0B] & 0x02)) { + vga_dirty_log_stop(&s->vga); cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, (s->vga.vram_offset + s->cirrus_bank_base[0]) | IO_MEM_RAM); cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, @@ -2579,12 +2587,14 @@ static void map_linear_vram(CirrusVGAState *s) cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000, s->vga.vga_io_memory); } +#endif vga_dirty_log_start(&s->vga); } static void unmap_linear_vram(CirrusVGAState *s) { + vga_dirty_log_stop(&s->vga); if (s->vga.map_addr && s->vga.lfb_addr && s->vga.lfb_end) { s->vga.map_addr = s->vga.map_end = 0; cpu_register_physical_memory(s->vga.lfb_addr, s->vga.vram_size, @@ -2592,6 +2602,8 @@ static void unmap_linear_vram(CirrusVGAState *s) } cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000, s->vga.vga_io_memory); + + vga_dirty_log_start(&s->vga); } /* Compute the memory access functions */ @@ -3145,6 +3157,8 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int region_num, { CirrusVGAState *s = &DO_UPCAST(PCICirrusVGAState, dev, d)->cirrus_vga; + vga_dirty_log_stop(&s->vga); + /* XXX: add byte swapping apertures */ cpu_register_physical_memory(addr, s->vga.vram_size, s->cirrus_linear_io_addr); @@ -3176,10 +3190,14 @@ static void pci_cirrus_write_config(PCIDevice *d, PCICirrusVGAState *pvs = DO_UPCAST(PCICirrusVGAState, dev, d); CirrusVGAState *s = &pvs->cirrus_vga; + vga_dirty_log_stop(&s->vga); + pci_default_write_config(d, address, val, len); if (s->vga.map_addr && d->io_regions[0].addr == PCI_BAR_UNMAPPED) s->vga.map_addr = 0; cirrus_update_memory_access(s); + + vga_dirty_log_start(&s->vga); } static int pci_cirrus_vga_initfn(PCIDevice *dev) diff --git a/hw/device-assignment.c b/hw/device-assignment.c new file mode 100644 index 000000000..5564504d9 --- /dev/null +++ b/hw/device-assignment.c @@ -0,0 +1,1453 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * + * Assign a PCI device from the host to a guest VM. + * + * Adapted for KVM by Qumranet. + * + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ +#include <stdio.h> +#include <unistd.h> +#include <sys/io.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "qemu-kvm.h" +#include "hw.h" +#include "pc.h" +#include "sysemu.h" +#include "console.h" +#include "device-assignment.h" +#include "loader.h" +#include <pci/pci.h> + +/* From linux/ioport.h */ +#define IORESOURCE_IO 0x00000100 /* Resource type */ +#define IORESOURCE_MEM 0x00000200 +#define IORESOURCE_IRQ 0x00000400 +#define IORESOURCE_DMA 0x00000800 +#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ + +/* #define DEVICE_ASSIGNMENT_DEBUG 1 */ + +#ifdef DEVICE_ASSIGNMENT_DEBUG +#define DEBUG(fmt, ...) \ + do { \ + fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \ + } while (0) +#else +#define DEBUG(fmt, ...) do { } while(0) +#endif + +static void assigned_dev_load_option_rom(AssignedDevice *dev); + +static uint32_t guest_to_host_ioport(AssignedDevRegion *region, uint32_t addr) +{ + return region->u.r_baseport + (addr - region->e_physbase); +} + +static void assigned_dev_ioport_writeb(void *opaque, uint32_t addr, + uint32_t value) +{ + AssignedDevRegion *r_access = opaque; + uint32_t r_pio = guest_to_host_ioport(r_access, addr); + + DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", + r_pio, (int)r_access->e_physbase, + (unsigned long)r_access->u.r_baseport, value); + + outb(value, r_pio); +} + +static void assigned_dev_ioport_writew(void *opaque, uint32_t addr, + uint32_t value) +{ + AssignedDevRegion *r_access = opaque; + uint32_t r_pio = guest_to_host_ioport(r_access, addr); + + DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", + r_pio, (int)r_access->e_physbase, + (unsigned long)r_access->u.r_baseport, value); + + outw(value, r_pio); +} + +static void assigned_dev_ioport_writel(void *opaque, uint32_t addr, + uint32_t value) +{ + AssignedDevRegion *r_access = opaque; + uint32_t r_pio = guest_to_host_ioport(r_access, addr); + + DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", + r_pio, (int)r_access->e_physbase, + (unsigned long)r_access->u.r_baseport, value); + + outl(value, r_pio); +} + +static uint32_t assigned_dev_ioport_readb(void *opaque, uint32_t addr) +{ + AssignedDevRegion *r_access = opaque; + uint32_t r_pio = guest_to_host_ioport(r_access, addr); + uint32_t value; + + value = inb(r_pio); + + DEBUG("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n", + r_pio, (int)r_access->e_physbase, + (unsigned long)r_access->u.r_baseport, value); + + return value; +} + +static uint32_t assigned_dev_ioport_readw(void *opaque, uint32_t addr) +{ + AssignedDevRegion *r_access = opaque; + uint32_t r_pio = guest_to_host_ioport(r_access, addr); + uint32_t value; + + value = inw(r_pio); + + DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", + r_pio, (int)r_access->e_physbase, + (unsigned long)r_access->u.r_baseport, value); + + return value; +} + +static uint32_t assigned_dev_ioport_readl(void *opaque, uint32_t addr) +{ + AssignedDevRegion *r_access = opaque; + uint32_t r_pio = guest_to_host_ioport(r_access, addr); + uint32_t value; + + value = inl(r_pio); + + DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", + r_pio, (int)r_access->e_physbase, + (unsigned long)r_access->u.r_baseport, value); + + return value; +} + +static void assigned_dev_iomem_map(PCIDevice *pci_dev, int region_num, + pcibus_t e_phys, pcibus_t e_size, int type) +{ + AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevRegion *region = &r_dev->v_addrs[region_num]; + PCIRegion *real_region = &r_dev->real_device.regions[region_num]; + pcibus_t old_ephys = region->e_physbase; + pcibus_t old_esize = region->e_size; + int first_map = (region->e_size == 0); + int ret = 0; + + DEBUG("e_phys=%08x r_virt=%p type=%d len=%08x region_num=%d \n", + e_phys, region->u.r_virtbase, type, e_size, region_num); + + region->e_physbase = e_phys; + region->e_size = e_size; + + if (!first_map) + kvm_destroy_phys_mem(kvm_context, old_ephys, + TARGET_PAGE_ALIGN(old_esize)); + + if (e_size > 0) { + /* deal with MSI-X MMIO page */ + if (real_region->base_addr <= r_dev->msix_table_addr && + real_region->base_addr + real_region->size >= + r_dev->msix_table_addr) { + int offset = r_dev->msix_table_addr - real_region->base_addr; + ret = munmap(region->u.r_virtbase + offset, TARGET_PAGE_SIZE); + if (ret == 0) + DEBUG("munmap done, virt_base 0x%p\n", + region->u.r_virtbase + offset); + else { + fprintf(stderr, "%s: fail munmap msix table!\n", __func__); + exit(1); + } + cpu_register_physical_memory(e_phys + offset, + TARGET_PAGE_SIZE, r_dev->mmio_index); + } + ret = kvm_register_phys_mem(kvm_context, e_phys, + region->u.r_virtbase, + TARGET_PAGE_ALIGN(e_size), 0); + } + + if (ret != 0) { + fprintf(stderr, "%s: Error: create new mapping failed\n", __func__); + exit(1); + } +} + +static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevRegion *region = &r_dev->v_addrs[region_num]; + int first_map = (region->e_size == 0); + CPUState *env; + + region->e_physbase = addr; + region->e_size = size; + + DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d \n", + addr, region->u.r_baseport, type, size, region_num); + + if (first_map) { + struct ioperm_data *data; + + data = qemu_mallocz(sizeof(struct ioperm_data)); + if (data == NULL) { + fprintf(stderr, "%s: Out of memory\n", __func__); + exit(1); + } + + data->start_port = region->u.r_baseport; + data->num = region->r_size; + data->turn_on = 1; + + kvm_add_ioperm_data(data); + + for (env = first_cpu; env; env = env->next_cpu) + kvm_ioperm(env, data); + } + + register_ioport_read(addr, size, 1, assigned_dev_ioport_readb, + (r_dev->v_addrs + region_num)); + register_ioport_read(addr, size, 2, assigned_dev_ioport_readw, + (r_dev->v_addrs + region_num)); + register_ioport_read(addr, size, 4, assigned_dev_ioport_readl, + (r_dev->v_addrs + region_num)); + register_ioport_write(addr, size, 1, assigned_dev_ioport_writeb, + (r_dev->v_addrs + region_num)); + register_ioport_write(addr, size, 2, assigned_dev_ioport_writew, + (r_dev->v_addrs + region_num)); + register_ioport_write(addr, size, 4, assigned_dev_ioport_writel, + (r_dev->v_addrs + region_num)); +} + +static uint8_t pci_find_cap_offset(struct pci_dev *pci_dev, uint8_t cap) +{ + int id; + int max_cap = 48; + int pos = PCI_CAPABILITY_LIST; + int status; + + status = pci_read_byte(pci_dev, PCI_STATUS); + if ((status & PCI_STATUS_CAP_LIST) == 0) + return 0; + + while (max_cap--) { + pos = pci_read_byte(pci_dev, pos); + if (pos < 0x40) + break; + + pos &= ~3; + id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID); + + if (id == 0xff) + break; + if (id == cap) + return pos; + + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + +static void assigned_dev_pci_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + int fd; + ssize_t ret; + AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + + DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", + ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7), + (uint16_t) address, val, len); + + if (address == 0x4) { + pci_default_write_config(d, address, val, len); + /* Continue to program the card */ + } + + if ((address >= 0x10 && address <= 0x24) || address == 0x30 || + address == 0x34 || address == 0x3c || address == 0x3d || + pci_access_cap_config(d, address, len)) { + /* used for update-mappings (BAR emulation) */ + pci_default_write_config(d, address, val, len); + return; + } + + DEBUG("NON BAR (%x.%x): address=%04x val=0x%08x len=%d\n", + ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7), + (uint16_t) address, val, len); + + fd = pci_dev->real_device.config_fd; + +again: + ret = pwrite(fd, &val, len, address); + if (ret != len) { + if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) + goto again; + + fprintf(stderr, "%s: pwrite failed, ret = %zd errno = %d\n", + __func__, ret, errno); + + exit(1); + } +} + +static uint32_t assigned_dev_pci_read_config(PCIDevice *d, uint32_t address, + int len) +{ + uint32_t val = 0; + int fd; + ssize_t ret; + AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + + if (address < 0x4 || (pci_dev->need_emulate_cmd && address == 0x4) || + (address >= 0x10 && address <= 0x24) || address == 0x30 || + address == 0x34 || address == 0x3c || address == 0x3d || + pci_access_cap_config(d, address, len)) { + val = pci_default_read_config(d, address, len); + DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", + (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); + return val; + } + + /* vga specific, remove later */ + if (address == 0xFC) + goto do_log; + + fd = pci_dev->real_device.config_fd; + +again: + ret = pread(fd, &val, len, address); + if (ret != len) { + if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) + goto again; + + fprintf(stderr, "%s: pread failed, ret = %zd errno = %d\n", + __func__, ret, errno); + + exit(1); + } + +do_log: + DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", + (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); + + if (!pci_dev->cap.available) { + /* kill the special capabilities */ + if (address == 4 && len == 4) + val &= ~0x100000; + else if (address == 6) + val &= ~0x10; + } + + return val; +} + +static int assigned_dev_register_regions(PCIRegion *io_regions, + unsigned long regions_num, + AssignedDevice *pci_dev) +{ + uint32_t i; + PCIRegion *cur_region = io_regions; + + for (i = 0; i < regions_num; i++, cur_region++) { + if (!cur_region->valid) + continue; + pci_dev->v_addrs[i].num = i; + + /* handle memory io regions */ + if (cur_region->type & IORESOURCE_MEM) { + int t = cur_region->type & IORESOURCE_PREFETCH + ? PCI_BASE_ADDRESS_MEM_PREFETCH + : PCI_BASE_ADDRESS_SPACE_MEMORY; + if (cur_region->size & 0xFFF) { + fprintf(stderr, "Unable to assign device: PCI region %d " + "at address 0x%llx has size 0x%x, " + " which is not a multiple of 4K\n", + i, (unsigned long long)cur_region->base_addr, + cur_region->size); + return -1; + } + + /* map physical memory */ + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + if (i == PCI_ROM_SLOT) { + pci_dev->v_addrs[i].u.r_virtbase = + mmap(NULL, + (cur_region->size + 0xFFF) & 0xFFFFF000, + PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, + 0, (off_t) 0); + + } else { + pci_dev->v_addrs[i].u.r_virtbase = + mmap(NULL, + (cur_region->size + 0xFFF) & 0xFFFFF000, + PROT_WRITE | PROT_READ, MAP_SHARED, + cur_region->resource_fd, (off_t) 0); + } + + if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) { + pci_dev->v_addrs[i].u.r_virtbase = NULL; + fprintf(stderr, "%s: Error: Couldn't mmap 0x%x!" + "\n", __func__, + (uint32_t) (cur_region->base_addr)); + return -1; + } + + if (i == PCI_ROM_SLOT) { + memset(pci_dev->v_addrs[i].u.r_virtbase, 0, + (cur_region->size + 0xFFF) & 0xFFFFF000); + mprotect(pci_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + (cur_region->size + 0xFFF) & 0xFFFFF000, PROT_READ); + } + + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size = 0; + + /* add offset */ + pci_dev->v_addrs[i].u.r_virtbase += + (cur_region->base_addr & 0xFFF); + + pci_register_bar((PCIDevice *) pci_dev, i, + cur_region->size, t, + assigned_dev_iomem_map); + continue; + } + /* handle port io regions */ + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size = 0; + + pci_register_bar((PCIDevice *) pci_dev, i, + cur_region->size, PCI_BASE_ADDRESS_SPACE_IO, + assigned_dev_ioport_map); + + /* not relevant for port io */ + pci_dev->v_addrs[i].memory_index = 0; + } + + /* success */ + return 0; +} + +static int get_real_device(AssignedDevice *pci_dev, uint8_t r_bus, + uint8_t r_dev, uint8_t r_func) +{ + char dir[128], name[128]; + int fd, r = 0; + FILE *f; + unsigned long long start, end, size, flags; + unsigned long id; + struct stat statbuf; + PCIRegion *rp; + PCIDevRegions *dev = &pci_dev->real_device; + + dev->region_number = 0; + + snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/0000:%02x:%02x.%x/", + r_bus, r_dev, r_func); + + snprintf(name, sizeof(name), "%sconfig", dir); + + fd = open(name, O_RDWR); + if (fd == -1) { + fprintf(stderr, "%s: %s: %m\n", __func__, name); + return 1; + } + dev->config_fd = fd; +again: + r = read(fd, pci_dev->dev.config, pci_config_size(&pci_dev->dev)); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) + goto again; + fprintf(stderr, "%s: read failed, errno = %d\n", __func__, errno); + } + + snprintf(name, sizeof(name), "%sresource", dir); + + f = fopen(name, "r"); + if (f == NULL) { + fprintf(stderr, "%s: %s: %m\n", __func__, name); + return 1; + } + + for (r = 0; r < PCI_NUM_REGIONS; r++) { + if (fscanf(f, "%lli %lli %lli\n", &start, &end, &flags) != 3) + break; + + rp = dev->regions + r; + rp->valid = 0; + size = end - start + 1; + flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; + if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0) + continue; + if (flags & IORESOURCE_MEM) { + flags &= ~IORESOURCE_IO; + if (r != PCI_ROM_SLOT) { + snprintf(name, sizeof(name), "%sresource%d", dir, r); + fd = open(name, O_RDWR); + if (fd == -1) + continue; + rp->resource_fd = fd; + } + } else + flags &= ~IORESOURCE_PREFETCH; + + rp->type = flags; + rp->valid = 1; + rp->base_addr = start; + rp->size = size; + DEBUG("region %d size %d start 0x%llx type %d resource_fd %d\n", + r, rp->size, start, rp->type, rp->resource_fd); + } + + fclose(f); + + /* read and fill device ID */ + snprintf(name, sizeof(name), "%svendor", dir); + f = fopen(name, "r"); + if (f == NULL) { + fprintf(stderr, "%s: %s: %m\n", __func__, name); + return 1; + } + if (fscanf(f, "%li\n", &id) == 1) { + pci_dev->dev.config[0] = id & 0xff; + pci_dev->dev.config[1] = (id & 0xff00) >> 8; + } + fclose(f); + + /* read and fill vendor ID */ + snprintf(name, sizeof(name), "%sdevice", dir); + f = fopen(name, "r"); + if (f == NULL) { + fprintf(stderr, "%s: %s: %m\n", __func__, name); + return 1; + } + if (fscanf(f, "%li\n", &id) == 1) { + pci_dev->dev.config[2] = id & 0xff; + pci_dev->dev.config[3] = (id & 0xff00) >> 8; + } + fclose(f); + + /* dealing with virtual function device */ + snprintf(name, sizeof(name), "%sphysfn/", dir); + if (!stat(name, &statbuf)) + pci_dev->need_emulate_cmd = 1; + else + pci_dev->need_emulate_cmd = 0; + + dev->region_number = r; + return 0; +} + +static QLIST_HEAD(, AssignedDevice) devs = QLIST_HEAD_INITIALIZER(devs); + +#ifdef KVM_CAP_IRQ_ROUTING +static void free_dev_irq_entries(AssignedDevice *dev) +{ + int i; + + for (i = 0; i < dev->irq_entries_nr; i++) + kvm_del_routing_entry(kvm_context, &dev->entry[i]); + free(dev->entry); + dev->entry = NULL; + dev->irq_entries_nr = 0; +} +#endif + +static void free_assigned_device(AssignedDevice *dev) +{ + if (dev) { + int i; + + for (i = 0; i < dev->real_device.region_number; i++) { + PCIRegion *pci_region = &dev->real_device.regions[i]; + AssignedDevRegion *region = &dev->v_addrs[i]; + + if (!pci_region->valid) + continue; + + if (pci_region->type & IORESOURCE_IO) { + kvm_remove_ioperm_data(region->u.r_baseport, region->r_size); + continue; + } else if (pci_region->type & IORESOURCE_MEM) { + if (region->e_size > 0) + kvm_destroy_phys_mem(kvm_context, region->e_physbase, + TARGET_PAGE_ALIGN(region->e_size)); + + if (region->u.r_virtbase) { + int ret = munmap(region->u.r_virtbase, + (pci_region->size + 0xFFF) & 0xFFFFF000); + if (ret != 0) + fprintf(stderr, + "Failed to unmap assigned device region: %s\n", + strerror(errno)); + } + } + } + + if (dev->real_device.config_fd) { + close(dev->real_device.config_fd); + dev->real_device.config_fd = 0; + } + +#ifdef KVM_CAP_IRQ_ROUTING + free_dev_irq_entries(dev); +#endif + } +} + +static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t devfn) +{ + return (uint32_t)bus << 8 | (uint32_t)devfn; +} + +static int assign_device(AssignedDevice *dev) +{ + struct kvm_assigned_pci_dev assigned_dev_data; + int r; + + memset(&assigned_dev_data, 0, sizeof(assigned_dev_data)); + assigned_dev_data.assigned_dev_id = + calc_assigned_dev_id(dev->h_busnr, dev->h_devfn); + assigned_dev_data.busnr = dev->h_busnr; + assigned_dev_data.devfn = dev->h_devfn; + +#ifdef KVM_CAP_IOMMU + /* We always enable the IOMMU unless disabled on the command line */ + if (dev->use_iommu) { + if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) { + fprintf(stderr, "No IOMMU found. Unable to assign device \"%s\"\n", + dev->dev.qdev.id); + return -ENODEV; + } + assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU; + } +#else + dev->use_iommu = 0; +#endif + + r = kvm_assign_pci_device(kvm_context, &assigned_dev_data); + if (r < 0) + fprintf(stderr, "Failed to assign device \"%s\" : %s\n", + dev->dev.qdev.id, strerror(-r)); + return r; +} + +static int assign_irq(AssignedDevice *dev) +{ + struct kvm_assigned_irq assigned_irq_data; + int irq, r = 0; + + /* Interrupt PIN 0 means don't use INTx */ + if (pci_read_byte(dev->pdev, PCI_INTERRUPT_PIN) == 0) + return 0; + + irq = pci_map_irq(&dev->dev, dev->intpin); + irq = piix_get_irq(irq); + +#ifdef TARGET_IA64 + irq = ipf_map_irq(&dev->dev, irq); +#endif + + if (dev->girq == irq) + return r; + + memset(&assigned_irq_data, 0, sizeof(assigned_irq_data)); + assigned_irq_data.assigned_dev_id = + calc_assigned_dev_id(dev->h_busnr, dev->h_devfn); + assigned_irq_data.guest_irq = irq; + assigned_irq_data.host_irq = dev->real_device.irq; +#ifdef KVM_CAP_ASSIGN_DEV_IRQ + if (dev->irq_requested_type) { + assigned_irq_data.flags = dev->irq_requested_type; + r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + /* -ENXIO means no assigned irq */ + if (r && r != -ENXIO) + perror("assign_irq: deassign"); + } + + assigned_irq_data.flags = KVM_DEV_IRQ_GUEST_INTX; + if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) + assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_MSI; + else + assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_INTX; +#endif + + r = kvm_assign_irq(kvm_context, &assigned_irq_data); + if (r < 0) { + fprintf(stderr, "Failed to assign irq for \"%s\": %s\n", + dev->dev.qdev.id, strerror(-r)); + fprintf(stderr, "Perhaps you are assigning a device " + "that shares an IRQ with another device?\n"); + return r; + } + + dev->girq = irq; + dev->irq_requested_type = assigned_irq_data.flags; + return r; +} + +static void deassign_device(AssignedDevice *dev) +{ +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT + struct kvm_assigned_pci_dev assigned_dev_data; + int r; + + memset(&assigned_dev_data, 0, sizeof(assigned_dev_data)); + assigned_dev_data.assigned_dev_id = + calc_assigned_dev_id(dev->h_busnr, dev->h_devfn); + + r = kvm_deassign_pci_device(kvm_context, &assigned_dev_data); + if (r < 0) + fprintf(stderr, "Failed to deassign device \"%s\" : %s\n", + dev->dev.qdev.id, strerror(-r)); +#endif +} + +#if 0 +AssignedDevInfo *get_assigned_device(int pcibus, int slot) +{ + AssignedDevice *assigned_dev = NULL; + AssignedDevInfo *adev = NULL; + + QLIST_FOREACH(adev, &adev_head, next) { + assigned_dev = adev->assigned_dev; + if (pci_bus_num(assigned_dev->dev.bus) == pcibus && + PCI_SLOT(assigned_dev->dev.devfn) == slot) + return adev; + } + + return NULL; +} +#endif + +/* The pci config space got updated. Check if irq numbers have changed + * for our devices + */ +void assigned_dev_update_irqs(void) +{ + AssignedDevice *dev, *next; + int r; + + dev = QLIST_FIRST(&devs); + while (dev) { + next = QLIST_NEXT(dev, next); + r = assign_irq(dev); + if (r < 0) + qdev_unplug(&dev->dev.qdev); + dev = next; + } +} + +#ifdef KVM_CAP_IRQ_ROUTING + +#ifdef KVM_CAP_DEVICE_MSI +static void assigned_dev_update_msi(PCIDevice *pci_dev, unsigned int ctrl_pos) +{ + struct kvm_assigned_irq assigned_irq_data; + AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev); + uint8_t ctrl_byte = pci_dev->config[ctrl_pos]; + int r; + + memset(&assigned_irq_data, 0, sizeof assigned_irq_data); + assigned_irq_data.assigned_dev_id = + calc_assigned_dev_id(assigned_dev->h_busnr, + (uint8_t)assigned_dev->h_devfn); + + if (assigned_dev->irq_requested_type) { + assigned_irq_data.flags = assigned_dev->irq_requested_type; + free_dev_irq_entries(assigned_dev); + r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + /* -ENXIO means no assigned irq */ + if (r && r != -ENXIO) + perror("assigned_dev_update_msi: deassign irq"); + } + + if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) { + assigned_dev->entry = calloc(1, sizeof(struct kvm_irq_routing_entry)); + if (!assigned_dev->entry) { + perror("assigned_dev_update_msi: "); + return; + } + assigned_dev->entry->u.msi.address_lo = + *(uint32_t *)(pci_dev->config + pci_dev->cap.start + + PCI_MSI_ADDRESS_LO); + assigned_dev->entry->u.msi.address_hi = 0; + assigned_dev->entry->u.msi.data = *(uint16_t *)(pci_dev->config + + pci_dev->cap.start + PCI_MSI_DATA_32); + assigned_dev->entry->type = KVM_IRQ_ROUTING_MSI; + r = kvm_get_irq_route_gsi(kvm_context); + if (r < 0) { + perror("assigned_dev_update_msi: kvm_get_irq_route_gsi"); + return; + } + assigned_dev->entry->gsi = r; + + kvm_add_routing_entry(kvm_context, assigned_dev->entry); + if (kvm_commit_irq_routes(kvm_context) < 0) { + perror("assigned_dev_update_msi: kvm_commit_irq_routes"); + assigned_dev->cap.state &= ~ASSIGNED_DEVICE_MSI_ENABLED; + return; + } + assigned_dev->irq_entries_nr = 1; + + assigned_irq_data.guest_irq = assigned_dev->entry->gsi; + assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSI | KVM_DEV_IRQ_GUEST_MSI; + if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0) + perror("assigned_dev_enable_msi: assign irq"); + + assigned_dev->irq_requested_type = assigned_irq_data.flags; + } +} +#endif + +#ifdef KVM_CAP_DEVICE_MSIX +static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) +{ + AssignedDevice *adev = container_of(pci_dev, AssignedDevice, dev); + u16 entries_nr = 0, entries_max_nr; + int pos = 0, i, r = 0; + u32 msg_addr, msg_upper_addr, msg_data, msg_ctrl; + struct kvm_assigned_msix_nr msix_nr; + struct kvm_assigned_msix_entry msix_entry; + void *va = adev->msix_table_page; + + if (adev->cap.available & ASSIGNED_DEVICE_CAP_MSI) + pos = pci_dev->cap.start + PCI_CAPABILITY_CONFIG_MSI_LENGTH; + else + pos = pci_dev->cap.start; + + entries_max_nr = pci_dev->config[pos + 2]; + entries_max_nr &= PCI_MSIX_TABSIZE; + entries_max_nr += 1; + + /* Get the usable entry number for allocating */ + for (i = 0; i < entries_max_nr; i++) { + memcpy(&msg_ctrl, va + i * 16 + 12, 4); + memcpy(&msg_data, va + i * 16 + 8, 4); + /* Ignore unused entry even it's unmasked */ + if (msg_data == 0) + continue; + entries_nr ++; + } + + if (entries_nr == 0) { + fprintf(stderr, "MSI-X entry number is zero!\n"); + return -EINVAL; + } + msix_nr.assigned_dev_id = calc_assigned_dev_id(adev->h_busnr, + (uint8_t)adev->h_devfn); + msix_nr.entry_nr = entries_nr; + r = kvm_assign_set_msix_nr(kvm_context, &msix_nr); + if (r != 0) { + fprintf(stderr, "fail to set MSI-X entry number for MSIX! %s\n", + strerror(-r)); + return r; + } + + free_dev_irq_entries(adev); + adev->irq_entries_nr = entries_nr; + adev->entry = calloc(entries_nr, sizeof(struct kvm_irq_routing_entry)); + if (!adev->entry) { + perror("assigned_dev_update_msix_mmio: "); + return -errno; + } + + msix_entry.assigned_dev_id = msix_nr.assigned_dev_id; + entries_nr = 0; + for (i = 0; i < entries_max_nr; i++) { + if (entries_nr >= msix_nr.entry_nr) + break; + memcpy(&msg_ctrl, va + i * 16 + 12, 4); + memcpy(&msg_data, va + i * 16 + 8, 4); + if (msg_data == 0) + continue; + + memcpy(&msg_addr, va + i * 16, 4); + memcpy(&msg_upper_addr, va + i * 16 + 4, 4); + + r = kvm_get_irq_route_gsi(kvm_context); + if (r < 0) + return r; + + adev->entry[entries_nr].gsi = r; + adev->entry[entries_nr].type = KVM_IRQ_ROUTING_MSI; + adev->entry[entries_nr].flags = 0; + adev->entry[entries_nr].u.msi.address_lo = msg_addr; + adev->entry[entries_nr].u.msi.address_hi = msg_upper_addr; + adev->entry[entries_nr].u.msi.data = msg_data; + DEBUG("MSI-X data 0x%x, MSI-X addr_lo 0x%x\n!", msg_data, msg_addr); + kvm_add_routing_entry(kvm_context, &adev->entry[entries_nr]); + + msix_entry.gsi = adev->entry[entries_nr].gsi; + msix_entry.entry = i; + r = kvm_assign_set_msix_entry(kvm_context, &msix_entry); + if (r) { + fprintf(stderr, "fail to set MSI-X entry! %s\n", strerror(-r)); + break; + } + DEBUG("MSI-X entry gsi 0x%x, entry %d\n!", + msix_entry.gsi, msix_entry.entry); + entries_nr ++; + } + + if (r == 0 && kvm_commit_irq_routes(kvm_context) < 0) { + perror("assigned_dev_update_msix_mmio: kvm_commit_irq_routes"); + return -EINVAL; + } + + return r; +} + +static void assigned_dev_update_msix(PCIDevice *pci_dev, unsigned int ctrl_pos) +{ + struct kvm_assigned_irq assigned_irq_data; + AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev); + uint16_t *ctrl_word = (uint16_t *)(pci_dev->config + ctrl_pos); + int r; + + memset(&assigned_irq_data, 0, sizeof assigned_irq_data); + assigned_irq_data.assigned_dev_id = + calc_assigned_dev_id(assigned_dev->h_busnr, + (uint8_t)assigned_dev->h_devfn); + + if (assigned_dev->irq_requested_type) { + assigned_irq_data.flags = assigned_dev->irq_requested_type; + free_dev_irq_entries(assigned_dev); + r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + /* -ENXIO means no assigned irq */ + if (r && r != -ENXIO) + perror("assigned_dev_update_msix: deassign irq"); + } + assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSIX | KVM_DEV_IRQ_GUEST_MSIX; + + if (*ctrl_word & PCI_MSIX_ENABLE) { + if (assigned_dev_update_msix_mmio(pci_dev) < 0) { + perror("assigned_dev_update_msix_mmio"); + return; + } + if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0) { + perror("assigned_dev_enable_msix: assign irq"); + return; + } + assigned_dev->irq_requested_type = assigned_irq_data.flags; + } +} +#endif +#endif + +static void assigned_device_pci_cap_write_config(PCIDevice *pci_dev, uint32_t address, + uint32_t val, int len) +{ + AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev); + unsigned int pos = pci_dev->cap.start, ctrl_pos; + + pci_default_cap_write_config(pci_dev, address, val, len); +#ifdef KVM_CAP_IRQ_ROUTING +#ifdef KVM_CAP_DEVICE_MSI + if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { + ctrl_pos = pos + PCI_MSI_FLAGS; + if (address <= ctrl_pos && address + len > ctrl_pos) + assigned_dev_update_msi(pci_dev, ctrl_pos); + pos += PCI_CAPABILITY_CONFIG_MSI_LENGTH; + } +#endif +#ifdef KVM_CAP_DEVICE_MSIX + if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { + ctrl_pos = pos + 3; + if (address <= ctrl_pos && address + len > ctrl_pos) { + ctrl_pos--; /* control is word long */ + assigned_dev_update_msix(pci_dev, ctrl_pos); + } + pos += PCI_CAPABILITY_CONFIG_MSIX_LENGTH; + } +#endif +#endif + return; +} + +static int assigned_device_pci_cap_init(PCIDevice *pci_dev) +{ + AssignedDevice *dev = container_of(pci_dev, AssignedDevice, dev); + PCIRegion *pci_region = dev->real_device.regions; + int next_cap_pt = 0; + + pci_dev->cap.length = 0; +#ifdef KVM_CAP_IRQ_ROUTING +#ifdef KVM_CAP_DEVICE_MSI + /* Expose MSI capability + * MSI capability is the 1st capability in capability config */ + if (pci_find_cap_offset(dev->pdev, PCI_CAP_ID_MSI)) { + dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI; + memset(&pci_dev->config[pci_dev->cap.start + pci_dev->cap.length], + 0, PCI_CAPABILITY_CONFIG_MSI_LENGTH); + pci_dev->config[pci_dev->cap.start + pci_dev->cap.length] = + PCI_CAP_ID_MSI; + pci_dev->cap.length += PCI_CAPABILITY_CONFIG_MSI_LENGTH; + next_cap_pt = 1; + } +#endif +#ifdef KVM_CAP_DEVICE_MSIX + /* Expose MSI-X capability */ + if (pci_find_cap_offset(dev->pdev, PCI_CAP_ID_MSIX)) { + int pos, entry_nr, bar_nr; + u32 msix_table_entry; + dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX; + memset(&pci_dev->config[pci_dev->cap.start + pci_dev->cap.length], + 0, PCI_CAPABILITY_CONFIG_MSIX_LENGTH); + pos = pci_find_cap_offset(dev->pdev, PCI_CAP_ID_MSIX); + entry_nr = pci_read_word(dev->pdev, pos + 2) & PCI_MSIX_TABSIZE; + pci_dev->config[pci_dev->cap.start + pci_dev->cap.length] = 0x11; + pci_dev->config[pci_dev->cap.start + + pci_dev->cap.length + 2] = entry_nr; + msix_table_entry = pci_read_long(dev->pdev, pos + PCI_MSIX_TABLE); + *(uint32_t *)(pci_dev->config + pci_dev->cap.start + + pci_dev->cap.length + PCI_MSIX_TABLE) = msix_table_entry; + *(uint32_t *)(pci_dev->config + pci_dev->cap.start + + pci_dev->cap.length + PCI_MSIX_PBA) = + pci_read_long(dev->pdev, pos + PCI_MSIX_PBA); + bar_nr = msix_table_entry & PCI_MSIX_BIR; + msix_table_entry &= ~PCI_MSIX_BIR; + dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry; + if (next_cap_pt != 0) { + pci_dev->config[pci_dev->cap.start + next_cap_pt] = + pci_dev->cap.start + pci_dev->cap.length; + next_cap_pt += PCI_CAPABILITY_CONFIG_MSI_LENGTH; + } else + next_cap_pt = 1; + pci_dev->cap.length += PCI_CAPABILITY_CONFIG_MSIX_LENGTH; + } +#endif +#endif + + return 0; +} + +static uint32_t msix_mmio_readl(void *opaque, target_phys_addr_t addr) +{ + AssignedDevice *adev = opaque; + unsigned int offset = addr & 0xfff; + void *page = adev->msix_table_page; + uint32_t val = 0; + + memcpy(&val, (void *)((char *)page + offset), 4); + + return val; +} + +static uint32_t msix_mmio_readb(void *opaque, target_phys_addr_t addr) +{ + return ((msix_mmio_readl(opaque, addr & ~3)) >> + (8 * (addr & 3))) & 0xff; +} + +static uint32_t msix_mmio_readw(void *opaque, target_phys_addr_t addr) +{ + return ((msix_mmio_readl(opaque, addr & ~3)) >> + (8 * (addr & 3))) & 0xffff; +} + +static void msix_mmio_writel(void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + AssignedDevice *adev = opaque; + unsigned int offset = addr & 0xfff; + void *page = adev->msix_table_page; + + DEBUG("write to MSI-X entry table mmio offset 0x%lx, val 0x%lx\n", + addr, val); + memcpy((void *)((char *)page + offset), &val, 4); +} + +static void msix_mmio_writew(void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + msix_mmio_writel(opaque, addr & ~3, + (val & 0xffff) << (8*(addr & 3))); +} + +static void msix_mmio_writeb(void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + msix_mmio_writel(opaque, addr & ~3, + (val & 0xff) << (8*(addr & 3))); +} + +static CPUWriteMemoryFunc *msix_mmio_write[] = { + msix_mmio_writeb, msix_mmio_writew, msix_mmio_writel +}; + +static CPUReadMemoryFunc *msix_mmio_read[] = { + msix_mmio_readb, msix_mmio_readw, msix_mmio_readl +}; + +static int assigned_dev_register_msix_mmio(AssignedDevice *dev) +{ + dev->msix_table_page = mmap(NULL, 0x1000, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); + if (dev->msix_table_page == MAP_FAILED) { + fprintf(stderr, "fail allocate msix_table_page! %s\n", + strerror(errno)); + return -EFAULT; + } + memset(dev->msix_table_page, 0, 0x1000); + dev->mmio_index = cpu_register_io_memory( + msix_mmio_read, msix_mmio_write, dev); + return 0; +} + +static int assigned_initfn(struct PCIDevice *pci_dev) +{ + AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); + struct pci_access *pacc; + uint8_t e_device, e_intx; + int r; + + if (!dev->host.bus && !dev->host.dev && !dev->host.func) { + qemu_error("pci-assign: error: no host device specified\n"); + goto out; + } + + if (get_real_device(dev, dev->host.bus, dev->host.dev, dev->host.func)) { + qemu_error("pci-assign: Error: Couldn't get real device (%s)!\n", + dev->dev.qdev.id); + goto out; + } + + /* handle real device's MMIO/PIO BARs */ + if (assigned_dev_register_regions(dev->real_device.regions, + dev->real_device.region_number, + dev)) + goto out; + + /* handle interrupt routing */ + e_device = (dev->dev.devfn >> 3) & 0x1f; + e_intx = dev->dev.config[0x3d] - 1; + dev->intpin = e_intx; + dev->run = 0; + dev->girq = 0; + dev->h_busnr = dev->host.bus; + dev->h_devfn = PCI_DEVFN(dev->host.dev, dev->host.func); + + pacc = pci_alloc(); + pci_init(pacc); + dev->pdev = pci_get_dev(pacc, 0, dev->host.bus, dev->host.dev, dev->host.func); + + if (pci_enable_capability_support(pci_dev, 0, NULL, + assigned_device_pci_cap_write_config, + assigned_device_pci_cap_init) < 0) + goto assigned_out; + + /* assign device to guest */ + r = assign_device(dev); + if (r < 0) + goto assigned_out; + + /* assign irq for the device */ + r = assign_irq(dev); + if (r < 0) + goto assigned_out; + + /* intercept MSI-X entry page in the MMIO */ + if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) + if (assigned_dev_register_msix_mmio(dev)) + goto assigned_out; + + assigned_dev_load_option_rom(dev); + QLIST_INSERT_HEAD(&devs, dev, next); + return 0; + +assigned_out: + deassign_device(dev); +out: + free_assigned_device(dev); + return -1; +} + +static int assigned_exitfn(struct PCIDevice *pci_dev) +{ + AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); + + deassign_device(dev); + free_assigned_device(dev); + return 0; +} + +static int parse_hostaddr(DeviceState *dev, Property *prop, const char *str) +{ + PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop); + int rc; + + rc = pci_parse_host_devaddr(str, &ptr->bus, &ptr->dev, &ptr->func); + if (rc != 0) + return -1; + return 0; +} + +static int print_hostaddr(DeviceState *dev, Property *prop, char *dest, size_t len) +{ + PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop); + + return snprintf(dest, len, "%02x:%02x.%x", ptr->bus, ptr->dev, ptr->func); +} + +PropertyInfo qdev_prop_hostaddr = { + .name = "pci-hostaddr", + .type = -1, + .size = sizeof(PCIHostDevice), + .parse = parse_hostaddr, + .print = print_hostaddr, +}; + +static PCIDeviceInfo assign_info = { + .qdev.name = "pci-assign", + .qdev.desc = "pass through host pci devices to the guest", + .qdev.size = sizeof(AssignedDevice), + .init = assigned_initfn, + .exit = assigned_exitfn, + .config_read = assigned_dev_pci_read_config, + .config_write = assigned_dev_pci_write_config, + .qdev.props = (Property[]) { + DEFINE_PROP("host", AssignedDevice, host, qdev_prop_hostaddr, PCIHostDevice), + DEFINE_PROP_UINT32("iommu", AssignedDevice, use_iommu, 1), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void assign_register_devices(void) +{ + pci_qdev_register(&assign_info); +} + +device_init(assign_register_devices) + + +/* + * Syntax to assign device: + * + * -pcidevice host=bus:dev.func[,dma=none][,name=Foo] + * + * Example: + * -pcidevice host=00:13.0,dma=pvdma + * + * dma can currently only be 'none' to disable iommu support. + */ +QemuOpts *add_assigned_device(const char *arg) +{ + QemuOpts *opts = NULL; + char host[64], id[64], dma[8]; + int r; + + r = get_param_value(host, sizeof(host), "host", arg); + if (!r) + goto bad; + r = get_param_value(id, sizeof(id), "id", arg); + if (!r) + r = get_param_value(id, sizeof(id), "name", arg); + if (!r) + r = get_param_value(id, sizeof(id), "host", arg); + + opts = qemu_opts_create(&qemu_device_opts, id, 0); + if (!opts) + goto bad; + qemu_opt_set(opts, "driver", "pci-assign"); + qemu_opt_set(opts, "host", host); + +#ifdef KVM_CAP_IOMMU + r = get_param_value(dma, sizeof(dma), "dma", arg); + if (r && !strncmp(dma, "none", 4)) + qemu_opt_set(opts, "iommu", "0"); +#endif + qemu_opts_print(opts, NULL); + return opts; + +bad: + fprintf(stderr, "pcidevice argument parse error; " + "please check the help text for usage\n"); + if (opts) + qemu_opts_del(opts); + return NULL; +} + +void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices) +{ + QemuOpts *opts; + int i; + + for (i = 0; i < n_devices; i++) { + opts = add_assigned_device(devices[i]); + if (opts == NULL) { + fprintf(stderr, "Could not add assigned device %s\n", devices[i]); + exit(1); + } + /* generic code will call qdev_device_add() for the device */ + } +} + +/* Option ROM header */ +struct option_rom_header { + uint8_t signature[2]; + uint8_t rom_size; + uint32_t entry_point; + uint8_t reserved[17]; + uint16_t pci_header_offset; + uint16_t expansion_header_offset; +} __attribute__ ((packed)); + +/* Option ROM PCI data structure */ +struct option_rom_pci_header { + uint8_t signature[4]; + uint16_t vendor_id; + uint16_t device_id; + uint16_t vital_product_data_offset; + uint16_t structure_length; + uint8_t structure_revision; + uint8_t class_code[3]; + uint16_t image_length; + uint16_t image_revision; + uint8_t code_type; + uint8_t indicator; + uint16_t reserved; +} __attribute__ ((packed)); + +/* + * Scan the list of Option ROMs at roms. If a suitable Option ROM is found, + * allocate a ram space and copy it there. Then return its size aligned to + * both 2KB and target page size. + */ +#define OPTION_ROM_ALIGN(x) (((x) + 2047) & ~2047) +static void scan_option_rom(const char *name, uint8_t devfn, void *roms) +{ + int i, size; + uint8_t csum; + struct option_rom_header *rom; + struct option_rom_pci_header *pcih; + + rom = roms; + + for ( ; ; ) { + /* Invalid signature means we're out of option ROMs. */ + if (strncmp((char *)rom->signature, "\x55\xaa", 2) || + (rom->rom_size == 0)) + break; + + size = rom->rom_size * 512; + /* Invalid checksum means we're out of option ROMs. */ + csum = 0; + for (i = 0; i < size; i++) + csum += ((uint8_t *)rom)[i]; + if (csum != 0) + break; + + /* Check the PCI header (if any) for a match. */ + pcih = (struct option_rom_pci_header *) + ((char *)rom + rom->pci_header_offset); + if ((rom->pci_header_offset != 0) && + !strncmp((char *)pcih->signature, "PCIR", 4)) + goto found; + + rom = (struct option_rom_header *)((char *)rom + size); + } + return; + + found: + rom_add_blob(name ? name : "assigned device", rom, size, 0); + return; +} + +/* + * Scan the assigned devices for the devices that have an option ROM, and then + * load the corresponding ROM data to RAM. If an error occurs while loading an + * option ROM, we just ignore that option ROM and continue with the next one. + */ +static void assigned_dev_load_option_rom(AssignedDevice *dev) +{ + int size, len; + void *buf; + FILE *fp; + uint8_t i = 1; + char rom_file[64]; + + snprintf(rom_file, sizeof(rom_file), + "/sys/bus/pci/devices/0000:%02x:%02x.%01x/rom", + dev->host.bus, dev->host.dev, dev->host.func); + + if (access(rom_file, F_OK)) + return; + + /* Write something to the ROM file to enable it */ + fp = fopen(rom_file, "wb"); + if (fp == NULL) + return; + len = fwrite(&i, 1, 1, fp); + fclose(fp); + if (len != 1) + return; + + /* The file has to be closed and reopened, otherwise it won't work */ + fp = fopen(rom_file, "rb"); + if (fp == NULL) + return; + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buf = malloc(size); + if (buf == NULL) { + fclose(fp); + return; + } + + fread(buf, size, 1, fp); + if (!feof(fp) || ferror(fp)) { + free(buf); + fclose(fp); + return; + } + fclose(fp); + + /* Copy ROM contents into the space backing the ROM BAR */ + if (dev->v_addrs[PCI_ROM_SLOT].r_size >= size && + dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase) { + mprotect(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + size, PROT_READ | PROT_WRITE); + memcpy(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + buf, size); + mprotect(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + size, PROT_READ); + } + + if (!dev->dev.qdev.hotplugged) { + /* Scan the buffer for suitable ROMs and increase the offset */ + scan_option_rom(dev->dev.qdev.id, dev->dev.devfn, buf); + } + free(buf); +} diff --git a/hw/device-assignment.h b/hw/device-assignment.h new file mode 100644 index 000000000..a23126099 --- /dev/null +++ b/hw/device-assignment.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Data structures for storing PCI state + * + * Adapted to kvm by Qumranet + * + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + */ + +#ifndef __DEVICE_ASSIGNMENT_H__ +#define __DEVICE_ASSIGNMENT_H__ + +#include <sys/mman.h> +#include "qemu-common.h" +#include "qemu-queue.h" +#include "pci.h" + +/* From include/linux/pci.h in the kernel sources */ +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +typedef struct PCIHostDevice { + int bus; + int dev; + int func; +} PCIHostDevice; + +typedef struct { + int type; /* Memory or port I/O */ + int valid; + uint32_t base_addr; + uint32_t size; /* size of the region */ + int resource_fd; +} PCIRegion; + +typedef struct { + uint8_t bus, dev, func; /* Bus inside domain, device and function */ + int irq; /* IRQ number */ + uint16_t region_number; /* number of active regions */ + + /* Port I/O or MMIO Regions */ + PCIRegion regions[PCI_NUM_REGIONS]; + int config_fd; +} PCIDevRegions; + +typedef struct { + pcibus_t e_physbase; + uint32_t memory_index; + union { + void *r_virtbase; /* mmapped access address for memory regions */ + uint32_t r_baseport; /* the base guest port for I/O regions */ + } u; + int num; /* our index within v_addrs[] */ + pcibus_t e_size; /* emulated size of region in bytes */ + pcibus_t r_size; /* real size of region in bytes */ +} AssignedDevRegion; + +typedef struct AssignedDevice { + PCIDevice dev; + PCIHostDevice host; + uint32_t use_iommu; + int intpin; + uint8_t debug_flags; + AssignedDevRegion v_addrs[PCI_NUM_REGIONS]; + PCIDevRegions real_device; + int run; + int girq; + unsigned char h_busnr; + unsigned int h_devfn; + int irq_requested_type; + int bound; + struct pci_dev *pdev; + struct { +#define ASSIGNED_DEVICE_CAP_MSI (1 << 0) +#define ASSIGNED_DEVICE_CAP_MSIX (1 << 1) + uint32_t available; +#define ASSIGNED_DEVICE_MSI_ENABLED (1 << 0) +#define ASSIGNED_DEVICE_MSIX_ENABLED (1 << 1) +#define ASSIGNED_DEVICE_MSIX_MASKED (1 << 2) + uint32_t state; + } cap; + int irq_entries_nr; + struct kvm_irq_routing_entry *entry; + void *msix_table_page; + target_phys_addr_t msix_table_addr; + int mmio_index; + int need_emulate_cmd; + QLIST_ENTRY(AssignedDevice) next; +} AssignedDevice; + +QemuOpts *add_assigned_device(const char *arg); +void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices); +void assigned_dev_update_irqs(void); + +#define MAX_DEV_ASSIGN_CMDLINE 8 + +extern const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE]; +extern int assigned_devices_index; + +#endif /* __DEVICE_ASSIGNMENT_H__ */ diff --git a/hw/extboot.c b/hw/extboot.c new file mode 100644 index 000000000..b91d54f2f --- /dev/null +++ b/hw/extboot.c @@ -0,0 +1,135 @@ +/* + * Extended boot option ROM support. + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "hw.h" +#include "pc.h" +#include "isa.h" +#include "block.h" + +/* Extended Boot ROM suport */ + +union extboot_cmd +{ + uint16_t type; + struct { + uint16_t type; + uint16_t cylinders; + uint16_t heads; + uint16_t sectors; + uint64_t nb_sectors; + } query_geometry; + struct { + uint16_t type; + uint16_t nb_sectors; + uint16_t segment; + uint16_t offset; + uint64_t sector; + } xfer; +}; + +static void get_translated_chs(BlockDriverState *bs, int *c, int *h, int *s) +{ + bdrv_get_geometry_hint(bs, c, h, s); + + if (*c <= 1024) { + *c >>= 0; + *h <<= 0; + } else if (*c <= 2048) { + *c >>= 1; + *h <<= 1; + } else if (*c <= 4096) { + *c >>= 2; + *h <<= 2; + } else if (*c <= 8192) { + *c >>= 3; + *h <<= 3; + } else { + *c >>= 4; + *h <<= 4; + } + + /* what is the correct algorithm for this?? */ + if (*h == 256) { + *h = 255; + *c = *c + 1; + } +} + +static uint32_t extboot_read(void *opaque, uint32_t addr) +{ + int *pcmd = opaque; + return *pcmd; +} + +static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value) +{ + union extboot_cmd cmd; + BlockDriverState *bs = opaque; + int cylinders, heads, sectors, err; + uint64_t nb_sectors; + target_phys_addr_t pa = 0; + int blen = 0; + void *buf = NULL; + + cpu_physical_memory_read((value & 0xFFFF) << 4, (uint8_t *)&cmd, + sizeof(cmd)); + + if (cmd.type == 0x01 || cmd.type == 0x02) { + pa = cmd.xfer.segment * 16 + cmd.xfer.offset; + blen = cmd.xfer.nb_sectors * 512; + buf = qemu_memalign(512, blen); + } + + switch (cmd.type) { + case 0x00: + get_translated_chs(bs, &cylinders, &heads, §ors); + bdrv_get_geometry(bs, &nb_sectors); + cmd.query_geometry.cylinders = cylinders; + cmd.query_geometry.heads = heads; + cmd.query_geometry.sectors = sectors; + cmd.query_geometry.nb_sectors = nb_sectors; + break; + case 0x01: + err = bdrv_read(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors); + if (err) + printf("Read failed\n"); + + cpu_physical_memory_write(pa, buf, blen); + + break; + case 0x02: + cpu_physical_memory_read(pa, buf, blen); + + err = bdrv_write(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors); + if (err) + printf("Write failed\n"); + + break; + } + + cpu_physical_memory_write((value & 0xFFFF) << 4, (uint8_t *)&cmd, + sizeof(cmd)); + if (buf) + qemu_free(buf); +} + +void extboot_init(BlockDriverState *bs, int cmd) +{ + int *pcmd; + + pcmd = qemu_mallocz(sizeof(int)); + + *pcmd = cmd; + register_ioport_read(0x404, 1, 1, extboot_read, pcmd); + register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs); +} @@ -170,6 +170,11 @@ static int hpet_post_load(void *opaque, int version_id) /* Recalculate the offset between the main counter and guest time */ s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock); + + if (hpet_in_legacy_mode()) { + hpet_disable_pit(); + } + return 0; } @@ -473,9 +478,11 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, } /* i8254 and RTC are disabled when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_disable(); + hpet_disable_pit(); + dprintf("qemu: hpet disabled pit\n"); } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_enable(); + hpet_enable_pit(); + dprintf("qemu: hpet enabled pit\n"); } break; case HPET_CFG + 4: @@ -559,7 +566,7 @@ static void hpet_reset(void *opaque) { * hpet_reset is called due to system reset. At this point control must * be returned to pit until SW reenables hpet. */ - hpet_pit_enable(); + hpet_enable_pit(); count = 1; } @@ -342,6 +342,10 @@ extern const VMStateInfo vmstate_info_uint16; extern const VMStateInfo vmstate_info_uint32; extern const VMStateInfo vmstate_info_uint64; +#ifdef __linux__ +extern const VMStateInfo vmstate_info_u64; +#endif + extern const VMStateInfo vmstate_info_timer; extern const VMStateInfo vmstate_info_ptimer; extern const VMStateInfo vmstate_info_buffer; @@ -622,6 +626,15 @@ extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_UINT64(_f, _s) \ VMSTATE_UINT64_V(_f, _s, 0) +/* This is needed because on linux __u64 is unsigned long long + and on glibc uint64_t is unsigned long on 64 bits */ +#ifdef __linux__ +#define VMSTATE_U64_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, vmstate_info_u64, __u64) +#define VMSTATE_U64(_f, _s) \ + VMSTATE_U64_V(_f, _s, 0) +#endif + #define VMSTATE_UINT8_EQUAL(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_uint8_equal, uint8_t) diff --git a/hw/i8254-kvm.c b/hw/i8254-kvm.c new file mode 100644 index 000000000..c62ab6a09 --- /dev/null +++ b/hw/i8254-kvm.c @@ -0,0 +1,122 @@ +/* + * QEMU 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "pc.h" +#include "isa.h" +#include "qemu-timer.h" +#include "i8254.h" +#include "qemu-kvm.h" + +extern VMStateDescription vmstate_pit; + +static PITState pit_state; + +static void kvm_pit_pre_save(void *opaque) +{ + PITState *s = (void *)opaque; + struct kvm_pit_state2 pit2; + struct kvm_pit_channel_state *c; + struct PITChannelState *sc; + int i; + + if(qemu_kvm_has_pit_state2()) { + kvm_get_pit2(kvm_context, &pit2); + s->flags = pit2.flags; + } else { + /* pit2 is superset of pit struct so just cast it and use it */ + kvm_get_pit(kvm_context, (struct kvm_pit_state *)&pit2); + } + for (i = 0; i < 3; i++) { + c = &pit2.channels[i]; + sc = &s->channels[i]; + sc->count = c->count; + sc->latched_count = c->latched_count; + sc->count_latched = c->count_latched; + sc->status_latched = c->status_latched; + sc->status = c->status; + sc->read_state = c->read_state; + sc->write_state = c->write_state; + sc->write_latch = c->write_latch; + sc->rw_mode = c->rw_mode; + sc->mode = c->mode; + sc->bcd = c->bcd; + sc->gate = c->gate; + sc->count_load_time = c->count_load_time; + } +} + +static int kvm_pit_post_load(void *opaque, int version_id) +{ + PITState *s = opaque; + struct kvm_pit_state2 pit2; + struct kvm_pit_channel_state *c; + struct PITChannelState *sc; + int i; + + pit2.flags = s->flags; + for (i = 0; i < 3; i++) { + c = &pit2.channels[i]; + sc = &s->channels[i]; + c->count = sc->count; + c->latched_count = sc->latched_count; + c->count_latched = sc->count_latched; + c->status_latched = sc->status_latched; + c->status = sc->status; + c->read_state = sc->read_state; + c->write_state = sc->write_state; + c->write_latch = sc->write_latch; + c->rw_mode = sc->rw_mode; + c->mode = sc->mode; + c->bcd = sc->bcd; + c->gate = sc->gate; + c->count_load_time = sc->count_load_time; + } + + if(qemu_kvm_has_pit_state2()) { + kvm_set_pit2(kvm_context, &pit2); + } else { + kvm_set_pit(kvm_context, (struct kvm_pit_state *)&pit2); + } + return 0; +} + +static void dummy_timer(void *opaque) +{ +} + +PITState *kvm_pit_init(int base, qemu_irq irq) +{ + PITState *pit = &pit_state; + PITChannelState *s; + + s = &pit->channels[0]; + s->irq_timer = qemu_new_timer(vm_clock, dummy_timer, s); + vmstate_pit.pre_save = kvm_pit_pre_save; + vmstate_pit.post_load = kvm_pit_post_load; + vmstate_register(base, &vmstate_pit, pit); + qemu_register_reset(pit_reset, pit); + pit_reset(pit); + + return pit; +} diff --git a/hw/i8254.c b/hw/i8254.c index faaa884d9..c4f8d2e5b 100644 --- a/hw/i8254.c +++ b/hw/i8254.c @@ -25,38 +25,11 @@ #include "pc.h" #include "isa.h" #include "qemu-timer.h" +#include "qemu-kvm.h" +#include "i8254.h" //#define DEBUG_PIT -#define RW_STATE_LSB 1 -#define RW_STATE_MSB 2 -#define RW_STATE_WORD0 3 -#define RW_STATE_WORD1 4 - -typedef struct PITChannelState { - int count; /* can be 65536 */ - uint16_t latched_count; - uint8_t count_latched; - uint8_t status_latched; - uint8_t status; - uint8_t read_state; - uint8_t write_state; - uint8_t write_latch; - uint8_t rw_mode; - uint8_t mode; - uint8_t bcd; /* not supported */ - uint8_t gate; /* timer start */ - int64_t count_load_time; - /* irq handling */ - int64_t next_transition_time; - QEMUTimer *irq_timer; - qemu_irq irq; -} PITChannelState; - -struct PITState { - PITChannelState channels[3]; -}; - static PITState pit_state; static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); @@ -228,13 +201,18 @@ int pit_get_mode(PITState *pit, int channel) return s->mode; } -static inline void pit_load_count(PITChannelState *s, int val) +static inline void pit_load_count(PITState *s, int val, int chan) { if (val == 0) val = 0x10000; - s->count_load_time = qemu_get_clock(vm_clock); - s->count = val; - pit_irq_timer_update(s, s->count_load_time); + s->channels[chan].count_load_time = qemu_get_clock(vm_clock); + s->channels[chan].count = val; +#ifdef TARGET_I386 + if (chan == 0 && pit_state.flags & PIT_FLAGS_HPET_LEGACY) { + return; + } +#endif + pit_irq_timer_update(&s->channels[chan], s->channels[chan].count_load_time); } /* if already latched, do not latch again */ @@ -294,17 +272,17 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) switch(s->write_state) { default: case RW_STATE_LSB: - pit_load_count(s, val); + pit_load_count(pit, val, addr); break; case RW_STATE_MSB: - pit_load_count(s, val << 8); + pit_load_count(pit, val << 8, addr); break; case RW_STATE_WORD0: s->write_latch = val; s->write_state = RW_STATE_WORD1; break; case RW_STATE_WORD1: - pit_load_count(s, s->write_latch | (val << 8)); + pit_load_count(pit, s->write_latch | (val << 8), addr); s->write_state = RW_STATE_WORD0; break; } @@ -364,6 +342,11 @@ static uint32_t pit_ioport_read(void *opaque, uint32_t addr) return ret; } +/* global counters for time-drift fix */ +int64_t timer_acks=0, timer_interrupts=0, timer_ints_to_push=0; + +extern int time_drift_fix; + static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) { int64_t expire_time; @@ -374,16 +357,35 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) expire_time = pit_get_next_transition_time(s, current_time); irq_level = pit_get_out1(s, current_time); qemu_set_irq(s->irq, irq_level); + if (time_drift_fix && irq_level==1) { + /* FIXME: fine tune timer_max_fix (max fix per tick). + * Should it be 1 (double time), 2 , 4, 10 ? + * Currently setting it to 5% of PIT-ticks-per-second (per PIT-tick) + */ + const long pit_ticks_per_sec = (s->count>0) ? (PIT_FREQ/s->count) : 0; + const long timer_max_fix = pit_ticks_per_sec/20; + const long delta = timer_interrupts - timer_acks; + const long max_delta = pit_ticks_per_sec * 60; /* one minute */ + if ((delta > max_delta) && (pit_ticks_per_sec > 0)) { + printf("time drift is too long, %ld seconds were lost\n", delta/pit_ticks_per_sec); + timer_acks = timer_interrupts; + timer_ints_to_push = 0; + } else if (delta > 0) { + timer_ints_to_push = MIN(delta, timer_max_fix); + } + timer_interrupts++; + } #ifdef DEBUG_PIT printf("irq_level=%d next_delay=%f\n", irq_level, (double)(expire_time - current_time) / get_ticks_per_sec()); #endif s->next_transition_time = expire_time; - if (expire_time != -1) + if (expire_time != -1) { qemu_mod_timer(s->irq_timer, expire_time); - else + } else { qemu_del_timer(s->irq_timer); + } } static void pit_irq_timer(void *opaque) @@ -423,9 +425,10 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) PITChannelState *s; int i; - if (version_id != 1) + if (version_id != PIT_SAVEVM_VERSION) return -EINVAL; + pit->flags = qemu_get_be32(f); for(i = 0; i < 3; i++) { s = &pit->channels[i]; s->count=qemu_get_be32(f); @@ -446,57 +449,85 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_timer(f, s->irq_timer); } } + return 0; } -static const VMStateDescription vmstate_pit = { +VMStateDescription vmstate_pit = { .name = "i8254", .version_id = 2, .minimum_version_id = 2, .minimum_version_id_old = 1, .load_state_old = pit_load_old, .fields = (VMStateField []) { + VMSTATE_UINT32(flags, PITState), VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState), VMSTATE_TIMER(channels[0].irq_timer, PITState), VMSTATE_END_OF_LIST() } }; -static void pit_reset(void *opaque) +void pit_reset(void *opaque) { PITState *pit = opaque; PITChannelState *s; int i; +#ifdef TARGET_I386 + pit->flags &= ~PIT_FLAGS_HPET_LEGACY; +#endif for(i = 0;i < 3; i++) { s = &pit->channels[i]; s->mode = 3; s->gate = (i != 2); - pit_load_count(s, 0); + pit_load_count(pit, 0, i); } } +#ifdef TARGET_I386 /* When HPET is operating in legacy mode, i8254 timer0 is disabled */ -void hpet_pit_disable(void) { - PITChannelState *s; - s = &pit_state.channels[0]; - if (s->irq_timer) - qemu_del_timer(s->irq_timer); + +void hpet_disable_pit(void) +{ + PITChannelState *s = &pit_state.channels[0]; + + if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + if (qemu_kvm_has_pit_state2()) { + kvm_hpet_disable_kpit(); + } else { + fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__); + exit(1); + } + } else { + pit_state.flags |= PIT_FLAGS_HPET_LEGACY; + if (s->irq_timer) { + qemu_del_timer(s->irq_timer); + } + } } /* When HPET is reset or leaving legacy mode, it must reenable i8254 * timer 0 */ -void hpet_pit_enable(void) +void hpet_enable_pit(void) { PITState *pit = &pit_state; - PITChannelState *s; - s = &pit->channels[0]; - s->mode = 3; - s->gate = 1; - pit_load_count(s, 0); + PITChannelState *s = &pit->channels[0]; + + if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + if (qemu_kvm_has_pit_state2()) { + kvm_hpet_enable_kpit(); + } else { + fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__); + exit(1); + } + } else { + pit_state.flags &= ~PIT_FLAGS_HPET_LEGACY; + pit_load_count(pit, s->count, 0); + } } +#endif PITState *pit_init(int base, qemu_irq irq) { diff --git a/hw/i8254.h b/hw/i8254.h new file mode 100644 index 000000000..d23303a8b --- /dev/null +++ b/hw/i8254.h @@ -0,0 +1,69 @@ +/* + * QEMU 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_I8254_H +#define QEMU_I8254_H + +#define PIT_SAVEVM_NAME "i8254" +#define PIT_SAVEVM_VERSION 2 + +#define RW_STATE_LSB 1 +#define RW_STATE_MSB 2 +#define RW_STATE_WORD0 3 +#define RW_STATE_WORD1 4 + +#define PIT_FLAGS_HPET_LEGACY 1 + +typedef struct PITChannelState { + int count; /* can be 65536 */ + uint16_t latched_count; + uint8_t count_latched; + uint8_t status_latched; + uint8_t status; + uint8_t read_state; + uint8_t write_state; + uint8_t write_latch; + uint8_t rw_mode; + uint8_t mode; + uint8_t bcd; /* not supported */ + uint8_t gate; /* timer start */ + int64_t count_load_time; + /* irq handling */ + int64_t next_transition_time; + QEMUTimer *irq_timer; + qemu_irq irq; +} PITChannelState; + +struct PITState { + PITChannelState channels[3]; + uint32_t flags; +}; + +void pit_save(QEMUFile *f, void *opaque); + +int pit_load(QEMUFile *f, void *opaque, int version_id); + +void pit_reset(void *opaque); + +#endif diff --git a/hw/i8259.c b/hw/i8259.c index 3de22e343..7a484c02e 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -27,6 +27,8 @@ #include "monitor.h" #include "qemu-timer.h" +#include "qemu-kvm.h" + /* debug PIC */ //#define DEBUG_PIC @@ -181,7 +183,6 @@ int64_t irq_time[16]; static void i8259_set_irq(void *opaque, int irq, int level) { PicState2 *s = opaque; - #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) if (level != irq_level[irq]) { #if defined(DEBUG_PIC) @@ -212,18 +213,35 @@ static inline void pic_intack(PicState *s, int irq) } else { s->isr |= (1 << irq); } + /* We don't clear a level sensitive interrupt here */ if (!(s->elcr & (1 << irq))) s->irr &= ~(1 << irq); + } +extern int time_drift_fix; + int pic_read_irq(PicState2 *s) { int irq, irq2, intno; irq = pic_get_irq(&s->pics[0]); if (irq >= 0) { + pic_intack(&s->pics[0], irq); +#ifndef TARGET_IA64 + if (time_drift_fix && irq == 0) { + extern int64_t timer_acks, timer_ints_to_push; + timer_acks++; + if (timer_ints_to_push > 0) { + timer_ints_to_push--; + /* simulate an edge irq0, like the one generated by i8254 */ + pic_set_irq1(&s->pics[0], 0, 0); + pic_set_irq1(&s->pics[0], 0, 1); + } + } +#endif if (irq == 2) { irq2 = pic_get_irq(&s->pics[1]); if (irq2 >= 0) { @@ -446,9 +464,33 @@ static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1) return s->elcr; } +static void kvm_kernel_pic_save_to_user(PicState *s); +static int kvm_kernel_pic_load_from_user(PicState *s); + +static void pic_pre_save(void *opaque) +{ + PicState *s = opaque; + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_pic_save_to_user(s); + } +} + +static int pic_post_load(void *opaque, int version_id) +{ + PicState *s = opaque; + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_pic_load_from_user(s); + } + return 0; +} + static const VMStateDescription vmstate_pic = { .name = "i8259", .version_id = 1, + .pre_save = pic_pre_save, + .post_load = pic_post_load, .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { @@ -535,3 +577,103 @@ qemu_irq *i8259_init(qemu_irq parent_irq) isa_pic = s; return qemu_allocate_irqs(i8259_set_irq, s, 16); } + +static void kvm_kernel_pic_save_to_user(PicState *s) +{ +#ifdef KVM_CAP_IRQCHIP + struct kvm_irqchip chip; + struct kvm_pic_state *kpic; + + chip.chip_id = (&s->pics_state->pics[0] == s) ? + KVM_IRQCHIP_PIC_MASTER : + KVM_IRQCHIP_PIC_SLAVE; + kvm_get_irqchip(kvm_context, &chip); + kpic = &chip.chip.pic; + + s->last_irr = kpic->last_irr; + s->irr = kpic->irr; + s->imr = kpic->imr; + s->isr = kpic->isr; + s->priority_add = kpic->priority_add; + s->irq_base = kpic->irq_base; + s->read_reg_select = kpic->read_reg_select; + s->poll = kpic->poll; + s->special_mask = kpic->special_mask; + s->init_state = kpic->init_state; + s->auto_eoi = kpic->auto_eoi; + s->rotate_on_auto_eoi = kpic->rotate_on_auto_eoi; + s->special_fully_nested_mode = kpic->special_fully_nested_mode; + s->init4 = kpic->init4; + s->elcr = kpic->elcr; + s->elcr_mask = kpic->elcr_mask; +#endif +} + +static int kvm_kernel_pic_load_from_user(PicState *s) +{ +#ifdef KVM_CAP_IRQCHIP + struct kvm_irqchip chip; + struct kvm_pic_state *kpic; + + chip.chip_id = (&s->pics_state->pics[0] == s) ? + KVM_IRQCHIP_PIC_MASTER : + KVM_IRQCHIP_PIC_SLAVE; + kpic = &chip.chip.pic; + + kpic->last_irr = s->last_irr; + kpic->irr = s->irr; + kpic->imr = s->imr; + kpic->isr = s->isr; + kpic->priority_add = s->priority_add; + kpic->irq_base = s->irq_base; + kpic->read_reg_select = s->read_reg_select; + kpic->poll = s->poll; + kpic->special_mask = s->special_mask; + kpic->init_state = s->init_state; + kpic->auto_eoi = s->auto_eoi; + kpic->rotate_on_auto_eoi = s->rotate_on_auto_eoi; + kpic->special_fully_nested_mode = s->special_fully_nested_mode; + kpic->init4 = s->init4; + kpic->elcr = s->elcr; + kpic->elcr_mask = s->elcr_mask; + + kvm_set_irqchip(kvm_context, &chip); +#endif + return 0; +} + +#ifdef KVM_CAP_IRQCHIP +static void kvm_i8259_set_irq(void *opaque, int irq, int level) +{ + int pic_ret; + if (kvm_set_irq(irq, level, &pic_ret)) { + if (pic_ret != 0) + apic_set_irq_delivered(); + return; + } +} + +static void kvm_pic_init1(int io_addr, PicState *s) +{ + vmstate_register(io_addr, &vmstate_pic, s); + qemu_register_reset(pic_reset, s); +} + +qemu_irq *kvm_i8259_init(qemu_irq parent_irq) +{ + PicState2 *s; + + s = qemu_mallocz(sizeof(PicState2)); + + kvm_pic_init1(0x20, &s->pics[0]); + kvm_pic_init1(0xa0, &s->pics[1]); + s->parent_irq = parent_irq; + s->pics[0].pics_state = s; + s->pics[1].pics_state = s; + isa_pic = s; + return qemu_allocate_irqs(kvm_i8259_set_irq, s, 24); +} +#endif + + + diff --git a/hw/ioapic.c b/hw/ioapic.c index b0ad78f24..a66325d42 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -22,12 +22,16 @@ #include "hw.h" #include "pc.h" +#include "sysemu.h" #include "qemu-timer.h" #include "host-utils.h" +#include "qemu-kvm.h" + //#define DEBUG_IOAPIC #define IOAPIC_NUM_PINS 0x18 +#define IOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000 #define IOAPIC_LVT_MASKED (1<<16) #define IOAPIC_TRIGGER_EDGE 0 @@ -45,6 +49,7 @@ struct IOAPICState { uint8_t id; uint8_t ioregsel; + uint64_t base_address; uint32_t irr; uint64_t ioredtbl[IOAPIC_NUM_PINS]; @@ -94,8 +99,9 @@ void ioapic_set_irq(void *opaque, int vector, int level) * to GSI 2. GSI maps to ioapic 1-1. This is not * the cleanest way of doing it but it should work. */ - if (vector == 0) + if (vector == 0 && irq0override) { vector = 2; + } if (vector >= 0 && vector < IOAPIC_NUM_PINS) { uint32_t mask = 1 << vector; @@ -191,14 +197,91 @@ static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t va } } +static void kvm_kernel_ioapic_save_to_user(IOAPICState *s) +{ +#if defined(KVM_CAP_IRQCHIP) && defined(TARGET_I386) + struct kvm_irqchip chip; + struct kvm_ioapic_state *kioapic; + int i; + + chip.chip_id = KVM_IRQCHIP_IOAPIC; + kvm_get_irqchip(kvm_context, &chip); + kioapic = &chip.chip.ioapic; + + s->id = kioapic->id; + s->ioregsel = kioapic->ioregsel; + s->base_address = kioapic->base_address; + s->irr = kioapic->irr; + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + s->ioredtbl[i] = kioapic->redirtbl[i].bits; + } +#endif +} + +static void kvm_kernel_ioapic_load_from_user(IOAPICState *s) +{ +#if defined(KVM_CAP_IRQCHIP) && defined(TARGET_I386) + struct kvm_irqchip chip; + struct kvm_ioapic_state *kioapic; + int i; + + chip.chip_id = KVM_IRQCHIP_IOAPIC; + kioapic = &chip.chip.ioapic; + + kioapic->id = s->id; + kioapic->ioregsel = s->ioregsel; + kioapic->base_address = s->base_address; + kioapic->irr = s->irr; + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + kioapic->redirtbl[i].bits = s->ioredtbl[i]; + } + + kvm_set_irqchip(kvm_context, &chip); +#endif +} + +static void ioapic_pre_save(void *opaque) +{ + IOAPICState *s = (void *)opaque; + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_ioapic_save_to_user(s); + } +} + +static int ioapic_pre_load(void *opaque) +{ + IOAPICState *s = opaque; + + /* in case we are doing version 1, we just set these to sane values */ + s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS; + s->irr = 0; + return 0; +} + +static int ioapic_post_load(void *opaque, int version_id) +{ + IOAPICState *s = opaque; + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_ioapic_load_from_user(s); + } + return 0; +} + static const VMStateDescription vmstate_ioapic = { .name = "ioapic", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, + .pre_load = ioapic_pre_load, + .post_load = ioapic_post_load, + .pre_save = ioapic_pre_save, .fields = (VMStateField []) { VMSTATE_UINT8(id, IOAPICState), VMSTATE_UINT8(ioregsel, IOAPICState), + VMSTATE_UINT64_V(base_address, IOAPICState, 2), + VMSTATE_UINT32_V(irr, IOAPICState, 2), VMSTATE_UINT64_ARRAY(ioredtbl, IOAPICState, IOAPIC_NUM_PINS), VMSTATE_END_OF_LIST() } @@ -210,8 +293,14 @@ static void ioapic_reset(void *opaque) int i; memset(s, 0, sizeof(*s)); + s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS; for(i = 0; i < IOAPIC_NUM_PINS; i++) s->ioredtbl[i] = 1 << 16; /* mask LVT */ +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_ioapic_load_from_user(s); + } +#endif } static CPUReadMemoryFunc * const ioapic_mem_read[3] = { diff --git a/hw/ipf.c b/hw/ipf.c new file mode 100644 index 000000000..21cff72b7 --- /dev/null +++ b/hw/ipf.c @@ -0,0 +1,713 @@ +/* + * Itanium Platform Emulator derived from QEMU PC System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Copyright (c) 2007 Intel + * Ported for IA64 Platform Zhang Xiantao <xiantao.zhang@intel.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "pc.h" +#include "fdc.h" +#include "pci.h" +#include "block.h" +#include "sysemu.h" +#include "audio/audio.h" +#include "net.h" +#include "smbus.h" +#include "boards.h" +#include "firmware.h" +#include "ia64intrin.h" +#include <unistd.h> +#include "device-assignment.h" +#include "virtio-blk.h" + +#include "qemu-kvm.h" + +#define FW_FILENAME "Flash.fd" + +/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */ +#define ACPI_DATA_SIZE 0x10000 + +#define MAX_IDE_BUS 2 + +static fdctrl_t *floppy_controller; +static RTCState *rtc_state; +static PCIDevice *i440fx_state; + +static uint32_t ipf_to_legacy_io(target_phys_addr_t addr) +{ + return (uint32_t)(((addr&0x3ffffff) >> 12 << 2)|((addr) & 0x3)); +} + +static void ipf_legacy_io_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) { + uint32_t port = ipf_to_legacy_io(addr); + + cpu_outb(0, port, val); +} + +static void ipf_legacy_io_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) { + uint32_t port = ipf_to_legacy_io(addr); + + cpu_outw(0, port, val); +} + +static void ipf_legacy_io_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) { + uint32_t port = ipf_to_legacy_io(addr); + + cpu_outl(0, port, val); +} + +static uint32_t ipf_legacy_io_readb(void *opaque, target_phys_addr_t addr) +{ + uint32_t port = ipf_to_legacy_io(addr); + + return cpu_inb(0, port); +} + +static uint32_t ipf_legacy_io_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t port = ipf_to_legacy_io(addr); + + return cpu_inw(0, port); +} + +static uint32_t ipf_legacy_io_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t port = ipf_to_legacy_io(addr); + + return cpu_inl(0, port); +} + +static CPUReadMemoryFunc *ipf_legacy_io_read[3] = { + ipf_legacy_io_readb, + ipf_legacy_io_readw, + ipf_legacy_io_readl, +}; + +static CPUWriteMemoryFunc *ipf_legacy_io_write[3] = { + ipf_legacy_io_writeb, + ipf_legacy_io_writew, + ipf_legacy_io_writel, +}; + +static void pic_irq_request(void *opaque, int irq, int level) +{ + fprintf(stderr,"pic_irq_request called!\n"); +} + +/* PC cmos mappings */ + +#define REG_EQUIPMENT_BYTE 0x14 + +static int cmos_get_fd_drive_type(int fd0) +{ + int val; + + switch (fd0) { + case 0: + /* 1.44 Mb 3"5 drive */ + val = 4; + break; + case 1: + /* 2.88 Mb 3"5 drive */ + val = 5; + break; + case 2: + /* 1.2 Mb 5"5 drive */ + val = 2; + break; + default: + val = 0; + break; + } + return val; +} + +static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd) +{ + RTCState *s = rtc_state; + int cylinders, heads, sectors; + + bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors); + rtc_set_memory(s, type_ofs, 47); + rtc_set_memory(s, info_ofs, cylinders); + rtc_set_memory(s, info_ofs + 1, cylinders >> 8); + rtc_set_memory(s, info_ofs + 2, heads); + rtc_set_memory(s, info_ofs + 3, 0xff); + rtc_set_memory(s, info_ofs + 4, 0xff); + rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); + rtc_set_memory(s, info_ofs + 6, cylinders); + rtc_set_memory(s, info_ofs + 7, cylinders >> 8); + rtc_set_memory(s, info_ofs + 8, sectors); +} + +/* convert boot_device letter to something recognizable by the bios */ +static int boot_device2nibble(char boot_device) +{ + switch(boot_device) { + case 'a': + case 'b': + return 0x01; /* floppy boot */ + case 'c': + return 0x02; /* hard drive boot */ + case 'd': + return 0x03; /* CD-ROM boot */ + case 'n': + return 0x04; /* Network boot */ + } + return 0; +} + +/* hd_table must contain 4 block drivers */ +static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, + const char *boot_device, BlockDriverState **hd_table) +{ + RTCState *s = rtc_state; + int nbds, bds[3] = { 0, }; + int val; + int fd0, fd1, nb; + int i; + + /* various important CMOS locations needed by PC/Bochs bios */ + + /* memory size */ + val = 640; /* base memory in K */ + rtc_set_memory(s, 0x15, val); + rtc_set_memory(s, 0x16, val >> 8); + + val = (ram_size / 1024) - 1024; + if (val > 65535) + val = 65535; + rtc_set_memory(s, 0x17, val); + rtc_set_memory(s, 0x18, val >> 8); + rtc_set_memory(s, 0x30, val); + rtc_set_memory(s, 0x31, val >> 8); + + if (above_4g_mem_size) { + rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16); + rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24); + rtc_set_memory(s, 0x5d, above_4g_mem_size >> 32); + } + rtc_set_memory(s, 0x5f, smp_cpus - 1); + + if (ram_size > (16 * 1024 * 1024)) + val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536); + else + val = 0; + if (val > 65535) + val = 65535; + rtc_set_memory(s, 0x34, val); + rtc_set_memory(s, 0x35, val >> 8); + + /* set boot devices, and disable floppy signature check if requested */ +#define PC_MAX_BOOT_DEVICES 3 + nbds = strlen(boot_device); + + if (nbds > PC_MAX_BOOT_DEVICES) { + fprintf(stderr, "Too many boot devices for PC\n"); + exit(1); + } + + for (i = 0; i < nbds; i++) { + bds[i] = boot_device2nibble(boot_device[i]); + if (bds[i] == 0) { + fprintf(stderr, "Invalid boot device for PC: '%c'\n", + boot_device[i]); + exit(1); + } + } + + rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]); + rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); + + /* floppy type */ + + fd0 = fdctrl_get_drive_type(floppy_controller, 0); + fd1 = fdctrl_get_drive_type(floppy_controller, 1); + + val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1); + rtc_set_memory(s, 0x10, val); + + val = 0; + nb = 0; + if (fd0 < 3) + nb++; + if (fd1 < 3) + nb++; + + switch (nb) { + case 0: + break; + case 1: + val |= 0x01; /* 1 drive, ready for boot */ + break; + case 2: + val |= 0x41; /* 2 drives, ready for boot */ + break; + } + + val |= 0x02; /* FPU is there */ + val |= 0x04; /* PS/2 mouse installed */ + rtc_set_memory(s, REG_EQUIPMENT_BYTE, val); + + /* hard drives */ + + rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); + if (hd_table[0]) + cmos_init_hd(0x19, 0x1b, hd_table[0]); + if (hd_table[1]) + cmos_init_hd(0x1a, 0x24, hd_table[1]); + + val = 0; + for (i = 0; i < 4; i++) { + if (hd_table[i]) { + int cylinders, heads, sectors, translation; + /* NOTE: bdrv_get_geometry_hint() returns the physical + geometry. It is always such that: 1 <= sects <= 63, 1 + <= heads <= 16, 1 <= cylinders <= 16383. The BIOS + geometry can be different if a translation is done. */ + translation = bdrv_get_translation_hint(hd_table[i]); + if (translation == BIOS_ATA_TRANSLATION_AUTO) { + bdrv_get_geometry_hint(hd_table[i], &cylinders, + &heads, §ors); + if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { + /* No translation. */ + translation = 0; + } else { + /* LBA translation. */ + translation = 1; + } + } else { + translation--; + } + val |= translation << (i * 2); + } + } + rtc_set_memory(s, 0x39, val); +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + cpu_reset(env); +} + +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 14, 15 }; + +#define NE2000_NB_MAX 6 + +static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, + 0x360, 0x280, 0x380 }; +static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; + +static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; +static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; + +static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; +static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; + +#ifdef HAS_AUDIO +static void audio_init (PCIBus *pci_bus, qemu_irq *pic) +{ + struct soundhw *c; + int audio_enabled = 0; + + for (c = soundhw; !audio_enabled && c->name; ++c) { + audio_enabled = c->enabled; + } + + if (audio_enabled) { + AudioState *s; + + s = AUD_init (); + if (s) { + for (c = soundhw; c->name; ++c) { + if (c->enabled) { + if (c->isa) { + c->init.init_isa (s, pic); + } else { + if (pci_bus) { + c->init.init_pci (pci_bus, s); + } + } + } + } + } + } +} +#endif + +static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic) +{ + static int nb_ne2k = 0; + + if (nb_ne2k == NE2000_NB_MAX) + return; + isa_ne2000_init(ne2000_io[nb_ne2k], pic[ne2000_irq[nb_ne2k]], nd); + nb_ne2k++; +} + +/* Itanium hardware initialisation */ +static void ipf_init1(ram_addr_t ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, + int pci_enabled, const char *cpu_model) +{ + char buf[1024]; + int i; + ram_addr_t ram_addr; + ram_addr_t above_4g_mem_size = 0; + PCIBus *pci_bus; + PCIDevice *pci_dev; + int piix3_devfn = -1; + CPUState *env; + qemu_irq *cpu_irq; + qemu_irq *i8259; + int page_size; + int index; + unsigned long ipf_legacy_io_base, ipf_legacy_io_mem; + BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + BlockDriverState *fd[MAX_FD]; + + page_size = getpagesize(); + if (page_size != TARGET_PAGE_SIZE) { + fprintf(stderr,"Error! Host page size != qemu target page size," + " you may need to change TARGET_PAGE_BITS in qemu!" + "host page size:0x%x\n", page_size); + exit(-1); + }; + + if (ram_size >= 0xc0000000 ) { + above_4g_mem_size = ram_size - 0xc0000000; + ram_size = 0xc0000000; + } + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "IA64"; + } + + for(i = 0; i < smp_cpus; i++) { + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + if (i != 0) + env->hflags |= HF_HALTED_MASK; + register_savevm("cpu", i, 4, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, 0, env); + } + + /* allocate RAM */ + if (kvm_enabled()) { + ram_addr = qemu_ram_alloc(0xa0000); + cpu_register_physical_memory(0, 0xa0000, ram_addr); + + ram_addr = qemu_ram_alloc(0x20000); // Workaround 0xa0000-0xc0000 + + ram_addr = qemu_ram_alloc(0x40000); + cpu_register_physical_memory(0xc0000, 0x40000, ram_addr); + + ram_addr = qemu_ram_alloc(ram_size - 0x100000); + cpu_register_physical_memory(0x100000, ram_size - 0x100000, ram_addr); + } else { + ram_addr = qemu_ram_alloc(ram_size); + cpu_register_physical_memory(0, ram_size, ram_addr); + } + + /* above 4giga memory allocation */ + if (above_4g_mem_size > 0) { + ram_addr = qemu_ram_alloc(above_4g_mem_size); + cpu_register_physical_memory(0x100000000, above_4g_mem_size, ram_addr); + } + + /*Load firware to its proper position.*/ + if (kvm_enabled()) { + unsigned long image_size; + uint8_t *image = NULL; + unsigned long nvram_addr; + unsigned long nvram_fd = 0; + unsigned long type = READ_FROM_NVRAM; + unsigned long i = 0; + unsigned long fw_offset; + ram_addr_t fw_mem = qemu_ram_alloc(GFW_SIZE); + + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, FW_FILENAME); + image = read_image(buf, &image_size ); + if (NULL == image || !image_size) { + fprintf(stderr, "Error when reading Guest Firmware!\n"); + fprintf(stderr, "Please check Guest firmware at %s\n", buf); + exit(1); + } + fw_offset = GFW_START + GFW_SIZE - image_size; + + cpu_register_physical_memory(GFW_START, GFW_SIZE, fw_mem); + cpu_physical_memory_write(fw_offset, image, image_size); + + free(image); + + if (nvram) { + nvram_addr = NVRAM_START; + nvram_fd = kvm_ia64_nvram_init(type); + if (nvram_fd != -1) { + kvm_ia64_copy_from_nvram_to_GFW(nvram_fd); + close(nvram_fd); + } + i = atexit((void *)kvm_ia64_copy_from_GFW_to_nvram); + if (i != 0) + fprintf(stderr, "cannot set exit function\n"); + } else + nvram_addr = 0; + + kvm_ia64_build_hob(ram_size + above_4g_mem_size, smp_cpus, nvram_addr); + } + + /*Register legacy io address space, size:64M*/ + ipf_legacy_io_base = 0xE0000000; + ipf_legacy_io_mem = cpu_register_io_memory(0, ipf_legacy_io_read, + ipf_legacy_io_write, NULL); + cpu_register_physical_memory(ipf_legacy_io_base, 64*1024*1024, + ipf_legacy_io_mem); + + cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1); + i8259 = kvm_i8259_init(cpu_irq[0]); + + if (pci_enabled) { + pci_bus = i440fx_init(&i440fx_state, i8259); + piix3_devfn = piix3_init(pci_bus, -1); + } else { + pci_bus = NULL; + } + + if (cirrus_vga_enabled) { + if (pci_enabled) + pci_cirrus_vga_init(pci_bus); + else + isa_cirrus_vga_init(); + } else { + if (pci_enabled) + pci_vga_init(pci_bus, 0, 0); + else + isa_vga_init(); + } + + rtc_state = rtc_init(0x70, i8259[8], 2000); + + if (pci_enabled) { + pic_set_alt_irq_func(isa_pic, NULL, NULL); + } + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { + serial_init(serial_io[i], i8259[serial_irq[i]], 115200, + serial_hds[i]); + } + } + + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_hds[i]) { + parallel_init(parallel_io[i], i8259[parallel_irq[i]], + parallel_hds[i]); + } + } + + for(i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + + if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) + pc_init_ne2k_isa(nd, i8259); + else + pci_nic_init(nd, "e1000", NULL); + } + +#undef USE_HYPERCALL //Disable it now, need to implement later! +#ifdef USE_HYPERCALL + pci_hypercall_init(pci_bus); +#endif + + if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { + fprintf(stderr, "qemu: too many IDE bus\n"); + exit(1); + } + + for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { + index = drive_get_index(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + if (index != -1) + hd[i] = drives_table[index].bdrv; + else + hd[i] = NULL; + } + + if (pci_enabled) { + pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1, i8259); + } else { + for(i = 0; i < MAX_IDE_BUS; i++) { + isa_ide_init(ide_iobase[i], ide_iobase2[i], i8259[ide_irq[i]], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + } + } + + i8042_init(i8259[1], i8259[12], 0x60); + DMA_init(0); +#ifdef HAS_AUDIO + audio_init(pci_enabled ? pci_bus : NULL, i8259); +#endif + + for(i = 0; i < MAX_FD; i++) { + index = drive_get_index(IF_FLOPPY, 0, i); + if (index != -1) + fd[i] = drives_table[index].bdrv; + else + fd[i] = NULL; + } + floppy_controller = fdctrl_init(i8259[6], 2, 0, 0x3f0, fd); + + cmos_init(ram_size, above_4g_mem_size, boot_device, hd); + + if (pci_enabled && usb_enabled) { + usb_uhci_piix3_init(pci_bus, piix3_devfn + 2); + } + + if (pci_enabled && acpi_enabled) { + uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ + i2c_bus *smbus; + + /* TODO: Populate SPD eeprom data. */ + smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, i8259[9]); + for (i = 0; i < 8; i++) { + DeviceState *eeprom; + eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); + qdev_set_prop_int(eeprom, "address", 0x50 + i); + qdev_set_prop_ptr(eeprom, "data", eeprom_buf + (i * 256)); + qdev_init(eeprom); + } + } + + if (i440fx_state) { + i440fx_init_memory_mappings(i440fx_state); + } + + if (pci_enabled) { + int max_bus; + int bus; + + max_bus = drive_get_max_bus(IF_SCSI); + for (bus = 0; bus <= max_bus; bus++) { + pci_create_simple(pci_bus, -1, "lsi53c895a"); + } + } + /* Add virtio block devices */ + if (pci_enabled) { + int index; + int unit_id = 0; + + while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) { + pci_dev = pci_create("virtio-blk-pci", + drives_table[index].devaddr); + qdev_init(&pci_dev->qdev); + unit_id++; + } + } + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + if (kvm_enabled()) + add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index); +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ + +} + +static void ipf_init_pci(ram_addr_t ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + ipf_init1(ram_size, boot_device, ds, kernel_filename, + kernel_cmdline, initrd_filename, 1, cpu_model); +} + +QEMUMachine ipf_machine = { + .name = "itanium", + .desc = "Itanium Platform", + .init = (QEMUMachineInitFunc *)ipf_init_pci, + .max_cpus = 255, + .is_default = 1, +}; + +static void ipf_machine_init(void) +{ + qemu_register_machine(&ipf_machine); +} + +machine_init(ipf_machine_init); + +#define IOAPIC_NUM_PINS 48 + +static int ioapic_irq_count[IOAPIC_NUM_PINS]; + +static int ioapic_map_irq(int devfn, int irq_num) +{ + int irq, dev; + dev = devfn >> 3; + irq = ((((dev << 2) + (dev >> 3) + irq_num) & 31) + 16); + return irq; +} + +/* + * Dummy function to provide match for call from hw/apic.c + */ +void apic_set_irq_delivered(void) { +} + +void ioapic_set_irq(void *opaque, int irq_num, int level) +{ + int vector, pic_ret; + + PCIDevice *pci_dev = (PCIDevice *)opaque; + vector = ioapic_map_irq(pci_dev->devfn, irq_num); + + if (level) + ioapic_irq_count[vector] += 1; + else + ioapic_irq_count[vector] -= 1; + + if (kvm_enabled()) { + if (kvm_set_irq(vector, ioapic_irq_count[vector] == 0, &pic_ret)) + if (pic_ret != 0) + apic_set_irq_delivered(); + return; + } +} + +int ipf_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return ioapic_map_irq(pci_dev->devfn, irq_num); +} @@ -14,6 +14,8 @@ #include "hw.h" #include "msix.h" #include "pci.h" +#define QEMU_KVM_NO_CPU +#include "qemu-kvm.h" /* Declaration from linux/pci_regs.h */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ @@ -62,6 +64,117 @@ /* Flag for interrupt controller to declare MSI-X support */ int msix_supported; +#ifdef CONFIG_KVM +/* KVM specific MSIX helpers */ +static void kvm_msix_free(PCIDevice *dev) +{ + int vector, changed = 0; + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { + if (dev->msix_entry_used[vector]) { + kvm_del_routing_entry(kvm_context, &dev->msix_irq_entries[vector]); + changed = 1; + } + } + if (changed) { + kvm_commit_irq_routes(kvm_context); + } +} + +static void kvm_msix_routing_entry(PCIDevice *dev, unsigned vector, + struct kvm_irq_routing_entry *entry) +{ + uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE; + entry->type = KVM_IRQ_ROUTING_MSI; + entry->flags = 0; + entry->u.msi.address_lo = pci_get_long(table_entry + MSIX_MSG_ADDR); + entry->u.msi.address_hi = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR); + entry->u.msi.data = pci_get_long(table_entry + MSIX_MSG_DATA); +} + +static void kvm_msix_update(PCIDevice *dev, int vector, + int was_masked, int is_masked) +{ + struct kvm_irq_routing_entry e = {}, *entry; + int mask_cleared = was_masked && !is_masked; + /* It is only legal to change an entry when it is masked. Therefore, it is + * enough to update the routing in kernel when mask is being cleared. */ + if (!mask_cleared) { + return; + } + if (!dev->msix_entry_used[vector]) { + return; + } + entry = dev->msix_irq_entries + vector; + e.gsi = entry->gsi; + kvm_msix_routing_entry(dev, vector, &e); + if (memcmp(&entry->u.msi, &e.u.msi, sizeof entry->u.msi)) { + int r; + r = kvm_update_routing_entry(kvm_context, entry, &e); + if (r) { + fprintf(stderr, "%s: kvm_update_routing_entry failed: %s\n", __func__, + strerror(-r)); + exit(1); + } + memcpy(&entry->u.msi, &e.u.msi, sizeof entry->u.msi); + r = kvm_commit_irq_routes(kvm_context); + if (r) { + fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, + strerror(-r)); + exit(1); + } + } +} + +static int kvm_msix_add(PCIDevice *dev, unsigned vector) +{ + struct kvm_irq_routing_entry *entry = dev->msix_irq_entries + vector; + int r; + + if (!kvm_has_gsi_routing(kvm_context)) { + fprintf(stderr, "Warning: no MSI-X support found. " + "At least kernel 2.6.30 is required for MSI-X support.\n" + ); + return -EOPNOTSUPP; + } + + r = kvm_get_irq_route_gsi(kvm_context); + if (r < 0) { + fprintf(stderr, "%s: kvm_get_irq_route_gsi failed: %s\n", __func__, strerror(-r)); + return r; + } + entry->gsi = r; + kvm_msix_routing_entry(dev, vector, entry); + r = kvm_add_routing_entry(kvm_context, entry); + if (r < 0) { + fprintf(stderr, "%s: kvm_add_routing_entry failed: %s\n", __func__, strerror(-r)); + return r; + } + + r = kvm_commit_irq_routes(kvm_context); + if (r < 0) { + fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, strerror(-r)); + return r; + } + return 0; +} + +static void kvm_msix_del(PCIDevice *dev, unsigned vector) +{ + if (dev->msix_entry_used[vector]) { + return; + } + kvm_del_routing_entry(kvm_context, &dev->msix_irq_entries[vector]); + kvm_commit_irq_routes(kvm_context); +} +#else + +static void kvm_msix_free(PCIDevice *dev) {} +static void kvm_msix_update(PCIDevice *dev, int vector, + int was_masked, int is_masked) {} +static int kvm_msix_add(PCIDevice *dev, unsigned vector) { return -1; } +static void kvm_msix_del(PCIDevice *dev, unsigned vector) {} +#endif + /* Add MSI-X capability to the config space for the device. */ /* Given a bar and its size, add MSI-X table on top of it * and fill MSI-X capability in the config space. @@ -200,7 +313,11 @@ static void msix_mmio_writel(void *opaque, target_phys_addr_t addr, PCIDevice *dev = opaque; unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; int vector = offset / MSIX_ENTRY_SIZE; + int was_masked = msix_is_masked(dev, vector); pci_set_long(dev->msix_table_page + offset, val); + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_msix_update(dev, vector, was_masked, msix_is_masked(dev, vector)); + } msix_handle_mask_update(dev, vector); } @@ -259,6 +376,12 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, if (nentries > MSIX_MAX_ENTRIES) return -EINVAL; +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + dev->msix_irq_entries = qemu_malloc(nentries * + sizeof *dev->msix_irq_entries); + } +#endif dev->msix_entry_used = qemu_mallocz(MSIX_MAX_ENTRIES * sizeof *dev->msix_entry_used); @@ -295,6 +418,10 @@ static void msix_free_irq_entries(PCIDevice *dev) { int vector; + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_msix_free(dev); + } + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { dev->msix_entry_used[vector] = 0; msix_clr_pending(dev, vector); @@ -315,6 +442,8 @@ int msix_uninit(PCIDevice *dev) dev->msix_table_page = NULL; qemu_free(dev->msix_entry_used); dev->msix_entry_used = NULL; + qemu_free(dev->msix_irq_entries); + dev->msix_irq_entries = NULL; dev->cap_present &= ~QEMU_PCI_CAP_MSIX; return 0; } @@ -323,10 +452,13 @@ void msix_save(PCIDevice *dev, QEMUFile *f) { unsigned n = dev->msix_entries_nr; - if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) { + if (!msix_supported) { return; } + if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) { + return; + } qemu_put_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE); qemu_put_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8); } @@ -336,6 +468,9 @@ void msix_load(PCIDevice *dev, QEMUFile *f) { unsigned n = dev->msix_entries_nr; + if (!msix_supported) + return; + if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) { return; } @@ -380,6 +515,13 @@ void msix_notify(PCIDevice *dev, unsigned vector) return; } +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_set_irq(dev->msix_irq_entries[vector].gsi, 1, NULL); + return; + } +#endif + address = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR); address = (address << 32) | pci_get_long(table_entry + MSIX_MSG_ADDR); data = pci_get_long(table_entry + MSIX_MSG_DATA); @@ -408,9 +550,19 @@ void msix_reset(PCIDevice *dev) /* Mark vector as used. */ int msix_vector_use(PCIDevice *dev, unsigned vector) { + int ret; if (vector >= dev->msix_entries_nr) return -EINVAL; - dev->msix_entry_used[vector]++; + if (dev->msix_entry_used[vector]) { + return 0; + } + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + ret = kvm_msix_add(dev, vector); + if (ret) { + return ret; + } + } + ++dev->msix_entry_used[vector]; return 0; } @@ -423,6 +575,9 @@ void msix_vector_unuse(PCIDevice *dev, unsigned vector) if (--dev->msix_entry_used[vector]) { return; } + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_msix_del(dev, vector); + } msix_clr_pending(dev, vector); } @@ -44,6 +44,9 @@ #include "ide.h" #include "loader.h" #include "elf.h" +#include "device-assignment.h" + +#include "qemu-kvm.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -52,6 +55,8 @@ //#define DEBUG_MULTIBOOT #define BIOS_FILENAME "bios.bin" +#define EXTBOOT_FILENAME "extboot.bin" +#define VAPIC_FILENAME "vapic.bin" #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024) @@ -69,6 +74,8 @@ static RTCState *rtc_state; static PITState *pit; static PCII440FXState *i440fx_state; +qemu_irq *ioapic_irq_hack; + typedef struct isa_irq_state { qemu_irq *i8259; qemu_irq *ioapic; @@ -952,7 +959,7 @@ int cpu_is_bsp(CPUState *env) return env->cpuid_apic_id == 0; } -static CPUState *pc_new_cpu(const char *cpu_model) +CPUState *pc_new_cpu(const char *cpu_model) { CPUState *env; @@ -961,6 +968,7 @@ static CPUState *pc_new_cpu(const char *cpu_model) fprintf(stderr, "Unable to find x86 CPU definition\n"); exit(1); } + env->kvm_cpu_state.regs_modified = 1; if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) { env->cpuid_apic_id = env->cpu_index; /* APIC reset callback resets cpu */ @@ -968,6 +976,11 @@ static CPUState *pc_new_cpu(const char *cpu_model) } else { qemu_register_reset((QEMUResetHandler*)cpu_reset, env); } + + /* kvm needs this to run after the apic is initialized. Otherwise, + * it can access invalid state and crash. + */ + qemu_init_vcpu(env); return env; } @@ -1015,6 +1028,9 @@ static void pc_init1(ram_addr_t ram_size, #endif } + if (kvm_enabled()) { + kvm_set_boot_cpu_id(0); + } for (i = 0; i < smp_cpus; i++) { env = pc_new_cpu(cpu_model); } @@ -1022,18 +1038,11 @@ static void pc_init1(ram_addr_t ram_size, vmport_init(); /* allocate RAM */ - ram_addr = qemu_ram_alloc(0xa0000); + ram_addr = qemu_ram_alloc(below_4g_mem_size); cpu_register_physical_memory(0, 0xa0000, ram_addr); - - /* Allocate, even though we won't register, so we don't break the - * phys_ram_base + PA assumption. This range includes vga (0xa0000 - 0xc0000), - * and some bios areas, which will be registered later - */ - ram_addr = qemu_ram_alloc(0x100000 - 0xa0000); - ram_addr = qemu_ram_alloc(below_4g_mem_size - 0x100000); cpu_register_physical_memory(0x100000, below_4g_mem_size - 0x100000, - ram_addr); + ram_addr + 0x100000); /* above 4giga memory allocation */ if (above_4g_mem_size > 0) { @@ -1075,11 +1084,17 @@ static void pc_init1(ram_addr_t ram_size, isa_bios_size = bios_size; if (isa_bios_size > (128 * 1024)) isa_bios_size = 128 * 1024; + cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size, + IO_MEM_UNASSIGNED); + /* kvm tpr optimization needs the bios accessible for write, at least to qemu itself */ cpu_register_physical_memory(0x100000 - isa_bios_size, isa_bios_size, - (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM); - + (bios_offset + bios_size - isa_bios_size) /* | IO_MEM_ROM */); + if (extboot_drive) { + option_rom[nb_option_roms++] = qemu_strdup(EXTBOOT_FILENAME); + } + option_rom[nb_option_roms++] = qemu_strdup(VAPIC_FILENAME); rom_enable_driver_roms = 1; option_rom_offset = qemu_ram_alloc(PC_ROM_SIZE); @@ -1101,10 +1116,18 @@ static void pc_init1(ram_addr_t ram_size, } cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1); - i8259 = i8259_init(cpu_irq[0]); - isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); - isa_irq_state->i8259 = i8259; - isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); + isa_irq = i8259 = kvm_i8259_init(cpu_irq[0]); + } else +#endif + { + i8259 = i8259_init(cpu_irq[0]); + isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); + isa_irq_state->i8259 = i8259; + isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); + } if (pci_enabled) { pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq); @@ -1149,8 +1172,14 @@ static void pc_init1(ram_addr_t ram_size, if (pci_enabled) { isa_irq_state->ioapic = ioapic_init(); + ioapic_irq_hack = isa_irq; } - pit = pit_init(0x40, isa_reserve_irq(0)); +#ifdef CONFIG_KVM_PIT + if (kvm_enabled() && qemu_kvm_pit_in_kernel()) + pit = kvm_pit_init(0x40, isa_reserve_irq(0)); + else +#endif + pit = pit_init(0x40, isa_reserve_irq(0)); pcspk_init(pit); if (!no_hpet) { hpet_init(isa_irq); @@ -1174,7 +1203,7 @@ static void pc_init1(ram_addr_t ram_size, if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) pc_init_ne2k_isa(nd); else - pci_nic_init_nofail(nd, "e1000", NULL); + pci_nic_init_nofail(nd, "rtl8139", NULL); } if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { @@ -1226,7 +1255,7 @@ static void pc_init1(ram_addr_t ram_size, qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); qdev_init_nofail(eeprom); } - piix4_acpi_system_hot_add_init(pci_bus); + piix4_acpi_system_hot_add_init(pci_bus, cpu_model); } if (i440fx_state) { @@ -1243,6 +1272,18 @@ static void pc_init1(ram_addr_t ram_size, } } + if (extboot_drive) { + DriveInfo *info = extboot_drive; + int cyls, heads, secs; + + if (info->type != IF_IDE && info->type != IF_VIRTIO) { + bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs); + bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs); + } + + extboot_init(info->bdrv, 1); + } + /* Add virtio console devices */ if (pci_enabled) { for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) { @@ -1251,6 +1292,12 @@ static void pc_init1(ram_addr_t ram_size, } } } + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + if (kvm_enabled()) { + add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index); + } +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ } static void pc_init_pci(ram_addr_t ram_size, @@ -28,6 +28,7 @@ extern PicState2 *isa_pic; void pic_set_irq(int irq, int level); void pic_set_irq_new(void *opaque, int irq, int level); qemu_irq *i8259_init(qemu_irq parent_irq); +qemu_irq *kvm_i8259_init(qemu_irq parent_irq); int pic_read_irq(PicState2 *s); void pic_update_irq(PicState2 *s); uint32_t pic_intack_read(PicState2 *s); @@ -48,6 +49,7 @@ qemu_irq *ioapic_init(void); void ioapic_set_irq(void *opaque, int vector, int level); void apic_reset_irq_delivered(void); int apic_get_irq_delivered(void); +void apic_set_irq_delivered(void); /* i8254.c */ @@ -62,8 +64,12 @@ int pit_get_initial_count(PITState *pit, int channel); int pit_get_mode(PITState *pit, int channel); int pit_get_out(PITState *pit, int channel, int64_t current_time); -void hpet_pit_disable(void); -void hpet_pit_enable(void); +/* i8254-kvm.c */ + +PITState *kvm_pit_init(int base, qemu_irq irq); + +void hpet_disable_pit(void); +void hpet_enable_pit(void); /* vmport.c */ void vmport_init(void); @@ -93,6 +99,7 @@ extern int fd_bootchk; void ioport_set_a20(int enable); int ioport_get_a20(void); +CPUState *pc_new_cpu(const char *cpu_model); /* acpi.c */ extern int acpi_enabled; @@ -106,7 +113,7 @@ int acpi_table_add(const char *table_desc); i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, qemu_irq sci_irq); void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr); -void piix4_acpi_system_hot_add_init(PCIBus *bus); +void piix4_acpi_system_hot_add_init(PCIBus *bus, const char *model); /* hpet.c */ extern int no_hpet; @@ -116,6 +123,9 @@ void pcspk_init(PITState *); int pcspk_audio_init(qemu_irq *pic); /* piix_pci.c */ +/* config space register for IRQ routing */ +#define PIIX_CONFIG_IRQ_ROUTE 0x60 + struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; @@ -127,6 +137,10 @@ void i440fx_init_memory_mappings(PCII440FXState *d); extern PCIDevice *piix4_dev; int piix4_init(PCIBus *bus, int devfn); +int piix_get_irq(int pin); + +int ipf_map_irq(PCIDevice *pci_dev, int irq_num); + /* vga.c */ enum vga_retrace_method { VGA_RETRACE_DUMB, @@ -149,5 +163,10 @@ void isa_cirrus_vga_init(void); void isa_ne2000_init(int base, int irq, NICInfo *nd); +/* extboot.c */ + +void extboot_init(BlockDriverState *bs, int cmd); + int cpu_is_bsp(CPUState *env); + #endif diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index ba13d2bce..41ce0041b 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -34,6 +34,7 @@ #include "virtio-blk.h" #include "qemu-config.h" #include "qemu-objects.h" +#include "device-assignment.h" #if defined(TARGET_I386) static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, @@ -225,6 +226,24 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, return dev; } +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +static PCIDevice *qemu_pci_hot_assign_device(Monitor *mon, + const char *devaddr, + const char *opts_str) +{ + QemuOpts *opts; + DeviceState *dev; + + opts = add_assigned_device(opts_str); + if (opts == NULL) { + monitor_printf(mon, "Error adding device; check syntax\n"); + return NULL; + } + dev = qdev_device_add(opts); + return DO_UPCAST(PCIDevice, qdev, dev); +} +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ + void pci_device_hot_add_print(Monitor *mon, const QObject *data) { QDict *qdict; @@ -277,6 +296,10 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict, QObject **ret_data) dev = qemu_pci_hot_add_nic(mon, pci_addr, opts); else if (strcmp(type, "storage") == 0) dev = qemu_pci_hot_add_storage(mon, pci_addr, opts); +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + else if (strcmp(type, "host") == 0) + dev = qemu_pci_hot_assign_device(mon, pci_addr, opts); +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ else monitor_printf(mon, "invalid type: %s\n", type); @@ -27,6 +27,9 @@ #include "net.h" #include "sysemu.h" #include "loader.h" +#include "qemu-kvm.h" +#include "hw/pc.h" +#include "device-assignment.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -423,6 +426,7 @@ static int pci_set_default_subsystem_id(PCIDevice *pci_dev) } /* + * Parse pci address in qemu command * Parse [[<domain>:]<bus>:]<slot>, return -1 on error */ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp) @@ -471,6 +475,55 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s return 0; } +/* + * Parse device bdf in device assignment command: + * + * -pcidevice host=bus:dev.func + * + * Parse <bus>:<slot>.<func> return -1 on error + */ +int pci_parse_host_devaddr(const char *addr, int *busp, + int *slotp, int *funcp) +{ + const char *p; + char *e; + int val; + int bus = 0, slot = 0, func = 0; + + p = addr; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == '.') { + slot = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + func = val; + } else + return -1; + } else + return -1; + + if (bus > 0xff || slot > 0x1f || func > 0x7) + return -1; + + if (*e) + return -1; + + *busp = bus; + *slotp = slot; + *funcp = func; + return 0; +} + int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, unsigned *slotp) { @@ -947,25 +1000,80 @@ static void pci_update_mappings(PCIDevice *d) } } -uint32_t pci_default_read_config(PCIDevice *d, - uint32_t address, int len) +static uint32_t pci_read_config(PCIDevice *d, + uint32_t address, int len) { uint32_t val = 0; - assert(len == 1 || len == 2 || len == 4); + len = MIN(len, pci_config_size(d) - address); memcpy(&val, d->config + address, len); return le32_to_cpu(val); } +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len) +{ + assert(len == 1 || len == 2 || len == 4); + + if (pci_access_cap_config(d, address, len)) { + return d->cap.config_read(d, address, len); + } + + return pci_read_config(d, address, len); +} + +static void pci_write_config(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len) +{ + int i; + for (i = 0; i < len; i++) { + pci_dev->config[address + i] = val & 0xff; + val >>= 8; + } +} + +int pci_access_cap_config(PCIDevice *pci_dev, uint32_t address, int len) +{ + if (pci_dev->cap.supported && address >= pci_dev->cap.start && + (address + len) < pci_dev->cap.start + pci_dev->cap.length) + return 1; + return 0; +} + +uint32_t pci_default_cap_read_config(PCIDevice *pci_dev, + uint32_t address, int len) +{ + return pci_read_config(pci_dev, address, len); +} + +void pci_default_cap_write_config(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len) +{ + pci_write_config(pci_dev, address, val, len); +} + void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) { int i; uint32_t config_size = pci_config_size(d); + if (pci_access_cap_config(d, addr, l)) { + d->cap.config_write(d, addr, val, l); + return; + } + for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) { uint8_t wmask = d->wmask[addr + i]; d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); } + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + if (kvm_enabled() && kvm_irqchip_in_kernel() && + addr >= PIIX_CONFIG_IRQ_ROUTE && + addr < PIIX_CONFIG_IRQ_ROUTE + 4) + assigned_dev_update_irqs(); +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ + if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || @@ -986,11 +1094,20 @@ static void pci_set_irq(void *opaque, int irq_num, int level) if (!change) return; +#if defined(TARGET_IA64) + ioapic_set_irq(pci_dev, irq_num, level); +#endif + pci_set_irq_state(pci_dev, irq_num, level); pci_update_irq_status(pci_dev); pci_change_irq_level(pci_dev, irq_num, change); } +int pci_map_irq(PCIDevice *pci_dev, int pin) +{ + return pci_dev->bus->map_irq(pci_dev, pin); +} + /***********************************************************/ /* monitor info on PCI */ @@ -1417,6 +1534,37 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) return dev; } +int pci_enable_capability_support(PCIDevice *pci_dev, + uint32_t config_start, + PCICapConfigReadFunc *config_read, + PCICapConfigWriteFunc *config_write, + PCICapConfigInitFunc *config_init) +{ + if (!pci_dev) + return -ENODEV; + + pci_dev->config[0x06] |= 0x10; // status = capabilities + + if (config_start == 0) + pci_dev->cap.start = PCI_CAPABILITY_CONFIG_DEFAULT_START_ADDR; + else if (config_start >= 0x40 && config_start < 0xff) + pci_dev->cap.start = config_start; + else + return -EINVAL; + + if (config_read) + pci_dev->cap.config_read = config_read; + else + pci_dev->cap.config_read = pci_default_cap_read_config; + if (config_write) + pci_dev->cap.config_write = config_write; + else + pci_dev->cap.config_write = pci_default_cap_write_config; + pci_dev->cap.supported = 1; + pci_dev->config[PCI_CAPABILITY_LIST] = pci_dev->cap.start; + return config_init(pci_dev); +} + static int pci_find_space(PCIDevice *pdev, uint8_t size) { int config_size = pci_config_size(pdev); @@ -5,11 +5,16 @@ #include "qdev.h" +struct kvm_irq_routing_entry; + /* PCI includes legacy ISA access. */ #include "isa.h" -/* PCI bus */ +/* imported from <linux/pci.h> */ +#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_FUNC(devfn) ((devfn) & 0x07) +/* PCI bus */ extern target_phys_addr_t pci_mem_base; #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) @@ -82,6 +87,12 @@ typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type); typedef int PCIUnregisterFunc(PCIDevice *pci_dev); +typedef void PCICapConfigWriteFunc(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len); +typedef uint32_t PCICapConfigReadFunc(PCIDevice *pci_dev, + uint32_t address, int len); +typedef int PCICapConfigInitFunc(PCIDevice *pci_dev); + typedef struct PCIIORegion { pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ #define PCI_BAR_UNMAPPED (~(pcibus_t)0) @@ -160,10 +171,19 @@ typedef struct PCIIORegion { /* Bits in the PCI Status Register (PCI 2.3 spec) */ #define PCI_STATUS_RESERVED1 0x007 #define PCI_STATUS_INT_STATUS 0x008 +#ifndef PCI_STATUS_CAP_LIST #define PCI_STATUS_CAP_LIST 0x010 +#endif +#ifndef PCI_STATUS_66MHZ #define PCI_STATUS_66MHZ 0x020 +#endif + #define PCI_STATUS_RESERVED2 0x040 + +#ifndef PCI_STATUS_FAST_BACK #define PCI_STATUS_FAST_BACK 0x080 +#endif + #define PCI_STATUS_DEVSEL 0x600 #define PCI_STATUS_RESERVED_MASK_LO (PCI_STATUS_RESERVED1 | \ @@ -192,6 +212,11 @@ enum { QEMU_PCI_CAP_EXPRESS = 0x2, }; +#define PCI_CAPABILITY_CONFIG_MAX_LENGTH 0x60 +#define PCI_CAPABILITY_CONFIG_DEFAULT_START_ADDR 0x40 +#define PCI_CAPABILITY_CONFIG_MSI_LENGTH 0x10 +#define PCI_CAPABILITY_CONFIG_MSIX_LENGTH 0x10 + struct PCIDevice { DeviceState qdev; /* PCI config space */ @@ -247,6 +272,23 @@ struct PCIDevice { char *romfile; ram_addr_t rom_offset; uint32_t rom_bar; + + /* How much space does an MSIX table need. */ + /* The spec requires giving the table structure + * a 4K aligned region all by itself. Align it to + * target pages so that drivers can do passthrough + * on the rest of the region. */ + target_phys_addr_t msix_page_size; + + struct kvm_irq_routing_entry *msix_irq_entries; + + /* Device capability configuration space */ + struct { + int supported; + unsigned int start, length; + PCICapConfigReadFunc *config_read; + PCICapConfigWriteFunc *config_write; + } cap; }; PCIDevice *pci_register_device(PCIBus *bus, const char *name, @@ -258,6 +300,14 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size, int type, PCIMapIORegionFunc *map_func); +int pci_enable_capability_support(PCIDevice *pci_dev, + uint32_t config_start, + PCICapConfigReadFunc *config_read, + PCICapConfigWriteFunc *config_write, + PCICapConfigInitFunc *config_init); + +int pci_map_irq(PCIDevice *pci_dev, int pin); + int pci_add_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); @@ -266,13 +316,17 @@ void pci_reserve_capability(PCIDevice *pci_dev, uint8_t offset, uint8_t size); uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id); - uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len); void pci_default_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len); void pci_device_save(PCIDevice *s, QEMUFile *f); int pci_device_load(PCIDevice *s, QEMUFile *f); +uint32_t pci_default_cap_read_config(PCIDevice *pci_dev, + uint32_t address, int len); +void pci_default_cap_write_config(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len); +int pci_access_cap_config(PCIDevice *pci_dev, uint32_t address, int len); typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); @@ -301,6 +355,9 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, unsigned *slotp); +int pci_parse_host_devaddr(const char *addr, int *busp, + int *slotp, int *funcp); + void pci_info(Monitor *mon); PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, pci_map_irq_fn map_irq, const char *name); diff --git a/hw/pcspk.c b/hw/pcspk.c index 26a0ecb9d..128836ba4 100644 --- a/hw/pcspk.c +++ b/hw/pcspk.c @@ -27,6 +27,8 @@ #include "isa.h" #include "audio/audio.h" #include "qemu-timer.h" +#include "i8254.h" +#include "qemu-kvm.h" #define PCSPK_BUF_LEN 1792 #define PCSPK_SAMPLE_RATE 32000 @@ -48,6 +50,43 @@ typedef struct { static const char *s_spk = "pcspk"; static PCSpkState pcspk_state; +#ifdef CONFIG_KVM_PIT +static void kvm_get_pit_ch2(PITState *pit, + struct kvm_pit_state *inkernel_state) +{ + struct kvm_pit_state pit_state; + + if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + kvm_get_pit(kvm_context, &pit_state); + pit->channels[2].mode = pit_state.channels[2].mode; + pit->channels[2].count = pit_state.channels[2].count; + pit->channels[2].count_load_time = pit_state.channels[2].count_load_time; + pit->channels[2].gate = pit_state.channels[2].gate; + if (inkernel_state) { + memcpy(inkernel_state, &pit_state, sizeof(*inkernel_state)); + } + } +} + +static void kvm_set_pit_ch2(PITState *pit, + struct kvm_pit_state *inkernel_state) +{ + if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + inkernel_state->channels[2].mode = pit->channels[2].mode; + inkernel_state->channels[2].count = pit->channels[2].count; + inkernel_state->channels[2].count_load_time = + pit->channels[2].count_load_time; + inkernel_state->channels[2].gate = pit->channels[2].gate; + kvm_set_pit(kvm_context, inkernel_state); + } +} +#else +static inline void kvm_get_pit_ch2(PITState *pit, + struct kvm_pit_state *inkernel_state) { } +static inline void kvm_set_pit_ch2(PITState *pit, + struct kvm_pit_state *inkernel_state) { } +#endif + static inline void generate_samples(PCSpkState *s) { unsigned int i; @@ -72,6 +111,8 @@ static void pcspk_callback(void *opaque, int free) PCSpkState *s = opaque; unsigned int n; + kvm_get_pit_ch2(s->pit, NULL); + if (pit_get_mode(s->pit, 2) != 3) return; @@ -117,6 +158,8 @@ static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr) PCSpkState *s = opaque; int out; + kvm_get_pit_ch2(s->pit, NULL); + s->dummy_refresh_clock ^= (1 << 4); out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5; @@ -125,9 +168,12 @@ static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr) static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) { + struct kvm_pit_state inkernel_state; PCSpkState *s = opaque; const int gate = val & 1; + kvm_get_pit_ch2(s->pit, &inkernel_state); + s->data_on = (val >> 1) & 1; pit_set_gate(s->pit, 2, gate); if (s->voice) { @@ -135,6 +181,8 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->play_pos = 0; AUD_set_active_out(s->voice, gate & s->data_on); } + + kvm_set_pit_ch2(s->pit, &inkernel_state); } void pcspk_init(PITState *pit) diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 1b67475e0..001bc93cd 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -29,6 +29,8 @@ #include "isa.h" #include "sysbus.h" +#include "qemu-kvm.h" + typedef PCIHostState I440FXState; typedef struct PIIX3State { @@ -88,6 +90,10 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) int i, r; uint32_t smram, addr; + if (kvm_enabled()) { + /* FIXME: Support remappings and protection changes. */ + return; + } update_pam(d, 0xf0000, 0x100000, (d->dev.config[0x59] >> 4) & 3); for(i = 0; i < 12; i++) { r = (d->dev.config[(i >> 1) + 0x5a] >> ((i & 1) * 4)) & 3; @@ -201,6 +207,8 @@ static int i440fx_initfn(PCIDevice *dev) return 0; } +static PIIX3State *piix3_dev; + PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic) { DeviceState *dev; @@ -226,6 +234,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq * *piix3_devfn = piix3->dev.devfn; + piix3_dev = piix3; + return b; } @@ -253,6 +263,13 @@ static void piix3_set_irq(void *opaque, int irq_num, int level) } } +int piix_get_irq(int pin) +{ + if (piix3_dev) + return piix3_dev->dev.config[0x60+pin]; + return 0; +} + static void piix3_reset(void *opaque) { PIIX3State *d = opaque; diff --git a/hw/ppc440.c b/hw/ppc440.c index abe0a560d..ef883735b 100644 --- a/hw/ppc440.c +++ b/hw/ppc440.c @@ -20,6 +20,7 @@ #include "ppc405.h" #include "sysemu.h" #include "kvm.h" +#include "qemu-kvm.h" #define PPC440EP_PCI_CONFIG 0xeec00000 #define PPC440EP_PCI_INTACK 0xeed00000 diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index a4882406a..25417e397 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -24,6 +24,7 @@ #include "device_tree.h" #include "loader.h" #include "elf.h" +#include "qemu-kvm.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index ea30816b3..45356ca02 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -31,6 +31,7 @@ #include "ppce500.h" #include "loader.h" #include "elf.h" +#include "qemu-kvm.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 diff --git a/hw/testdev.c b/hw/testdev.c new file mode 100644 index 000000000..ac5b9cd1d --- /dev/null +++ b/hw/testdev.c @@ -0,0 +1,63 @@ +#include "hw.h" +#include "qdev.h" +#include "isa.h" + +struct testdev { + ISADevice dev; + CharDriverState *chr; +}; + +static void test_device_serial_write(void *opaque, uint32_t addr, uint32_t data) +{ + struct testdev *dev = opaque; + uint8_t buf[1] = { data }; + + if (dev->chr) { + qemu_chr_write(dev->chr, buf, 1); + } +} + +static void test_device_exit(void *opaque, uint32_t addr, uint32_t data) +{ + exit(data); +} + +static uint32_t test_device_memsize_read(void *opaque, uint32_t addr) +{ + return ram_size; +} + +static void test_device_irq_line(void *opaque, uint32_t addr, uint32_t data) +{ + extern qemu_irq *ioapic_irq_hack; + + qemu_set_irq(ioapic_irq_hack[addr - 0x2000], !!data); +} + +static int init_test_device(ISADevice *isa) +{ + struct testdev *dev = DO_UPCAST(struct testdev, dev, isa); + + register_ioport_write(0xf1, 1, 1, test_device_serial_write, dev); + register_ioport_write(0xf4, 1, 4, test_device_exit, dev); + register_ioport_read(0xd1, 1, 4, test_device_memsize_read, dev); + register_ioport_write(0x2000, 24, 1, test_device_irq_line, NULL); + return 0; +} + +static ISADeviceInfo testdev_info = { + .qdev.name = "testdev", + .qdev.size = sizeof(struct testdev), + .init = init_test_device, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", struct testdev, chr), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void testdev_register_devices(void) +{ + isa_qdev_register(&testdev_info); +} + +device_init(testdev_register_devices) diff --git a/hw/vga-pci.c b/hw/vga-pci.c index eef78ed08..9089c9f5d 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -68,9 +68,11 @@ static void pci_vga_write_config(PCIDevice *d, PCIVGAState *pvs = container_of(d, PCIVGAState, dev); VGACommonState *s = &pvs->vga; + vga_dirty_log_stop(s); pci_default_write_config(d, address, val, len); if (s->map_addr && pvs->dev.io_regions[0].addr == -1) s->map_addr = 0; + vga_dirty_log_start(s); } static int pci_vga_initfn(PCIDevice *dev) @@ -1277,6 +1277,8 @@ static void vga_draw_text(VGACommonState *s, int full_update) vga_draw_glyph8_func *vga_draw_glyph8; vga_draw_glyph9_func *vga_draw_glyph9; + vga_dirty_log_stop(s); + /* compute font data address (in plane 2) */ v = s->sr[3]; offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; @@ -1589,40 +1591,65 @@ static void vga_sync_dirty_bitmap(VGACommonState *s) } #endif + vga_dirty_log_start(s); +} + +static int s1, s2, s3; + +static void mark_dirty(target_phys_addr_t start, target_phys_addr_t len) +{ + target_phys_addr_t end = start + len; + + while (start < end) { + cpu_physical_memory_set_dirty(cpu_get_physical_page_desc(start)); + start += TARGET_PAGE_SIZE; + } } void vga_dirty_log_start(VGACommonState *s) { if (kvm_enabled() && s->map_addr) - kvm_log_start(s->map_addr, s->map_end - s->map_addr); - + if (!s1) { + kvm_log_start(s->map_addr, s->map_end - s->map_addr); + mark_dirty(s->map_addr, s->map_end - s->map_addr); + s1 = 1; + } if (kvm_enabled() && s->lfb_vram_mapped) { - kvm_log_start(isa_mem_base + 0xa0000, 0x8000); - kvm_log_start(isa_mem_base + 0xa8000, 0x8000); + if (!s2) { + kvm_log_start(isa_mem_base + 0xa0000, 0x8000); + kvm_log_start(isa_mem_base + 0xa8000, 0x8000); + mark_dirty(isa_mem_base + 0xa0000, 0x10000); + } + s2 = 1; } #ifdef CONFIG_BOCHS_VBE if (kvm_enabled() && s->vbe_mapped) { - kvm_log_start(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); + if (!s3) { + kvm_log_start(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); + } + s3 = 1; } #endif } void vga_dirty_log_stop(VGACommonState *s) { - if (kvm_enabled() && s->map_addr) + if (kvm_enabled() && s->map_addr && s1) kvm_log_stop(s->map_addr, s->map_end - s->map_addr); - if (kvm_enabled() && s->lfb_vram_mapped) { + if (kvm_enabled() && s->lfb_vram_mapped && s2) { kvm_log_stop(isa_mem_base + 0xa0000, 0x80000); kvm_log_stop(isa_mem_base + 0xa8000, 0x80000); } #ifdef CONFIG_BOCHS_VBE - if (kvm_enabled() && s->vbe_mapped) { + if (kvm_enabled() && s->vbe_mapped && s3) { kvm_log_stop(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); } #endif + + s1 = s2 = s3 = 0; } void vga_dirty_log_restart(VGACommonState *s) @@ -1860,6 +1887,7 @@ static void vga_draw_blank(VGACommonState *s, int full_update) return; if (s->last_scr_width <= 0 || s->last_scr_height <= 0) return; + vga_dirty_log_stop(s); s->rgb_to_pixel = rgb_to_pixel_dup_table[get_depth_index(s->ds)]; @@ -1904,6 +1932,9 @@ static void vga_update_display(void *opaque) vga_draw_text(s, full_update); break; case GMODE_GRAPH: +#ifdef TARGET_IA64 + full_update = 1; +#endif vga_draw_graphic(s, full_update); break; case GMODE_BLANK: diff --git a/hw/vga_int.h b/hw/vga_int.h index 23a42efce..9e52e709c 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -33,8 +33,8 @@ /* bochs VBE support */ #define CONFIG_BOCHS_VBE -#define VBE_DISPI_MAX_XRES 1600 -#define VBE_DISPI_MAX_YRES 1200 +#define VBE_DISPI_MAX_XRES 2560 +#define VBE_DISPI_MAX_YRES 1600 #define VBE_DISPI_MAX_BPP 32 #define VBE_DISPI_INDEX_ID 0x0 @@ -224,7 +224,7 @@ void vga_init_vbe(VGACommonState *s); extern const uint8_t sr_mask[8]; extern const uint8_t gr_mask[16]; -#define VGA_RAM_SIZE (8192 * 1024) +#define VGA_RAM_SIZE (16 * 1024 * 1024) #define VGABIOS_FILENAME "vgabios.bin" #define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin" diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index cfd3b413f..7ca783e49 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -19,6 +19,7 @@ #include "balloon.h" #include "virtio-balloon.h" #include "kvm.h" +#include "qemu-kvm.h" #if defined(__linux__) #include <sys/mman.h> diff --git a/hw/virtio-console.c b/hw/virtio-console.c index 57f8f89af..92c953c35 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -129,6 +129,9 @@ VirtIODevice *virtio_console_init(DeviceState *dev) s = (VirtIOConsole *)virtio_common_init("virtio-console", VIRTIO_ID_CONSOLE, 0, sizeof(VirtIOConsole)); + if (s == NULL) + return NULL; + s->vdev.get_features = virtio_console_get_features; s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input); diff --git a/hw/vmport.c b/hw/vmport.c index 884af3fd9..648861b4d 100644 --- a/hw/vmport.c +++ b/hw/vmport.c @@ -21,10 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "hw.h" #include "isa.h" #include "pc.h" #include "sysemu.h" +#include "qemu-kvm.h" //#define VMPORT_DEBUG @@ -57,6 +59,10 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr) CPUState *env = cpu_single_env; unsigned char command; uint32_t eax; + uint32_t ret; + + if (kvm_enabled()) + kvm_save_registers(env); eax = env->regs[R_EAX]; if (eax != VMPORT_MAGIC) @@ -73,7 +79,12 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr) return eax; } - return s->func[command](s->opaque[command], addr); + ret = s->func[command](s->opaque[command], addr); + + if (kvm_enabled()) + kvm_load_registers(env); + + return ret; } static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -7,7 +7,7 @@ ENTRY(_start) SECTIONS { /* Read-only sections, merged into text segment: */ - PROVIDE (__executable_start = 0x60000000); . = 0x60000000 + SIZEOF_HEADERS; + PROVIDE (__executable_start = 0x4000000060000000); . = 0x4000000060000000 + SIZEOF_HEADERS; .interp : { *(.interp) } .hash : { *(.hash) } .dynsym : { *(.dynsym) } diff --git a/ia64intrin.h b/ia64intrin.h new file mode 100644 index 000000000..ddd5ed980 --- /dev/null +++ b/ia64intrin.h @@ -0,0 +1,150 @@ +#ifndef IA64_INTRINSIC_H +#define IA64_INTRINSIC_H + +/* + * Compiler-dependent Intrinsics + * + * Copyright (C) 2002,2003 Jun Nakajima <jun.nakajima@intel.com> + * Copyright (C) 2002,2003 Suresh Siddha <suresh.b.siddha@intel.com> + * + */ +extern long ia64_cmpxchg_called_with_bad_pointer (void); +extern void ia64_bad_param_for_getreg (void); +#define ia64_cmpxchg(sem,ptr,o,n,s) ({ \ + uint64_t _o, _r; \ + switch(s) { \ + case 1: _o = (uint8_t)(long)(o); break; \ + case 2: _o = (uint16_t)(long)(o); break; \ + case 4: _o = (uint32_t)(long)(o); break; \ + case 8: _o = (uint64_t)(long)(o); break; \ + default: break; \ + } \ + switch(s) { \ + case 1: \ + _r = ia64_cmpxchg1_##sem((uint8_t*)ptr,n,_o); break; \ + case 2: \ + _r = ia64_cmpxchg2_##sem((uint16_t*)ptr,n,_o); break; \ + case 4: \ + _r = ia64_cmpxchg4_##sem((uint32_t*)ptr,n,_o); break; \ + case 8: \ + _r = ia64_cmpxchg8_##sem((uint64_t*)ptr,n,_o); break; \ + default: \ + _r = ia64_cmpxchg_called_with_bad_pointer(); break; \ + } \ + (__typeof__(o)) _r; \ +}) + +#define cmpxchg_acq(ptr,o,n) ia64_cmpxchg(acq,ptr,o,n,sizeof(*ptr)) +#define cmpxchg_rel(ptr,o,n) ia64_cmpxchg(rel,ptr,o,n,sizeof(*ptr)) + +#ifdef __INTEL_COMPILER +void __fc(uint64_t *addr); +void __synci(void); +void __isrlz(void); +void __dsrlz(void); +uint64_t __getReg(const int whichReg); +uint64_t _InterlockedCompareExchange8_rel(volatile uint8_t *dest, uint64_t xchg, uint64_t comp); +uint64_t _InterlockedCompareExchange8_acq(volatile uint8_t *dest, uint64_t xchg, uint64_t comp); +uint64_t _InterlockedCompareExchange16_rel(volatile uint16_t *dest, uint64_t xchg, uint64_t comp); +uint64_t _InterlockedCompareExchange16_acq(volatile uint16_t *dest, uint64_t xchg, uint64_t comp); +uint64_t _InterlockedCompareExchange_rel(volatile uint32_t *dest, uint64_t xchg, uint64_t comp); +uint64_t _InterlockedCompareExchange_acq(volatile uint32_t *dest, uint64_t xchg, uint64_t comp); +uint64_t _InterlockedCompareExchange64_rel(volatile uint64_t *dest, uint64_t xchg, uint64_t comp); +u64_t _InterlockedCompareExchange64_acq(volatile uint64_t *dest, uint64_t xchg, uint64_t comp); + +#define ia64_cmpxchg1_rel _InterlockedCompareExchange8_rel +#define ia64_cmpxchg1_acq _InterlockedCompareExchange8_acq +#define ia64_cmpxchg2_rel _InterlockedCompareExchange16_rel +#define ia64_cmpxchg2_acq _InterlockedCompareExchange16_acq +#define ia64_cmpxchg4_rel _InterlockedCompareExchange_rel +#define ia64_cmpxchg4_acq _InterlockedCompareExchange_acq +#define ia64_cmpxchg8_rel _InterlockedCompareExchange64_rel +#define ia64_cmpxchg8_acq _InterlockedCompareExchange64_acq + +#define ia64_srlz_d __dsrlz +#define ia64_srlz_i __isrlz +#define __ia64_fc __fc +#define ia64_sync_i __synci +#define __ia64_getreg __getReg +#else /* __INTEL_COMPILER */ +#define ia64_cmpxchg1_acq(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + asm volatile ("cmpxchg1.acq %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg1_rel(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + asm volatile ("cmpxchg1.rel %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg2_acq(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + asm volatile ("cmpxchg2.acq %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg2_rel(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + \ + asm volatile ("cmpxchg2.rel %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg4_acq(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + asm volatile ("cmpxchg4.acq %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg4_rel(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + asm volatile ("cmpxchg4.rel %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg8_acq(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + asm volatile ("cmpxchg8.acq %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_cmpxchg8_rel(ptr, new, old) \ +({ \ + uint64_t ia64_intri_res; \ + asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \ + \ + asm volatile ("cmpxchg8.rel %0=[%1],%2,ar.ccv": \ + "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \ + ia64_intri_res; \ +}) + +#define ia64_srlz_i() asm volatile (";; srlz.i ;;" ::: "memory") +#define ia64_srlz_d() asm volatile (";; srlz.d" ::: "memory"); +#define __ia64_fc(addr) asm volatile ("fc %0" :: "r"(addr) : "memory") +#define ia64_sync_i() asm volatile (";; sync.i" ::: "memory") + +#endif /* __INTEL_COMPILER */ +#endif /* IA64_INTRINSIC_H */ @@ -26,6 +26,7 @@ #include "gdbstub.h" #include "kvm.h" +#ifdef KVM_UPSTREAM /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ #define PAGE_SIZE TARGET_PAGE_SIZE @@ -57,7 +58,6 @@ struct KVMState KVMSlot slots[32]; int fd; int vmfd; - int regs_modified; int coalesced_mmio; int broken_set_mem_region; int migration_log; @@ -157,12 +157,14 @@ static void kvm_reset_vcpu(void *opaque) abort(); } } +#endif int kvm_irqchip_in_kernel(void) { return kvm_state->irqchip_in_kernel; } +#ifdef KVM_UPSTREAM int kvm_pit_in_kernel(void) { return kvm_state->pit_in_kernel; @@ -343,6 +345,7 @@ int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, return ret; } +#endif int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) { @@ -393,6 +396,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension) return ret; } +#ifdef KVM_UPSTREAM int kvm_init(int smp_cpus) { @@ -504,6 +508,7 @@ err: return ret; } +#endif static int kvm_handle_io(uint16_t port, void *data, int direction, int size, uint32_t count) @@ -544,6 +549,7 @@ static int kvm_handle_io(uint16_t port, void *data, int direction, int size, return 1; } +#ifdef KVM_UPSTREAM static void kvm_run_coalesced_mmio(CPUState *env, struct kvm_run *run) { #ifdef KVM_CAP_COALESCED_MMIO @@ -812,6 +818,7 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr, } } +#endif int kvm_ioctl(KVMState *s, int type, ...) { int ret; @@ -879,6 +886,7 @@ int kvm_has_vcpu_events(void) return kvm_state->vcpu_events; } +#ifdef KVM_UPSTREAM void kvm_setup_guest_memory(void *start, size_t size) { if (!kvm_has_sync_mmu()) { @@ -897,7 +905,11 @@ void kvm_setup_guest_memory(void *start, size_t size) } } +#endif /* KVM_UPSTREAM */ + #ifdef KVM_CAP_SET_GUEST_DEBUG + +#ifdef KVM_UPSTREAM static void on_vcpu(CPUState *env, void (*func)(void *data), void *data) { #ifdef CONFIG_IOTHREAD @@ -910,6 +922,7 @@ static void on_vcpu(CPUState *env, void (*func)(void *data), void *data) func(data); #endif } +#endif /* KVM_UPSTREAM */ struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, target_ulong pc) @@ -928,6 +941,8 @@ int kvm_sw_breakpoints_active(CPUState *env) return !QTAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); } +#ifdef KVM_UPSTREAM + struct kvm_set_guest_debug_data { struct kvm_guest_debug dbg; CPUState *env; @@ -961,6 +976,7 @@ int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) on_vcpu(env, kvm_invoke_set_guest_debug, &data); return data.err; } +#endif int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, target_ulong len, int type) @@ -1085,3 +1101,5 @@ void kvm_remove_all_breakpoints(CPUState *current_env) { } #endif /* !KVM_CAP_SET_GUEST_DEBUG */ + +#include "qemu-kvm.c" diff --git a/kvm-tpr-opt.c b/kvm-tpr-opt.c new file mode 100644 index 000000000..bf9c9a06e --- /dev/null +++ b/kvm-tpr-opt.c @@ -0,0 +1,410 @@ +/* + * tpr optimization for qemu/kvm + * + * Copyright (C) 2007-2008 Qumranet Technologies + * + * Licensed under the terms of the GNU GPL version 2 or higher. + */ + +#include "config.h" +#include "config-host.h" + +#include <string.h> + +#include "hw/hw.h" +#include "hw/isa.h" +#include "sysemu.h" +#include "qemu-kvm.h" +#include "cpu.h" + +#include <stdio.h> + +static uint64_t map_addr(struct kvm_sregs *sregs, target_ulong virt, unsigned *perms) +{ + uint64_t mask = ((1ull << 48) - 1) & ~4095ull; + uint64_t p, pp = 7; + + p = sregs->cr3; + if (sregs->cr4 & 0x20) { + p &= ~31ull; + p = ldq_phys(p + 8 * (virt >> 30)); + if (!(p & 1)) + return -1ull; + p &= mask; + p = ldq_phys(p + 8 * ((virt >> 21) & 511)); + if (!(p & 1)) + return -1ull; + pp &= p; + if (p & 128) { + p += ((virt >> 12) & 511) << 12; + } else { + p &= mask; + p = ldq_phys(p + 8 * ((virt >> 12) & 511)); + if (!(p & 1)) + return -1ull; + pp &= p; + } + } else { + p &= mask; + p = ldl_phys(p + 4 * ((virt >> 22) & 1023)); + if (!(p & 1)) + return -1ull; + pp &= p; + if (p & 128) { + p += ((virt >> 12) & 1023) << 12; + } else { + p &= mask; + p = ldl_phys(p + 4 * ((virt >> 12) & 1023)); + pp &= p; + if (!(p & 1)) + return -1ull; + } + } + if (perms) + *perms = pp >> 1; + p &= mask; + return p + (virt & 4095); +} + +static uint8_t read_byte_virt(CPUState *env, target_ulong virt) +{ + struct kvm_sregs sregs; + + kvm_get_sregs(env, &sregs); + return ldub_phys(map_addr(&sregs, virt, NULL)); +} + +static void write_byte_virt(CPUState *env, target_ulong virt, uint8_t b) +{ + struct kvm_sregs sregs; + + kvm_get_sregs(env, &sregs); + stb_phys(map_addr(&sregs, virt, NULL), b); +} + +static __u64 kvm_rsp_read(CPUState *env) +{ + struct kvm_regs regs; + + kvm_get_regs(env, ®s); + return regs.rsp; +} + +struct vapic_bios { + char signature[8]; + uint32_t virt_base; + uint32_t fixup_start; + uint32_t fixup_end; + uint32_t vapic; + uint32_t vapic_size; + uint32_t vcpu_shift; + uint32_t real_tpr; + struct vapic_patches { + uint32_t set_tpr; + uint32_t set_tpr_eax; + uint32_t get_tpr[8]; + uint32_t get_tpr_stack; + } __attribute__((packed)) up, mp; +} __attribute__((packed)); + +static struct vapic_bios vapic_bios; + +static uint32_t real_tpr; +static uint32_t bios_addr; +static uint32_t vapic_phys; +static uint32_t bios_enabled; +static uint32_t vbios_desc_phys; +static uint32_t vapic_bios_addr; + +static void update_vbios_real_tpr(void) +{ + cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 0); + vapic_bios.real_tpr = real_tpr; + vapic_bios.vcpu_shift = 7; + cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 1); +} + +static unsigned modrm_reg(uint8_t modrm) +{ + return (modrm >> 3) & 7; +} + +static int is_abs_modrm(uint8_t modrm) +{ + return (modrm & 0xc7) == 0x05; +} + +static int instruction_is_ok(CPUState *env, uint64_t rip, int is_write) +{ + uint8_t b1, b2; + unsigned addr_offset; + uint32_t addr; + uint64_t p; + + if ((rip & 0xf0000000) != 0x80000000 && (rip & 0xf0000000) != 0xe0000000) + return 0; + if (kvm_rsp_read(env) == 0) + return 0; + b1 = read_byte_virt(env, rip); + b2 = read_byte_virt(env, rip + 1); + switch (b1) { + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + if (modrm_reg(b2) != 0) + return 0; + /* fall through */ + case 0x89: /* mov r32 to r/m32 */ + case 0x8b: /* mov r/m32 to r32 */ + if (!is_abs_modrm(b2)) + return 0; + addr_offset = 2; + break; + case 0xa1: /* mov abs to eax */ + case 0xa3: /* mov eax to abs */ + addr_offset = 1; + break; + case 0xff: /* push r/m32 */ + if (modrm_reg(b2) != 6 || !is_abs_modrm(b2)) + return 0; + addr_offset = 2; + default: + return 0; + } + p = rip + addr_offset; + addr = read_byte_virt(env, p++); + addr |= read_byte_virt(env, p++) << 8; + addr |= read_byte_virt(env, p++) << 16; + addr |= read_byte_virt(env, p++) << 24; + if ((addr & 0xfff) != 0x80) + return 0; + real_tpr = addr; + update_vbios_real_tpr(); + return 1; +} + +static int bios_is_mapped(CPUState *env, uint64_t rip) +{ + uint32_t probe; + uint64_t phys; + struct kvm_sregs sregs; + unsigned perms; + uint32_t i; + uint32_t offset, fixup, start = vapic_bios_addr ? : 0xe0000; + + if (bios_enabled) + return 1; + + kvm_get_sregs(env, &sregs); + + probe = (rip & 0xf0000000) + start; + phys = map_addr(&sregs, probe, &perms); + if (phys != start) + return 0; + bios_addr = probe; + for (i = 0; i < 64; ++i) { + cpu_physical_memory_read(phys, (void *)&vapic_bios, sizeof(vapic_bios)); + if (memcmp(vapic_bios.signature, "kvm aPiC", 8) == 0) + break; + phys += 1024; + bios_addr += 1024; + } + if (i == 64) + return 0; + if (bios_addr == vapic_bios.virt_base) + return 1; + vbios_desc_phys = phys; + for (i = vapic_bios.fixup_start; i < vapic_bios.fixup_end; i += 4) { + offset = ldl_phys(phys + i - vapic_bios.virt_base); + fixup = phys + offset; + stl_phys(fixup, ldl_phys(fixup) + bios_addr - vapic_bios.virt_base); + } + vapic_phys = vapic_bios.vapic - vapic_bios.virt_base + phys; + return 1; +} + +static int get_pcr_cpu(CPUState *env) +{ + uint8_t b; + + cpu_synchronize_state(env); + + if (cpu_memory_rw_debug(env, env->segs[R_FS].base + 0x51, &b, 1, 0) < 0) + return -1; + + return (int)b; +} + +int kvm_tpr_enable_vapic(CPUState *env) +{ + static uint8_t one = 1; + int pcr_cpu = get_pcr_cpu(env); + + if (pcr_cpu < 0) + return 0; + + kvm_enable_vapic(env, vapic_phys + (pcr_cpu << 7)); + cpu_physical_memory_rw(vapic_phys + (pcr_cpu << 7) + 4, &one, 1, 1); + env->update_vapic = 0; + bios_enabled = 1; + return 1; +} + +static int enable_vapic(CPUState *env) +{ + bios_enabled = 1; + env->update_vapic = 1; + return 1; +} + +static void patch_call(CPUState *env, uint64_t rip, uint32_t target) +{ + uint32_t offset; + + offset = target - vapic_bios.virt_base + bios_addr - rip - 5; + write_byte_virt(env, rip, 0xe8); /* call near */ + write_byte_virt(env, rip + 1, offset); + write_byte_virt(env, rip + 2, offset >> 8); + write_byte_virt(env, rip + 3, offset >> 16); + write_byte_virt(env, rip + 4, offset >> 24); +} + +static void patch_instruction(CPUState *env, uint64_t rip) +{ + uint8_t b1, b2; + struct vapic_patches *vp; + + vp = smp_cpus == 1 ? &vapic_bios.up : &vapic_bios.mp; + b1 = read_byte_virt(env, rip); + b2 = read_byte_virt(env, rip + 1); + switch (b1) { + case 0x89: /* mov r32 to r/m32 */ + write_byte_virt(env, rip, 0x50 + modrm_reg(b2)); /* push reg */ + patch_call(env, rip + 1, vp->set_tpr); + break; + case 0x8b: /* mov r/m32 to r32 */ + write_byte_virt(env, rip, 0x90); + patch_call(env, rip + 1, vp->get_tpr[modrm_reg(b2)]); + break; + case 0xa1: /* mov abs to eax */ + patch_call(env, rip, vp->get_tpr[0]); + break; + case 0xa3: /* mov eax to abs */ + patch_call(env, rip, vp->set_tpr_eax); + break; + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + write_byte_virt(env, rip, 0x68); /* push imm32 */ + write_byte_virt(env, rip + 1, read_byte_virt(env, rip+6)); + write_byte_virt(env, rip + 2, read_byte_virt(env, rip+7)); + write_byte_virt(env, rip + 3, read_byte_virt(env, rip+8)); + write_byte_virt(env, rip + 4, read_byte_virt(env, rip+9)); + patch_call(env, rip + 5, vp->set_tpr); + break; + case 0xff: /* push r/m32 */ + printf("patching push\n"); + write_byte_virt(env, rip, 0x50); /* push eax */ + patch_call(env, rip + 1, vp->get_tpr_stack); + break; + default: + printf("funny insn %02x %02x\n", b1, b2); + } +} + +void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write) +{ + if (!instruction_is_ok(env, rip, is_write)) + return; + if (!bios_is_mapped(env, rip)) + return; + if (!kvm_tpr_enable_vapic(env)) + return; + patch_instruction(env, rip); +} + +void kvm_tpr_vcpu_start(CPUState *env) +{ + kvm_enable_tpr_access_reporting(env); + if (bios_enabled) + kvm_tpr_enable_vapic(env); +} + +static void tpr_save(QEMUFile *f, void *s) +{ + int i; + + for (i = 0; i < (sizeof vapic_bios) / 4; ++i) + qemu_put_be32s(f, &((uint32_t *)&vapic_bios)[i]); + qemu_put_be32s(f, &bios_enabled); + qemu_put_be32s(f, &real_tpr); + qemu_put_be32s(f, &bios_addr); + qemu_put_be32s(f, &vapic_phys); + qemu_put_be32s(f, &vbios_desc_phys); +} + +static int tpr_load(QEMUFile *f, void *s, int version_id) +{ + int i; + + if (version_id != 1) + return -EINVAL; + + for (i = 0; i < (sizeof vapic_bios) / 4; ++i) + qemu_get_be32s(f, &((uint32_t *)&vapic_bios)[i]); + qemu_get_be32s(f, &bios_enabled); + qemu_get_be32s(f, &real_tpr); + qemu_get_be32s(f, &bios_addr); + qemu_get_be32s(f, &vapic_phys); + qemu_get_be32s(f, &vbios_desc_phys); + + if (bios_enabled) { + CPUState *env = first_cpu->next_cpu; + + for (env = first_cpu; env != NULL; env = env->next_cpu) + enable_vapic(env); + } + + return 0; +} + +static void vtpr_ioport_write16(void *opaque, uint32_t addr, uint32_t val) +{ + struct kvm_regs regs; + CPUState *env = cpu_single_env; + struct kvm_sregs sregs; + kvm_get_regs(env, ®s); + kvm_get_sregs(env, &sregs); + vapic_bios_addr = ((sregs.cs.base + regs.rip) & ~(512 - 1)) + val; + bios_enabled = 0; +} + +static void vtpr_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + CPUState *env = cpu_single_env; + struct kvm_regs regs; + struct kvm_sregs sregs; + uint32_t rip; + + kvm_get_regs(env, ®s); + rip = regs.rip - 2; + write_byte_virt(env, rip, 0x66); + write_byte_virt(env, rip + 1, 0x90); + if (bios_enabled) + return; + if (!bios_is_mapped(env, rip)) + printf("bios not mapped?\n"); + kvm_get_sregs(env, &sregs); + for (addr = 0xfffff000u; addr >= 0x80000000u; addr -= 4096) + if (map_addr(&sregs, addr, NULL) == 0xfee00000u) { + real_tpr = addr + 0x80; + break; + } + bios_enabled = 1; + update_vbios_real_tpr(); + kvm_tpr_enable_vapic(env); +} + +void kvm_tpr_opt_setup(void) +{ + register_savevm("kvm-tpr-opt", 0, 1, tpr_save, tpr_load, NULL); + register_ioport_write(0x7e, 1, 1, vtpr_ioport_write, NULL); + register_ioport_write(0x7e, 2, 2, vtpr_ioport_write16, NULL); +} + @@ -16,6 +16,9 @@ #include "config.h" #include "qemu-queue.h" +#include "qemu-kvm.h" + +#ifdef KVM_UPSTREAM #ifdef CONFIG_KVM extern int kvm_allowed; @@ -47,7 +50,12 @@ int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size); int kvm_set_migration_log(int enable); int kvm_has_sync_mmu(void); +#endif /* KVM_UPSTREAM */ int kvm_has_vcpu_events(void); +int kvm_put_vcpu_events(CPUState *env); +int kvm_get_vcpu_events(CPUState *env); + +#ifdef KVM_UPSTREAM void kvm_setup_guest_memory(void *start, size_t size); @@ -91,7 +99,9 @@ int kvm_arch_init(KVMState *s, int smp_cpus); int kvm_arch_init_vcpu(CPUState *env); +#endif void kvm_arch_reset_vcpu(CPUState *env); +#ifdef KVM_UPSTREAM struct kvm_guest_debug; struct kvm_debug_exit_arch; @@ -140,3 +150,5 @@ static inline void cpu_synchronize_state(CPUState *env) } #endif + +#endif diff --git a/kvm/.gitignore b/kvm/.gitignore new file mode 100644 index 000000000..22a820011 --- /dev/null +++ b/kvm/.gitignore @@ -0,0 +1,66 @@ +*.o +*.d +*~ +*.flat +*.a +config.mak +.*.cmd +qemu/config-host.h +qemu/config-host.mak +user/test/bootstrap +user/kvmctl +qemu/dyngen +qemu/x86_64-softmmu +qemu/qemu-img +qemu/qemu-nbd +*.ko +*.mod.c +bios/*.bin +bios/*.sym +bios/*.txt +bios/acpi-dsdt.aml +vgabios/*.bin +vgabios/*.txt +extboot/extboot.bin +extboot/extboot.img +extboot/signrom +kernel/config.kbuild +kernel/modules.order +kernel/Module.symvers +kernel/Modules.symvers +kernel/Module.markers +kernel/.tmp_versions +kernel/include-compat/asm +kernel/include-compat/asm-x86/asm-x86 +kernel/include +kernel/x86/modules.order +kernel/x86/i825[49].[ch] +kernel/x86/kvm_main.c +kernel/x86/kvm_svm.h +kernel/x86/vmx.[ch] +kernel/x86/svm.[ch] +kernel/x86/mmu.[ch] +kernel/x86/paging_tmpl.h +kernel/x86/x86_emulate.[ch] +kernel/x86/ioapic.[ch] +kernel/x86/iodev.h +kernel/x86/irq.[ch] +kernel/x86/kvm_trace.c +kernel/x86/lapic.[ch] +kernel/x86/tss.h +kernel/x86/x86.[ch] +kernel/x86/coalesced_mmio.[ch] +kernel/x86/kvm_cache_regs.h +kernel/x86/vtd.c +kernel/x86/irq_comm.c +kernel/x86/timer.c +kernel/x86/kvm_timer.h +kernel/x86/iommu.c +qemu/pc-bios/extboot.bin +qemu/qemu-doc.html +qemu/*.[18] +qemu/*.pod +qemu/qemu-tech.html +qemu/qemu-options.texi +user/kvmtrace +user/test/x86/bootstrap diff --git a/kvm/Makefile b/kvm/Makefile new file mode 100644 index 000000000..617504caf --- /dev/null +++ b/kvm/Makefile @@ -0,0 +1,125 @@ + +include config.mak + +DESTDIR= + +rpmrelease = devel + +sane-arch = $(subst i386,x86,$(subst x86_64,x86,$(subst s390x,s390,$(ARCH)))) + +.PHONY: kernel user libkvm qemu bios vgabios extboot clean libfdt cscope + +all: libkvm qemu +ifneq '$(filter $(ARCH), x86_64 i386 ia64)' '' + all: $(if $(WANT_MODULE), kernel) user +endif + +kcmd = $(if $(WANT_MODULE),,@\#) + +qemu kernel user libkvm: + $(MAKE) -C $@ + +qemu: libkvm +ifneq '$(filter $(ARCH), i386 x86_64)' '' + qemu: extboot +endif +ifneq '$(filter $(ARCH), powerpc ia64)' '' + qemu: libfdt +endif +user: libkvm + +# sync if kernel/Makefile exists and if using --with-patched-kernel +user libkvm qemu: header-sync-$(if $(wildcard kernel/Makefile),$(if $(WANT_MODULE),n,y),n) + +header-sync-n: + +header-sync-y: + make -C kernel \ + LINUX=$(if $(KERNELSOURCEDIR),$(KERNELSOURCEDIR),$(KERNELDIR)) \ + header-sync + rm -f kernel/include/asm + ln -sf asm-$(sane-arch) kernel/include/asm + +bios: + $(MAKE) -C $@ + cp bios/BIOS-bochs-latest qemu/pc-bios/bios.bin + +vgabios: + $(MAKE) -C $@ + cp vgabios/VGABIOS-lgpl-latest.bin qemu/pc-bios/vgabios.bin + cp vgabios/VGABIOS-lgpl-latest.cirrus.bin qemu/pc-bios/vgabios-cirrus.bin + +extboot: + $(MAKE) -C $@ + if ! [ -f qemu/pc-bios/extboot.bin ] \ + || ! cmp -s qemu/pc-bios/extboot.bin extboot/extboot.bin; then \ + cp extboot/extboot.bin qemu/pc-bios/extboot.bin; \ + fi +libfdt: + $(MAKE) -C $@ + +LINUX=linux-2.6 + +sync: + make -C kernel sync LINUX=$(shell readlink -f "$(LINUX)") + +bindir = /usr/bin +bin = $(bindir)/kvm +initdir = /etc/init.d +confdir = /etc/kvm +utilsdir = /etc/kvm/utils + +install-rpm: + mkdir -p $(DESTDIR)/$(bindir) + mkdir -p $(DESTDIR)/$(confdir) + mkdir -p $(DESTDIR)/$(initdir) + mkdir -p $(DESTDIR)/$(utilsdir) + mkdir -p $(DESTDIR)/etc/udev/rules.d + make -C qemu DESTDIR=$(DESTDIR)/ install + ln -sf /usr/kvm/bin/qemu-system-x86_64 $(DESTDIR)/$(bin) + install -m 755 kvm_stat $(DESTDIR)/$(bindir)/kvm_stat + cp scripts/kvm $(DESTDIR)/$(initdir)/kvm + cp scripts/qemu-ifup $(DESTDIR)/$(confdir)/qemu-ifup + install -t $(DESTDIR)/etc/udev/rules.d scripts/*kvm*.rules + +install: + $(kcmd)make -C kernel DESTDIR="$(DESTDIR)" install + make -C libkvm DESTDIR="$(DESTDIR)" install + make -C qemu DESTDIR="$(DESTDIR)" install + +tmpspec = .tmp.kvm.spec +RPMTOPDIR = $$(pwd)/rpmtop + +rpm: srpm + rm -rf $(RPMTOPDIR)/BUILD + mkdir -p $(RPMTOPDIR)/{BUILD,RPMS/$$(uname -i)} + rpmbuild --rebuild \ + --define="_topdir $(RPMTOPDIR)" \ + $(RPMTOPDIR)/SRPMS/kvm-0.0-$(rpmrelease).src.rpm + +srpm: + mkdir -p $(RPMTOPDIR)/{SOURCES,SRPMS} + sed 's/^Release:.*/Release: $(rpmrelease)/' kvm.spec > $(tmpspec) + tar czf $(RPMTOPDIR)/SOURCES/kvm.tar.gz qemu + tar czf $(RPMTOPDIR)/SOURCES/user.tar.gz user + tar czf $(RPMTOPDIR)/SOURCES/libkvm.tar.gz libkvm + tar czf $(RPMTOPDIR)/SOURCES/kernel.tar.gz kernel + tar czf $(RPMTOPDIR)/SOURCES/scripts.tar.gz scripts + tar czf $(RPMTOPDIR)/SOURCES/extboot.tar.gz extboot + cp Makefile configure kvm_stat $(RPMTOPDIR)/SOURCES + rpmbuild --define="_topdir $(RPMTOPDIR)" -bs $(tmpspec) + $(RM) $(tmpspec) + +clean: + for i in $(if $(WANT_MODULE), kernel) user libkvm qemu libfdt; do \ + make -C $$i clean; \ + done + rm -f ./cscope.* + +distclean: clean + rm -f config.mak user/config.mak + +cscope: + rm -f ./cscope.* + find . -wholename './kernel' -prune -o -name "*.[ch]" -print > ./cscope.files + cscope -b diff --git a/kvm/configure b/kvm/configure new file mode 100755 index 000000000..249c743f6 --- /dev/null +++ b/kvm/configure @@ -0,0 +1,159 @@ +#!/bin/bash + +prefix=/usr/local +kerneldir=/lib/modules/$(uname -r)/build +cc=gcc +ld=ld +objcopy=objcopy +ar=ar +want_module=1 +qemu_cflags= +qemu_ldflags= +kvm_trace= +qemu_opts=() +cross_prefix= +arch=`uname -m` +target_exec= +# don't use uname if kerneldir is set +no_uname= +if [ -z "TMPDIR" ] ; then + TMPDIR=. +fi + +if [ ! -e kernel/Makefile ]; then + want_module= +fi + +usage() { + cat <<-EOF + Usage: $0 [options] + + Options include: + --arch=ARCH architecture to compile for ($arch) + --cross-prefix=PREFIX prefix for cross compile + --prefix=PREFIX where to install things ($prefix) + --with-patched-kernel don't use external module + --with-kvm-trace Enable kvm_trace + --kerneldir=DIR kernel build directory ($kerneldir) + --qemu-cflags=CFLAGS CFLAGS to add to qemu configuration + --qemu-ldflags=LDFLAGS LDFLAGS to add to qemu configuration + + Any additional option is given to qemu's configure verbatim; including: + +EOF + cd qemu + ./configure --help | egrep "enable-|disable-" \ + | grep -v user | grep -v system | grep -v kqemu | grep -v kvm \ + | sed -e "s/^ / /g" \ + | sed -e"s/ enable/enable/g" | sed -e "s/ disable/disable/g" + exit 1 +} + +while [[ "$1" = -* ]]; do + opt="$1"; shift + arg= + hasarg= + if [[ "$opt" = *=* ]]; then + arg="${opt#*=}" + opt="${opt%%=*}" + hasarg=1 + fi + case "$opt" in + --prefix) + prefix="$arg" + ;; + --kerneldir) + kerneldir="$arg" + no_uname=1 + ;; + --with-patched-kernel) + want_module= + ;; + --with-kvm-trace) + kvm_trace=y + ;; + --qemu-cflags) + qemu_cflags="$arg" + ;; + --qemu-ldflags) + qemu_ldflags="$arg" + ;; + --arch) + arch="$arg" + ;; + --cross-prefix) + cross_prefix="$arg" + ;; + --help) + usage + ;; + *) + qemu_opts=("${qemu_opts[@]}" "$opt${hasarg:+=$arg}") + ;; + esac +done + + +#set kenel directory +libkvm_kerneldir=$(readlink -f kernel) + +case $arch in + i?86*|x86_64*) + arch=${arch/#i?86/i386} + target_exec="x86_64-softmmu" + qemu_cflags="$qemu_cflags -DCONFIG_X86" + ;; + ia64*) + target_exec="ia64-softmmu" + ;; + powerpc*) + target_exec="ppcemb-softmmu" + qemu_cflags="$qemu_cflags -I $PWD/libfdt" + qemu_ldflags="$qemu_ldflags -L $PWD/libfdt" + ;; +esac + +processor=${arch#*-} +arch=${arch%%-*} + +#configure kernel module +[ -e kernel/Makefile ] && (cd kernel; + ./configure \ + --kerneldir="$kerneldir" \ + --arch="$arch" \ + $([ -z ${want_module} ] && echo "--with-patched-kernel") \ + ${cross_prefix:+"--cross-prefix=$cross_prefix"} \ + ${kvm_trace:+"--with-kvm-trace"} +) + +#configure user dir +(cd user; ./configure --prefix="$prefix" --kerneldir="$libkvm_kerneldir" \ + --arch="$arch" --processor="$processor" \ + ${cross_prefix:+"--cross-prefix=$cross_prefix"}) + +#configure qemu +(cd qemu; ./configure --target-list=$target_exec \ + --disable-kqemu \ + --extra-cflags="-I $PWD/../libkvm $qemu_cflags" \ + --extra-ldflags="-L $PWD/../libkvm $qemu_ldflags" \ + --kerneldir="$libkvm_kerneldir" \ + --prefix="$prefix" \ + ${cross_prefix:+"--cross-prefix=$cross_prefix"} \ + ${cross_prefix:+"--cpu=$arch"} "${qemu_opts[@]}" +) || usage + + +cat <<EOF > config.mak +ARCH=$arch +PROCESSOR=$processor +PREFIX=$prefix +KERNELDIR=$kerneldir +KERNELSOURCEDIR=$kernelsourcedir +LIBKVM_KERNELDIR=$libkvm_kerneldir +WANT_MODULE=$want_module +CROSS_COMPILE=$cross_prefix +CC=$cross_prefix$cc +LD=$cross_prefix$ld +OBJCOPY=$cross_prefix$objcopy +AR=$cross_prefix$ar +EOF diff --git a/kvm/doxygen.conf b/kvm/doxygen.conf new file mode 100644 index 000000000..21a04c0a9 --- /dev/null +++ b/kvm/doxygen.conf @@ -0,0 +1,1252 @@ +# Doxyfile 1.5.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = KVM + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = Release 7 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = NO + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = user/ kernel/ + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = NO + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/kvm/extboot/Makefile b/kvm/extboot/Makefile new file mode 100644 index 000000000..ab2dae70d --- /dev/null +++ b/kvm/extboot/Makefile @@ -0,0 +1,41 @@ +OBJCOPY=objcopy + +# from kernel sources - scripts/Kbuild.include +# try-run +# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise) +# Exit code chooses option. "$$TMP" is can be used as temporary file and +# is automatically cleaned up. +try-run = $(shell set -e; \ + TMP="$(TMPOUT).$$$$.tmp"; \ + if ($(1)) >/dev/null 2>&1; \ + then echo "$(2)"; \ + else echo "$(3)"; \ + fi; \ + rm -f "$$TMP") + +# cc-option-yn +# Usage: flag := $(call cc-option-yn,-march=winchip-c6) +cc-option-yn = $(call try-run,\ + $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",y,n) + +CFLAGS = -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin +ifeq ($(call cc-option-yn,-fno-stack-protector),y) +CFLAGS += -fno-stack-protector +endif + +all: extboot.bin + +%.o: %.S + $(CC) $(CFLAGS) -o $@ -c $< + +extboot.img: extboot.o + $(LD) --oformat binary -Ttext 0 -o $@ $< + +extboot.bin: extboot.img signrom + ./signrom extboot.img extboot.bin + +signrom: signrom.c + $(CC) -o $@ -g -Wall $^ + +clean: + $(RM) *.o *.img *.bin signrom *~ diff --git a/kvm/extboot/STATUS b/kvm/extboot/STATUS new file mode 100644 index 000000000..687c6d64c --- /dev/null +++ b/kvm/extboot/STATUS @@ -0,0 +1,6 @@ +Working +------- + +Ubuntu Server 7.04 (i386) +Windows 2000 Professional (i386) +Windows XP SP2 (i386) diff --git a/kvm/extboot/signrom.c b/kvm/extboot/signrom.c new file mode 100644 index 000000000..fe8d67745 --- /dev/null +++ b/kvm/extboot/signrom.c @@ -0,0 +1,79 @@ +/* + * Extended Boot Option ROM + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corporation, 2007 + * Authors: Anthony Liguori <aliguori@us.ibm.com> + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +int main(int argc, char **argv) +{ + FILE *fin, *fout; + char buffer[512], oldbuffer[512]; + int i, size, lag = 0; + uint8_t sum = 0; + + if (argc != 3) { + printf("Usage: %s ROM OUTPUT\n", argv[0]); + return 1; + } + + fin = fopen(argv[1], "rb"); + fout = fopen(argv[2], "wb"); + + if (fin == NULL || fout == NULL) { + fprintf(stderr, "Could not open input/output files\n"); + return 1; + } + + do { + size = fread(buffer, 512, 1, fin); + if (size == 1) { + for (i = 0; i < 512; i++) + sum += buffer[i]; + + if (lag) { + if (fwrite(oldbuffer, 512, 1, fout) != 1) { + fprintf(stderr, "Write failed\n"); + return 1; + } + } + lag = 1; + memcpy(oldbuffer, buffer, 512); + } + } while (size == 1); + + if (size != 0) { + fprintf(stderr, "Failed to read from input file\n"); + return 1; + } + + oldbuffer[511] = -sum; + + if (fwrite(oldbuffer, 512, 1, fout) != 1) { + fprintf(stderr, "Failed to write to output file\n"); + return 1; + } + + fclose(fin); + fclose(fout); + + return 0; +} diff --git a/kvm/include/ia64/asm/kvm.h b/kvm/include/ia64/asm/kvm.h new file mode 100644 index 000000000..bc90c75ad --- /dev/null +++ b/kvm/include/ia64/asm/kvm.h @@ -0,0 +1,264 @@ +#ifndef __ASM_IA64_KVM_H +#define __ASM_IA64_KVM_H + +/* + * kvm structure definitions for ia64 + * + * Copyright (C) 2007 Xiantao Zhang <xiantao.zhang@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#include <linux/types.h> +#include <linux/ioctl.h> + +/* Select x86 specific features in <linux/kvm.h> */ +#define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_DEVICE_ASSIGNMENT + +/* Architectural interrupt line count. */ +#define KVM_NR_INTERRUPTS 256 + +#define KVM_IOAPIC_NUM_PINS 48 + +struct kvm_ioapic_state { + __u64 base_address; + __u32 ioregsel; + __u32 id; + __u32 irr; + __u32 pad; + union { + __u64 bits; + struct { + __u8 vector; + __u8 delivery_mode:3; + __u8 dest_mode:1; + __u8 delivery_status:1; + __u8 polarity:1; + __u8 remote_irr:1; + __u8 trig_mode:1; + __u8 mask:1; + __u8 reserve:7; + __u8 reserved[4]; + __u8 dest_id; + } fields; + } redirtbl[KVM_IOAPIC_NUM_PINS]; +}; + +#define KVM_IRQCHIP_PIC_MASTER 0 +#define KVM_IRQCHIP_PIC_SLAVE 1 +#define KVM_IRQCHIP_IOAPIC 2 +#define KVM_NR_IRQCHIPS 3 + +#define KVM_CONTEXT_SIZE 8*1024 + +struct kvm_fpreg { + union { + unsigned long bits[2]; + long double __dummy; /* force 16-byte alignment */ + } u; +}; + +union context { + /* 8K size */ + char dummy[KVM_CONTEXT_SIZE]; + struct { + unsigned long psr; + unsigned long pr; + unsigned long caller_unat; + unsigned long pad; + unsigned long gr[32]; + unsigned long ar[128]; + unsigned long br[8]; + unsigned long cr[128]; + unsigned long rr[8]; + unsigned long ibr[8]; + unsigned long dbr[8]; + unsigned long pkr[8]; + struct kvm_fpreg fr[128]; + }; +}; + +struct thash_data { + union { + struct { + unsigned long p : 1; /* 0 */ + unsigned long rv1 : 1; /* 1 */ + unsigned long ma : 3; /* 2-4 */ + unsigned long a : 1; /* 5 */ + unsigned long d : 1; /* 6 */ + unsigned long pl : 2; /* 7-8 */ + unsigned long ar : 3; /* 9-11 */ + unsigned long ppn : 38; /* 12-49 */ + unsigned long rv2 : 2; /* 50-51 */ + unsigned long ed : 1; /* 52 */ + unsigned long ig1 : 11; /* 53-63 */ + }; + struct { + unsigned long __rv1 : 53; /* 0-52 */ + unsigned long contiguous : 1; /*53 */ + unsigned long tc : 1; /* 54 TR or TC */ + unsigned long cl : 1; + /* 55 I side or D side cache line */ + unsigned long len : 4; /* 56-59 */ + unsigned long io : 1; /* 60 entry is for io or not */ + unsigned long nomap : 1; + /* 61 entry cann't be inserted into machine TLB.*/ + unsigned long checked : 1; + /* 62 for VTLB/VHPT sanity check */ + unsigned long invalid : 1; + /* 63 invalid entry */ + }; + unsigned long page_flags; + }; /* same for VHPT and TLB */ + + union { + struct { + unsigned long rv3 : 2; + unsigned long ps : 6; + unsigned long key : 24; + unsigned long rv4 : 32; + }; + unsigned long itir; + }; + union { + struct { + unsigned long ig2 : 12; + unsigned long vpn : 49; + unsigned long vrn : 3; + }; + unsigned long ifa; + unsigned long vadr; + struct { + unsigned long tag : 63; + unsigned long ti : 1; + }; + unsigned long etag; + }; + union { + struct thash_data *next; + unsigned long rid; + unsigned long gpaddr; + }; +}; + +#define NITRS 8 +#define NDTRS 8 + +struct saved_vpd { + unsigned long vhpi; + unsigned long vgr[16]; + unsigned long vbgr[16]; + unsigned long vnat; + unsigned long vbnat; + unsigned long vcpuid[5]; + unsigned long vpsr; + unsigned long vpr; + union { + unsigned long vcr[128]; + struct { + unsigned long dcr; + unsigned long itm; + unsigned long iva; + unsigned long rsv1[5]; + unsigned long pta; + unsigned long rsv2[7]; + unsigned long ipsr; + unsigned long isr; + unsigned long rsv3; + unsigned long iip; + unsigned long ifa; + unsigned long itir; + unsigned long iipa; + unsigned long ifs; + unsigned long iim; + unsigned long iha; + unsigned long rsv4[38]; + unsigned long lid; + unsigned long ivr; + unsigned long tpr; + unsigned long eoi; + unsigned long irr[4]; + unsigned long itv; + unsigned long pmv; + unsigned long cmcv; + unsigned long rsv5[5]; + unsigned long lrr0; + unsigned long lrr1; + unsigned long rsv6[46]; + }; + }; +}; + +struct kvm_regs { + struct saved_vpd vpd; + /*Arch-regs*/ + int mp_state; + unsigned long vmm_rr; + /* TR and TC. */ + struct thash_data itrs[NITRS]; + struct thash_data dtrs[NDTRS]; + /* Bit is set if there is a tr/tc for the region. */ + unsigned char itr_regions; + unsigned char dtr_regions; + unsigned char tc_regions; + + char irq_check; + unsigned long saved_itc; + unsigned long itc_check; + unsigned long timer_check; + unsigned long timer_pending; + unsigned long last_itc; + + unsigned long vrr[8]; + unsigned long ibr[8]; + unsigned long dbr[8]; + unsigned long insvc[4]; /* Interrupt in service. */ + unsigned long xtp; + + unsigned long metaphysical_rr0; /* from kvm_arch (so is pinned) */ + unsigned long metaphysical_rr4; /* from kvm_arch (so is pinned) */ + unsigned long metaphysical_saved_rr0; /* from kvm_arch */ + unsigned long metaphysical_saved_rr4; /* from kvm_arch */ + unsigned long fp_psr; /*used for lazy float register */ + unsigned long saved_gp; + /*for phycial emulation */ + + union context saved_guest; + + unsigned long reserved[64]; /* for future use */ +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { +}; + +#define KVM_IA64_VCPU_STACK_SHIFT 16 +#define KVM_IA64_VCPU_STACK_SIZE (1UL << KVM_IA64_VCPU_STACK_SHIFT) + +struct kvm_ia64_vcpu_stack { + unsigned char stack[KVM_IA64_VCPU_STACK_SIZE]; +}; + +struct kvm_debug_exit_arch { +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { +}; + +#endif diff --git a/kvm/include/ia64/asm/kvm_para.h b/kvm/include/ia64/asm/kvm_para.h new file mode 100644 index 000000000..1588aee78 --- /dev/null +++ b/kvm/include/ia64/asm/kvm_para.h @@ -0,0 +1,31 @@ +#ifndef __IA64_KVM_PARA_H +#define __IA64_KVM_PARA_H + +/* + * Copyright (C) 2007 Xiantao Zhang <xiantao.zhang@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#ifdef __KERNEL__ + +static inline unsigned int kvm_arch_para_features(void) +{ + return 0; +} + +#endif + +#endif diff --git a/kvm/include/linux/compiler.h b/kvm/include/linux/compiler.h new file mode 100644 index 000000000..f70c49f76 --- /dev/null +++ b/kvm/include/linux/compiler.h @@ -0,0 +1,2 @@ +/* dummy file */ + diff --git a/kvm/include/linux/config.h b/kvm/include/linux/config.h new file mode 100644 index 000000000..a421f31cf --- /dev/null +++ b/kvm/include/linux/config.h @@ -0,0 +1,43 @@ +#ifndef KVM_UNIFDEF_H +#define KVM_UNIFDEF_H + +#ifdef __i386__ +#ifndef CONFIG_X86_32 +#define CONFIG_X86_32 1 +#endif +#endif + +#ifdef __x86_64__ +#ifndef CONFIG_X86_64 +#define CONFIG_X86_64 1 +#endif +#endif + +#if defined(__i386__) || defined (__x86_64__) +#ifndef CONFIG_X86 +#define CONFIG_X86 1 +#endif +#endif + +#ifdef __ia64__ +#ifndef CONFIG_IA64 +#define CONFIG_IA64 1 +#endif +#endif + +#ifdef __PPC__ +#ifndef CONFIG_PPC +#define CONFIG_PPC 1 +#endif +#endif + +#ifdef __s390__ +#ifndef CONFIG_S390 +#define CONFIG_S390 1 +#endif +#endif + +#endif + + +#define __user diff --git a/kvm/include/linux/kvm.h b/kvm/include/linux/kvm.h new file mode 100644 index 000000000..3fd3371a1 --- /dev/null +++ b/kvm/include/linux/kvm.h @@ -0,0 +1,740 @@ +#ifndef __LINUX_KVM_H +#define __LINUX_KVM_H + +/* + * Userspace interface for /dev/kvm - kernel based virtual machine + * + * Note: you must update KVM_API_VERSION if you change this interface. + */ + +#include <linux/types.h> +#include <linux/compiler.h> +#include <linux/ioctl.h> +#include <asm/kvm.h> + +#define KVM_API_VERSION 12 + +/* *** Deprecated interfaces *** */ + +#define KVM_TRC_SHIFT 16 + +#define KVM_TRC_ENTRYEXIT (1 << KVM_TRC_SHIFT) +#define KVM_TRC_HANDLER (1 << (KVM_TRC_SHIFT + 1)) + +#define KVM_TRC_VMENTRY (KVM_TRC_ENTRYEXIT + 0x01) +#define KVM_TRC_VMEXIT (KVM_TRC_ENTRYEXIT + 0x02) +#define KVM_TRC_PAGE_FAULT (KVM_TRC_HANDLER + 0x01) + +#define KVM_TRC_HEAD_SIZE 12 +#define KVM_TRC_CYCLE_SIZE 8 +#define KVM_TRC_EXTRA_MAX 7 + +#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02) +#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03) +#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04) +#define KVM_TRC_IO_READ (KVM_TRC_HANDLER + 0x05) +#define KVM_TRC_IO_WRITE (KVM_TRC_HANDLER + 0x06) +#define KVM_TRC_CR_READ (KVM_TRC_HANDLER + 0x07) +#define KVM_TRC_CR_WRITE (KVM_TRC_HANDLER + 0x08) +#define KVM_TRC_DR_READ (KVM_TRC_HANDLER + 0x09) +#define KVM_TRC_DR_WRITE (KVM_TRC_HANDLER + 0x0A) +#define KVM_TRC_MSR_READ (KVM_TRC_HANDLER + 0x0B) +#define KVM_TRC_MSR_WRITE (KVM_TRC_HANDLER + 0x0C) +#define KVM_TRC_CPUID (KVM_TRC_HANDLER + 0x0D) +#define KVM_TRC_INTR (KVM_TRC_HANDLER + 0x0E) +#define KVM_TRC_NMI (KVM_TRC_HANDLER + 0x0F) +#define KVM_TRC_VMMCALL (KVM_TRC_HANDLER + 0x10) +#define KVM_TRC_HLT (KVM_TRC_HANDLER + 0x11) +#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12) +#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13) +#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14) +#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15) +#define KVM_TRC_GTLB_WRITE (KVM_TRC_HANDLER + 0x16) +#define KVM_TRC_STLB_WRITE (KVM_TRC_HANDLER + 0x17) +#define KVM_TRC_STLB_INVAL (KVM_TRC_HANDLER + 0x18) +#define KVM_TRC_PPC_INSTR (KVM_TRC_HANDLER + 0x19) + +struct kvm_user_trace_setup { + __u32 buf_size; + __u32 buf_nr; +}; + +#define __KVM_DEPRECATED_MAIN_W_0x06 \ + _IOW(KVMIO, 0x06, struct kvm_user_trace_setup) +#define __KVM_DEPRECATED_MAIN_0x07 _IO(KVMIO, 0x07) +#define __KVM_DEPRECATED_MAIN_0x08 _IO(KVMIO, 0x08) + +#define __KVM_DEPRECATED_VM_R_0x70 _IOR(KVMIO, 0x70, struct kvm_assigned_irq) + +struct kvm_breakpoint { + __u32 enabled; + __u32 padding; + __u64 address; +}; + +struct kvm_debug_guest { + __u32 enabled; + __u32 pad; + struct kvm_breakpoint breakpoints[4]; + __u32 singlestep; +}; + +#define __KVM_DEPRECATED_VCPU_W_0x87 _IOW(KVMIO, 0x87, struct kvm_debug_guest) + +/* *** End of deprecated interfaces *** */ + + +/* for KVM_CREATE_MEMORY_REGION */ +struct kvm_memory_region { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; /* bytes */ +}; + +/* for KVM_SET_USER_MEMORY_REGION */ +struct kvm_userspace_memory_region { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; /* bytes */ + __u64 userspace_addr; /* start of the userspace allocated memory */ +}; + +/* for kvm_memory_region::flags */ +#define KVM_MEM_LOG_DIRTY_PAGES 1UL + + +/* for KVM_IRQ_LINE */ +struct kvm_irq_level { + /* + * ACPI gsi notion of irq. + * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47.. + * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23.. + */ + union { + __u32 irq; + __s32 status; + }; + __u32 level; +}; + + +struct kvm_irqchip { + __u32 chip_id; + __u32 pad; + union { + char dummy[512]; /* reserving space */ +#ifdef __KVM_HAVE_PIT + struct kvm_pic_state pic; +#endif +#ifdef __KVM_HAVE_IOAPIC + struct kvm_ioapic_state ioapic; +#endif + } chip; +}; + +/* for KVM_CREATE_PIT2 */ +struct kvm_pit_config { + __u32 flags; + __u32 pad[15]; +}; + +#define KVM_PIT_SPEAKER_DUMMY 1 + +#define KVM_EXIT_UNKNOWN 0 +#define KVM_EXIT_EXCEPTION 1 +#define KVM_EXIT_IO 2 +#define KVM_EXIT_HYPERCALL 3 +#define KVM_EXIT_DEBUG 4 +#define KVM_EXIT_HLT 5 +#define KVM_EXIT_MMIO 6 +#define KVM_EXIT_IRQ_WINDOW_OPEN 7 +#define KVM_EXIT_SHUTDOWN 8 +#define KVM_EXIT_FAIL_ENTRY 9 +#define KVM_EXIT_INTR 10 +#define KVM_EXIT_SET_TPR 11 +#define KVM_EXIT_TPR_ACCESS 12 +#define KVM_EXIT_S390_SIEIC 13 +#define KVM_EXIT_S390_RESET 14 +#define KVM_EXIT_DCR 15 +#define KVM_EXIT_NMI 16 +#define KVM_EXIT_INTERNAL_ERROR 17 + +/* For KVM_EXIT_INTERNAL_ERROR */ +#define KVM_INTERNAL_ERROR_EMULATION 1 +#define KVM_INTERNAL_ERROR_SIMUL_EX 2 + +/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ +struct kvm_run { + /* in */ + __u8 request_interrupt_window; + __u8 padding1[7]; + + /* out */ + __u32 exit_reason; + __u8 ready_for_interrupt_injection; + __u8 if_flag; + __u8 padding2[2]; + + /* in (pre_kvm_run), out (post_kvm_run) */ + __u64 cr8; + __u64 apic_base; + +#ifdef __KVM_S390 + /* the processor status word for s390 */ + __u64 psw_mask; /* psw upper half */ + __u64 psw_addr; /* psw lower half */ +#endif + union { + /* KVM_EXIT_UNKNOWN */ + struct { + __u64 hardware_exit_reason; + } hw; + /* KVM_EXIT_FAIL_ENTRY */ + struct { + __u64 hardware_entry_failure_reason; + } fail_entry; + /* KVM_EXIT_EXCEPTION */ + struct { + __u32 exception; + __u32 error_code; + } ex; + /* KVM_EXIT_IO */ + struct { +#define KVM_EXIT_IO_IN 0 +#define KVM_EXIT_IO_OUT 1 + __u8 direction; + __u8 size; /* bytes */ + __u16 port; + __u32 count; + __u64 data_offset; /* relative to kvm_run start */ + } io; + struct { + struct kvm_debug_exit_arch arch; + } debug; + /* KVM_EXIT_MMIO */ + struct { + __u64 phys_addr; + __u8 data[8]; + __u32 len; + __u8 is_write; + } mmio; + /* KVM_EXIT_HYPERCALL */ + struct { + __u64 nr; + __u64 args[6]; + __u64 ret; + __u32 longmode; + __u32 pad; + } hypercall; + /* KVM_EXIT_TPR_ACCESS */ + struct { + __u64 rip; + __u32 is_write; + __u32 pad; + } tpr_access; + /* KVM_EXIT_S390_SIEIC */ + struct { + __u8 icptcode; + __u16 ipa; + __u32 ipb; + } s390_sieic; + /* KVM_EXIT_S390_RESET */ +#define KVM_S390_RESET_POR 1 +#define KVM_S390_RESET_CLEAR 2 +#define KVM_S390_RESET_SUBSYSTEM 4 +#define KVM_S390_RESET_CPU_INIT 8 +#define KVM_S390_RESET_IPL 16 + __u64 s390_reset_flags; + /* KVM_EXIT_DCR */ + struct { + __u32 dcrn; + __u32 data; + __u8 is_write; + } dcr; + struct { + __u32 suberror; + /* Available with KVM_CAP_INTERNAL_ERROR_DATA: */ + __u32 ndata; + __u64 data[16]; + } internal; + /* Fix the size of the union. */ + char padding[256]; + }; +}; + +/* for KVM_REGISTER_COALESCED_MMIO / KVM_UNREGISTER_COALESCED_MMIO */ + +struct kvm_coalesced_mmio_zone { + __u64 addr; + __u32 size; + __u32 pad; +}; + +struct kvm_coalesced_mmio { + __u64 phys_addr; + __u32 len; + __u32 pad; + __u8 data[8]; +}; + +struct kvm_coalesced_mmio_ring { + __u32 first, last; + struct kvm_coalesced_mmio coalesced_mmio[0]; +}; + +#define KVM_COALESCED_MMIO_MAX \ + ((PAGE_SIZE - sizeof(struct kvm_coalesced_mmio_ring)) / \ + sizeof(struct kvm_coalesced_mmio)) + +/* for KVM_TRANSLATE */ +struct kvm_translation { + /* in */ + __u64 linear_address; + + /* out */ + __u64 physical_address; + __u8 valid; + __u8 writeable; + __u8 usermode; + __u8 pad[5]; +}; + +/* for KVM_INTERRUPT */ +struct kvm_interrupt { + /* in */ + __u32 irq; +}; + +/* for KVM_GET_DIRTY_LOG */ +struct kvm_dirty_log { + __u32 slot; + __u32 padding1; + union { + void *dirty_bitmap; /* one bit per page */ + __u64 padding2; + }; +}; + +/* for KVM_SET_SIGNAL_MASK */ +struct kvm_signal_mask { + __u32 len; + __u8 sigset[0]; +}; + +/* for KVM_TPR_ACCESS_REPORTING */ +struct kvm_tpr_access_ctl { + __u32 enabled; + __u32 flags; + __u32 reserved[8]; +}; + +/* for KVM_SET_VAPIC_ADDR */ +struct kvm_vapic_addr { + __u64 vapic_addr; +}; + +/* for KVM_SET_MPSTATE */ + +#define KVM_MP_STATE_RUNNABLE 0 +#define KVM_MP_STATE_UNINITIALIZED 1 +#define KVM_MP_STATE_INIT_RECEIVED 2 +#define KVM_MP_STATE_HALTED 3 +#define KVM_MP_STATE_SIPI_RECEIVED 4 + +struct kvm_mp_state { + __u32 mp_state; +}; + +struct kvm_s390_psw { + __u64 mask; + __u64 addr; +}; + +/* valid values for type in kvm_s390_interrupt */ +#define KVM_S390_SIGP_STOP 0xfffe0000u +#define KVM_S390_PROGRAM_INT 0xfffe0001u +#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u +#define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_INT_VIRTIO 0xffff2603u +#define KVM_S390_INT_SERVICE 0xffff2401u +#define KVM_S390_INT_EMERGENCY 0xffff1201u + +struct kvm_s390_interrupt { + __u32 type; + __u32 parm; + __u64 parm64; +}; + +/* for KVM_SET_GUEST_DEBUG */ + +#define KVM_GUESTDBG_ENABLE 0x00000001 +#define KVM_GUESTDBG_SINGLESTEP 0x00000002 + +struct kvm_guest_debug { + __u32 control; + __u32 pad; + struct kvm_guest_debug_arch arch; +}; + +enum { + kvm_ioeventfd_flag_nr_datamatch, + kvm_ioeventfd_flag_nr_pio, + kvm_ioeventfd_flag_nr_deassign, + kvm_ioeventfd_flag_nr_max, +}; + +#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 << kvm_ioeventfd_flag_nr_datamatch) +#define KVM_IOEVENTFD_FLAG_PIO (1 << kvm_ioeventfd_flag_nr_pio) +#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign) + +#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1) + +struct kvm_ioeventfd { + __u64 datamatch; + __u64 addr; /* legal pio/mmio address */ + __u32 len; /* 1, 2, 4, or 8 bytes */ + __s32 fd; + __u32 flags; + __u8 pad[36]; +}; + +#define KVMIO 0xAE + +/* + * ioctls for /dev/kvm fds: + */ +#define KVM_GET_API_VERSION _IO(KVMIO, 0x00) +#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */ +#define KVM_GET_MSR_INDEX_LIST _IOWR(KVMIO, 0x02, struct kvm_msr_list) + +#define KVM_S390_ENABLE_SIE _IO(KVMIO, 0x06) +/* + * Check if a kvm extension is available. Argument is extension number, + * return is 1 (yes) or 0 (no, sorry). + */ +#define KVM_CHECK_EXTENSION _IO(KVMIO, 0x03) +/* + * Get size for mmap(vcpu_fd) + */ +#define KVM_GET_VCPU_MMAP_SIZE _IO(KVMIO, 0x04) /* in bytes */ +#define KVM_GET_SUPPORTED_CPUID _IOWR(KVMIO, 0x05, struct kvm_cpuid2) +#define KVM_TRACE_ENABLE __KVM_DEPRECATED_MAIN_W_0x06 +#define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 +#define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 + +/* + * Extension capability list. + */ +#define KVM_CAP_IRQCHIP 0 +#define KVM_CAP_HLT 1 +#define KVM_CAP_MMU_SHADOW_CACHE_CONTROL 2 +#define KVM_CAP_USER_MEMORY 3 +#define KVM_CAP_SET_TSS_ADDR 4 +#define KVM_CAP_VAPIC 6 +#define KVM_CAP_EXT_CPUID 7 +#define KVM_CAP_CLOCKSOURCE 8 +#define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */ +#define KVM_CAP_NR_MEMSLOTS 10 /* returns max memory slots per vm */ +#define KVM_CAP_PIT 11 +#define KVM_CAP_NOP_IO_DELAY 12 +#define KVM_CAP_PV_MMU 13 +#define KVM_CAP_MP_STATE 14 +#define KVM_CAP_COALESCED_MMIO 15 +#define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */ +#ifdef __KVM_HAVE_DEVICE_ASSIGNMENT +#define KVM_CAP_DEVICE_ASSIGNMENT 17 +#endif +#define KVM_CAP_IOMMU 18 +#ifdef __KVM_HAVE_MSI +#define KVM_CAP_DEVICE_MSI 20 +#endif +/* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ +#define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 +#ifdef __KVM_HAVE_USER_NMI +#define KVM_CAP_USER_NMI 22 +#endif +#ifdef __KVM_HAVE_GUEST_DEBUG +#define KVM_CAP_SET_GUEST_DEBUG 23 +#endif +#ifdef __KVM_HAVE_PIT +#define KVM_CAP_REINJECT_CONTROL 24 +#endif +#ifdef __KVM_HAVE_IOAPIC +#define KVM_CAP_IRQ_ROUTING 25 +#endif +#define KVM_CAP_IRQ_INJECT_STATUS 26 +#ifdef __KVM_HAVE_DEVICE_ASSIGNMENT +#define KVM_CAP_DEVICE_DEASSIGNMENT 27 +#endif +#ifdef __KVM_HAVE_MSIX +#define KVM_CAP_DEVICE_MSIX 28 +#endif +#define KVM_CAP_ASSIGN_DEV_IRQ 29 +/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ +#define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 +#ifdef __KVM_HAVE_MCE +#define KVM_CAP_MCE 31 +#endif +#define KVM_CAP_IRQFD 32 +#ifdef __KVM_HAVE_PIT +#define KVM_CAP_PIT2 33 +#endif +#define KVM_CAP_SET_BOOT_CPU_ID 34 +#ifdef __KVM_HAVE_PIT_STATE2 +#define KVM_CAP_PIT_STATE2 35 +#endif +#define KVM_CAP_IOEVENTFD 36 +#define KVM_CAP_SET_IDENTITY_MAP_ADDR 37 +#ifdef __KVM_HAVE_XEN_HVM +#define KVM_CAP_XEN_HVM 38 +#endif +#define KVM_CAP_ADJUST_CLOCK 39 +#define KVM_CAP_INTERNAL_ERROR_DATA 40 +#ifdef __KVM_HAVE_VCPU_EVENTS +#define KVM_CAP_VCPU_EVENTS 41 +#endif +#define KVM_CAP_S390_PSW 42 +#define KVM_CAP_PPC_SEGSTATE 43 + +#ifdef KVM_CAP_IRQ_ROUTING + +struct kvm_irq_routing_irqchip { + __u32 irqchip; + __u32 pin; +}; + +struct kvm_irq_routing_msi { + __u32 address_lo; + __u32 address_hi; + __u32 data; + __u32 pad; +}; + +/* gsi routing entry types */ +#define KVM_IRQ_ROUTING_IRQCHIP 1 +#define KVM_IRQ_ROUTING_MSI 2 + +struct kvm_irq_routing_entry { + __u32 gsi; + __u32 type; + __u32 flags; + __u32 pad; + union { + struct kvm_irq_routing_irqchip irqchip; + struct kvm_irq_routing_msi msi; + __u32 pad[8]; + } u; +}; + +struct kvm_irq_routing { + __u32 nr; + __u32 flags; + struct kvm_irq_routing_entry entries[0]; +}; + +#endif + +#ifdef KVM_CAP_MCE +/* x86 MCE */ +struct kvm_x86_mce { + __u64 status; + __u64 addr; + __u64 misc; + __u64 mcg_status; + __u8 bank; + __u8 pad1[7]; + __u64 pad2[3]; +}; +#endif + +#ifdef KVM_CAP_XEN_HVM +struct kvm_xen_hvm_config { + __u32 flags; + __u32 msr; + __u64 blob_addr_32; + __u64 blob_addr_64; + __u8 blob_size_32; + __u8 blob_size_64; + __u8 pad2[30]; +}; +#endif + +#define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) + +struct kvm_irqfd { + __u32 fd; + __u32 gsi; + __u32 flags; + __u8 pad[20]; +}; + +struct kvm_clock_data { + __u64 clock; + __u32 flags; + __u32 pad[9]; +}; + +/* + * ioctls for VM fds + */ +#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region) +/* + * KVM_CREATE_VCPU receives as a parameter the vcpu slot, and returns + * a vcpu fd. + */ +#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) +#define KVM_GET_DIRTY_LOG _IOW(KVMIO, 0x42, struct kvm_dirty_log) +#define KVM_SET_MEMORY_ALIAS _IOW(KVMIO, 0x43, struct kvm_memory_alias) +#define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44) +#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45) +#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \ + struct kvm_userspace_memory_region) +#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47) +#define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64) +/* Device model IOC */ +#define KVM_CREATE_IRQCHIP _IO(KVMIO, 0x60) +#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level) +#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip) +#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64) +#define KVM_GET_PIT _IOWR(KVMIO, 0x65, struct kvm_pit_state) +#define KVM_SET_PIT _IOR(KVMIO, 0x66, struct kvm_pit_state) +#define KVM_IRQ_LINE_STATUS _IOWR(KVMIO, 0x67, struct kvm_irq_level) +#define KVM_REGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone) +#define KVM_UNREGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone) +#define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \ + struct kvm_assigned_pci_dev) +#define KVM_SET_GSI_ROUTING _IOW(KVMIO, 0x6a, struct kvm_irq_routing) +/* deprecated, replaced by KVM_ASSIGN_DEV_IRQ */ +#define KVM_ASSIGN_IRQ __KVM_DEPRECATED_VM_R_0x70 +#define KVM_ASSIGN_DEV_IRQ _IOW(KVMIO, 0x70, struct kvm_assigned_irq) +#define KVM_REINJECT_CONTROL _IO(KVMIO, 0x71) +#define KVM_DEASSIGN_PCI_DEVICE _IOW(KVMIO, 0x72, \ + struct kvm_assigned_pci_dev) +#define KVM_ASSIGN_SET_MSIX_NR _IOW(KVMIO, 0x73, \ + struct kvm_assigned_msix_nr) +#define KVM_ASSIGN_SET_MSIX_ENTRY _IOW(KVMIO, 0x74, \ + struct kvm_assigned_msix_entry) +#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq) +#define KVM_IRQFD _IOW(KVMIO, 0x76, struct kvm_irqfd) +#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x77, struct kvm_pit_config) +#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78) +#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd) +#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config) +#define KVM_SET_CLOCK _IOW(KVMIO, 0x7b, struct kvm_clock_data) +#define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +/* Available with KVM_CAP_PIT_STATE2 */ +#define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2) +#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2) + +/* + * ioctls for vcpu fds + */ +#define KVM_RUN _IO(KVMIO, 0x80) +#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs) +#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs) +#define KVM_GET_SREGS _IOR(KVMIO, 0x83, struct kvm_sregs) +#define KVM_SET_SREGS _IOW(KVMIO, 0x84, struct kvm_sregs) +#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) +#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) +/* KVM_DEBUG_GUEST is no longer supported, use KVM_SET_GUEST_DEBUG instead */ +#define KVM_DEBUG_GUEST __KVM_DEPRECATED_VCPU_W_0x87 +#define KVM_GET_MSRS _IOWR(KVMIO, 0x88, struct kvm_msrs) +#define KVM_SET_MSRS _IOW(KVMIO, 0x89, struct kvm_msrs) +#define KVM_SET_CPUID _IOW(KVMIO, 0x8a, struct kvm_cpuid) +#define KVM_SET_SIGNAL_MASK _IOW(KVMIO, 0x8b, struct kvm_signal_mask) +#define KVM_GET_FPU _IOR(KVMIO, 0x8c, struct kvm_fpu) +#define KVM_SET_FPU _IOW(KVMIO, 0x8d, struct kvm_fpu) +#define KVM_GET_LAPIC _IOR(KVMIO, 0x8e, struct kvm_lapic_state) +#define KVM_SET_LAPIC _IOW(KVMIO, 0x8f, struct kvm_lapic_state) +#define KVM_SET_CPUID2 _IOW(KVMIO, 0x90, struct kvm_cpuid2) +#define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2) +/* Available with KVM_CAP_VAPIC */ +#define KVM_TPR_ACCESS_REPORTING _IOWR(KVMIO, 0x92, struct kvm_tpr_access_ctl) +/* Available with KVM_CAP_VAPIC */ +#define KVM_SET_VAPIC_ADDR _IOW(KVMIO, 0x93, struct kvm_vapic_addr) +/* valid for virtual machine (for floating interrupt)_and_ vcpu */ +#define KVM_S390_INTERRUPT _IOW(KVMIO, 0x94, struct kvm_s390_interrupt) +/* store status for s390 */ +#define KVM_S390_STORE_STATUS_NOADDR (-1ul) +#define KVM_S390_STORE_STATUS_PREFIXED (-2ul) +#define KVM_S390_STORE_STATUS _IOW(KVMIO, 0x95, unsigned long) +/* initial ipl psw for s390 */ +#define KVM_S390_SET_INITIAL_PSW _IOW(KVMIO, 0x96, struct kvm_s390_psw) +/* initial reset for s390 */ +#define KVM_S390_INITIAL_RESET _IO(KVMIO, 0x97) +#define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state) +#define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state) +/* Available with KVM_CAP_NMI */ +#define KVM_NMI _IO(KVMIO, 0x9a) +/* Available with KVM_CAP_SET_GUEST_DEBUG */ +#define KVM_SET_GUEST_DEBUG _IOW(KVMIO, 0x9b, struct kvm_guest_debug) +/* MCE for x86 */ +#define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64) +#define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64) +#define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce) +/* IA64 stack access */ +#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) +#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) +/* Available with KVM_CAP_VCPU_EVENTS */ +#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) +#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) + +#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) + +struct kvm_assigned_pci_dev { + __u32 assigned_dev_id; + __u32 busnr; + __u32 devfn; + __u32 flags; + union { + __u32 reserved[12]; + }; +}; + +#define KVM_DEV_IRQ_HOST_INTX (1 << 0) +#define KVM_DEV_IRQ_HOST_MSI (1 << 1) +#define KVM_DEV_IRQ_HOST_MSIX (1 << 2) + +#define KVM_DEV_IRQ_GUEST_INTX (1 << 8) +#define KVM_DEV_IRQ_GUEST_MSI (1 << 9) +#define KVM_DEV_IRQ_GUEST_MSIX (1 << 10) + +#define KVM_DEV_IRQ_HOST_MASK 0x00ff +#define KVM_DEV_IRQ_GUEST_MASK 0xff00 + +struct kvm_assigned_irq { + __u32 assigned_dev_id; + __u32 host_irq; + __u32 guest_irq; + __u32 flags; + union { + struct { + __u32 addr_lo; + __u32 addr_hi; + __u32 data; + } guest_msi; + __u32 reserved[12]; + }; +}; + + +struct kvm_assigned_msix_nr { + __u32 assigned_dev_id; + __u16 entry_nr; + __u16 padding; +}; + +#define KVM_MAX_MSIX_PER_DEV 256 +struct kvm_assigned_msix_entry { + __u32 assigned_dev_id; + __u32 gsi; + __u16 entry; /* The index of entry in the MSI-X table */ + __u16 padding[3]; +}; + +#endif /* __LINUX_KVM_H */ diff --git a/kvm/include/linux/kvm_para.h b/kvm/include/linux/kvm_para.h new file mode 100644 index 000000000..d73109243 --- /dev/null +++ b/kvm/include/linux/kvm_para.h @@ -0,0 +1,41 @@ +#ifndef __LINUX_KVM_PARA_H +#define __LINUX_KVM_PARA_H + +/* + * This header file provides a method for making a hypercall to the host + * Architectures should define: + * - kvm_hypercall0, kvm_hypercall1... + * - kvm_arch_para_features + * - kvm_para_available + */ + +/* Return values for hypercalls */ +#define KVM_ENOSYS 1000 +#define KVM_EFAULT EFAULT +#define KVM_E2BIG E2BIG +#define KVM_EPERM EPERM + +#define KVM_HC_VAPIC_POLL_IRQ 1 +#define KVM_HC_MMU_OP 2 + +/* + * hypercalls use architecture specific + */ +#include <asm/kvm_para.h> + +#ifdef __KERNEL__ +#ifdef CONFIG_KVM_GUEST +void __init kvm_guest_init(void); +#else +#define kvm_guest_init() do { } while (0) +#endif + +static inline int kvm_para_has_feature(unsigned int feature) +{ + if (kvm_arch_para_features() & (1UL << feature)) + return 1; + return 0; +} +#endif /* __KERNEL__ */ +#endif /* __LINUX_KVM_PARA_H */ + diff --git a/kvm/include/powerpc/asm/kvm.h b/kvm/include/powerpc/asm/kvm.h new file mode 100644 index 000000000..bb2de6aa5 --- /dev/null +++ b/kvm/include/powerpc/asm/kvm.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2007 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#ifndef __LINUX_KVM_POWERPC_H +#define __LINUX_KVM_POWERPC_H + +#include <linux/types.h> + +struct kvm_regs { + __u64 pc; + __u64 cr; + __u64 ctr; + __u64 lr; + __u64 xer; + __u64 msr; + __u64 srr0; + __u64 srr1; + __u64 pid; + + __u64 sprg0; + __u64 sprg1; + __u64 sprg2; + __u64 sprg3; + __u64 sprg4; + __u64 sprg5; + __u64 sprg6; + __u64 sprg7; + + __u64 gpr[32]; +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { + __u64 fpr[32]; +}; + +struct kvm_debug_exit_arch { +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { +}; + +#endif /* __LINUX_KVM_POWERPC_H */ diff --git a/kvm/include/powerpc/asm/kvm_para.h b/kvm/include/powerpc/asm/kvm_para.h new file mode 100644 index 000000000..2d48f6a63 --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_para.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#ifndef __POWERPC_KVM_PARA_H__ +#define __POWERPC_KVM_PARA_H__ + +#ifdef __KERNEL__ + +static inline int kvm_para_available(void) +{ + return 0; +} + +static inline unsigned int kvm_arch_para_features(void) +{ + return 0; +} + +#endif /* __KERNEL__ */ + +#endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/kvm/include/x86/asm/kvm.h b/kvm/include/x86/asm/kvm.h new file mode 100644 index 000000000..f46b79f6c --- /dev/null +++ b/kvm/include/x86/asm/kvm.h @@ -0,0 +1,287 @@ +#ifndef _ASM_X86_KVM_H +#define _ASM_X86_KVM_H + +/* + * KVM x86 specific structures and definitions + * + */ + +#include <linux/types.h> +#include <linux/ioctl.h> + +/* Select x86 specific features in <linux/kvm.h> */ +#define __KVM_HAVE_PIT +#define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_DEVICE_ASSIGNMENT +#define __KVM_HAVE_MSI +#define __KVM_HAVE_USER_NMI +#define __KVM_HAVE_GUEST_DEBUG +#define __KVM_HAVE_MSIX +#define __KVM_HAVE_MCE +#define __KVM_HAVE_PIT_STATE2 +#define __KVM_HAVE_XEN_HVM +#define __KVM_HAVE_VCPU_EVENTS + +/* Architectural interrupt line count. */ +#define KVM_NR_INTERRUPTS 256 + +struct kvm_memory_alias { + __u32 slot; /* this has a different namespace than memory slots */ + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; + __u64 target_phys_addr; +}; + +/* for KVM_GET_IRQCHIP and KVM_SET_IRQCHIP */ +struct kvm_pic_state { + __u8 last_irr; /* edge detection */ + __u8 irr; /* interrupt request register */ + __u8 imr; /* interrupt mask register */ + __u8 isr; /* interrupt service register */ + __u8 priority_add; /* highest irq priority */ + __u8 irq_base; + __u8 read_reg_select; + __u8 poll; + __u8 special_mask; + __u8 init_state; + __u8 auto_eoi; + __u8 rotate_on_auto_eoi; + __u8 special_fully_nested_mode; + __u8 init4; /* true if 4 byte init */ + __u8 elcr; /* PIIX edge/trigger selection */ + __u8 elcr_mask; +}; + +#define KVM_IOAPIC_NUM_PINS 24 +struct kvm_ioapic_state { + __u64 base_address; + __u32 ioregsel; + __u32 id; + __u32 irr; + __u32 pad; + union { + __u64 bits; + struct { + __u8 vector; + __u8 delivery_mode:3; + __u8 dest_mode:1; + __u8 delivery_status:1; + __u8 polarity:1; + __u8 remote_irr:1; + __u8 trig_mode:1; + __u8 mask:1; + __u8 reserve:7; + __u8 reserved[4]; + __u8 dest_id; + } fields; + } redirtbl[KVM_IOAPIC_NUM_PINS]; +}; + +#define KVM_IRQCHIP_PIC_MASTER 0 +#define KVM_IRQCHIP_PIC_SLAVE 1 +#define KVM_IRQCHIP_IOAPIC 2 +#define KVM_NR_IRQCHIPS 3 + +/* for KVM_GET_REGS and KVM_SET_REGS */ +struct kvm_regs { + /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */ + __u64 rax, rbx, rcx, rdx; + __u64 rsi, rdi, rsp, rbp; + __u64 r8, r9, r10, r11; + __u64 r12, r13, r14, r15; + __u64 rip, rflags; +}; + +/* for KVM_GET_LAPIC and KVM_SET_LAPIC */ +#define KVM_APIC_REG_SIZE 0x400 +struct kvm_lapic_state { + char regs[KVM_APIC_REG_SIZE]; +}; + +struct kvm_segment { + __u64 base; + __u32 limit; + __u16 selector; + __u8 type; + __u8 present, dpl, db, s, l, g, avl; + __u8 unusable; + __u8 padding; +}; + +struct kvm_dtable { + __u64 base; + __u16 limit; + __u16 padding[3]; +}; + + +/* for KVM_GET_SREGS and KVM_SET_SREGS */ +struct kvm_sregs { + /* out (KVM_GET_SREGS) / in (KVM_SET_SREGS) */ + struct kvm_segment cs, ds, es, fs, gs, ss; + struct kvm_segment tr, ldt; + struct kvm_dtable gdt, idt; + __u64 cr0, cr2, cr3, cr4, cr8; + __u64 efer; + __u64 apic_base; + __u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64]; +}; + +/* for KVM_GET_FPU and KVM_SET_FPU */ +struct kvm_fpu { + __u8 fpr[8][16]; + __u16 fcw; + __u16 fsw; + __u8 ftwx; /* in fxsave format */ + __u8 pad1; + __u16 last_opcode; + __u64 last_ip; + __u64 last_dp; + __u8 xmm[16][16]; + __u32 mxcsr; + __u32 pad2; +}; + +struct kvm_msr_entry { + __u32 index; + __u32 reserved; + __u64 data; +}; + +/* for KVM_GET_MSRS and KVM_SET_MSRS */ +struct kvm_msrs { + __u32 nmsrs; /* number of msrs in entries */ + __u32 pad; + + struct kvm_msr_entry entries[0]; +}; + +/* for KVM_GET_MSR_INDEX_LIST */ +struct kvm_msr_list { + __u32 nmsrs; /* number of msrs in entries */ + __u32 indices[0]; +}; + + +struct kvm_cpuid_entry { + __u32 function; + __u32 eax; + __u32 ebx; + __u32 ecx; + __u32 edx; + __u32 padding; +}; + +/* for KVM_SET_CPUID */ +struct kvm_cpuid { + __u32 nent; + __u32 padding; + struct kvm_cpuid_entry entries[0]; +}; + +struct kvm_cpuid_entry2 { + __u32 function; + __u32 index; + __u32 flags; + __u32 eax; + __u32 ebx; + __u32 ecx; + __u32 edx; + __u32 padding[3]; +}; + +#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1 +#define KVM_CPUID_FLAG_STATEFUL_FUNC 2 +#define KVM_CPUID_FLAG_STATE_READ_NEXT 4 + +/* for KVM_SET_CPUID2 */ +struct kvm_cpuid2 { + __u32 nent; + __u32 padding; + struct kvm_cpuid_entry2 entries[0]; +}; + +/* for KVM_GET_PIT and KVM_SET_PIT */ +struct kvm_pit_channel_state { + __u32 count; /* can be 65536 */ + __u16 latched_count; + __u8 count_latched; + __u8 status_latched; + __u8 status; + __u8 read_state; + __u8 write_state; + __u8 write_latch; + __u8 rw_mode; + __u8 mode; + __u8 bcd; + __u8 gate; + __s64 count_load_time; +}; + +struct kvm_debug_exit_arch { + __u32 exception; + __u32 pad; + __u64 pc; + __u64 dr6; + __u64 dr7; +}; + +#define KVM_GUESTDBG_USE_SW_BP 0x00010000 +#define KVM_GUESTDBG_USE_HW_BP 0x00020000 +#define KVM_GUESTDBG_INJECT_DB 0x00040000 +#define KVM_GUESTDBG_INJECT_BP 0x00080000 + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { + __u64 debugreg[8]; +}; + +struct kvm_pit_state { + struct kvm_pit_channel_state channels[3]; +}; + +#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 + +struct kvm_pit_state2 { + struct kvm_pit_channel_state channels[3]; + __u32 flags; + __u32 reserved[9]; +}; + +struct kvm_reinject_control { + __u8 pit_reinject; + __u8 reserved[31]; +}; + +/* When set in flags, include corresponding fields on KVM_SET_VCPU_EVENTS */ +#define KVM_VCPUEVENT_VALID_NMI_PENDING 0x00000001 +#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002 + +/* for KVM_GET/SET_VCPU_EVENTS */ +struct kvm_vcpu_events { + struct { + __u8 injected; + __u8 nr; + __u8 has_error_code; + __u8 pad; + __u32 error_code; + } exception; + struct { + __u8 injected; + __u8 nr; + __u8 soft; + __u8 pad; + } interrupt; + struct { + __u8 injected; + __u8 pending; + __u8 masked; + __u8 pad; + } nmi; + __u32 sipi_vector; + __u32 flags; + __u32 reserved[10]; +}; + +#endif /* _ASM_X86_KVM_H */ diff --git a/kvm/include/x86/asm/kvm_para.h b/kvm/include/x86/asm/kvm_para.h new file mode 100644 index 000000000..c584076a4 --- /dev/null +++ b/kvm/include/x86/asm/kvm_para.h @@ -0,0 +1,149 @@ +#ifndef _ASM_X86_KVM_PARA_H +#define _ASM_X86_KVM_PARA_H + +#include <linux/types.h> + +/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It + * should be used to determine that a VM is running under KVM. + */ +#define KVM_CPUID_SIGNATURE 0x40000000 + +/* This CPUID returns a feature bitmap in eax. Before enabling a particular + * paravirtualization, the appropriate feature bit should be checked. + */ +#define KVM_CPUID_FEATURES 0x40000001 +#define KVM_FEATURE_CLOCKSOURCE 0 +#define KVM_FEATURE_NOP_IO_DELAY 1 +#define KVM_FEATURE_MMU_OP 2 + +#define MSR_KVM_WALL_CLOCK 0x11 +#define MSR_KVM_SYSTEM_TIME 0x12 + +#define KVM_MAX_MMU_OP_BATCH 32 + +/* Operations for KVM_HC_MMU_OP */ +#define KVM_MMU_OP_WRITE_PTE 1 +#define KVM_MMU_OP_FLUSH_TLB 2 +#define KVM_MMU_OP_RELEASE_PT 3 + +/* Payload for KVM_HC_MMU_OP */ +struct kvm_mmu_op_header { + __u32 op; + __u32 pad; +}; + +struct kvm_mmu_op_write_pte { + struct kvm_mmu_op_header header; + __u64 pte_phys; + __u64 pte_val; +}; + +struct kvm_mmu_op_flush_tlb { + struct kvm_mmu_op_header header; +}; + +struct kvm_mmu_op_release_pt { + struct kvm_mmu_op_header header; + __u64 pt_phys; +}; + +#ifdef __KERNEL__ +#include <asm/processor.h> + +extern void kvmclock_init(void); + + +/* This instruction is vmcall. On non-VT architectures, it will generate a + * trap that we will then rewrite to the appropriate instruction. + */ +#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1" + +/* For KVM hypercalls, a three-byte sequence of either the vmrun or the vmmrun + * instruction. The hypervisor may replace it with something else but only the + * instructions are guaranteed to be supported. + * + * Up to four arguments may be passed in rbx, rcx, rdx, and rsi respectively. + * The hypercall number should be placed in rax and the return value will be + * placed in rax. No other registers will be clobbered unless explicited + * noted by the particular hypercall. + */ + +static inline long kvm_hypercall0(unsigned int nr) +{ + long ret; + asm volatile(KVM_HYPERCALL + : "=a"(ret) + : "a"(nr) + : "memory"); + return ret; +} + +static inline long kvm_hypercall1(unsigned int nr, unsigned long p1) +{ + long ret; + asm volatile(KVM_HYPERCALL + : "=a"(ret) + : "a"(nr), "b"(p1) + : "memory"); + return ret; +} + +static inline long kvm_hypercall2(unsigned int nr, unsigned long p1, + unsigned long p2) +{ + long ret; + asm volatile(KVM_HYPERCALL + : "=a"(ret) + : "a"(nr), "b"(p1), "c"(p2) + : "memory"); + return ret; +} + +static inline long kvm_hypercall3(unsigned int nr, unsigned long p1, + unsigned long p2, unsigned long p3) +{ + long ret; + asm volatile(KVM_HYPERCALL + : "=a"(ret) + : "a"(nr), "b"(p1), "c"(p2), "d"(p3) + : "memory"); + return ret; +} + +static inline long kvm_hypercall4(unsigned int nr, unsigned long p1, + unsigned long p2, unsigned long p3, + unsigned long p4) +{ + long ret; + asm volatile(KVM_HYPERCALL + : "=a"(ret) + : "a"(nr), "b"(p1), "c"(p2), "d"(p3), "S"(p4) + : "memory"); + return ret; +} + +static inline int kvm_para_available(void) +{ + unsigned int eax, ebx, ecx, edx; + char signature[13]; + + cpuid(KVM_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx); + memcpy(signature + 0, &ebx, 4); + memcpy(signature + 4, &ecx, 4); + memcpy(signature + 8, &edx, 4); + signature[12] = 0; + + if (strcmp(signature, "KVMKVMKVM") == 0) + return 1; + + return 0; +} + +static inline unsigned int kvm_arch_para_features(void) +{ + return cpuid_eax(KVM_CPUID_FEATURES); +} + +#endif + +#endif /* _ASM_X86_KVM_PARA_H */ diff --git a/kvm/kvm.spec b/kvm/kvm.spec new file mode 100644 index 000000000..92acb0ed2 --- /dev/null +++ b/kvm/kvm.spec @@ -0,0 +1,139 @@ +Name: kvm +Version: 0.0 +Release: 0 +Summary: Kernel Virtual Machine virtualization environment + +Group: System Environment/Kernel +License: GPL +URL: http://www.qumranet.com +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release} + +ExclusiveArch: i386 x86_64 ia64 + +Requires: kvm-kmod bridge-utils + +%define Distribution %(rpm -q -qf /etc/redhat-release --qf '%%{name}' | cut -d"-" -f 1) +%define os_version %(rpm -q --qf '%%{version}' %{Distribution}-release) +%define os_release %(rpm -q --qf '%%{release}' %{Distribution}-release | cut -d"." -f 1) + +%if %([ x"%{Distribution}" = x"fedora" -a x"%{os_version}" = x"5" ] && echo 1 || echo 0) +%define require_gccver 32 +%endif + +%if %([ x"%{Distribution}" = x"fedora" -a 0"%{os_version}" -ge "8" ] && echo 1 || echo 0) +%define qemuldflags --qemu-ldflags=-Wl,--build-id +%else +%define qemuldflags "" +%endif + +%if %([ x"%{Distribution}" = x"centos" -a x"%{os_version}" = x"4" ] && echo 1 || echo 0) +%define require_gccver 32 +%endif + +%if %([ x"%{Distribution}" = x"redhat" -a x"%{os_release}" = x"5" ] && echo 1 || echo 0) +%define require_gccver 34 +%endif + +%if %( [ x"%{require_gccver}" = x"32" ] && echo 1 || echo 0) +BuildRequires: compat-gcc-32 +%else +BuildRequires: compat-gcc-34 +%endif + +BuildRequires: SDL-devel zlib-devel alsa-lib-devel + +%define _prebuilt %{?prebuilt:1}%{!?prebuilt:0} + +%if !%{_prebuilt} +Source0: kvm.tar.gz +Source1: user.tar.gz +Source2: kernel.tar.gz +Source3: scripts.tar.gz +Source4: Makefile +Source5: configure +Source6: kvm_stat +Source7: libkvm.tar.gz +Source8: extboot.tar.gz +%endif + +%description +The Kernel Virtual Machine provides a virtualization enviroment for processors +with hardware support for virtualization: Intel's VT-x&VT-i and AMD's AMD-V. + +%prep + +%if !%{_prebuilt} +%setup -T -b 0 -n qemu +%setup -T -b 1 -n user -D +%setup -T -b 2 -n kernel -D +%setup -T -b 7 -n libkvm -D +%setup -T -b 3 -n scripts -D +%setup -T -b 8 -n extboot -D +cd .. +cp %{_sourcedir}/Makefile %{_sourcedir}/configure %{_sourcedir}/kvm_stat . +%endif + +%build + +rm -rf %{buildroot} + +%if !%{_prebuilt} +cd .. +./configure --prefix=/usr/kvm %{qemuldflags} +make -C libkvm +make -C user +%ifarch i386 x86_64 +make extboot +%endif +#(cd qemu; +# ./co +# kpath="$(readlink -f ../kernel/include)" +# upath="$(readlink -f ../user)" +# ./configure --target-list=$(uname -i)-softmmu \ +# --extra-cflags="-I$kpath -I$upath" \ +# --extra-ldflags="-L$upath" \ +# --disable-kqemu --enable-kvm --prefix=/usr/kvm +#) +make -C qemu +%endif + +%install + +%if !%{_prebuilt} +cd .. +%else +cd %{objdir} +%endif + +make DESTDIR=%{buildroot} install-rpm + +%define bindir /usr/bin +%define bin %{bindir}/kvm +%define initdir /etc/init.d +%define confdir /etc/kvm +%define utilsdir /etc/kvm/utils + +%post +/sbin/chkconfig --add kvm +/sbin/chkconfig --level 2345 kvm on +/sbin/chkconfig --level 16 kvm off +/usr/sbin/groupadd -fg 444 kvm + +%preun +if [ "$1" != 0 ]; then + /sbin/service kvm stop + /sbin/chkconfig --level 2345 kvm off + /sbin/chkconfig --del kvm +fi + +%clean +%{__rm} -rf %{buildroot} + +%files +/usr/bin/kvm +/usr/bin/kvm_stat +%{confdir}/qemu-ifup +%{initdir}/kvm +/etc/udev/rules.d/*kvm*.rules +/usr/kvm +%changelog diff --git a/kvm/kvm_stat b/kvm/kvm_stat new file mode 100755 index 000000000..21aff5b68 --- /dev/null +++ b/kvm/kvm_stat @@ -0,0 +1,129 @@ +#!/usr/bin/python + +import curses +import sys, os, time, optparse + +class Stats: + def __init__(self, fields = None): + def wanted(key): + import re + if not fields: + return True + return re.match(fields, key) != None + self.base = '/sys/kernel/debug/kvm' + self.values = {} + for key in os.listdir(self.base): + if wanted(key): + self.values[key] = None + def get(self): + for key, oldval in self.values.iteritems(): + newval = int(file(self.base + '/' + key).read()) + newdelta = None + if oldval is not None: + newdelta = newval - oldval[0] + self.values[key] = (newval, newdelta) + return self.values + +if not os.access('/sys/kernel/debug', os.F_OK): + print 'Please enable CONFIG_DEBUG_FS in your kernel' + sys.exit(1) +if not os.access('/sys/kernel/debug/kvm', os.F_OK): + print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')" + print "and ensure the kvm modules are loaded" + sys.exit(1) + +label_width = 20 +number_width = 10 + +def tui(screen, stats): + curses.use_default_colors() + curses.noecho() + def refresh(): + screen.erase() + screen.addstr(0, 0, 'kvm statistics') + row = 2 + s = stats.get() + for key in sorted(s.keys()): + if row >= screen.getmaxyx()[0]: + break + values = s[key] + col = 1 + screen.addstr(row, col, key) + col += label_width + screen.addstr(row, col, '%10d' % (values[0],)) + col += number_width + if values[1] is not None: + screen.addstr(row, col, '%8d' % (values[1],)) + row += 1 + screen.refresh() + + while True: + refresh() + curses.halfdelay(10) + try: + c = screen.getkey() + if c == 'q': + break + except KeyboardInterrupt: + break + except curses.error: + continue + +def batch(stats): + s = stats.get() + time.sleep(1) + s = stats.get() + for key in sorted(s.keys()): + values = s[key] + print '%-22s%10d%10d' % (key, values[0], values[1]) + +def log(stats): + keys = sorted(stats.get().iterkeys()) + def banner(): + for k in keys: + print '%10s' % k[0:9], + print + def statline(): + s = stats.get() + for k in keys: + print ' %9d' % s[k][1], + print + line = 0 + banner_repeat = 20 + while True: + time.sleep(1) + if line % banner_repeat == 0: + banner() + statline() + line += 1 + +options = optparse.OptionParser() +options.add_option('-1', '--once', '--batch', + action = 'store_true', + default = False, + dest = 'once', + help = 'run in batch mode for one second', + ) +options.add_option('-l', '--log', + action = 'store_true', + default = False, + dest = 'log', + help = 'run in logging mode (like vmstat)', + ) +options.add_option('-f', '--fields', + action = 'store', + default = None, + dest = 'fields', + help = 'fields to display (regex)', + ) +(options, args) = options.parse_args(sys.argv) + +stats = Stats(fields = options.fields) + +if options.log: + log(stats) +elif not options.once: + import curses.wrapper + curses.wrapper(tui, stats) +else: + batch(stats) diff --git a/kvm/libfdt/Makefile b/kvm/libfdt/Makefile new file mode 100644 index 000000000..db80e47a6 --- /dev/null +++ b/kvm/libfdt/Makefile @@ -0,0 +1,19 @@ +include ../config.mak +include ../user/config.mak + +LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c +LIBFDT_INCLUDES = fdt.h libfdt.h +LIBFDT_EXTRA = libfdt_internal.h +LIBFDT_LIB = libfdt.a + +LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) + +CFLAGS += -I . + +$(LIBFDT_LIB): $(LIBFDT_OBJS) + $(AR) rcs $@ $^ + +all: $(LIBFDT_LIB) + +clean: + rm -rf *.o *.a diff --git a/kvm/libfdt/README b/kvm/libfdt/README new file mode 100644 index 000000000..491bc76ee --- /dev/null +++ b/kvm/libfdt/README @@ -0,0 +1,3 @@ +libfdt was grabbed from dtc source. This is the upstream source for libfdt. +It can be found here: +http://www.jdl.com/software/ diff --git a/kvm/libfdt/fdt.c b/kvm/libfdt/fdt.c new file mode 100644 index 000000000..bd9171237 --- /dev/null +++ b/kvm/libfdt/fdt.c @@ -0,0 +1,194 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_check_header(const void *fdt) +{ + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } else if (fdt_magic(fdt) == SW_MAGIC) { + /* Unfinished sequential-write blob */ + if (fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, int len) +{ + const void *p; + + if (fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + p = _fdt_offset_ptr(fdt, offset); + + if (p + len < p) + return NULL; + return p; +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset) +{ + const uint32_t *tagp, *lenp; + uint32_t tag; + const char *p; + + if (offset % FDT_TAGSIZE) + return -1; + + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (! tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu(*tagp); + offset += FDT_TAGSIZE; + + switch (tag) { + case FDT_BEGIN_NODE: + /* skip name */ + do { + p = fdt_offset_ptr(fdt, offset++, 1); + } while (p && (*p != '\0')); + if (! p) + return FDT_END; + break; + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (! lenp) + return FDT_END; + /* skip name offset, length and value */ + offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp); + break; + } + + if (nextoffset) + *nextoffset = ALIGN(offset, FDT_TAGSIZE); + + return tag; +} + +int fdt_next_node(const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) { + tag = fdt_next_tag(fdt, offset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + } + + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth) + (*depth)--; + break; + + case FDT_END: + return -FDT_ERR_NOTFOUND; + + default: + return -FDT_ERR_BADSTRUCTURE; + } + } while (tag != FDT_BEGIN_NODE); + + return offset; +} + +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) +{ + int len = strlen(s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memeq(p, s, len)) + return p; + return NULL; +} + +int fdt_move(const void *fdt, void *buf, int bufsize) +{ + CHECK_HEADER(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/kvm/libfdt/fdt.h b/kvm/libfdt/fdt.h new file mode 100644 index 000000000..48ccfd910 --- /dev/null +++ b/kvm/libfdt/fdt.h @@ -0,0 +1,60 @@ +#ifndef _FDT_H +#define _FDT_H + +#ifndef __ASSEMBLY__ + +struct fdt_header { + uint32_t magic; /* magic word FDT_MAGIC */ + uint32_t totalsize; /* total size of DT block */ + uint32_t off_dt_struct; /* offset to structure */ + uint32_t off_dt_strings; /* offset to strings */ + uint32_t off_mem_rsvmap; /* offset to memory reserve map */ + uint32_t version; /* format version */ + uint32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + uint32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + uint32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + uint32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + uint64_t address; + uint64_t size; +}; + +struct fdt_node_header { + uint32_t tag; + char name[0]; +}; + +struct fdt_property { + uint32_t tag; + uint32_t len; + uint32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(uint32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(uint32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t)) + +#endif /* _FDT_H */ diff --git a/kvm/libfdt/fdt_ro.c b/kvm/libfdt/fdt_ro.c new file mode 100644 index 000000000..63fa1290b --- /dev/null +++ b/kvm/libfdt/fdt_ro.c @@ -0,0 +1,476 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int nodename_eq(const void *fdt, int offset, + const char *s, int len) +{ + const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); + + if (! p) + /* short match */ + return 0; + + if (memcmp(p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr(s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset; +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + CHECK_HEADER(fdt); + *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); + *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i = 0; + + while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) + i++; + return i; +} + +int fdt_subnode_offset_namelen(const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + CHECK_HEADER(fdt); + + for (depth = 0; + offset >= 0; + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth < 0) + return -FDT_ERR_NOTFOUND; + else if ((depth == 1) + && nodename_eq(fdt, offset, name, namelen)) + return offset; + } + + return offset; /* error */ +} + +int fdt_subnode_offset(const void *fdt, int parentoffset, + const char *name) +{ + return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_path_offset(const void *fdt, const char *path) +{ + const char *end = path + strlen(path); + const char *p = path; + int offset = 0; + + CHECK_HEADER(fdt); + + if (*path != '/') + return -FDT_ERR_BADPATH; + + while (*p) { + const char *q; + + while (*p == '/') + p++; + if (! *p) + return offset; + q = strchr(p, '/'); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh; + int err; + + if ((err = fdt_check_header(fdt)) != 0) + goto fail; + + err = -FDT_ERR_BADOFFSET; + nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh)); + if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE)) + goto fail; + + if (len) + *len = strlen(nh->name); + + return nh->name; + + fail: + if (len) + *len = err; + return NULL; +} + +const struct fdt_property *fdt_get_property(const void *fdt, + int nodeoffset, + const char *name, int *lenp) +{ + uint32_t tag; + const struct fdt_property *prop; + int namestroff; + int offset, nextoffset; + int err; + + if ((err = fdt_check_header(fdt)) != 0) + goto fail; + + err = -FDT_ERR_BADOFFSET; + if (nodeoffset % FDT_TAGSIZE) + goto fail; + + tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + goto fail; + + do { + offset = nextoffset; + + tag = fdt_next_tag(fdt, offset, &nextoffset); + switch (tag) { + case FDT_END: + err = -FDT_ERR_TRUNCATED; + goto fail; + + case FDT_BEGIN_NODE: + case FDT_END_NODE: + case FDT_NOP: + break; + + case FDT_PROP: + err = -FDT_ERR_BADSTRUCTURE; + prop = fdt_offset_ptr(fdt, offset, sizeof(*prop)); + if (! prop) + goto fail; + namestroff = fdt32_to_cpu(prop->nameoff); + if (streq(fdt_string(fdt, namestroff), name)) { + /* Found it! */ + int len = fdt32_to_cpu(prop->len); + prop = fdt_offset_ptr(fdt, offset, + sizeof(*prop)+len); + if (! prop) + goto fail; + + if (lenp) + *lenp = len; + + return prop; + } + break; + + default: + err = -FDT_ERR_BADSTRUCTURE; + goto fail; + } + } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE)); + + err = -FDT_ERR_NOTFOUND; + fail: + if (lenp) + *lenp = err; + return NULL; +} + +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property(fdt, nodeoffset, name, lenp); + if (! prop) + return NULL; + + return prop->data; +} + +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) +{ + const uint32_t *php; + int len; + + php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof(*php))) + return 0; + + return fdt32_to_cpu(*php); +} + +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + CHECK_HEADER(fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (pdepth < depth) + continue; /* overflowed buffer */ + + while (pdepth > depth) { + do { + p--; + } while (buf[p-1] != '/'); + pdepth--; + } + + name = fdt_get_name(fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) { + memcpy(buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + + if (offset == nodeoffset) { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return p; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + CHECK_HEADER(fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_depth(const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); + if (err) + return (err < 0) ? err : -FDT_ERR_INTERNAL; + return nodedepth; +} + +int fdt_parent_offset(const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth(fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset(fdt, nodeoffset, + nodedepth - 1, NULL); +} + +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + val = fdt_getprop(fdt, offset, propname, &len); + if (val && (len == proplen) + && (memcmp(val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) +{ + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + phandle = cpu_to_fdt32(phandle); + return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle", + &phandle, sizeof(phandle)); +} + +int _stringlist_contains(const void *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const void *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + if (_stringlist_contains(prop, len, compatible)) + return 0; + else + return 1; +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/kvm/libfdt/fdt_rw.c b/kvm/libfdt/fdt_rw.c new file mode 100644 index 000000000..0df472bc5 --- /dev/null +++ b/kvm/libfdt/fdt_rw.c @@ -0,0 +1,467 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int _blocks_misordered(const void *fdt, + int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap(fdt) < ALIGN(sizeof(struct fdt_header), 8)) + || (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) + || (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + struct_size)) + || (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); +} + +static int rw_check_header(void *fdt) +{ + CHECK_HEADER(fdt); + + if (fdt_version(fdt) < 17) + return -FDT_ERR_BADVERSION; + if (_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_BADLAYOUT; + if (fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define RW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = rw_check_header(fdt)) != 0) \ + return err; \ + } + +static inline int _blob_data_size(void *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int _blob_splice(void *fdt, void *p, int oldlen, int newlen) +{ + void *end = fdt + _blob_data_size(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > (fdt + fdt_totalsize(fdt))) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int _blob_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof(*p); + int err; + err = _blob_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); + if (err) + return err; + fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _blob_splice_struct(void *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = _blob_splice(fdt, p, oldlen, newlen))) + return err; + + fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _blob_splice_string(void *fdt, int newlen) +{ + void *p = fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = _blob_splice(fdt, p, 0, newlen))) + return err; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +static int _find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = _blob_splice_string(fdt, len); + if (err) + return err; + + memcpy(new, s, len); + return (new - strtab); +} + +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + if ((err = rw_check_header(fdt))) + return err; + + re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt)); + err = _blob_splice_mem_rsv(fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64(address); + re->size = cpu_to_fdt64(size); + return 0; +} + +int fdt_del_mem_rsv(void *fdt, int n) +{ + struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n); + int err; + + if ((err = rw_check_header(fdt))) + return err; + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + err = _blob_splice_mem_rsv(fdt, re, 1, 0); + if (err) + return err; + return 0; +} + +static int _resize_property(void *fdt, int nodeoffset, const char *name, int len, + struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (! (*prop)) + return oldlen; + + if ((err = _blob_splice_struct(fdt, (*prop)->data, + ALIGN(oldlen, FDT_TAGSIZE), + ALIGN(len, FDT_TAGSIZE)))) + return err; + + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +static int _add_property(void *fdt, int nodeoffset, const char *name, int len, + struct fdt_property **prop) +{ + uint32_t tag; + int proplen; + int nextoffset; + int namestroff; + int err; + + tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + + namestroff = _find_add_string(fdt, name); + if (namestroff < 0) + return namestroff; + + *prop = _fdt_offset_ptr_w(fdt, nextoffset); + proplen = sizeof(**prop) + ALIGN(len, FDT_TAGSIZE); + + err = _blob_splice_struct(fdt, *prop, 0, proplen); + if (err) + return err; + + (*prop)->tag = cpu_to_fdt32(FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32(namestroff); + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +int fdt_set_name(void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + if ((err = rw_check_header(fdt))) + return err; + + namep = (char *)fdt_get_name(fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen(name); + + err = _blob_splice_struct(fdt, namep, ALIGN(oldlen+1, FDT_TAGSIZE), + ALIGN(newlen+1, FDT_TAGSIZE)); + if (err) + return err; + + memcpy(namep, name, newlen+1); + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + if ((err = rw_check_header(fdt))) + return err; + + err = _resize_property(fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = _add_property(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + memcpy(prop->data, val, len); + return 0; +} + +int fdt_delprop(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + RW_CHECK_HEADER(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + proplen = sizeof(*prop) + ALIGN(len, FDT_TAGSIZE); + return _blob_splice_struct(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + uint32_t *endtag; + + RW_CHECK_HEADER(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + } while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = _fdt_offset_ptr_w(fdt, offset); + nodelen = sizeof(*nh) + ALIGN(namelen+1, FDT_TAGSIZE) + FDT_TAGSIZE; + + err = _blob_splice_struct(fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, ALIGN(namelen+1, FDT_TAGSIZE)); + memcpy(nh->name, name, namelen); + endtag = (uint32_t *)((void *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(void *fdt, int nodeoffset) +{ + int endoffset; + + RW_CHECK_HEADER(fdt); + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return _blob_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void _packblocks(const void *fdt, void *buf, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = ALIGN(sizeof(struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove(buf + mem_rsv_off, fdt + fdt_off_mem_rsvmap(fdt), mem_rsv_size); + fdt_set_off_mem_rsvmap(buf, mem_rsv_off); + + memmove(buf + struct_off, fdt + fdt_off_dt_struct(fdt), struct_size); + fdt_set_off_dt_struct(buf, struct_off); + fdt_set_size_dt_struct(buf, struct_size); + + memmove(buf + strings_off, fdt + fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(buf, strings_off); + fdt_set_size_dt_strings(buf, fdt_size_dt_strings(fdt)); +} + +int fdt_open_into(const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + void *tmp; + + CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + } + + if (!_blocks_misordered(fdt, mem_rsv_size, struct_size)) { + /* no further work necessary */ + err = fdt_move(fdt, buf, bufsize); + if (err) + return err; + fdt_set_version(buf, 17); + fdt_set_size_dt_struct(buf, struct_size); + fdt_set_totalsize(buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings(fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + if (((buf + newsize) <= fdt) + || (buf >= (fdt + fdt_totalsize(fdt)))) { + tmp = buf; + } else { + tmp = (void *)fdt + fdt_totalsize(fdt); + if ((tmp + newsize) > (buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + _packblocks(fdt, tmp, mem_rsv_size, struct_size); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); + fdt_set_totalsize(buf, bufsize); + fdt_set_version(buf, 17); + fdt_set_last_comp_version(buf, 16); + fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + + return 0; +} + +int fdt_pack(void *fdt) +{ + int mem_rsv_size; + int err; + + err = rw_check_header(fdt); + if (err) + return err; + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + _packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_set_totalsize(fdt, _blob_data_size(fdt)); + + return 0; +} diff --git a/kvm/libfdt/fdt_strerror.c b/kvm/libfdt/fdt_strerror.c new file mode 100644 index 000000000..f9d32ef53 --- /dev/null +++ b/kvm/libfdt/fdt_strerror.c @@ -0,0 +1,96 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +struct errtabent { + const char *str; +}; + +#define ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct errtabent errtable[] = { + ERRTABENT(FDT_ERR_NOTFOUND), + ERRTABENT(FDT_ERR_EXISTS), + ERRTABENT(FDT_ERR_NOSPACE), + + ERRTABENT(FDT_ERR_BADOFFSET), + ERRTABENT(FDT_ERR_BADPATH), + ERRTABENT(FDT_ERR_BADSTATE), + + ERRTABENT(FDT_ERR_TRUNCATED), + ERRTABENT(FDT_ERR_BADMAGIC), + ERRTABENT(FDT_ERR_BADVERSION), + ERRTABENT(FDT_ERR_BADSTRUCTURE), + ERRTABENT(FDT_ERR_BADLAYOUT), +}; +#define ERRTABSIZE (sizeof(errtable) / sizeof(errtable[0])) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return "<valid offset/length>"; + else if (errval == 0) + return "<no error>"; + else if (errval > -ERRTABSIZE) { + const char *s = errtable[-errval].str; + + if (s) + return s; + } + + return "<unknown error>"; +} diff --git a/kvm/libfdt/fdt_sw.c b/kvm/libfdt/fdt_sw.c new file mode 100644 index 000000000..dda2de34b --- /dev/null +++ b/kvm/libfdt/fdt_sw.c @@ -0,0 +1,258 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int check_header_sw(void *fdt) +{ + if (fdt_magic(fdt) != SW_MAGIC) + return -FDT_ERR_BADMAGIC; + return 0; +} + +static void *grab_space(void *fdt, int len) +{ + int offset = fdt_size_dt_struct(fdt); + int spaceleft; + + spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) + - fdt_size_dt_strings(fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct(fdt, offset + len); + return fdt_offset_ptr_w(fdt, offset, len); +} + +int fdt_create(void *buf, int bufsize) +{ + void *fdt = buf; + + if (bufsize < sizeof(struct fdt_header)) + return -FDT_ERR_NOSPACE; + + memset(buf, 0, bufsize); + + fdt_set_magic(fdt, SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry))); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, bufsize); + + return 0; +} + +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err = check_header_sw(fdt); + int offset; + + if (err) + return err; + if (fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTATE; + + offset = fdt_off_dt_struct(fdt); + if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *)(fdt + offset); + re->address = cpu_to_fdt64(addr); + re->size = cpu_to_fdt64(size); + + fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); + + return 0; +} + +int fdt_finish_reservemap(void *fdt) +{ + return fdt_add_reservemap_entry(fdt, 0, 0); +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int err = check_header_sw(fdt); + int namelen = strlen(name) + 1; + + if (err) + return err; + + nh = grab_space(fdt, sizeof(*nh) + ALIGN(namelen, FDT_TAGSIZE)); + if (! nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memcpy(nh->name, name, namelen); + return 0; +} + +int fdt_end_node(void *fdt) +{ + uint32_t *en; + int err = check_header_sw(fdt); + + if (err) + return err; + + en = grab_space(fdt, FDT_TAGSIZE); + if (! en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32(FDT_END_NODE); + return 0; +} + +static int find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + const char *p; + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + p = _fdt_find_string(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + /* Add it */ + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + if (fdt_totalsize(fdt) + offset < struct_top) + return 0; /* no more room :( */ + + memcpy(strtab + offset, s, len); + fdt_set_size_dt_strings(fdt, strtabsize + len); + return offset; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + struct fdt_property *prop; + int err = check_header_sw(fdt); + int nameoff; + + if (err) + return err; + + nameoff = find_add_string(fdt, name); + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = grab_space(fdt, sizeof(*prop) + ALIGN(len, FDT_TAGSIZE)); + if (! prop) + return -FDT_ERR_NOSPACE; + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + memcpy(prop->data, val, len); + return 0; +} + +int fdt_finish(void *fdt) +{ + int err = check_header_sw(fdt); + char *p = (char *)fdt; + uint32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + if (err) + return err; + + /* Add terminator */ + end = grab_space(fdt, sizeof(*end)); + if (! end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32(FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); + newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { + if (tag == FDT_PROP) { + struct fdt_property *prop = + fdt_offset_ptr_w(fdt, offset, sizeof(*prop)); + int nameoff; + + if (! prop) + return -FDT_ERR_BADSTRUCTURE; + + nameoff = fdt32_to_cpu(prop->nameoff); + nameoff += fdt_size_dt_strings(fdt); + prop->nameoff = cpu_to_fdt32(nameoff); + } + offset = nextoffset; + } + + /* Finally, adjust the header */ + fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + return 0; +} diff --git a/kvm/libfdt/fdt_wip.c b/kvm/libfdt/fdt_wip.c new file mode 100644 index 000000000..88e24b831 --- /dev/null +++ b/kvm/libfdt/fdt_wip.c @@ -0,0 +1,144 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen); + if (! propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + memcpy(propval, val, len); + return 0; +} + +static void nop_region(void *start, int len) +{ + uint32_t *p; + + for (p = start; (void *)p < (start + len); p++) + *p = cpu_to_fdt32(FDT_NOP); +} + +int fdt_nop_property(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + nop_region(prop, len + sizeof(*prop)); + + return 0; +} + +int _fdt_node_end_offset(void *fdt, int nodeoffset) +{ + int level = 0; + uint32_t tag; + int offset, nextoffset; + + tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + return offset; + + case FDT_BEGIN_NODE: + level++; + break; + + case FDT_END_NODE: + level--; + break; + + case FDT_PROP: + case FDT_NOP: + break; + + default: + return -FDT_ERR_BADSTRUCTURE; + } + } while (level >= 0); + + return nextoffset; +} + +int fdt_nop_node(void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset); + return 0; +} diff --git a/kvm/libfdt/libfdt.h b/kvm/libfdt/libfdt.h new file mode 100644 index 000000000..8645de082 --- /dev/null +++ b/kvm/libfdt/libfdt.h @@ -0,0 +1,1076 @@ +#ifndef _LIBFDT_H +#define _LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <libfdt_env.h> +#include <fdt.h> + +#define FDT_FIRST_SUPPORTED_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attemped to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle + * value. phandle values of 0 and -1 are not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: Structure block of the given device tree + * ends without an FDT_END tag. */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +#define FDT_ERR_MAX 13 + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +const void *fdt_offset_ptr(const void *fdt, int offset, int checklen); +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +#define fdt_get_header(fdt, field) \ + (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define __fdt_set_hdr(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +__fdt_set_hdr(magic); +__fdt_set_hdr(totalsize); +__fdt_set_hdr(off_dt_struct); +__fdt_set_hdr(off_dt_strings); +__fdt_set_hdr(off_mem_rsvmap); +__fdt_set_hdr(version); +__fdt_set_hdr(last_comp_version); +__fdt_set_hdr(boot_cpuid_phys); +__fdt_set_hdr(size_dt_strings); +__fdt_set_hdr(size_dt_struct); +#undef __fdt_set_hdr + +/** + * fdt_check_header - sanity check a device tree or possible device tree + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree with sane information in its + * header. + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +/** + * fdt_string - retreive a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_num_mem_rsv - retreive the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retreive one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retreive the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)fdt_get_property(fdt, nodeoffset, + name, lenp); +} + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retreive the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on succes (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag +* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * stucture block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_prop_value() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: cell (32-bit integer) value to replace the property with + * + * fdt_setprop_inplace_cell() replaces the value of a given property + * with the 32-bit integer cell value in val, converting val to + * big-endian if necessary. This function cannot change the size of a + * property, and so will only work if the property already exists and + * has length 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + val = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val)); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +int fdt_create(void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + val = cpu_to_fdt32(val); + return fdt_property(fdt, name, &val, sizeof(val)); +} +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @addres, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therfore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therfore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creeating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_cell - set a property to a single cell value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_cell() sets the value of the named property in the + * given node to the given cell value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + val = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val)); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#endif /* _LIBFDT_H */ diff --git a/kvm/libfdt/libfdt_env.h b/kvm/libfdt/libfdt_env.h new file mode 100644 index 000000000..59f2536d2 --- /dev/null +++ b/kvm/libfdt/libfdt_env.h @@ -0,0 +1,22 @@ +#ifndef _LIBFDT_ENV_H +#define _LIBFDT_ENV_H + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <endian.h> +#include <byteswap.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +#define fdt32_to_cpu(x) (x) +#define cpu_to_fdt32(x) (x) +#define fdt64_to_cpu(x) (x) +#define cpu_to_fdt64(x) (x) +#else +#define fdt32_to_cpu(x) (bswap_32((x))) +#define cpu_to_fdt32(x) (bswap_32((x))) +#define fdt64_to_cpu(x) (bswap_64((x))) +#define cpu_to_fdt64(x) (bswap_64((x))) +#endif + +#endif /* _LIBFDT_ENV_H */ diff --git a/kvm/libfdt/libfdt_internal.h b/kvm/libfdt/libfdt_internal.h new file mode 100644 index 000000000..52e1b8d81 --- /dev/null +++ b/kvm/libfdt/libfdt_internal.h @@ -0,0 +1,96 @@ +#ifndef _LIBFDT_INTERNAL_H +#define _LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <fdt.h> + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define PALIGN(p, a) ((void *)ALIGN((unsigned long)(p), (a))) + +#define memeq(p, q, n) (memcmp((p), (q), (n)) == 0) +#define streq(p, q) (strcmp((p), (q)) == 0) + +#define CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = fdt_check_header(fdt)) != 0) \ + return err; \ + } + +uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset); +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); +int _fdt_node_end_offset(void *fdt, int nodeoffset); + +static inline const void *_fdt_offset_ptr(const void *fdt, int offset) +{ + return fdt + fdt_off_dt_struct(fdt) + offset; +} + +static inline void *_fdt_offset_ptr_w(void *fdt, int offset) +{ + return (void *)_fdt_offset_ptr(fdt, offset); +} + +static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + fdt + fdt_off_mem_rsvmap(fdt); + + return rsv_table + n; +} +static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) +{ + return (void *)_fdt_mem_rsv(fdt, n); +} + +#define SW_MAGIC (~FDT_MAGIC) + +#endif /* _LIBFDT_INTERNAL_H */ diff --git a/kvm/libkvm/Makefile b/kvm/libkvm/Makefile new file mode 100644 index 000000000..4db773de0 --- /dev/null +++ b/kvm/libkvm/Makefile @@ -0,0 +1,54 @@ +all: libkvm.a + +include ../../config-host.mak +ifneq ($(VPATH),) +srcdir=$(VPATH)/kvm/libkvm +else +srcdir=. +endif + +include $(srcdir)/config-$(ARCH).mak + + +# libkvm is not -Wredundant-decls friendly yet +CFLAGS += -Wno-redundant-decls + +# cc-option +# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0) +cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ + > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) + +CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall +CFLAGS += $(call cc-option, -fno-stack-protector, "") +CFLAGS += $(call cc-option, -fno-stack-protector-all, "") +CFLAGS += $(KVM_CFLAGS) + +LDFLAGS += $(CFLAGS) + +CXXFLAGS = $(autodepend-flags) + +VPATH:=$(VPATH)/kvm/libkvm + +autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d + + +libkvm.a: libkvm.o $(libkvm-$(ARCH)-objs) + $(AR) rcs $@ $^ + +install: + @echo skipping libkvm install + +install-libkvm: + install -D libkvm.h $(DESTDIR)/$(PREFIX)/include/libkvm.h + install -D libkvm.a $(DESTDIR)/$(PREFIX)/$(LIBDIR)/libkvm.a + +install-kernel-headers: + install -D $(LIBKVM_KERNELDIR)/include/linux/kvm.h \ + $(DESTDIR)/$(PREFIX)/include/linux/kvm.h + install -D $(LIBKVM_KERNELDIR)/include/linux/kvm_para.h \ + $(DESTDIR)/$(PREFIX)/include/linux/kvm_para.h + +-include .*.d + +clean: + $(RM) *.o *.a .*.d diff --git a/kvm/libkvm/config-i386.mak b/kvm/libkvm/config-i386.mak new file mode 100644 index 000000000..2706b70f7 --- /dev/null +++ b/kvm/libkvm/config-i386.mak @@ -0,0 +1,6 @@ + +LIBDIR := /lib +CFLAGS += -m32 +CFLAGS += -D__i386__ + +libkvm-$(ARCH)-objs := libkvm-x86.o diff --git a/kvm/libkvm/config-ia64.mak b/kvm/libkvm/config-ia64.mak new file mode 100644 index 000000000..568c39707 --- /dev/null +++ b/kvm/libkvm/config-ia64.mak @@ -0,0 +1,5 @@ + +LIBDIR := /lib +CFLAGS += -D__ia64__ + +libkvm-$(ARCH)-objs := libkvm-ia64.o diff --git a/kvm/libkvm/config-ppc.mak b/kvm/libkvm/config-ppc.mak new file mode 100644 index 000000000..091da370d --- /dev/null +++ b/kvm/libkvm/config-ppc.mak @@ -0,0 +1,4 @@ + +LIBDIR := /lib + +libkvm-$(ARCH)-objs := libkvm-powerpc.o diff --git a/kvm/libkvm/config-s390.mak b/kvm/libkvm/config-s390.mak new file mode 100644 index 000000000..8177e4ad0 --- /dev/null +++ b/kvm/libkvm/config-s390.mak @@ -0,0 +1,3 @@ +# s390 31bit mode +LIBDIR := /lib +libkvm-$(ARCH)-objs := libkvm-s390.o diff --git a/kvm/libkvm/config-s390x.mak b/kvm/libkvm/config-s390x.mak new file mode 100644 index 000000000..f08ed3d88 --- /dev/null +++ b/kvm/libkvm/config-s390x.mak @@ -0,0 +1,3 @@ +# s390 64 bit mode (arch=s390x) +LIBDIR := /lib64 +libkvm-$(ARCH)-objs := libkvm-s390.o diff --git a/kvm/libkvm/config-x86_64.mak b/kvm/libkvm/config-x86_64.mak new file mode 100644 index 000000000..e6389775a --- /dev/null +++ b/kvm/libkvm/config-x86_64.mak @@ -0,0 +1,6 @@ + +LIBDIR := /lib64 +CFLAGS += -m64 +CFLAGS += -D__x86_64__ + +libkvm-$(ARCH)-objs := libkvm-x86.o diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h new file mode 100644 index 000000000..c95c59169 --- /dev/null +++ b/kvm/libkvm/kvm-common.h @@ -0,0 +1,94 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm. + * + * derived from libkvm.c + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_COMMON_H +#define KVM_COMMON_H + +/* FIXME: share this number with kvm */ +/* FIXME: or dynamically alloc/realloc regions */ +#ifdef __s390__ +#define KVM_MAX_NUM_MEM_REGIONS 1u +#define MAX_VCPUS 64 +#define LIBKVM_S390_ORIGIN (0UL) +#elif defined(__ia64__) +#define KVM_MAX_NUM_MEM_REGIONS 32u +#define MAX_VCPUS 256 +#else +#define KVM_MAX_NUM_MEM_REGIONS 32u +#define MAX_VCPUS 16 +#endif + + +/* kvm abi verison variable */ +extern int kvm_abi; + +/** + * \brief The KVM context + * + * The verbose KVM context + */ + +struct kvm_context { + /// Filedescriptor to /dev/kvm + int fd; + int vm_fd; + int vcpu_fd[MAX_VCPUS]; + struct kvm_run *run[MAX_VCPUS]; + /// Callbacks that KVM uses to emulate various unvirtualizable functionality + struct kvm_callbacks *callbacks; + void *opaque; + /// is dirty pages logging enabled for all regions or not + int dirty_pages_log_all; + /// do not create in-kernel irqchip if set + int no_irqchip_creation; + /// in-kernel irqchip status + int irqchip_in_kernel; + /// ioctl to use to inject interrupts + int irqchip_inject_ioctl; + /// do not create in-kernel pit if set + int no_pit_creation; + /// in-kernel pit status + int pit_in_kernel; + /// in-kernel coalesced mmio + int coalesced_mmio; +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing *irq_routes; + int nr_allocated_irq_routes; +#endif + void *used_gsi_bitmap; + int max_gsi; +}; + +int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory, + void **vm_mem); +int kvm_alloc_userspace_memory(kvm_context_t kvm, unsigned long memory, + void **vm_mem); + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem); +int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu); + + +void kvm_show_code(kvm_context_t kvm, int vcpu); + +int handle_halt(kvm_context_t kvm, int vcpu); +int handle_shutdown(kvm_context_t kvm, void *env); +void post_kvm_run(kvm_context_t kvm, void *env); +int pre_kvm_run(kvm_context_t kvm, void *env); +int handle_io_window(kvm_context_t kvm); +int handle_debug(kvm_context_t kvm, int vcpu, void *env); +int try_push_interrupts(kvm_context_t kvm); + +#endif diff --git a/kvm/libkvm/kvm-ia64.h b/kvm/libkvm/kvm-ia64.h new file mode 100644 index 000000000..ad87ae764 --- /dev/null +++ b/kvm/libkvm/kvm-ia64.h @@ -0,0 +1,31 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for x86. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * derived from libkvm.c + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_IA64_H +#define KVM_IA64_H + +#include "kvm-common.h" + +extern int kvm_page_size; + +#define PAGE_SIZE kvm_page_size +#define PAGE_MASK (~(kvm_page_size - 1)) + +#define ia64_mf() asm volatile ("mf" ::: "memory") +#define smp_wmb() ia64_mf() + +#endif diff --git a/kvm/libkvm/kvm-powerpc.h b/kvm/libkvm/kvm-powerpc.h new file mode 100644 index 000000000..b09511c1c --- /dev/null +++ b/kvm/libkvm/kvm-powerpc.h @@ -0,0 +1,36 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for powerpc. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright 2007 IBM Corporation. + * Added by: Jerone Young <jyoung5@us.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_POWERPC_H +#define KVM_POWERPC_H + +#include "kvm-common.h" + +extern int kvm_page_size; + +#define PAGE_SIZE kvm_page_size +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +static inline void eieio(void) +{ + asm volatile("eieio" : : : "memory"); +} + +#define smp_wmb() eieio() + +#endif diff --git a/kvm/libkvm/kvm-s390.h b/kvm/libkvm/kvm-s390.h new file mode 100644 index 000000000..9edd9a33b --- /dev/null +++ b/kvm/libkvm/kvm-s390.h @@ -0,0 +1,31 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for s390. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright 2008 IBM Corporation. + * Authors: + * Carsten Otte <cotte@de.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_S390_H +#define KVM_S390_H + +#include <asm/ptrace.h> +#include "kvm-common.h" + +#define PAGE_SIZE 4096ul +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +#define smp_wmb() asm volatile("" ::: "memory") + +#endif diff --git a/kvm/libkvm/kvm-x86.h b/kvm/libkvm/kvm-x86.h new file mode 100644 index 000000000..e988cb7bb --- /dev/null +++ b/kvm/libkvm/kvm-x86.h @@ -0,0 +1,55 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for x86. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * derived from libkvm.c + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_X86_H +#define KVM_X86_H + +#include "kvm-common.h" + +#define PAGE_SIZE 4096ul +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr); + +#ifdef KVM_CAP_VAPIC + +/*! + * \brief Enable kernel tpr access reporting + * + * When tpr access reporting is enabled, the kernel will call the + * ->tpr_access() callback every time the guest vcpu accesses the tpr. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu vcpu to enable tpr access reporting on + */ +int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu); + +/*! + * \brief Disable kernel tpr access reporting + * + * Undoes the effect of kvm_enable_tpr_access_reporting(). + * + * \param kvm Pointer to the current kvm_context + * \param vcpu vcpu to disable tpr access reporting on + */ +int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu); + +#endif + +#define smp_wmb() asm volatile("" ::: "memory") + +#endif diff --git a/kvm/libkvm/libkvm-ia64.c b/kvm/libkvm/libkvm-ia64.c new file mode 100644 index 000000000..2f1567595 --- /dev/null +++ b/kvm/libkvm/libkvm-ia64.c @@ -0,0 +1,82 @@ +/* + * libkvm-ia64.c :Kernel-based Virtual Machine control library for ia64. + * + * This library provides an API to control the kvm hardware virtualization + * module. + * + * Copyright (C) 2006 Qumranet + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright (C) 2007 Intel + * Added by : Zhang Xiantao <xiantao.zhang@intel.com> + * + * This work is licensed under the GNU LGPL license, version 2. + * + */ + +#include "libkvm.h" +#include "kvm-ia64.h" +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> +#include <unistd.h> +#include <stropts.h> +#include <sys/mman.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + int r; + + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) + return r; + + return 0; +} + +int kvm_arch_run(struct kvm_run *run,kvm_context_t kvm, int vcpu) +{ + int r = 0; + + switch (run->exit_reason) { + default: + r = 1; + break; + } + + return r; +} + +void kvm_show_code(kvm_context_t kvm, int vcpu) +{ + fprintf(stderr, "kvm_show_code not supported yet!\n"); +} + +void kvm_show_regs(kvm_context_t kvm, int vcpu) +{ + fprintf(stderr,"kvm_show_regs not supportted today!\n"); +} + +int kvm_create_memory_alias(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len, + uint64_t target_phys) +{ + return 0; +} + +int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start) +{ + return 0; +} diff --git a/kvm/libkvm/libkvm-powerpc.c b/kvm/libkvm/libkvm-powerpc.c new file mode 100644 index 000000000..f2cd8dc32 --- /dev/null +++ b/kvm/libkvm/libkvm-powerpc.c @@ -0,0 +1,100 @@ +/* + * This file contains the powerpc specific implementation for the + * architecture dependent functions defined in kvm-common.h and + * libkvm.h + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright IBM Corp. 2007,2008 + * Authors: + * Jerone Young <jyoung5@us.ibm.com> + * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#include "libkvm.h" +#include "kvm-powerpc.h" +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> + +int handle_dcr(struct kvm_run *run, kvm_context_t kvm, int vcpu) +{ + int ret = 0; + + if (run->dcr.is_write) + ret = kvm->callbacks->powerpc_dcr_write(vcpu, + run->dcr.dcrn, + run->dcr.data); + else + ret = kvm->callbacks->powerpc_dcr_read(vcpu, + run->dcr.dcrn, + &(run->dcr.data)); + + return ret; +} + +void kvm_show_code(kvm_context_t kvm, int vcpu) +{ + fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__); +} + +void kvm_show_regs(kvm_context_t kvm, int vcpu) +{ + struct kvm_regs regs; + int i; + + if (kvm_get_regs(kvm, vcpu, ®s)) + return; + + fprintf(stderr,"guest vcpu #%d\n", vcpu); + fprintf(stderr,"pc: %016"PRIx64" msr: %016"PRIx64"\n", + regs.pc, regs.msr); + fprintf(stderr,"lr: %016"PRIx64" ctr: %016"PRIx64"\n", + regs.lr, regs.ctr); + fprintf(stderr,"srr0: %016"PRIx64" srr1: %016"PRIx64"\n", + regs.srr0, regs.srr1); + for (i=0; i<32; i+=4) + { + fprintf(stderr, "gpr%02d: %016"PRIx64" %016"PRIx64" %016"PRIx64 + " %016"PRIx64"\n", i, + regs.gpr[i], + regs.gpr[i+1], + regs.gpr[i+2], + regs.gpr[i+3]); + } + + fflush(stdout); +} + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + int r; + + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) + return r; + + return 0; +} + +int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu) +{ + int ret = 0; + + switch (run->exit_reason){ + case KVM_EXIT_DCR: + ret = handle_dcr(run, kvm, vcpu); + break; + default: + ret = 1; + break; + } + return ret; +} diff --git a/kvm/libkvm/libkvm-s390.c b/kvm/libkvm/libkvm-s390.c new file mode 100644 index 000000000..041c0ce31 --- /dev/null +++ b/kvm/libkvm/libkvm-s390.c @@ -0,0 +1,110 @@ +/* + * This file contains the s390 specific implementation for the + * architecture dependent functions defined in kvm-common.h and + * libkvm.h + * + * Copyright (C) 2006 Qumranet + * Copyright IBM Corp. 2008 + * + * Authors: + * Carsten Otte <cotte@de.ibm.com> + * Christian Borntraeger <borntraeger@de.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#include <sys/ioctl.h> +#include <asm/ptrace.h> + +#include "libkvm.h" +#include "kvm-common.h" +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> + +void kvm_show_code(kvm_context_t kvm, int vcpu) +{ + fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__); +} + +void kvm_show_regs(kvm_context_t kvm, int vcpu) +{ + struct kvm_regs regs; + struct kvm_sregs sregs; + int i; + + if (kvm_get_regs(kvm, vcpu, ®s)) + return; + + if (kvm_get_sregs(kvm, vcpu, &sregs)) + return; + + fprintf(stderr, "guest vcpu #%d\n", vcpu); + fprintf(stderr, "PSW:\t%16.16lx %16.16lx\n", + kvm->run[vcpu]->s390_sieic.mask, + kvm->run[vcpu]->s390_sieic.addr); + fprintf(stderr,"GPRS:"); + for (i=0; i<15; i+=4) + fprintf(stderr, "\t%16.16lx %16.16lx %16.16lx %16.16lx\n", + regs.gprs[i], + regs.gprs[i+1], + regs.gprs[i+2], + regs.gprs[i+3]); + fprintf(stderr,"ACRS:"); + for (i=0; i<15; i+=4) + fprintf(stderr, "\t%8.8x %8.8x %8.8x %8.8x\n", + sregs.acrs[i], + sregs.acrs[i+1], + sregs.acrs[i+2], + sregs.acrs[i+3]); + + fprintf(stderr,"CRS:"); + for (i=0; i<15; i+=4) + fprintf(stderr, "\t%16.16lx %16.16lx %16.16lx %16.16lx\n", + sregs.crs[i], + sregs.crs[i+1], + sregs.crs[i+2], + sregs.crs[i+3]); +} + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + return 0; +} + +int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu) +{ + int ret = 0; + + switch (run->exit_reason){ + default: + ret = 1; + break; + } + return ret; +} + +int kvm_s390_initial_reset(kvm_context_t kvm, int slot) +{ + return ioctl(kvm->vcpu_fd[slot], KVM_S390_INITIAL_RESET, NULL); +} + +int kvm_s390_interrupt(kvm_context_t kvm, int slot, + struct kvm_s390_interrupt *kvmint) +{ + if (slot>=0) + return ioctl(kvm->vcpu_fd[slot], KVM_S390_INTERRUPT, kvmint); + else + return ioctl(kvm->vm_fd, KVM_S390_INTERRUPT, kvmint); +} + +int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw) +{ + return ioctl(kvm->vcpu_fd[slot], KVM_S390_SET_INITIAL_PSW, &psw); +} + +int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr) +{ + return ioctl(kvm->vcpu_fd[slot], KVM_S390_STORE_STATUS, addr); +} diff --git a/kvm/libkvm/libkvm-x86.c b/kvm/libkvm/libkvm-x86.c new file mode 100644 index 000000000..f1aef7617 --- /dev/null +++ b/kvm/libkvm/libkvm-x86.c @@ -0,0 +1,676 @@ +#include "libkvm.h" +#include "kvm-x86.h" +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr) +{ +#ifdef KVM_CAP_SET_TSS_ADDR + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); + if (r > 0) { + r = ioctl(kvm->vm_fd, KVM_SET_TSS_ADDR, addr); + if (r == -1) { + fprintf(stderr, "kvm_set_tss_addr: %m\n"); + return -errno; + } + return 0; + } +#endif + return -ENOSYS; +} + +static int kvm_init_tss(kvm_context_t kvm) +{ +#ifdef KVM_CAP_SET_TSS_ADDR + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); + if (r > 0) { + /* + * this address is 3 pages before the bios, and the bios should present + * as unavaible memory + */ + r = kvm_set_tss_addr(kvm, 0xfffbd000); + if (r < 0) { + fprintf(stderr, "kvm_init_tss: unable to set tss addr\n"); + return r; + } + + } +#endif + return 0; +} + +static int kvm_create_pit(kvm_context_t kvm) +{ +#ifdef KVM_CAP_PIT + int r; + + kvm->pit_in_kernel = 0; + if (!kvm->no_pit_creation) { +#ifdef KVM_CAP_PIT2 + struct kvm_pit_config config = { .flags = 0 }; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_PIT2); + if (r > 0) + r = ioctl(kvm->vm_fd, KVM_CREATE_PIT2, &config); + else +#endif + { + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_PIT); + if (r <= 0) + return 0; + + r = ioctl(kvm->vm_fd, KVM_CREATE_PIT); + } + if (r < 0) { + fprintf(stderr, "Create kernel PIC irqchip failed\n"); + return r; + } + kvm->pit_in_kernel = 1; + } +#endif + return 0; +} + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + int r = 0; + + r = kvm_init_tss(kvm); + if (r < 0) + return r; + + r = kvm_create_pit(kvm); + if (r < 0) + return r; + + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) + return r; + + return 0; +} + +#ifdef KVM_EXIT_TPR_ACCESS + +static int handle_tpr_access(kvm_context_t kvm, struct kvm_run *run, int vcpu) +{ + return kvm->callbacks->tpr_access(kvm->opaque, vcpu, + run->tpr_access.rip, + run->tpr_access.is_write); +} + + +int kvm_enable_vapic(kvm_context_t kvm, int vcpu, uint64_t vapic) +{ + int r; + struct kvm_vapic_addr va = { + .vapic_addr = vapic, + }; + + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_VAPIC_ADDR, &va); + if (r == -1) { + r = -errno; + perror("kvm_enable_vapic"); + return r; + } + return 0; +} + +#endif + +int kvm_arch_run(struct kvm_run *run,kvm_context_t kvm, int vcpu) +{ + int r = 0; + + switch (run->exit_reason) { +#ifdef KVM_EXIT_SET_TPR + case KVM_EXIT_SET_TPR: + break; +#endif +#ifdef KVM_EXIT_TPR_ACCESS + case KVM_EXIT_TPR_ACCESS: + r = handle_tpr_access(kvm, run, vcpu); + break; +#endif + default: + r = 1; + break; + } + + return r; +} + +#define MAX_ALIAS_SLOTS 4 +static struct { + uint64_t start; + uint64_t len; +} kvm_aliases[MAX_ALIAS_SLOTS]; + +static int get_alias_slot(uint64_t start) +{ + int i; + + for (i=0; i<MAX_ALIAS_SLOTS; i++) + if (kvm_aliases[i].start == start) + return i; + return -1; +} +static int get_free_alias_slot(void) +{ + int i; + + for (i=0; i<MAX_ALIAS_SLOTS; i++) + if (kvm_aliases[i].len == 0) + return i; + return -1; +} + +static void register_alias(int slot, uint64_t start, uint64_t len) +{ + kvm_aliases[slot].start = start; + kvm_aliases[slot].len = len; +} + +int kvm_create_memory_alias(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len, + uint64_t target_phys) +{ + struct kvm_memory_alias alias = { + .flags = 0, + .guest_phys_addr = phys_start, + .memory_size = len, + .target_phys_addr = target_phys, + }; + int fd = kvm->vm_fd; + int r; + int slot; + + slot = get_alias_slot(phys_start); + if (slot < 0) + slot = get_free_alias_slot(); + if (slot < 0) + return -EBUSY; + alias.slot = slot; + + r = ioctl(fd, KVM_SET_MEMORY_ALIAS, &alias); + if (r == -1) + return -errno; + + register_alias(slot, phys_start, len); + return 0; +} + +int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start) +{ + return kvm_create_memory_alias(kvm, phys_start, 0, 0); +} + +#ifdef KVM_CAP_IRQCHIP + +int kvm_get_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s) +{ + int r; + if (!kvm->irqchip_in_kernel) + return 0; + r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_LAPIC, s); + if (r == -1) { + r = -errno; + perror("kvm_get_lapic"); + } + return r; +} + +int kvm_set_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s) +{ + int r; + if (!kvm->irqchip_in_kernel) + return 0; + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_LAPIC, s); + if (r == -1) { + r = -errno; + perror("kvm_set_lapic"); + } + return r; +} + +#endif + +#ifdef KVM_CAP_PIT + +int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s) +{ + int r; + if (!kvm->pit_in_kernel) + return 0; + r = ioctl(kvm->vm_fd, KVM_GET_PIT, s); + if (r == -1) { + r = -errno; + perror("kvm_get_pit"); + } + return r; +} + +int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s) +{ + int r; + if (!kvm->pit_in_kernel) + return 0; + r = ioctl(kvm->vm_fd, KVM_SET_PIT, s); + if (r == -1) { + r = -errno; + perror("kvm_set_pit"); + } + return r; +} + +#endif + +void kvm_show_code(kvm_context_t kvm, int vcpu) +{ +#define SHOW_CODE_LEN 50 + int fd = kvm->vcpu_fd[vcpu]; + struct kvm_regs regs; + struct kvm_sregs sregs; + int r, n; + int back_offset; + unsigned char code; + char code_str[SHOW_CODE_LEN * 3 + 1]; + unsigned long rip; + + r = ioctl(fd, KVM_GET_SREGS, &sregs); + if (r == -1) { + perror("KVM_GET_SREGS"); + return; + } + r = ioctl(fd, KVM_GET_REGS, ®s); + if (r == -1) { + perror("KVM_GET_REGS"); + return; + } + rip = sregs.cs.base + regs.rip; + back_offset = regs.rip; + if (back_offset > 20) + back_offset = 20; + *code_str = 0; + for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) { + if (n == 0) + strcat(code_str, " -->"); + r = kvm->callbacks->mmio_read(kvm->opaque, rip + n, &code, 1); + if (r < 0) { + strcat(code_str, " xx"); + continue; + } + sprintf(code_str + strlen(code_str), " %02x", code); + } + fprintf(stderr, "code:%s\n", code_str); +} + + +/* + * Returns available msr list. User must free. + */ +struct kvm_msr_list *kvm_get_msr_list(kvm_context_t kvm) +{ + struct kvm_msr_list sizer, *msrs; + int r, e; + + sizer.nmsrs = 0; + r = ioctl(kvm->fd, KVM_GET_MSR_INDEX_LIST, &sizer); + if (r == -1 && errno != E2BIG) + return NULL; + msrs = malloc(sizeof *msrs + sizer.nmsrs * sizeof *msrs->indices); + if (!msrs) { + errno = ENOMEM; + return NULL; + } + msrs->nmsrs = sizer.nmsrs; + r = ioctl(kvm->fd, KVM_GET_MSR_INDEX_LIST, msrs); + if (r == -1) { + e = errno; + free(msrs); + errno = e; + return NULL; + } + return msrs; +} + +int kvm_get_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs, + int n) +{ + struct kvm_msrs *kmsrs = malloc(sizeof *kmsrs + n * sizeof *msrs); + int r, e; + + if (!kmsrs) { + errno = ENOMEM; + return -1; + } + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_MSRS, kmsrs); + e = errno; + memcpy(msrs, kmsrs->entries, n * sizeof *msrs); + free(kmsrs); + errno = e; + return r; +} + +int kvm_set_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs, + int n) +{ + struct kvm_msrs *kmsrs = malloc(sizeof *kmsrs + n * sizeof *msrs); + int r, e; + + if (!kmsrs) { + errno = ENOMEM; + return -1; + } + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_MSRS, kmsrs); + e = errno; + free(kmsrs); + errno = e; + return r; +} + +static void print_seg(FILE *file, const char *name, struct kvm_segment *seg) +{ + fprintf(stderr, + "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d" + " g %d avl %d)\n", + name, seg->selector, seg->base, seg->limit, seg->present, + seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g, + seg->avl); +} + +static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt) +{ + fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit); +} + +void kvm_show_regs(kvm_context_t kvm, int vcpu) +{ + int fd = kvm->vcpu_fd[vcpu]; + struct kvm_regs regs; + struct kvm_sregs sregs; + int r; + + r = ioctl(fd, KVM_GET_REGS, ®s); + if (r == -1) { + perror("KVM_GET_REGS"); + return; + } + fprintf(stderr, + "rax %016llx rbx %016llx rcx %016llx rdx %016llx\n" + "rsi %016llx rdi %016llx rsp %016llx rbp %016llx\n" + "r8 %016llx r9 %016llx r10 %016llx r11 %016llx\n" + "r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n" + "rip %016llx rflags %08llx\n", + regs.rax, regs.rbx, regs.rcx, regs.rdx, + regs.rsi, regs.rdi, regs.rsp, regs.rbp, + regs.r8, regs.r9, regs.r10, regs.r11, + regs.r12, regs.r13, regs.r14, regs.r15, + regs.rip, regs.rflags); + r = ioctl(fd, KVM_GET_SREGS, &sregs); + if (r == -1) { + perror("KVM_GET_SREGS"); + return; + } + print_seg(stderr, "cs", &sregs.cs); + print_seg(stderr, "ds", &sregs.ds); + print_seg(stderr, "es", &sregs.es); + print_seg(stderr, "ss", &sregs.ss); + print_seg(stderr, "fs", &sregs.fs); + print_seg(stderr, "gs", &sregs.gs); + print_seg(stderr, "tr", &sregs.tr); + print_seg(stderr, "ldt", &sregs.ldt); + print_dt(stderr, "gdt", &sregs.gdt); + print_dt(stderr, "idt", &sregs.idt); + fprintf(stderr, "cr0 %llx cr2 %llx cr3 %llx cr4 %llx cr8 %llx" + " efer %llx\n", + sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4, sregs.cr8, + sregs.efer); +} + +uint64_t kvm_get_apic_base(kvm_context_t kvm, int vcpu) +{ + struct kvm_run *run = kvm->run[vcpu]; + + return run->apic_base; +} + +void kvm_set_cr8(kvm_context_t kvm, int vcpu, uint64_t cr8) +{ + struct kvm_run *run = kvm->run[vcpu]; + + run->cr8 = cr8; +} + +__u64 kvm_get_cr8(kvm_context_t kvm, int vcpu) +{ + return kvm->run[vcpu]->cr8; +} + +int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent, + struct kvm_cpuid_entry *entries) +{ + struct kvm_cpuid *cpuid; + int r; + + cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries)); + if (!cpuid) + return -ENOMEM; + + cpuid->nent = nent; + memcpy(cpuid->entries, entries, nent * sizeof(*entries)); + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid); + + free(cpuid); + return r; +} + +int kvm_setup_cpuid2(kvm_context_t kvm, int vcpu, int nent, + struct kvm_cpuid_entry2 *entries) +{ + struct kvm_cpuid2 *cpuid; + int r; + + cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries)); + if (!cpuid) + return -ENOMEM; + + cpuid->nent = nent; + memcpy(cpuid->entries, entries, nent * sizeof(*entries)); + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID2, cpuid); + if (r == -1) { + fprintf(stderr, "kvm_setup_cpuid2: %m\n"); + r = -errno; + } + free(cpuid); + return r; +} + +int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages) +{ +#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, + KVM_CAP_MMU_SHADOW_CACHE_CONTROL); + if (r > 0) { + r = ioctl(kvm->vm_fd, KVM_SET_NR_MMU_PAGES, nrshadow_pages); + if (r == -1) { + fprintf(stderr, "kvm_set_shadow_pages: %m\n"); + return -errno; + } + return 0; + } +#endif + return -1; +} + +int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages) +{ +#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, + KVM_CAP_MMU_SHADOW_CACHE_CONTROL); + if (r > 0) { + *nrshadow_pages = ioctl(kvm->vm_fd, KVM_GET_NR_MMU_PAGES); + return 0; + } +#endif + return -1; +} + +#ifdef KVM_CAP_VAPIC + +static int tpr_access_reporting(kvm_context_t kvm, int vcpu, int enabled) +{ + int r; + struct kvm_tpr_access_ctl tac = { + .enabled = enabled, + }; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC); + if (r == -1 || r == 0) + return -ENOSYS; + r = ioctl(kvm->vcpu_fd[vcpu], KVM_TPR_ACCESS_REPORTING, &tac); + if (r == -1) { + r = -errno; + perror("KVM_TPR_ACCESS_REPORTING"); + return r; + } + return 0; +} + +int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu) +{ + return tpr_access_reporting(kvm, vcpu, 1); +} + +int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu) +{ + return tpr_access_reporting(kvm, vcpu, 0); +} + +#endif + +#ifdef KVM_CAP_EXT_CPUID + +static struct kvm_cpuid2 *try_get_cpuid(kvm_context_t kvm, int max) +{ + struct kvm_cpuid2 *cpuid; + int r, size; + + size = sizeof(*cpuid) + max * sizeof(*cpuid->entries); + cpuid = (struct kvm_cpuid2 *)malloc(size); + cpuid->nent = max; + r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, cpuid); + if (r == -1) + r = -errno; + else if (r == 0 && cpuid->nent >= max) + r = -E2BIG; + if (r < 0) { + if (r == -E2BIG) { + free(cpuid); + return NULL; + } else { + fprintf(stderr, "KVM_GET_SUPPORTED_CPUID failed: %s\n", + strerror(-r)); + exit(1); + } + } + return cpuid; +} + +#define R_EAX 0 +#define R_ECX 1 +#define R_EDX 2 +#define R_EBX 3 +#define R_ESP 4 +#define R_EBP 5 +#define R_ESI 6 +#define R_EDI 7 + +uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg) +{ + struct kvm_cpuid2 *cpuid; + int i, max; + uint32_t ret = 0; + uint32_t cpuid_1_edx; + + if (!kvm_check_extension(kvm, KVM_CAP_EXT_CPUID)) { + return -1U; + } + + max = 1; + while ((cpuid = try_get_cpuid(kvm, max)) == NULL) { + max *= 2; + } + + for (i = 0; i < cpuid->nent; ++i) { + if (cpuid->entries[i].function == function) { + switch (reg) { + case R_EAX: + ret = cpuid->entries[i].eax; + break; + case R_EBX: + ret = cpuid->entries[i].ebx; + break; + case R_ECX: + ret = cpuid->entries[i].ecx; + break; + case R_EDX: + ret = cpuid->entries[i].edx; + if (function == 1) { + /* kvm misreports the following features + */ + ret |= 1 << 12; /* MTRR */ + ret |= 1 << 16; /* PAT */ + ret |= 1 << 7; /* MCE */ + ret |= 1 << 14; /* MCA */ + } + + /* On Intel, kvm returns cpuid according to + * the Intel spec, so add missing bits + * according to the AMD spec: + */ + if (function == 0x80000001) { + cpuid_1_edx = kvm_get_supported_cpuid(kvm, 1, R_EDX); + ret |= cpuid_1_edx & 0xdfeff7ff; + } + break; + } + } + } + + free(cpuid); + + return ret; +} + +#else + +uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg) +{ + return -1U; +} + +#endif diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c new file mode 100644 index 000000000..c5d6a7f5e --- /dev/null +++ b/kvm/libkvm/libkvm.c @@ -0,0 +1,1497 @@ +/* + * Kernel-based Virtual Machine control library + * + * This library provides an API to control the kvm hardware virtualization + * module. + * + * Copyright (C) 2006 Qumranet + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef __user +#define __user /* temporary, until installed via make headers_install */ +#endif + +#include <linux/kvm.h> + +#define EXPECTED_KVM_API_VERSION 12 + +#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION +#error libkvm: userspace and kernel version mismatch +#endif + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <inttypes.h> +#include "libkvm.h" + +#if defined(__x86_64__) || defined(__i386__) +#include "kvm-x86.h" +#endif + +#if defined(__ia64__) +#include "kvm-ia64.h" +#endif + +#if defined(__powerpc__) +#include "kvm-powerpc.h" +#endif + +#if defined(__s390__) +#include "kvm-s390.h" +#endif + +//#define DEBUG_MEMREG +#ifdef DEBUG_MEMREG +#define DPRINTF(fmt, args...) \ + do { fprintf(stderr, "%s:%d " fmt , __func__, __LINE__, ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while (0) +#endif + +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) + +int kvm_abi = EXPECTED_KVM_API_VERSION; +int kvm_page_size; + +static inline void set_gsi(kvm_context_t kvm, unsigned int gsi) +{ + uint32_t *bitmap = kvm->used_gsi_bitmap; + + if (gsi < kvm->max_gsi) + bitmap[gsi / 32] |= 1U << (gsi % 32); + else + DPRINTF("Invalid GSI %d\n"); +} + +static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi) +{ + uint32_t *bitmap = kvm->used_gsi_bitmap; + + if (gsi < kvm->max_gsi) + bitmap[gsi / 32] &= ~(1U << (gsi % 32)); + else + DPRINTF("Invalid GSI %d\n"); +} + +struct slot_info { + unsigned long phys_addr; + unsigned long len; + unsigned long userspace_addr; + unsigned flags; + int logging_count; +}; + +struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS]; + +static void init_slots(void) +{ + int i; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) + slots[i].len = 0; +} + +static int get_free_slot(kvm_context_t kvm) +{ + int i; + int tss_ext; + +#if defined(KVM_CAP_SET_TSS_ADDR) && !defined(__s390__) + tss_ext = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); +#else + tss_ext = 0; +#endif + + /* + * on older kernels where the set tss ioctl is not supprted we must save + * slot 0 to hold the extended memory, as the vmx will use the last 3 + * pages of this slot. + */ + if (tss_ext > 0) + i = 0; + else + i = 1; + + for (; i < KVM_MAX_NUM_MEM_REGIONS; ++i) + if (!slots[i].len) + return i; + return -1; +} + +static void register_slot(int slot, unsigned long phys_addr, unsigned long len, + unsigned long userspace_addr, unsigned flags) +{ + slots[slot].phys_addr = phys_addr; + slots[slot].len = len; + slots[slot].userspace_addr = userspace_addr; + slots[slot].flags = flags; +} + +static void free_slot(int slot) +{ + slots[slot].len = 0; + slots[slot].logging_count = 0; +} + +static int get_slot(unsigned long phys_addr) +{ + int i; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS ; ++i) { + if (slots[i].len && slots[i].phys_addr <= phys_addr && + (slots[i].phys_addr + slots[i].len-1) >= phys_addr) + return i; + } + return -1; +} + +/* Returns -1 if this slot is not totally contained on any other, + * and the number of the slot otherwise */ +static int get_container_slot(uint64_t phys_addr, unsigned long size) +{ + int i; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS ; ++i) + if (slots[i].len && slots[i].phys_addr <= phys_addr && + (slots[i].phys_addr + slots[i].len) >= phys_addr + size) + return i; + return -1; +} + +int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_addr, unsigned long size) +{ + int slot = get_container_slot(phys_addr, size); + if (slot == -1) + return 0; + return 1; +} + +/* + * dirty pages logging control + */ +static int kvm_dirty_pages_log_change(kvm_context_t kvm, + unsigned long phys_addr, + unsigned flags, + unsigned mask) +{ + int r = -1; + int slot = get_slot(phys_addr); + + if (slot == -1) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__); + return 1; + } + + flags = (slots[slot].flags & ~mask) | flags; + if (flags == slots[slot].flags) + return 0; + slots[slot].flags = flags; + + { + struct kvm_userspace_memory_region mem = { + .slot = slot, + .memory_size = slots[slot].len, + .guest_phys_addr = slots[slot].phys_addr, + .userspace_addr = slots[slot].userspace_addr, + .flags = slots[slot].flags, + }; + + + DPRINTF("slot %d start %llx len %llx flags %x\n", + mem.slot, + mem.guest_phys_addr, + mem.memory_size, + mem.flags); + r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem); + if (r == -1) + fprintf(stderr, "%s: %m\n", __FUNCTION__); + } + return r; +} + +static int kvm_dirty_pages_log_change_all(kvm_context_t kvm, + int (*change)(kvm_context_t kvm, + uint64_t start, + uint64_t len)) +{ + int i, r; + + for (i=r=0; i<KVM_MAX_NUM_MEM_REGIONS && r==0; i++) { + if (slots[i].len) + r = change(kvm, slots[i].phys_addr, slots[i].len); + } + return r; +} + +int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, + uint64_t phys_addr, + uint64_t len) +{ + int slot = get_slot(phys_addr); + + DPRINTF("start %"PRIx64" len %"PRIx64"\n", phys_addr, len); + if (slot == -1) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); + return -EINVAL; + } + + if (slots[slot].logging_count++) + return 0; + + return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr, + KVM_MEM_LOG_DIRTY_PAGES, + KVM_MEM_LOG_DIRTY_PAGES); +} + +int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, + uint64_t phys_addr, + uint64_t len) +{ + int slot = get_slot(phys_addr); + + if (slot == -1) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); + return -EINVAL; + } + + if (--slots[slot].logging_count) + return 0; + + return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr, + 0, + KVM_MEM_LOG_DIRTY_PAGES); +} + +/** + * Enable dirty page logging for all memory regions + */ +int kvm_dirty_pages_log_enable_all(kvm_context_t kvm) +{ + if (kvm->dirty_pages_log_all) + return 0; + kvm->dirty_pages_log_all = 1; + return kvm_dirty_pages_log_change_all(kvm, + kvm_dirty_pages_log_enable_slot); +} + +/** + * Enable dirty page logging only for memory regions that were created with + * dirty logging enabled (disable for all other memory regions). + */ +int kvm_dirty_pages_log_reset(kvm_context_t kvm) +{ + if (!kvm->dirty_pages_log_all) + return 0; + kvm->dirty_pages_log_all = 0; + return kvm_dirty_pages_log_change_all(kvm, + kvm_dirty_pages_log_disable_slot); +} + + +kvm_context_t kvm_init(struct kvm_callbacks *callbacks, + void *opaque) +{ + int fd; + kvm_context_t kvm; + int r, gsi_count; + + fd = open("/dev/kvm", O_RDWR); + if (fd == -1) { + perror("open /dev/kvm"); + return NULL; + } + r = ioctl(fd, KVM_GET_API_VERSION, 0); + if (r == -1) { + fprintf(stderr, "kvm kernel version too old: " + "KVM_GET_API_VERSION ioctl not supported\n"); + goto out_close; + } + if (r < EXPECTED_KVM_API_VERSION) { + fprintf(stderr, "kvm kernel version too old: " + "We expect API version %d or newer, but got " + "version %d\n", + EXPECTED_KVM_API_VERSION, r); + goto out_close; + } + if (r > EXPECTED_KVM_API_VERSION) { + fprintf(stderr, "kvm userspace version too old\n"); + goto out_close; + } + kvm_abi = r; + kvm_page_size = getpagesize(); + kvm = malloc(sizeof(*kvm)); + if (kvm == NULL) + goto out_close; + memset(kvm, 0, sizeof(*kvm)); + kvm->fd = fd; + kvm->vm_fd = -1; + kvm->callbacks = callbacks; + kvm->opaque = opaque; + kvm->dirty_pages_log_all = 0; + kvm->no_irqchip_creation = 0; + kvm->no_pit_creation = 0; + + gsi_count = kvm_get_gsi_count(kvm); + if (gsi_count > 0) { + int gsi_bits, i; + + /* Round up so we can search ints using ffs */ + gsi_bits = ALIGN(gsi_count, 32); + kvm->used_gsi_bitmap = malloc(gsi_bits / 8); + if (!kvm->used_gsi_bitmap) + goto out_close; + memset(kvm->used_gsi_bitmap, 0, gsi_bits / 8); + kvm->max_gsi = gsi_bits; + + /* Mark any over-allocated bits as already in use */ + for (i = gsi_count; i < gsi_bits; i++) + set_gsi(kvm, i); + } + + return kvm; + out_close: + close(fd); + return NULL; +} + +void kvm_finalize(kvm_context_t kvm) +{ + if (kvm->vcpu_fd[0] != -1) + close(kvm->vcpu_fd[0]); + if (kvm->vm_fd != -1) + close(kvm->vm_fd); + close(kvm->fd); + free(kvm); +} + +void kvm_disable_irqchip_creation(kvm_context_t kvm) +{ + kvm->no_irqchip_creation = 1; +} + +void kvm_disable_pit_creation(kvm_context_t kvm) +{ + kvm->no_pit_creation = 1; +} + +int kvm_create_vcpu(kvm_context_t kvm, int slot) +{ + long mmap_size; + int r; + + r = ioctl(kvm->vm_fd, KVM_CREATE_VCPU, slot); + if (r == -1) { + r = -errno; + fprintf(stderr, "kvm_create_vcpu: %m\n"); + return r; + } + kvm->vcpu_fd[slot] = r; + mmap_size = ioctl(kvm->fd, KVM_GET_VCPU_MMAP_SIZE, 0); + if (mmap_size == -1) { + r = -errno; + fprintf(stderr, "get vcpu mmap size: %m\n"); + return r; + } + kvm->run[slot] = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, + kvm->vcpu_fd[slot], 0); + if (kvm->run[slot] == MAP_FAILED) { + r = -errno; + fprintf(stderr, "mmap vcpu area: %m\n"); + return r; + } + return 0; +} + +int kvm_create_vm(kvm_context_t kvm) +{ + int fd = kvm->fd; + +#ifdef KVM_CAP_IRQ_ROUTING + kvm->irq_routes = malloc(sizeof(*kvm->irq_routes)); + if (!kvm->irq_routes) + return -ENOMEM; + memset(kvm->irq_routes, 0, sizeof(*kvm->irq_routes)); + kvm->nr_allocated_irq_routes = 0; +#endif + + kvm->vcpu_fd[0] = -1; + + fd = ioctl(fd, KVM_CREATE_VM, 0); + if (fd == -1) { + fprintf(stderr, "kvm_create_vm: %m\n"); + return -1; + } + kvm->vm_fd = fd; + return 0; +} + +static int kvm_create_default_phys_mem(kvm_context_t kvm, + unsigned long phys_mem_bytes, + void **vm_mem) +{ +#ifdef KVM_CAP_USER_MEMORY + int r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY); + if (r > 0) + return 0; + fprintf(stderr, "Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported\n"); +#else +#error Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported +#endif + return -1; +} + +int kvm_check_extension(kvm_context_t kvm, int ext) +{ + int ret; + + ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, ext); + if (ret > 0) + return ret; + return 0; +} + +void kvm_create_irqchip(kvm_context_t kvm) +{ + int r; + + kvm->irqchip_in_kernel = 0; +#ifdef KVM_CAP_IRQCHIP + if (!kvm->no_irqchip_creation) { + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_IRQCHIP); + if (r > 0) { /* kernel irqchip supported */ + r = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP); + if (r >= 0) { + kvm->irqchip_inject_ioctl = KVM_IRQ_LINE; +#if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS) + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, + KVM_CAP_IRQ_INJECT_STATUS); + if (r > 0) + kvm->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; +#endif + kvm->irqchip_in_kernel = 1; + } + else + fprintf(stderr, "Create kernel PIC irqchip failed\n"); + } + } +#endif +} + +int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem) +{ + int r; + + r = kvm_create_vm(kvm); + if (r < 0) + return r; + r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem); + if (r < 0) + return r; + init_slots(); + r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem); + if (r < 0) + return r; + kvm_create_irqchip(kvm); + + return 0; +} + + +void *kvm_create_phys_mem(kvm_context_t kvm, unsigned long phys_start, + unsigned long len, int log, int writable) +{ + int r; + int prot = PROT_READ; + void *ptr; + struct kvm_userspace_memory_region memory = { + .memory_size = len, + .guest_phys_addr = phys_start, + .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0, + }; + + if (writable) + prot |= PROT_WRITE; + +#if !defined(__s390__) + ptr = mmap(NULL, len, prot, MAP_ANONYMOUS | MAP_SHARED, -1, 0); +#else + ptr = mmap(LIBKVM_S390_ORIGIN, len, prot | PROT_EXEC, + MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0); +#endif + if (ptr == MAP_FAILED) { + fprintf(stderr, "%s: %s", __func__, strerror(errno)); + return 0; + } + + memset(ptr, 0, len); + + memory.userspace_addr = (unsigned long)ptr; + memory.slot = get_free_slot(kvm); + DPRINTF("slot %d start %llx len %llx flags %x\n", + memory.slot, + memory.guest_phys_addr, + memory.memory_size, + memory.flags); + r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory); + if (r == -1) { + fprintf(stderr, "%s: %s", __func__, strerror(errno)); + return 0; + } + register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size, + memory.userspace_addr, memory.flags); + + return ptr; +} + +int kvm_register_phys_mem(kvm_context_t kvm, + unsigned long phys_start, void *userspace_addr, + unsigned long len, int log) +{ + + struct kvm_userspace_memory_region memory = { + .memory_size = len, + .guest_phys_addr = phys_start, + .userspace_addr = (unsigned long)(intptr_t)userspace_addr, + .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0, + }; + int r; + + memory.slot = get_free_slot(kvm); + DPRINTF("memory: gpa: %llx, size: %llx, uaddr: %llx, slot: %x, flags: %lx\n", + memory.guest_phys_addr, memory.memory_size, + memory.userspace_addr, memory.slot, memory.flags); + r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory); + if (r == -1) { + fprintf(stderr, "create_userspace_phys_mem: %s\n", strerror(errno)); + return -1; + } + register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size, + memory.userspace_addr, memory.flags); + return 0; +} + + +/* destroy/free a whole slot. + * phys_start, len and slot are the params passed to kvm_create_phys_mem() + */ +void kvm_destroy_phys_mem(kvm_context_t kvm, unsigned long phys_start, + unsigned long len) +{ + int slot; + int r; + struct kvm_userspace_memory_region memory = { + .memory_size = 0, + .guest_phys_addr = phys_start, + .userspace_addr = 0, + .flags = 0, + }; + + slot = get_slot(phys_start); + + if ((slot >= KVM_MAX_NUM_MEM_REGIONS) || (slot == -1)) { + fprintf(stderr, "BUG: %s: invalid parameters (slot=%d)\n", + __FUNCTION__, slot); + return; + } + if (phys_start != slots[slot].phys_addr) { + fprintf(stderr, + "WARNING: %s: phys_start is 0x%lx expecting 0x%lx\n", + __FUNCTION__, phys_start, slots[slot].phys_addr); + phys_start = slots[slot].phys_addr; + } + + memory.slot = slot; + DPRINTF("slot %d start %llx len %llx flags %x\n", + memory.slot, + memory.guest_phys_addr, + memory.memory_size, + memory.flags); + r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory); + if (r == -1) { + fprintf(stderr, "destroy_userspace_phys_mem: %s", + strerror(errno)); + return; + } + + free_slot(memory.slot); +} + +void kvm_unregister_memory_area(kvm_context_t kvm, uint64_t phys_addr, unsigned long size) +{ + + int slot = get_container_slot(phys_addr, size); + + if (slot != -1) { + DPRINTF("Unregistering memory region %llx (%lx)\n", phys_addr, size); + kvm_destroy_phys_mem(kvm, phys_addr, size); + return; + } +} + +static int kvm_get_map(kvm_context_t kvm, int ioctl_num, int slot, void *buf) +{ + int r; + struct kvm_dirty_log log = { + .slot = slot, + }; + + log.dirty_bitmap = buf; + + r = ioctl(kvm->vm_fd, ioctl_num, &log); + if (r == -1) + return -errno; + return 0; +} + +int kvm_get_dirty_pages(kvm_context_t kvm, unsigned long phys_addr, void *buf) +{ + int slot; + + slot = get_slot(phys_addr); + return kvm_get_map(kvm, KVM_GET_DIRTY_LOG, slot, buf); +} + +int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr, + unsigned long len, void *buf, void *opaque, + int (*cb)(unsigned long start, unsigned long len, + void*bitmap, void *opaque)) +{ + int i; + int r; + unsigned long end_addr = phys_addr + len; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) { + if ((slots[i].len && (uint64_t)slots[i].phys_addr >= phys_addr) + && ((uint64_t)slots[i].phys_addr + slots[i].len <= end_addr)) { + r = kvm_get_map(kvm, KVM_GET_DIRTY_LOG, i, buf); + if (r) + return r; + r = cb(slots[i].phys_addr, slots[i].len, buf, opaque); + if (r) + return r; + } + } + return 0; +} + +#ifdef KVM_CAP_IRQCHIP + +int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status) +{ + struct kvm_irq_level event; + int r; + + if (!kvm->irqchip_in_kernel) + return 0; + event.level = level; + event.irq = irq; + r = ioctl(kvm->vm_fd, kvm->irqchip_inject_ioctl, &event); + if (r == -1) + perror("kvm_set_irq_level"); + + if (status) { +#ifdef KVM_CAP_IRQ_INJECT_STATUS + *status = (kvm->irqchip_inject_ioctl == KVM_IRQ_LINE) ? + 1 : event.status; +#else + *status = 1; +#endif + } + + return 1; +} + +int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip) +{ + int r; + + if (!kvm->irqchip_in_kernel) + return 0; + r = ioctl(kvm->vm_fd, KVM_GET_IRQCHIP, chip); + if (r == -1) { + r = -errno; + perror("kvm_get_irqchip\n"); + } + return r; +} + +int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip) +{ + int r; + + if (!kvm->irqchip_in_kernel) + return 0; + r = ioctl(kvm->vm_fd, KVM_SET_IRQCHIP, chip); + if (r == -1) { + r = -errno; + perror("kvm_set_irqchip\n"); + } + return r; +} + +#endif + +static int handle_io(kvm_context_t kvm, struct kvm_run *run, int vcpu) +{ + uint16_t addr = run->io.port; + int r; + int i; + void *p = (void *)run + run->io.data_offset; + + for (i = 0; i < run->io.count; ++i) { + switch (run->io.direction) { + case KVM_EXIT_IO_IN: + switch (run->io.size) { + case 1: + r = kvm->callbacks->inb(kvm->opaque, addr, p); + break; + case 2: + r = kvm->callbacks->inw(kvm->opaque, addr, p); + break; + case 4: + r = kvm->callbacks->inl(kvm->opaque, addr, p); + break; + default: + fprintf(stderr, "bad I/O size %d\n", run->io.size); + return -EMSGSIZE; + } + break; + case KVM_EXIT_IO_OUT: + switch (run->io.size) { + case 1: + r = kvm->callbacks->outb(kvm->opaque, addr, + *(uint8_t *)p); + break; + case 2: + r = kvm->callbacks->outw(kvm->opaque, addr, + *(uint16_t *)p); + break; + case 4: + r = kvm->callbacks->outl(kvm->opaque, addr, + *(uint32_t *)p); + break; + default: + fprintf(stderr, "bad I/O size %d\n", run->io.size); + return -EMSGSIZE; + } + break; + default: + fprintf(stderr, "bad I/O direction %d\n", run->io.direction); + return -EPROTO; + } + + p += run->io.size; + } + + return 0; +} + +int handle_debug(kvm_context_t kvm, int vcpu, void *env) +{ +#ifdef KVM_CAP_SET_GUEST_DEBUG + struct kvm_run *run = kvm->run[vcpu]; + + return kvm->callbacks->debug(kvm->opaque, env, &run->debug.arch); +#else + return 0; +#endif +} + +int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_REGS, regs); +} + +int kvm_set_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_REGS, regs); +} + +int kvm_get_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_FPU, fpu); +} + +int kvm_set_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_FPU, fpu); +} + +int kvm_get_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *sregs) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_SREGS, sregs); +} + +int kvm_set_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *sregs) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SREGS, sregs); +} + +#ifdef KVM_CAP_MP_STATE +int kvm_get_mpstate(kvm_context_t kvm, int vcpu, struct kvm_mp_state *mp_state) +{ + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); + if (r > 0) + return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_MP_STATE, mp_state); + return -ENOSYS; +} + +int kvm_set_mpstate(kvm_context_t kvm, int vcpu, struct kvm_mp_state *mp_state) +{ + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); + if (r > 0) + return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_MP_STATE, mp_state); + return -ENOSYS; +} +#endif + +static int handle_mmio(kvm_context_t kvm, struct kvm_run *kvm_run) +{ + unsigned long addr = kvm_run->mmio.phys_addr; + void *data = kvm_run->mmio.data; + + /* hack: Red Hat 7.1 generates these weird accesses. */ + if ((addr > 0xa0000-4 && addr <= 0xa0000) && kvm_run->mmio.len == 3) + return 0; + + if (kvm_run->mmio.is_write) + return kvm->callbacks->mmio_write(kvm->opaque, addr, data, + kvm_run->mmio.len); + else + return kvm->callbacks->mmio_read(kvm->opaque, addr, data, + kvm_run->mmio.len); +} + +int handle_io_window(kvm_context_t kvm) +{ + return kvm->callbacks->io_window(kvm->opaque); +} + +int handle_halt(kvm_context_t kvm, int vcpu) +{ + return kvm->callbacks->halt(kvm->opaque, vcpu); +} + +int handle_shutdown(kvm_context_t kvm, void *env) +{ + return kvm->callbacks->shutdown(kvm->opaque, env); +} + +int try_push_interrupts(kvm_context_t kvm) +{ + return kvm->callbacks->try_push_interrupts(kvm->opaque); +} + +static inline void push_nmi(kvm_context_t kvm) +{ +#ifdef KVM_CAP_USER_NMI + kvm->callbacks->push_nmi(kvm->opaque); +#endif /* KVM_CAP_USER_NMI */ +} + +void post_kvm_run(kvm_context_t kvm, void *env) +{ + kvm->callbacks->post_kvm_run(kvm->opaque, env); +} + +int pre_kvm_run(kvm_context_t kvm, void *env) +{ + return kvm->callbacks->pre_kvm_run(kvm->opaque, env); +} + +int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu) +{ + struct kvm_run *run = kvm->run[vcpu]; + + return run->if_flag; +} + +int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu) +{ + struct kvm_run *run = kvm->run[vcpu]; + + return run->ready_for_interrupt_injection; +} + +int kvm_run(kvm_context_t kvm, int vcpu, void *env) +{ + int r; + int fd = kvm->vcpu_fd[vcpu]; + struct kvm_run *run = kvm->run[vcpu]; + +again: + push_nmi(kvm); +#if !defined(__s390__) + if (!kvm->irqchip_in_kernel) + run->request_interrupt_window = try_push_interrupts(kvm); +#endif + r = pre_kvm_run(kvm, env); + if (r) + return r; + r = ioctl(fd, KVM_RUN, 0); + + if (r == -1 && errno != EINTR && errno != EAGAIN) { + r = -errno; + post_kvm_run(kvm, env); + fprintf(stderr, "kvm_run: %s\n", strerror(-r)); + return r; + } + + post_kvm_run(kvm, env); + +#if defined(KVM_CAP_COALESCED_MMIO) + if (kvm->coalesced_mmio) { + struct kvm_coalesced_mmio_ring *ring = (void *)run + + kvm->coalesced_mmio * PAGE_SIZE; + while (ring->first != ring->last) { + kvm->callbacks->mmio_write(kvm->opaque, + ring->coalesced_mmio[ring->first].phys_addr, + &ring->coalesced_mmio[ring->first].data[0], + ring->coalesced_mmio[ring->first].len); + smp_wmb(); + ring->first = (ring->first + 1) % + KVM_COALESCED_MMIO_MAX; + } + } +#endif + +#if !defined(__s390__) + if (r == -1) { + r = handle_io_window(kvm); + goto more; + } +#endif + if (1) { + switch (run->exit_reason) { + case KVM_EXIT_UNKNOWN: + fprintf(stderr, "unhandled vm exit: 0x%x vcpu_id %d\n", + (unsigned)run->hw.hardware_exit_reason, vcpu); + kvm_show_regs(kvm, vcpu); + abort(); + break; + case KVM_EXIT_FAIL_ENTRY: + fprintf(stderr, "kvm_run: failed entry, reason %u\n", + (unsigned)run->fail_entry.hardware_entry_failure_reason & 0xffff); + kvm_show_regs(kvm, vcpu); + return -ENOEXEC; + break; + case KVM_EXIT_EXCEPTION: + fprintf(stderr, "exception %d (%x)\n", + run->ex.exception, + run->ex.error_code); + kvm_show_regs(kvm, vcpu); + kvm_show_code(kvm, vcpu); + abort(); + break; + case KVM_EXIT_IO: + r = handle_io(kvm, run, vcpu); + break; + case KVM_EXIT_DEBUG: + r = handle_debug(kvm, vcpu, env); + break; + case KVM_EXIT_MMIO: + r = handle_mmio(kvm, run); + break; + case KVM_EXIT_HLT: + r = handle_halt(kvm, vcpu); + break; + case KVM_EXIT_IRQ_WINDOW_OPEN: + break; + case KVM_EXIT_SHUTDOWN: + r = handle_shutdown(kvm, env); + break; +#if defined(__s390__) + case KVM_EXIT_S390_SIEIC: + r = kvm->callbacks->s390_handle_intercept(kvm, vcpu, + run); + break; + case KVM_EXIT_S390_RESET: + r = kvm->callbacks->s390_handle_reset(kvm, vcpu, run); + break; +#endif + default: + if (kvm_arch_run(run, kvm, vcpu)) { + fprintf(stderr, "unhandled vm exit: 0x%x\n", + run->exit_reason); + kvm_show_regs(kvm, vcpu); + abort(); + } + break; + } + } +more: + if (!r) + goto again; + return r; +} + +int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq) +{ + struct kvm_interrupt intr; + + intr.irq = irq; + return ioctl(kvm->vcpu_fd[vcpu], KVM_INTERRUPT, &intr); +} + +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_set_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_guest_debug *dbg) +{ + return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_GUEST_DEBUG, dbg); +} +#endif + +int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset) +{ + struct kvm_signal_mask *sigmask; + int r; + + if (!sigset) { + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SIGNAL_MASK, NULL); + if (r == -1) + r = -errno; + return r; + } + sigmask = malloc(sizeof(*sigmask) + sizeof(*sigset)); + if (!sigmask) + return -ENOMEM; + + sigmask->len = 8; + memcpy(sigmask->sigset, sigset, sizeof(*sigset)); + r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SIGNAL_MASK, sigmask); + if (r == -1) + r = -errno; + free(sigmask); + return r; +} + +int kvm_irqchip_in_kernel(kvm_context_t kvm) +{ + return kvm->irqchip_in_kernel; +} + +int kvm_pit_in_kernel(kvm_context_t kvm) +{ + return kvm->pit_in_kernel; +} + +int kvm_has_sync_mmu(kvm_context_t kvm) +{ + int r = 0; +#ifdef KVM_CAP_SYNC_MMU + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU); +#endif + return r; +} + +int kvm_inject_nmi(kvm_context_t kvm, int vcpu) +{ +#ifdef KVM_CAP_USER_NMI + return ioctl(kvm->vcpu_fd[vcpu], KVM_NMI); +#else + return -ENOSYS; +#endif +} + +int kvm_init_coalesced_mmio(kvm_context_t kvm) +{ + int r = 0; + kvm->coalesced_mmio = 0; +#ifdef KVM_CAP_COALESCED_MMIO + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO); + if (r > 0) { + kvm->coalesced_mmio = r; + return 0; + } +#endif + return r; +} + +int kvm_register_coalesced_mmio(kvm_context_t kvm, uint64_t addr, uint32_t size) +{ +#ifdef KVM_CAP_COALESCED_MMIO + struct kvm_coalesced_mmio_zone zone; + int r; + + if (kvm->coalesced_mmio) { + + zone.addr = addr; + zone.size = size; + + r = ioctl(kvm->vm_fd, KVM_REGISTER_COALESCED_MMIO, &zone); + if (r == -1) { + perror("kvm_register_coalesced_mmio_zone"); + return -errno; + } + return 0; + } +#endif + return -ENOSYS; +} + +int kvm_unregister_coalesced_mmio(kvm_context_t kvm, uint64_t addr, uint32_t size) +{ +#ifdef KVM_CAP_COALESCED_MMIO + struct kvm_coalesced_mmio_zone zone; + int r; + + if (kvm->coalesced_mmio) { + + zone.addr = addr; + zone.size = size; + + r = ioctl(kvm->vm_fd, KVM_UNREGISTER_COALESCED_MMIO, &zone); + if (r == -1) { + perror("kvm_unregister_coalesced_mmio_zone"); + return -errno; + } + DPRINTF("Unregistered coalesced mmio region for %llx (%lx)\n", addr, size); + return 0; + } +#endif + return -ENOSYS; +} + +#ifdef KVM_CAP_DEVICE_ASSIGNMENT +int kvm_assign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev) +{ + int ret; + + ret = ioctl(kvm->vm_fd, KVM_ASSIGN_PCI_DEVICE, assigned_dev); + if (ret < 0) + return -errno; + + return ret; +} + +static int kvm_old_assign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq) +{ + int ret; + + ret = ioctl(kvm->vm_fd, KVM_ASSIGN_IRQ, assigned_irq); + if (ret < 0) + return -errno; + + return ret; +} + +#ifdef KVM_CAP_ASSIGN_DEV_IRQ +int kvm_assign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq) +{ + int ret; + + ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ); + if (ret > 0) { + ret = ioctl(kvm->vm_fd, KVM_ASSIGN_DEV_IRQ, assigned_irq); + if (ret < 0) + return -errno; + return ret; + } + + return kvm_old_assign_irq(kvm, assigned_irq); +} + +int kvm_deassign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq) +{ + int ret; + + ret = ioctl(kvm->vm_fd, KVM_DEASSIGN_DEV_IRQ, assigned_irq); + if (ret < 0) + return -errno; + + return ret; +} +#else +int kvm_assign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq) +{ + return kvm_old_assign_irq(kvm, assigned_irq); +} +#endif +#endif + +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT +int kvm_deassign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev) +{ + int ret; + + ret = ioctl(kvm->vm_fd, KVM_DEASSIGN_PCI_DEVICE, assigned_dev); + if (ret < 0) + return -errno; + + return ret; +} +#endif + +int kvm_destroy_memory_region_works(kvm_context_t kvm) +{ + int ret = 0; + +#ifdef KVM_CAP_DESTROY_MEMORY_REGION_WORKS + ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, + KVM_CAP_DESTROY_MEMORY_REGION_WORKS); + if (ret <= 0) + ret = 0; +#endif + return ret; +} + +int kvm_reinject_control(kvm_context_t kvm, int pit_reinject) +{ +#ifdef KVM_CAP_REINJECT_CONTROL + int r; + struct kvm_reinject_control control; + + control.pit_reinject = pit_reinject; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL); + if (r > 0) { + r = ioctl(kvm->vm_fd, KVM_REINJECT_CONTROL, &control); + if (r == -1) + return -errno; + return r; + } +#endif + return -ENOSYS; +} + +int kvm_has_gsi_routing(kvm_context_t kvm) +{ + int r = 0; + +#ifdef KVM_CAP_IRQ_ROUTING + r = kvm_check_extension(kvm, KVM_CAP_IRQ_ROUTING); +#endif + return r; +} + +int kvm_get_gsi_count(kvm_context_t kvm) +{ +#ifdef KVM_CAP_IRQ_ROUTING + return kvm_check_extension(kvm, KVM_CAP_IRQ_ROUTING); +#else + return -EINVAL; +#endif +} + +int kvm_clear_gsi_routes(kvm_context_t kvm) +{ +#ifdef KVM_CAP_IRQ_ROUTING + kvm->irq_routes->nr = 0; + return 0; +#else + return -EINVAL; +#endif +} + +int kvm_add_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry* entry) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing *z; + struct kvm_irq_routing_entry *new; + int n, size; + + if (kvm->irq_routes->nr == kvm->nr_allocated_irq_routes) { + n = kvm->nr_allocated_irq_routes * 2; + if (n < 64) + n = 64; + size = sizeof(struct kvm_irq_routing); + size += n * sizeof(*new); + z = realloc(kvm->irq_routes, size); + if (!z) + return -ENOMEM; + kvm->nr_allocated_irq_routes = n; + kvm->irq_routes = z; + } + n = kvm->irq_routes->nr++; + new = &kvm->irq_routes->entries[n]; + memset(new, 0, sizeof(*new)); + new->gsi = entry->gsi; + new->type = entry->type; + new->flags = entry->flags; + new->u = entry->u; + + set_gsi(kvm, entry->gsi); + + return 0; +#else + return -ENOSYS; +#endif +} + +int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry e; + + e.gsi = gsi; + e.type = KVM_IRQ_ROUTING_IRQCHIP; + e.flags = 0; + e.u.irqchip.irqchip = irqchip; + e.u.irqchip.pin = pin; + return kvm_add_routing_entry(kvm, &e); +#else + return -ENOSYS; +#endif +} + +int kvm_del_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry* entry) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry *e, *p; + int i, gsi, found = 0; + + gsi = entry->gsi; + + for (i = 0; i < kvm->irq_routes->nr; ++i) { + e = &kvm->irq_routes->entries[i]; + if (e->type == entry->type + && e->gsi == gsi) { + switch (e->type) + { + case KVM_IRQ_ROUTING_IRQCHIP: { + if (e->u.irqchip.irqchip == + entry->u.irqchip.irqchip + && e->u.irqchip.pin == + entry->u.irqchip.pin) { + p = &kvm->irq_routes-> + entries[--kvm->irq_routes->nr]; + *e = *p; + found = 1; + } + break; + } + case KVM_IRQ_ROUTING_MSI: { + if (e->u.msi.address_lo == + entry->u.msi.address_lo + && e->u.msi.address_hi == + entry->u.msi.address_hi + && e->u.msi.data == entry->u.msi.data) { + p = &kvm->irq_routes-> + entries[--kvm->irq_routes->nr]; + *e = *p; + found = 1; + } + break; + } + default: + break; + } + if (found) { + /* If there are no other users of this GSI + * mark it available in the bitmap */ + for (i = 0; i < kvm->irq_routes->nr; i++) { + e = &kvm->irq_routes->entries[i]; + if (e->gsi == gsi) + break; + } + if (i == kvm->irq_routes->nr) + clear_gsi(kvm, gsi); + + return 0; + } + } + } + return -ESRCH; +#else + return -ENOSYS; +#endif +} + +int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry e; + + e.gsi = gsi; + e.type = KVM_IRQ_ROUTING_IRQCHIP; + e.flags = 0; + e.u.irqchip.irqchip = irqchip; + e.u.irqchip.pin = pin; + return kvm_del_routing_entry(kvm, &e); +#else + return -ENOSYS; +#endif +} + +int kvm_commit_irq_routes(kvm_context_t kvm) +{ +#ifdef KVM_CAP_IRQ_ROUTING + int r; + + kvm->irq_routes->flags = 0; + r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, kvm->irq_routes); + if (r == -1) + r = -errno; + return r; +#else + return -ENOSYS; +#endif +} + +int kvm_get_irq_route_gsi(kvm_context_t kvm) +{ + int i, bit; + uint32_t *buf = kvm->used_gsi_bitmap; + + /* Return the lowest unused GSI in the bitmap */ + for (i = 0; i < kvm->max_gsi / 32; i++) { + bit = ffs(~buf[i]); + if (!bit) + continue; + + return bit - 1 + i * 32; + } + + return -ENOSPC; +} + +#ifdef KVM_CAP_DEVICE_MSIX +int kvm_assign_set_msix_nr(kvm_context_t kvm, + struct kvm_assigned_msix_nr *msix_nr) +{ + int ret; + + ret = ioctl(kvm->vm_fd, KVM_ASSIGN_SET_MSIX_NR, msix_nr); + if (ret < 0) + return -errno; + + return ret; +} + +int kvm_assign_set_msix_entry(kvm_context_t kvm, + struct kvm_assigned_msix_entry *entry) +{ + int ret; + + ret = ioctl(kvm->vm_fd, KVM_ASSIGN_SET_MSIX_ENTRY, entry); + if (ret < 0) + return -errno; + + return ret; +} +#endif diff --git a/kvm/libkvm/libkvm.h b/kvm/libkvm/libkvm.h new file mode 100644 index 000000000..4821a1e4c --- /dev/null +++ b/kvm/libkvm/libkvm.h @@ -0,0 +1,868 @@ +/** \file libkvm.h + * libkvm API + */ + +#ifndef LIBKVM_H +#define LIBKVM_H + +#if defined(__s390__) +#include <asm/ptrace.h> +#endif + +#include <stdint.h> + +#ifndef __user +#define __user /* temporary, until installed via make headers_install */ +#endif + +#include <linux/kvm.h> + +#include <signal.h> + +struct kvm_context; + +typedef struct kvm_context *kvm_context_t; + +#if defined(__x86_64__) || defined(__i386__) +struct kvm_msr_list *kvm_get_msr_list(kvm_context_t); +int kvm_get_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n); +int kvm_set_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n); +#endif + +/*! + * \brief KVM callbacks structure + * + * This structure holds pointers to various functions that KVM will call + * when it encounters something that cannot be virtualized, such as + * accessing hardware devices via MMIO or regular IO. + */ +struct kvm_callbacks { + /// For 8bit IO reads from the guest (Usually when executing 'inb') + int (*inb)(void *opaque, uint16_t addr, uint8_t *data); + /// For 16bit IO reads from the guest (Usually when executing 'inw') + int (*inw)(void *opaque, uint16_t addr, uint16_t *data); + /// For 32bit IO reads from the guest (Usually when executing 'inl') + int (*inl)(void *opaque, uint16_t addr, uint32_t *data); + /// For 8bit IO writes from the guest (Usually when executing 'outb') + int (*outb)(void *opaque, uint16_t addr, uint8_t data); + /// For 16bit IO writes from the guest (Usually when executing 'outw') + int (*outw)(void *opaque, uint16_t addr, uint16_t data); + /// For 32bit IO writes from the guest (Usually when executing 'outl') + int (*outl)(void *opaque, uint16_t addr, uint32_t data); + /// generic memory reads to unmapped memory (For MMIO devices) + int (*mmio_read)(void *opaque, uint64_t addr, uint8_t *data, + int len); + /// generic memory writes to unmapped memory (For MMIO devices) + int (*mmio_write)(void *opaque, uint64_t addr, uint8_t *data, + int len); +#ifdef KVM_CAP_SET_GUEST_DEBUG + int (*debug)(void *opaque, void *env, + struct kvm_debug_exit_arch *arch_info); +#endif + /*! + * \brief Called when the VCPU issues an 'hlt' instruction. + * + * Typically, you should yeild here to prevent 100% CPU utilization + * on the host CPU. + */ + int (*halt)(void *opaque, int vcpu); + int (*shutdown)(void *opaque, void *env); + int (*io_window)(void *opaque); + int (*try_push_interrupts)(void *opaque); +#ifdef KVM_CAP_USER_NMI + void (*push_nmi)(void *opaque); +#endif + void (*post_kvm_run)(void *opaque, void *env); + int (*pre_kvm_run)(void *opaque, void *env); + int (*tpr_access)(void *opaque, int vcpu, uint64_t rip, int is_write); +#if defined(__powerpc__) + int (*powerpc_dcr_read)(int vcpu, uint32_t dcrn, uint32_t *data); + int (*powerpc_dcr_write)(int vcpu, uint32_t dcrn, uint32_t data); +#endif +#if defined(__s390__) + int (*s390_handle_intercept)(kvm_context_t context, int vcpu, + struct kvm_run *run); + int (*s390_handle_reset)(kvm_context_t context, int vcpu, + struct kvm_run *run); +#endif +}; + +/*! + * \brief Create new KVM context + * + * This creates a new kvm_context. A KVM context is a small area of data that + * holds information about the KVM instance that gets created by this call.\n + * This should always be your first call to KVM. + * + * \param callbacks Pointer to a valid kvm_callbacks structure + * \param opaque Not used + * \return NULL on failure + */ +kvm_context_t kvm_init(struct kvm_callbacks *callbacks, + void *opaque); + +/*! + * \brief Cleanup the KVM context + * + * Should always be called when closing down KVM.\n + * Exception: If kvm_init() fails, this function should not be called, as the + * context would be invalid + * + * \param kvm Pointer to the kvm_context that is to be freed + */ +void kvm_finalize(kvm_context_t kvm); + +/*! + * \brief Disable the in-kernel IRQCHIP creation + * + * In-kernel irqchip is enabled by default. If userspace irqchip is to be used, + * this should be called prior to kvm_create(). + * + * \param kvm Pointer to the kvm_context + */ +void kvm_disable_irqchip_creation(kvm_context_t kvm); + +/*! + * \brief Disable the in-kernel PIT creation + * + * In-kernel pit is enabled by default. If userspace pit is to be used, + * this should be called prior to kvm_create(). + * + * \param kvm Pointer to the kvm_context + */ +void kvm_disable_pit_creation(kvm_context_t kvm); + +/*! + * \brief Create new virtual machine + * + * This creates a new virtual machine, maps physical RAM to it, and creates a + * virtual CPU for it.\n + * \n + * Memory gets mapped for addresses 0->0xA0000, 0xC0000->phys_mem_bytes + * + * \param kvm Pointer to the current kvm_context + * \param phys_mem_bytes The amount of physical ram you want the VM to have + * \param phys_mem This pointer will be set to point to the memory that + * kvm_create allocates for physical RAM + * \return 0 on success + */ +int kvm_create(kvm_context_t kvm, + unsigned long phys_mem_bytes, + void **phys_mem); +int kvm_create_vm(kvm_context_t kvm); +int kvm_check_extension(kvm_context_t kvm, int ext); +void kvm_create_irqchip(kvm_context_t kvm); + +/*! + * \brief Create a new virtual cpu + * + * This creates a new virtual cpu (the first vcpu is created by kvm_create()). + * Should be called from a thread dedicated to the vcpu. + * + * \param kvm kvm context + * \param slot vcpu number (> 0) + * \return 0 on success, -errno on failure + */ +int kvm_create_vcpu(kvm_context_t kvm, int slot); + +/*! + * \brief Start the VCPU + * + * This starts the VCPU and virtualization is started.\n + * \n + * This function will not return until any of these conditions are met: + * - An IO/MMIO handler does not return "0" + * - An exception that neither the guest OS, nor KVM can handle occurs + * + * \note This function will call the callbacks registered in kvm_init() + * to emulate those functions + * \note If you at any point want to interrupt the VCPU, kvm_run() will + * listen to the EINTR signal. This allows you to simulate external interrupts + * and asyncronous IO. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be started + * \return 0 on success, but you really shouldn't expect this function to + * return except for when an error has occured, or when you have sent it + * an EINTR signal. + */ +int kvm_run(kvm_context_t kvm, int vcpu, void *env); + +/*! + * \brief Get interrupt flag from on last exit to userspace + * + * This gets the CPU interrupt flag as it was on the last exit to userspace. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return interrupt flag value (0 or 1) + */ +int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu); + +/*! + * \brief Get the value of the APIC_BASE msr as of last exit to userspace + * + * This gets the APIC_BASE msr as it was on the last exit to userspace. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return APIC_BASE msr contents + */ +uint64_t kvm_get_apic_base(kvm_context_t kvm, int vcpu); + +/*! + * \brief Check if a vcpu is ready for interrupt injection + * + * This checks if vcpu interrupts are not masked by mov ss or sti. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return boolean indicating interrupt injection readiness + */ +int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu); + +/*! + * \brief Read VCPU registers + * + * This gets the GP registers from the VCPU and outputs them + * into a kvm_regs structure + * + * \note This function returns a \b copy of the VCPUs registers.\n + * If you wish to modify the VCPUs GP registers, you should call kvm_set_regs() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_regs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs); + +/*! + * \brief Write VCPU registers + * + * This sets the GP registers on the VCPU from a kvm_regs structure + * + * \note When this function returns, the regs pointer and the data it points to + * can be discarded + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_regs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_set_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs); +/*! + * \brief Read VCPU fpu registers + * + * This gets the FPU registers from the VCPU and outputs them + * into a kvm_fpu structure + * + * \note This function returns a \b copy of the VCPUs registers.\n + * If you wish to modify the VCPU FPU registers, you should call kvm_set_fpu() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param fpu Pointer to a kvm_fpu which will be populated with the VCPUs + * fpu registers values + * \return 0 on success + */ +int kvm_get_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu); + +/*! + * \brief Write VCPU fpu registers + * + * This sets the FPU registers on the VCPU from a kvm_fpu structure + * + * \note When this function returns, the fpu pointer and the data it points to + * can be discarded + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param fpu Pointer to a kvm_fpu which holds the new vcpu fpu state + * \return 0 on success + */ +int kvm_set_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu); + +/*! + * \brief Read VCPU system registers + * + * This gets the non-GP registers from the VCPU and outputs them + * into a kvm_sregs structure + * + * \note This function returns a \b copy of the VCPUs registers.\n + * If you wish to modify the VCPUs non-GP registers, you should call + * kvm_set_sregs() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_get_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *regs); + +/*! + * \brief Write VCPU system registers + * + * This sets the non-GP registers on the VCPU from a kvm_sregs structure + * + * \note When this function returns, the regs pointer and the data it points to + * can be discarded + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_set_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *regs); + +#ifdef KVM_CAP_MP_STATE +/*! + * * \brief Read VCPU MP state + * + */ +int kvm_get_mpstate(kvm_context_t kvm, int vcpu, + struct kvm_mp_state *mp_state); + +/*! + * * \brief Write VCPU MP state + * + */ +int kvm_set_mpstate(kvm_context_t kvm, int vcpu, + struct kvm_mp_state *mp_state); +/*! + * * \brief Reset VCPU MP state + * + */ +static inline int kvm_reset_mpstate(kvm_context_t kvm, int vcpu) +{ + struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED}; + return kvm_set_mpstate(kvm, vcpu, &mp_state); +} +#endif + +/*! + * \brief Simulate an external vectored interrupt + * + * This allows you to simulate an external vectored interrupt. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param irq Vector number + * \return 0 on success + */ +int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq); + +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_set_guest_debug(kvm_context_t, int vcpu, struct kvm_guest_debug *dbg); +#endif + +#if defined(__i386__) || defined(__x86_64__) +/*! + * \brief Setup a vcpu's cpuid instruction emulation + * + * Set up a table of cpuid function to cpuid outputs.\n + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be initialized + * \param nent number of entries to be installed + * \param entries cpuid function entries table + * \return 0 on success, or -errno on error + */ +int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent, + struct kvm_cpuid_entry *entries); + +/*! + * \brief Setup a vcpu's cpuid instruction emulation + * + * Set up a table of cpuid function to cpuid outputs. + * This call replaces the older kvm_setup_cpuid interface by adding a few + * parameters to support cpuid functions that have sub-leaf values. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be initialized + * \param nent number of entries to be installed + * \param entries cpuid function entries table + * \return 0 on success, or -errno on error + */ +int kvm_setup_cpuid2(kvm_context_t kvm, int vcpu, int nent, + struct kvm_cpuid_entry2 *entries); + +/*! + * \brief Setting the number of shadow pages to be allocated to the vm + * + * \param kvm pointer to kvm_context + * \param nrshadow_pages number of pages to be allocated + */ +int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages); + +/*! + * \brief Getting the number of shadow pages that are allocated to the vm + * + * \param kvm pointer to kvm_context + * \param nrshadow_pages number of pages to be allocated + */ +int kvm_get_shadow_pages(kvm_context_t kvm , unsigned int *nrshadow_pages); + +/*! + * \brief Set up cr8 for next time the vcpu is executed + * + * This is a fast setter for cr8, which will be applied when the + * vcpu next enters guest mode. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param cr8 next cr8 value + */ +void kvm_set_cr8(kvm_context_t kvm, int vcpu, uint64_t cr8); + +/*! + * \brief Get cr8 for sync tpr in qemu apic emulation + * + * This is a getter for cr8, which used to sync with the tpr in qemu + * apic emualtion. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + */ +__u64 kvm_get_cr8(kvm_context_t kvm, int vcpu); +#endif + +/*! + * \brief Set a vcpu's signal mask for guest mode + * + * A vcpu can have different signals blocked in guest mode and user mode. + * This allows guest execution to be interrupted on a signal, without requiring + * that the signal be delivered to a signal handler (the signal can be + * dequeued using sigwait(2). + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be initialized + * \param sigset signal mask for guest mode + * \return 0 on success, or -errno on error + */ +int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset); + +/*! + * \brief Dump all VCPU information + * + * This dumps \b all the information that KVM has about a virtual CPU, namely: + * - GP Registers + * - System registers (selectors, descriptors, etc) + * - VMCS Data + * - MSRS + * - Pending interrupts + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return 0 on success + */ +int kvm_dump_vcpu(kvm_context_t kvm, int vcpu); + +/*! + * \brief Dump VCPU registers + * + * This dumps some of the information that KVM has about a virtual CPU, namely: + * - GP Registers + * + * A much more verbose version of this is available as kvm_dump_vcpu() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return 0 on success + */ +void kvm_show_regs(kvm_context_t kvm, int vcpu); + + +void *kvm_create_phys_mem(kvm_context_t, unsigned long phys_start, + unsigned long len, int log, int writable); +void kvm_destroy_phys_mem(kvm_context_t, unsigned long phys_start, + unsigned long len); +void kvm_unregister_memory_area(kvm_context_t, uint64_t phys_start, + unsigned long len); + +int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_start, unsigned long size); +int kvm_register_phys_mem(kvm_context_t kvm, + unsigned long phys_start, void *userspace_addr, + unsigned long len, int log); +int kvm_get_dirty_pages(kvm_context_t, unsigned long phys_addr, void *buf); +int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr, + unsigned long end_addr, void *buf, void*opaque, + int (*cb)(unsigned long start, unsigned long len, + void*bitmap, void *opaque)); +int kvm_register_coalesced_mmio(kvm_context_t kvm, + uint64_t addr, uint32_t size); +int kvm_unregister_coalesced_mmio(kvm_context_t kvm, + uint64_t addr, uint32_t size); + +/*! + * \brief Create a memory alias + * + * Aliases a portion of physical memory to another portion. If the guest + * accesses the alias region, it will behave exactly as if it accessed + * the target memory. + */ +int kvm_create_memory_alias(kvm_context_t, + uint64_t phys_start, uint64_t len, + uint64_t target_phys); + +/*! + * \brief Destroy a memory alias + * + * Removes an alias created with kvm_create_memory_alias(). + */ +int kvm_destroy_memory_alias(kvm_context_t, uint64_t phys_start); + +/*! + * \brief Get a bitmap of guest ram pages which are allocated to the guest. + * + * \param kvm Pointer to the current kvm_context + * \param phys_addr Memory slot phys addr + * \param bitmap Long aligned address of a big enough bitmap (one bit per page) + */ +int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *bitmap); +int kvm_get_mem_map_range(kvm_context_t kvm, unsigned long phys_addr, + unsigned long len, void *buf, void *opaque, + int (*cb)(unsigned long start,unsigned long len, + void* bitmap, void* opaque)); +int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status); + +int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len); +int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len); +/*! + * \brief Enable dirty-pages-logging for all memory regions + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_dirty_pages_log_enable_all(kvm_context_t kvm); + +/*! + * \brief Disable dirty-page-logging for some memory regions + * + * Disable dirty-pages-logging for those memory regions that were + * created with dirty-page-logging disabled. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_dirty_pages_log_reset(kvm_context_t kvm); + +/*! + * \brief Query whether in kernel irqchip is used + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_irqchip_in_kernel(kvm_context_t kvm); + +int kvm_has_sync_mmu(kvm_context_t kvm); + +#ifdef KVM_CAP_IRQCHIP +/*! + * \brief Dump in kernel IRQCHIP contents + * + * Dump one of the in kernel irq chip devices, including PIC (master/slave) + * and IOAPIC into a kvm_irqchip structure + * + * \param kvm Pointer to the current kvm_context + * \param chip The irq chip device to be dumped + */ +int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip); + +/*! + * \brief Set in kernel IRQCHIP contents + * + * Write one of the in kernel irq chip devices, including PIC (master/slave) + * and IOAPIC + * + * + * \param kvm Pointer to the current kvm_context + * \param chip THe irq chip device to be written + */ +int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip); + +#if defined(__i386__) || defined(__x86_64__) +/*! + * \brief Get in kernel local APIC for vcpu + * + * Save the local apic state including the timer of a virtual CPU + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be accessed + * \param s Local apic state of the specific virtual CPU + */ +int kvm_get_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s); + +/*! + * \brief Set in kernel local APIC for vcpu + * + * Restore the local apic state including the timer of a virtual CPU + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be accessed + * \param s Local apic state of the specific virtual CPU + */ +int kvm_set_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s); + +#endif + +/*! + * \brief Simulate an NMI + * + * This allows you to simulate a non-maskable interrupt. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return 0 on success + */ +int kvm_inject_nmi(kvm_context_t kvm, int vcpu); + +#endif + +/*! + * \brief Query wheather in kernel pit is used + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_pit_in_kernel(kvm_context_t kvm); + +/*! + * \brief Initialize coalesced MMIO + * + * Check for coalesced MMIO capability and store in context + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_init_coalesced_mmio(kvm_context_t kvm); + +#ifdef KVM_CAP_PIT + +#if defined(__i386__) || defined(__x86_64__) +/*! + * \brief Get in kernel PIT of the virtual domain + * + * Save the PIT state. + * + * \param kvm Pointer to the current kvm_context + * \param s PIT state of the virtual domain + */ +int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s); + +/*! + * \brief Set in kernel PIT of the virtual domain + * + * Restore the PIT state. + * Timer would be retriggerred after restored. + * + * \param kvm Pointer to the current kvm_context + * \param s PIT state of the virtual domain + */ +int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s); +#endif + +int kvm_reinject_control(kvm_context_t kvm, int pit_reinject); + +#endif + +#ifdef KVM_CAP_VAPIC + +/*! + * \brief Enable kernel tpr access reporting + * + * When tpr access reporting is enabled, the kernel will call the + * ->tpr_access() callback every time the guest vcpu accesses the tpr. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu vcpu to enable tpr access reporting on + */ +int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu); + +/*! + * \brief Disable kernel tpr access reporting + * + * Undoes the effect of kvm_enable_tpr_access_reporting(). + * + * \param kvm Pointer to the current kvm_context + * \param vcpu vcpu to disable tpr access reporting on + */ +int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu); + +int kvm_enable_vapic(kvm_context_t kvm, int vcpu, uint64_t vapic); + +#endif + +#if defined(__s390__) +int kvm_s390_initial_reset(kvm_context_t kvm, int slot); +int kvm_s390_interrupt(kvm_context_t kvm, int slot, + struct kvm_s390_interrupt *kvmint); +int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw); +int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr); +#endif + +#ifdef KVM_CAP_DEVICE_ASSIGNMENT +/*! + * \brief Notifies host kernel about a PCI device to be assigned to a guest + * + * Used for PCI device assignment, this function notifies the host + * kernel about the assigning of the physical PCI device to a guest. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_dev Parameters, like bus, devfn number, etc + */ +int kvm_assign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev); + +/*! + * \brief Assign IRQ for an assigned device + * + * Used for PCI device assignment, this function assigns IRQ numbers for + * an physical device and guest IRQ handling. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc + */ +int kvm_assign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq); + +#ifdef KVM_CAP_ASSIGN_DEV_IRQ +/*! + * \brief Deassign IRQ for an assigned device + * + * Used for PCI device assignment, this function deassigns IRQ numbers + * for an assigned device. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc + */ +int kvm_deassign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq); +#endif +#endif + +/*! + * \brief Determines whether destroying memory regions is allowed + * + * KVM before 2.6.29 had a bug when destroying memory regions. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_destroy_memory_region_works(kvm_context_t kvm); + +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT +/*! + * \brief Notifies host kernel about a PCI device to be deassigned from a guest + * + * Used for hot remove PCI device, this function notifies the host + * kernel about the deassigning of the physical PCI device from a guest. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_dev Parameters, like bus, devfn number, etc + */ +int kvm_deassign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev); +#endif + +/*! + * \brief Checks whether the generic irq routing capability is present + * + * Checks whether kvm can reroute interrupts among the various interrupt + * controllers. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_has_gsi_routing(kvm_context_t kvm); + +/*! + * \brief Determines the number of gsis that can be routed + * + * Returns the number of distinct gsis that can be routed by kvm. This is + * also the number of distinct routes (if a gsi has two routes, than another + * gsi cannot be used...) + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_get_gsi_count(kvm_context_t kvm); + +/*! + * \brief Clears the temporary irq routing table + * + * Clears the temporary irq routing table. Nothing is committed to the + * running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_clear_gsi_routes(kvm_context_t kvm); + +/*! + * \brief Adds an irq route to the temporary irq routing table + * + * Adds an irq route to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin); + +/*! + * \brief Removes an irq route from the temporary irq routing table + * + * Adds an irq route to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin); + +struct kvm_irq_routing_entry; +/*! + * \brief Adds a routing entry to the temporary irq routing table + * + * Adds a filled routing entry to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_add_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry* entry); + +/*! + * \brief Removes a routing from the temporary irq routing table + * + * Remove a routing to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_del_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry* entry); + +/*! + * \brief Commit the temporary irq routing table + * + * Commit the temporary irq routing table to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_commit_irq_routes(kvm_context_t kvm); + +/*! + * \brief Get unused GSI number for irq routing table + * + * Get unused GSI number for irq routing table + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_get_irq_route_gsi(kvm_context_t kvm); + +#ifdef KVM_CAP_DEVICE_MSIX +int kvm_assign_set_msix_nr(kvm_context_t kvm, + struct kvm_assigned_msix_nr *msix_nr); +int kvm_assign_set_msix_entry(kvm_context_t kvm, + struct kvm_assigned_msix_entry *entry); +#endif + +uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg); + +#endif diff --git a/kvm/scripts/65-kvm.rules b/kvm/scripts/65-kvm.rules new file mode 100644 index 000000000..481cfcf4a --- /dev/null +++ b/kvm/scripts/65-kvm.rules @@ -0,0 +1 @@ +KERNEL=="kvm", NAME="%k", GROUP="kvm", MODE="0660" diff --git a/kvm/scripts/kvm b/kvm/scripts/kvm new file mode 100755 index 000000000..cddc931fd --- /dev/null +++ b/kvm/scripts/kvm @@ -0,0 +1,226 @@ +#!/bin/sh +# kvm init script Takes care for all VMM tasks +# +# chkconfig: - 99 01 +# description: The KVM is a kernel level Virtual Machine Monitor. \ +# Currently it starts a bridge and attached eth0 for it + +dir=$(dirname "$0") + +ifnum=${ifnum:-$(ip route list | awk '/^default / { print $NF }' | sed 's/^[^0-9]*//')} +ifnum=${ifnum:-0} +switch=${sw0:-sw${ifnum}} +pif=${pif:-eth${ifnum}} +antispoof=${antispoof:-no} +command=$1 + +if [ -f /etc/sysconfig/network-scripts/network-functions ]; then + . /etc/sysconfig/network-scripts/network-functions +fi + +#check for bonding link aggregation +bond_int=$(awk < /etc/sysconfig/network-scripts/ifcfg-${pif} '/^MASTER=/ { print $BF }' | sed 's/MASTER=//') +if [ ${bond_int}"0" != "0" ]; then + pif=${bond_int} +fi + +if [ -f /etc/sysconfig/network-scripts/ifcfg-${pif} ]; then + . /etc/sysconfig/network-scripts/ifcfg-${pif} +fi + +get_ip_info() { + addr=`ip addr show dev $1 | egrep '^ *inet' | sed -e 's/ *inet //' -e 's/ .*//'` + gateway=$(ip route list | awk '/^default / { print $3 }') + broadcast=$(/sbin/ip addr show dev $1 | grep inet | awk '/brd / { print $4 }') +} + +#When a bonding device link goes down, its slave interfaces +#are getting detached so they should be re-added +bond_link_up () { + dev=$1 + is_bonding=$(echo ${dev} | awk '/^bond/ { print $NF }') + if [ ${is_bonding}"0" != "0" ]; then + for slave in `awk < /proc/net/bonding/bond0 '/Slave Interface: / {print $3 }'`; do + ifenslave $dev $slave + done + fi +} + + +do_ifup() { + if [ ${addr} ] ; then + ip addr flush $1 + bond_link_up $1 + ip addr add ${addr} broadcast ${broadcast} dev $1 + ip link set dev $1 up + fi +} + +link_exists() +{ + if ip link show "$1" >/dev/null 2>/dev/null + then + return 0 + else + return 1 + fi +} + +create_switch () { + local switch=$1 + + if [ ! -e "/sys/class/net/${switch}/bridge" ]; then + brctl addbr ${switch} >/dev/null 2>&1 + brctl stp ${switch} off >/dev/null 2>&1 + brctl setfd ${switch} 0.1 >/dev/null 2>&1 + fi + ip link set ${switch} up >/dev/null 2>&1 +} + + +add_to_switch () { + local switch=$1 + local dev=$2 + + if [ ! -e "/sys/class/net/${switch}/brif/${dev}" ]; then + brctl addif ${switch} ${dev} >/dev/null 2>&1 + fi + + ip link set ${dev} up >/dev/null 2>&1 +} + +#taken from Xen +transfer_routes () { + local src=$1 + local dst=$2 + # List all routes and grep the ones with $src in. + # Stick 'ip route del' on the front to delete. + # Change $src to $dst and use 'ip route add' to add. + ip route list | sed -ne " +/dev ${src}\( \|$\)/ { + h + s/^/ip route del / + P + g + s/${src}/${dst}/ + s/^/ip route add / + P + d +}" | sh -e +} + + +change_ips() { + local src=$1 + local dst=$2 + + #take care also for case we do not have /etc/sysconfig data (the switch as a src case) + if [ -x $BOOTPROTO ]; then + if [ -x $(pgrep dhclient) ];then + BOOTPROTO="null" + else + BOOTPROTO="dhcp" + fi + fi + + if [ $BOOTPROTO = "dhcp" ]; then + ifdown ${src} >/dev/null 2>&1 || true + ip link set ${src} up >/dev/null 2>&1 + bond_link_up ${src} + pkill dhclient >/dev/null 2>&1 + for ((i=0;i<3;i++)); do + pgrep dhclient >/dev/null 2>&1 || i=4 + sleep 1 + done + dhclient ${dst} >/dev/null 2>&1 + else + get_ip_info ${src} + ifconfig ${src} 0.0.0.0 + do_ifup ${dst} + transfer_routes ${src} ${dst} + ip route add default via ${gateway} dev ${dst} + fi +} + +antispoofing () { + iptables -P FORWARD DROP >/dev/null 2>&1 + iptables -F FORWARD >/dev/null 2>&1 + iptables -A FORWARD -m physdev --physdev-in ${dev} -j ACCEPT >/dev/null 2>&1 +} + +status () { + local dev=$1 + local sw=$2 + + echo '============================================================' + ip addr show ${dev} + ip addr show ${sw} + echo ' ' + brctl show ${sw} + echo ' ' + ip route list + echo ' ' + route -n + echo '============================================================' + gateway=$(ip route list | awk '/^default / { print $3 }') + ping -c 1 ${gateway} || true + echo '============================================================' +} + +start () { + if [ "${switch}" = "null" ] ; then + return + fi + + create_switch ${switch} + add_to_switch ${switch} ${pif} + change_ips ${pif} ${switch} + + if [ ${antispoof} = 'yes' ] ; then + antispoofing + fi + + grep -q GenuineIntel /proc/cpuinfo && /sbin/modprobe kvm-intel + grep -q AuthenticAMD /proc/cpuinfo && /sbin/modprobe kvm-amd +} + +stop () { + if [ "${switch}" = "null" ]; then + return + fi + if ! link_exists "$switch"; then + return + fi + + change_ips ${switch} ${pif} + ip link set ${switch} down + brctl delbr ${switch} + + grep -q GenuineIntel /proc/cpuinfo && /sbin/modprobe -r kvm-intel + grep -q AuthenticAMD /proc/cpuinfo && /sbin/modprobe -r kvm-amd + /sbin/modprobe -r kvm +} + + +case "$command" in + start) + echo -n $"Starting KVM: " + start + echo + ;; + + stop) + echo -n $"Shutting down KVM: " + stop + echo + ;; + + status) + status ${pif} ${switch} + ;; + + *) + echo "Unknown command: $command" >&2 + echo 'Valid commands are: start, stop, status' >&2 + exit 1 +esac diff --git a/kvm/scripts/make-combined-release b/kvm/scripts/make-combined-release new file mode 100755 index 000000000..adef8f610 --- /dev/null +++ b/kvm/scripts/make-combined-release @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import sys, tarfile, os.path + +# usage: $0 combined.tar.gz qemu.tar.gz kvm-kmod.tar.gz + +outname, qemuname, kmodname = sys.argv[1:4] + +out = tarfile.open(name = outname, mode = 'w:gz') + +def tarcopy(dst, src, transform): + for member in src: + f = src.extractfile(member) + member.name = transform(member.name) + dst.addfile(member, f) + +def stem(fname): + fname = os.path.basename(fname) + if fname.endswith('.tar.gz'): + fname = fname[:-7] + return fname + +def transformer(old, new): + def transform(fname): + if fname.startswith(old + '/'): + fname = new + fname[len(old):] + return fname + return transform + +tarcopy(out, tarfile.open(name = qemuname), + transformer(stem(qemuname), stem(outname))) + +tarcopy(out, tarfile.open(name = kmodname), + transformer(stem(kmodname), stem(outname) + '/kvm/kernel')) + + diff --git a/kvm/scripts/make-release b/kvm/scripts/make-release new file mode 100755 index 000000000..11d9c27ac --- /dev/null +++ b/kvm/scripts/make-release @@ -0,0 +1,67 @@ +#!/bin/bash -e + +usage() { + echo "usage: $0 [--upload] [--formal] commit [name]" + exit 1 +} + +[[ -f ~/.kvmreleaserc ]] && . ~/.kvmreleaserc + +upload= +formal= + +releasedir=~/sf-release +[[ -z "$TMP" ]] && TMP="/tmp" +tmpdir="$TMP/qemu-kvm-make-release.$$" +while [[ "$1" = -* ]]; do + opt="$1" + shift + case "$opt" in + --upload) + upload="yes" + ;; + --formal) + formal="yes" + ;; + *) + usage + ;; + esac +done + +commit="$1" +name="$2" + +if [[ -z "$commit" ]]; then + usage +fi + +if [[ -z "$name" ]]; then + name="$commit" +fi + +tarball="$releasedir/$name.tar" + +cd "$(dirname "$0")"/../.. +git archive --prefix="$name/" --format=tar "$commit" > "$tarball" + +mkdir -p "$tmpdir" +git cat-file -p "${commit}:roms" | awk ' { print $4, $3 } ' \ + > "$tmpdir/EXTERNAL_DEPENDENCIES" +tar -rf "$tarball" --transform "s,^,$name/," -C "$tmpdir" \ + "EXTERNAL_DEPENDENCIES" +rm -rf "$tmpdir" + +if [[ -n "$formal" ]]; then + mkdir -p "$tmpdir" + echo "$name" > "$tmpdir/KVM_VERSION" + tar -rf "$tarball" --transform "s,^,$name/," -C "$tmpdir" "KVM_VERSION" + rm -rf "$tmpdir" +fi + +gzip -9 "$tarball" +tarball="$tarball.gz" + +if [[ -n "$upload" ]]; then + rsync --progress -h "$tarball" avik@frs.sourceforge.net:uploads/ +fi diff --git a/kvm/scripts/mkbootdisk b/kvm/scripts/mkbootdisk new file mode 100755 index 000000000..3b7f7c0b4 --- /dev/null +++ b/kvm/scripts/mkbootdisk @@ -0,0 +1,30 @@ +#!/bin/sh + +set -e + +kernel="$1" +mnt_dir="/tmp/mkbootdisk/mnt" +img_file="/tmp/mkbootdisk/boot.img" + +[[ -f "$kernel" ]] || { echo need kernel; exit 1; } + +mkdir -p $mnt_dir + +[[ -d "$mnt_dir" ]] || { echo mount dir err; exit 1; } + +dd < /dev/zero > $img_file bs=1M count=10 +mkfs -t vfat $img_file + +mount -o loop $img_file $mnt_dir + +cp "$kernel" $mnt_dir/kernel + +cat <<EOF > $mnt_dir/SYSLINUX.CFG +DEFAULT kernel +APPEND console=ttyS0 +EOF + +umount $mnt_dir + +syslinux $img_file + diff --git a/kvm/scripts/qemu-ifup b/kvm/scripts/qemu-ifup new file mode 100755 index 000000000..284b176c1 --- /dev/null +++ b/kvm/scripts/qemu-ifup @@ -0,0 +1,5 @@ +#!/bin/sh + +switch=$(/sbin/ip route list | awk '/^default / { print $5 }') +/sbin/ifconfig $1 0.0.0.0 up +/usr/sbin/brctl addif ${switch} $1 diff --git a/kvm/scripts/run_img b/kvm/scripts/run_img new file mode 100755 index 000000000..10c749787 --- /dev/null +++ b/kvm/scripts/run_img @@ -0,0 +1,4 @@ +sudo /sbin/rmmod kvm +sudo /sbin/insmod ../kernel/kvm.ko +sudo chmod a+rw /dev/hvm +../qemu/x86_64-softmmu/qemu-system-x86_64 -boot c -L /usr/share/qemu -hda /tmp/mkbootdisk/boot.img -m 384 -serial file:/tmp/qemu_serial.out diff --git a/kvm/scripts/vmxcap b/kvm/scripts/vmxcap new file mode 100755 index 000000000..b7f58acb2 --- /dev/null +++ b/kvm/scripts/vmxcap @@ -0,0 +1,215 @@ +#!/usr/bin/python + +MSR_IA32_VMX_BASIC = 0x480 +MSR_IA32_VMX_PINBASED_CTLS = 0x481 +MSR_IA32_VMX_PROCBASED_CTLS = 0x482 +MSR_IA32_VMX_EXIT_CTLS = 0x483 +MSR_IA32_VMX_ENTRY_CTLS = 0x484 +MSR_IA32_VMX_MISC_CTLS = 0x485 +MSR_IA32_VMX_PROCBASED_CTLS2 = 0x48B +MSR_IA32_VMX_EPT_VPID_CAP = 0x48C +MSR_IA32_VMX_TRUE_PINBASED_CTLS = 0x48D +MSR_IA32_VMX_TRUE_PROCBASED_CTLS = 0x48E +MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F +MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490 + +class msr(object): + def __init__(self): + try: + self.f = file('/dev/cpu/0/msr') + except: + self.f = file('/dev/msr0') + def read(self, index, default = None): + import struct + self.f.seek(index) + try: + return struct.unpack('Q', self.f.read(8))[0] + except: + return default + +class Control(object): + def __init__(self, name, bits, cap_msr, true_cap_msr = None): + self.name = name + self.bits = bits + self.cap_msr = cap_msr + self.true_cap_msr = true_cap_msr + def read2(self, nr): + m = msr() + val = m.read(nr, 0) + return (val & 0xffffffff, val >> 32) + def show(self): + print self.name + mbz, mb1 = self.read2(self.cap_msr) + tmbz, tmb1 = 0, 0 + if self.true_cap_msr: + tmbz, tmb1 = self.read2(self.true_cap_msr) + for bit in sorted(self.bits.keys()): + zero = not (mbz & (1 << bit)) + one = mb1 & (1 << bit) + true_zero = not (tmbz & (1 << bit)) + true_one = tmb1 & (1 << bit) + s= '?' + if (self.true_cap_msr and true_zero and true_one + and one and not zero): + s = 'default' + elif zero and not one: + s = 'no' + elif one and not zero: + s = 'forced' + elif one and zero: + s = 'yes' + print ' %-40s %s' % (self.bits[bit], s) + +class Misc(object): + def __init__(self, name, bits, msr): + self.name = name + self.bits = bits + self.msr = msr + def show(self): + print self.name + value = msr().read(self.msr, 0) + def first_bit(key): + if type(key) is tuple: + return key[0] + else: + return key + for bits in sorted(self.bits.keys(), key = first_bit): + if type(bits) is tuple: + lo, hi = bits + fmt = int + else: + lo = hi = bits + def fmt(x): + return { True: 'yes', False: 'no' }[x] + v = (value >> lo) & ((1 << (hi - lo + 1)) - 1) + print ' %-40s %s' % (self.bits[bits], fmt(v)) + +controls = [ + Control( + name = 'pin-based controls', + bits = { + 0: 'External interrupt exiting', + 3: 'NMI exiting', + 5: 'Virtual NMIs', + 6: 'Activate VMX-preemption timer', + }, + cap_msr = MSR_IA32_VMX_PINBASED_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_PINBASED_CTLS, + ), + + Control( + name = 'primary processor-based controls', + bits = { + 2: 'Interrupt window exiting', + 3: 'Use TSC offsetting', + 7: 'HLT exiting', + 9: 'INVLPG exiting', + 10: 'MWAIT exiting', + 11: 'RDPMC exiting', + 12: 'RDTSC exiting', + 15: 'CR3-load exiting', + 16: 'CR3-store exiting', + 19: 'CR8-load exiting', + 20: 'CR8-store exiting', + 21: 'Use TPR shadow', + 22: 'NMI-window exiting', + 23: 'MOV-DR exiting', + 24: 'Unconditional I/O exiting', + 25: 'Use I/O bitmaps', + 27: 'Monitor trap flag', + 28: 'Use MSR bitmaps', + 29: 'MONITOR exiting', + 30: 'PAUSE exiting', + 31: 'Activate secondary control', + }, + cap_msr = MSR_IA32_VMX_PROCBASED_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_PROCBASED_CTLS, + ), + + Control( + name = 'secondary processor-based controls', + bits = { + 0: 'Virtualize APIC accesses', + 1: 'Enable EPT', + 2: 'Descriptor-table exiting', + 4: 'Virtualize x2APIC mode', + 5: 'Enable VPID', + 6: 'WBINVD exiting', + 7: 'Unrestricted guest', + 10: 'PAUSE-loop exiting', + }, + cap_msr = MSR_IA32_VMX_PROCBASED_CTLS2, + ), + + Control( + name = 'VM-Exit controls', + bits = { + 2: 'Save debug controls', + 9: 'Host address-space size', + 12: 'Load IA32_PERF_GLOBAL_CTRL', + 15: 'Acknowledge interrupt on exit', + 18: 'Save IA32_PAT', + 19: 'Load IA32_PAT', + 20: 'Save IA32_EFER', + 21: 'Load IA32_EFER', + 22: 'Save VMX-preemption timer value', + }, + cap_msr = MSR_IA32_VMX_EXIT_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_EXIT_CTLS, + ), + + Control( + name = 'VM-Entry controls', + bits = { + 2: 'Load debug controls', + 9: 'IA-64 mode guest', + 10: 'Entry to SMM', + 11: 'Deactivate dual-monitor treatment', + 13: 'Load IA32_PERF_GLOBAL_CTRL', + 14: 'Load IA32_PAT', + 15: 'Load IA32_EFER', + }, + cap_msr = MSR_IA32_VMX_ENTRY_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_ENTRY_CTLS, + ), + + Misc( + name = 'Miscellaneous data', + bits = { + (0,4): 'VMX-preemption timer scale (log2)', + 5: 'Store EFER.LMA into IA-32e mode guest control', + 6: 'HLT activity state', + 7: 'Shutdown activity state', + 8: 'Wait-for-SIPI activity state', + (16,24): 'Number of CR3-target values', + (25,27): 'MSR-load/store count recommenation', + (32,62): 'MSEG revision identifier', + }, + msr = MSR_IA32_VMX_MISC_CTLS, + ), + + Misc( + name = 'VPID and EPT capabilities', + bits = { + 0: 'Execute-only EPT translations', + 6: 'Page-walk length 4', + 8: 'Paging-structure memory type UC', + 14: 'Paging-structure memory type WB', + 16: '2MB EPT pages', + 20: 'INVEPT supported', + 25: 'Single-context INVEPT', + 26: 'All-context INVEPT', + 32: 'INVVPID supported', + 40: 'Individual-address INVVPID', + 41: 'Single-context INVVPID', + 42: 'All-context INVVPID', + 43: 'Single-context-retaining-globals INVVPID', + }, + msr = MSR_IA32_VMX_EPT_VPID_CAP, + ), + ] + +for c in controls: + c.show() + + diff --git a/kvm/user/COPYRIGHT b/kvm/user/COPYRIGHT new file mode 100644 index 000000000..d35649cb9 --- /dev/null +++ b/kvm/user/COPYRIGHT @@ -0,0 +1,4 @@ +Copyright (C) 2006 Qumranet. + +The files in this directory and its subdirectories are licensed under the +GNU LGPL, version 2. diff --git a/kvm/user/Makefile b/kvm/user/Makefile new file mode 100644 index 000000000..ed462bb57 --- /dev/null +++ b/kvm/user/Makefile @@ -0,0 +1,60 @@ + +include config.mak + +DESTDIR := + +.PHONY: arch_clean clean + +#make sure env CFLAGS variable is not used +CFLAGS = + +libgcc := $(shell $(CC) --print-libgcc-file-name) + +libcflat := test/lib/libcflat.a +cflatobjs := \ + test/lib/panic.o \ + test/lib/printf.o \ + test/lib/string.o + +#include architecure specific make rules +include config-$(ARCH).mak + +# cc-option +# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0) + +cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ + > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) + +CFLAGS += -O1 +CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall +CFLAGS += $(call cc-option, -fno-stack-protector, "") +CFLAGS += $(call cc-option, -fno-stack-protector-all, "") +CFLAGS += -I../include +CFLAGS += -I ../libkvm + +LDFLAGS += $(CFLAGS) -L ../libkvm + +CXXFLAGS = $(autodepend-flags) + +autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d + +LDFLAGS += -pthread -lrt + +kvmtrace_objs= kvmtrace.o + +kvmctl: $(kvmctl_objs) + $(CC) $(LDFLAGS) $^ -o $@ + +kvmtrace: $(kvmtrace_objs) + $(CC) $(LDFLAGS) $^ -o $@ + +$(libcflat): $(cflatobjs) + $(AR) rcs $@ $^ + +%.o: %.S + $(CC) $(CFLAGS) -c -nostdlib -o $@ $< + +-include .*.d + +clean: arch_clean + $(RM) kvmctl kvmtrace *.o *.a .*.d $(libcflat) $(cflatobjs) diff --git a/kvm/user/balloon_ctl.c b/kvm/user/balloon_ctl.c new file mode 100755 index 000000000..e65b08d59 --- /dev/null +++ b/kvm/user/balloon_ctl.c @@ -0,0 +1,92 @@ +/* + * This binary provides access to the guest's balloon driver + * module. + * + * Copyright (C) 2007 Qumranet + * + * Author: + * + * Dor Laor <dor.laor@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> + +#define __user +#include <linux/kvm.h> + +#define PAGE_SIZE 4096ul + + +static int balloon_op(int *fd, int bytes) +{ + struct kvm_balloon_op bop; + int r; + + bop.npages = bytes/PAGE_SIZE; + r = ioctl(*fd, KVM_BALLOON_OP, &bop); + if (r == -1) + return -errno; + printf("Ballon handled %d pages successfully\n", bop.npages); + + return 0; +} + +static int balloon_init(int *fd) +{ + *fd = open("/dev/kvm_balloon", O_RDWR); + if (*fd == -1) { + perror("open /dev/kvm_balloon"); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int fd; + int r; + int bytes; + + if (argc != 3) { + perror("Please provide op=[i|d], bytes\n"); + return 1; + } + bytes = atoi(argv[2]); + + switch (*argv[1]) { + case 'i': + break; + case 'd': + bytes = -bytes; + break; + default: + perror("Wrong op param\n"); + return 1; + } + + if (balloon_init(&fd)) { + perror("balloon_init failed\n"); + return 1; + } + + if ((r = balloon_op(&fd, bytes))) { + perror("balloon_op failed\n"); + goto out; + } + +out: + close(fd); + + return r; +} + diff --git a/kvm/user/bootstrap.lds b/kvm/user/bootstrap.lds new file mode 100644 index 000000000..fd0a4f8a9 --- /dev/null +++ b/kvm/user/bootstrap.lds @@ -0,0 +1,15 @@ +OUTPUT_FORMAT(binary) + +SECTIONS +{ + . = 0; + stext = .; + .text : { *(.init) *(.text) } + . = ALIGN(4K); + .data : { *(.data) } + . = ALIGN(16); + .bss : { *(.bss) } + . = ALIGN(4K); + edata = .; +} + diff --git a/kvm/user/config-i386.mak b/kvm/user/config-i386.mak new file mode 100644 index 000000000..09175d579 --- /dev/null +++ b/kvm/user/config-i386.mak @@ -0,0 +1,10 @@ +TEST_DIR=test/x86 +cstart.o = $(TEST_DIR)/cstart.o +bits = 32 +ldarch = elf32-i386 +CFLAGS += -D__i386__ +CFLAGS += -I $(KERNELDIR)/include + +tests= + +include config-x86-common.mak diff --git a/kvm/user/config-ia64.mak b/kvm/user/config-ia64.mak new file mode 100644 index 000000000..d9350fcc5 --- /dev/null +++ b/kvm/user/config-ia64.mak @@ -0,0 +1,7 @@ +bits = 64 +CFLAGS += -m64 +CFLAGS += -D__ia64__ +CFLAGS += -I../include/ia64 + +all: + diff --git a/kvm/user/config-powerpc-440.mak b/kvm/user/config-powerpc-440.mak new file mode 100644 index 000000000..12698e631 --- /dev/null +++ b/kvm/user/config-powerpc-440.mak @@ -0,0 +1,15 @@ + + +# for some reason binutils hates tlbsx unless we say we're 405 :( +CFLAGS += -Wa,-m405 -I test/lib/powerpc/44x + +cflatobjs += \ + test/lib/powerpc/44x/map.o \ + test/lib/powerpc/44x/tlbwe.o \ + test/lib/powerpc/44x/timebase.o + +simpletests += \ + test/powerpc/44x/tlbsx.bin \ + test/powerpc/44x/tlbwe_16KB.bin \ + test/powerpc/44x/tlbwe_hole.bin \ + test/powerpc/44x/tlbwe.bin diff --git a/kvm/user/config-powerpc.mak b/kvm/user/config-powerpc.mak new file mode 100644 index 000000000..ec6086bf4 --- /dev/null +++ b/kvm/user/config-powerpc.mak @@ -0,0 +1,39 @@ +CFLAGS += -I../include/powerpc +CFLAGS += -Wa,-mregnames -I test/lib +CFLAGS += -ffreestanding + +cstart := test/powerpc/cstart.o + +cflatobjs += \ + test/lib/powerpc/io.o + +$(libcflat): LDFLAGS += -nostdlib + +# these tests do not use libcflat +simpletests := \ + test/powerpc/spin.bin \ + test/powerpc/io.bin \ + test/powerpc/sprg.bin + +# theses tests use cstart.o, libcflat, and libgcc +tests := \ + test/powerpc/exit.bin \ + test/powerpc/helloworld.bin + +include config-powerpc-$(PROCESSOR).mak + + +all: kvmtrace kvmctl $(libcflat) $(simpletests) $(tests) + +$(simpletests): %.bin: %.o + $(CC) -nostdlib $^ -Wl,-T,flat.lds -o $@ + +$(tests): %.bin: $(cstart) %.o $(libcflat) + $(CC) -nostdlib $^ $(libgcc) -Wl,-T,flat.lds -o $@ + +kvmctl_objs = main-ppc.o iotable.o ../libkvm/libkvm.a + +arch_clean: + $(RM) $(simpletests) $(tests) $(cstart) + $(RM) $(patsubst %.bin, %.elf, $(simpletests) $(tests)) + $(RM) $(patsubst %.bin, %.o, $(simpletests) $(tests)) diff --git a/kvm/user/config-x86-common.mak b/kvm/user/config-x86-common.mak new file mode 100644 index 000000000..63cca421d --- /dev/null +++ b/kvm/user/config-x86-common.mak @@ -0,0 +1,77 @@ +#This is a make file with common rules for both x86 & x86-64 + +CFLAGS += -I../include/x86 + +all: kvmtrace test_cases + +balloon_ctl: balloon_ctl.o + +cflatobjs += \ + test/lib/x86/io.o \ + test/lib/x86/smp.o + +cflatobjs += test/lib/x86/fwcfg.o +cflatobjs += test/lib/x86/apic.o + +$(libcflat): LDFLAGS += -nostdlib +$(libcflat): CFLAGS += -ffreestanding -I test/lib + +CFLAGS += -m$(bits) + +FLATLIBS = test/lib/libcflat.a $(libgcc) +%.flat: %.o $(FLATLIBS) + $(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,flat.lds $^ $(FLATLIBS) + +tests-common = $(TEST_DIR)/bootstrap \ + $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ + $(TEST_DIR)/smptest.flat $(TEST_DIR)/port80.flat \ + $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat + +test_cases: $(tests-common) $(tests) + +$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I test/lib -I test/lib/x86 + +$(TEST_DIR)/bootstrap: $(TEST_DIR)/bootstrap.o + $(CC) -nostdlib -o $@ -Wl,-T,bootstrap.lds $^ + +$(TEST_DIR)/access.flat: $(cstart.o) $(TEST_DIR)/access.o $(TEST_DIR)/print.o + +$(TEST_DIR)/hypercall.flat: $(cstart.o) $(TEST_DIR)/hypercall.o $(TEST_DIR)/print.o + +$(TEST_DIR)/sieve.flat: $(cstart.o) $(TEST_DIR)/sieve.o \ + $(TEST_DIR)/print.o $(TEST_DIR)/vm.o + +$(TEST_DIR)/vmexit.flat: $(cstart.o) $(TEST_DIR)/vmexit.o + +$(TEST_DIR)/test32.flat: $(TEST_DIR)/test32.o + +$(TEST_DIR)/smptest.flat: $(cstart.o) $(TEST_DIR)/smptest.o + +$(TEST_DIR)/emulator.flat: $(cstart.o) $(TEST_DIR)/emulator.o \ + $(TEST_DIR)/vm.o $(TEST_DIR)/print.o + +$(TEST_DIR)/port80.flat: $(cstart.o) $(TEST_DIR)/port80.o + +$(TEST_DIR)/tsc.flat: $(cstart.o) $(TEST_DIR)/tsc.o + +$(TEST_DIR)/apic.flat: $(cstart.o) $(TEST_DIR)/apic.o $(TEST_DIR)/vm.o \ + $(TEST_DIR)/print.o + +$(TEST_DIR)/realmode.flat: $(TEST_DIR)/realmode.o + $(CC) -m32 -nostdlib -o $@ -Wl,-T,$(TEST_DIR)/realmode.lds $^ + +$(TEST_DIR)/realmode.o: bits = 32 + +$(TEST_DIR)/memtest1.flat: $(TEST_DIR)/memtest1.o + +$(TEST_DIR)/stringio.flat: $(TEST_DIR)/stringio.o + +$(TEST_DIR)/simple.flat: $(TEST_DIR)/simple.o + +$(TEST_DIR)/msr.flat: $(cstart.o) $(TEST_DIR)/msr.o + +arch_clean: + $(RM) $(TEST_DIR)/bootstrap $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \ + $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o + +-include $(TEST_DIR)/.*.d test/lib/.*.d test/lib/x86/.*.d diff --git a/kvm/user/config-x86_64.mak b/kvm/user/config-x86_64.mak new file mode 100644 index 000000000..97990bb53 --- /dev/null +++ b/kvm/user/config-x86_64.mak @@ -0,0 +1,12 @@ +TEST_DIR=test/x86 +cstart.o = $(TEST_DIR)/cstart64.o +bits = 64 +ldarch = elf64-x86-64 +CFLAGS += -D__x86_64__ + +tests = $(TEST_DIR)/access.flat $(TEST_DIR)/sieve.flat \ + $(TEST_DIR)/simple.flat $(TEST_DIR)/stringio.flat \ + $(TEST_DIR)/memtest1.flat $(TEST_DIR)/emulator.flat \ + $(TEST_DIR)/hypercall.flat $(TEST_DIR)/apic.flat + +include config-x86-common.mak diff --git a/kvm/user/configure b/kvm/user/configure new file mode 100755 index 000000000..efb8705fc --- /dev/null +++ b/kvm/user/configure @@ -0,0 +1,75 @@ +#!/bin/bash + +prefix=/usr/local +kerneldir=/lib/modules/$(uname -r)/build +cc=gcc +ld=ld +objcopy=objcopy +ar=ar +arch=`uname -m | sed -e s/i.86/i386/` +processor="$arch" +cross_prefix= + +usage() { + cat <<-EOF + Usage: $0 [options] + + Options include: + --arch=ARCH architecture to compile for ($arch) + --cross-prefix=PREFIX cross compiler prefix + --cc=CC c compiler to use ($cc) + --ld=LD ld linker to use ($ld) + --prefix=PREFIX where to install things ($prefix) + --kerneldir=DIR kernel build directory for kvm.h ($kerneldir) +EOF + exit 1 +} + +while [[ "$1" = -* ]]; do + opt="$1"; shift + arg= + if [[ "$opt" = *=* ]]; then + arg="${opt#*=}" + opt="${opt%%=*}" + fi + case "$opt" in + --prefix) + prefix="$arg" + ;; + --kerneldir) + kerneldir="$arg" + ;; + --arch) + arch="$arg" + ;; + --processor) + processor="$arg" + ;; + --cross-prefix) + cross_prefix="$arg" + ;; + --cc) + cc="$arg" + ;; + --ld) + ld="$arg" + ;; + --help) + usage + ;; + *) + usage + ;; + esac +done + +cat <<EOF > config.mak +PREFIX=$prefix +KERNELDIR=$(readlink -f $kerneldir) +ARCH=$arch +PROCESSOR=$processor +CC=$cross_prefix$cc +LD=$cross_prefix$ld +OBJCOPY=$cross_prefix$objcopy +AR=$cross_prefix$ar +EOF diff --git a/kvm/user/flat.lds b/kvm/user/flat.lds new file mode 100644 index 000000000..412059501 --- /dev/null +++ b/kvm/user/flat.lds @@ -0,0 +1,15 @@ +SECTIONS +{ + . = 4M + SIZEOF_HEADERS; + stext = .; + .text : { *(.init) *(.text) *(.text.*) } + . = ALIGN(4K); + .data : { *(.data) } + . = ALIGN(16); + .rodata : { *(.rodata) } + . = ALIGN(16); + .bss : { *(.bss) } + . = ALIGN(4K); + edata = .; +} + diff --git a/kvm/user/formats b/kvm/user/formats new file mode 100644 index 000000000..7f4ebdbce --- /dev/null +++ b/kvm/user/formats @@ -0,0 +1,31 @@ +0x00000000 %(ts)d (+%(relts)12d) unknown (0x%(event)016x) vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ 0x%(1)08x 0x%(2)08x 0x%(3)08x 0x%(4)08x 0x%(5)08x ] + +0x00010001 %(ts)d (+%(relts)12d) VMENTRY vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x +0x00010002 %(ts)d (+%(relts)12d) VMEXIT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ exitcode = 0x%(1)08x, rip = 0x%(3)08x %(2)08x ] +0x00020001 %(ts)d (+%(relts)12d) PAGE_FAULT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ] +0x00020002 %(ts)d (+%(relts)12d) INJ_VIRQ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] +0x00020003 %(ts)d (+%(relts)12d) REDELIVER_EVT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] +0x00020004 %(ts)d (+%(relts)12d) PEND_INTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] +0x00020005 %(ts)d (+%(relts)12d) IO_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ] +0x00020006 %(ts)d (+%(relts)12d) IO_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ] +0x00020007 %(ts)d (+%(relts)12d) CR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ] +0x00020008 %(ts)d (+%(relts)12d) CR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ] +0x00020009 %(ts)d (+%(relts)12d) DR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ] +0x0002000A %(ts)d (+%(relts)12d) DR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ] +0x0002000B %(ts)d (+%(relts)12d) MSR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ] +0x0002000C %(ts)d (+%(relts)12d) MSR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ] +0x0002000D %(ts)d (+%(relts)12d) CPUID vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ func = 0x%(1)08x, eax = 0x%(2)08x, ebx = 0x%(3)08x, ecx = 0x%(4)08x edx = 0x%(5)08x] +0x0002000E %(ts)d (+%(relts)12d) INTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] +0x0002000F %(ts)d (+%(relts)12d) NMI vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x +0x00020010 %(ts)d (+%(relts)12d) VMMCALL vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ func = 0x%(1)08x ] +0x00020011 %(ts)d (+%(relts)12d) HLT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x +0x00020012 %(ts)d (+%(relts)12d) CLTS vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x +0x00020013 %(ts)d (+%(relts)12d) LMSW vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ value = 0x%(1)08x ] +0x00020014 %(ts)d (+%(relts)12d) APIC_ACCESS vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ offset = 0x%(1)08x ] +0x00020015 %(ts)d (+%(relts)12d) TDP_FAULT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ] +# ppc: tlb traces +0x00020016 GTLB_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ] +0x00020017 STLB_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ] +0x00020018 STLB_INVAL vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ] +# ppc: instruction emulation - this type is handled more complex in kvmtrace_format, but listed to show the eventid and transported data +#0x00020019 %(ts)d (+%(relts)12d) PPC_INSTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ instr = 0x%(1)08x, pc = 0x%(2)08x, emul = 0x%(3)08x, nsec = %(4)08d ] diff --git a/kvm/user/iotable.c b/kvm/user/iotable.c new file mode 100644 index 000000000..91a5016c4 --- /dev/null +++ b/kvm/user/iotable.c @@ -0,0 +1,53 @@ +/* + * Kernel-based Virtual Machine test driver + * + * This test driver provides a simple way of testing kvm, without a full + * device model. + * + * Copyright (C) 2006 Qumranet + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> + +#include "iotable.h" + +struct io_table_entry *io_table_lookup(struct io_table *io_table, uint64_t addr) +{ + int i; + + for (i = 0; i < io_table->nr_entries; i++) { + if (io_table->entries[i].start <= addr && + addr < io_table->entries[i].end) + return &io_table->entries[i]; + } + + return NULL; +} + +int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size, + io_table_handler_t *handler, void *opaque) +{ + struct io_table_entry *entry; + + if (io_table->nr_entries == MAX_IO_TABLE) + return -ENOSPC; + + entry = &io_table->entries[io_table->nr_entries]; + io_table->nr_entries++; + + entry->start = start; + entry->end = start + size; + entry->handler = handler; + entry->opaque = opaque; + + return 0; +} diff --git a/kvm/user/iotable.h b/kvm/user/iotable.h new file mode 100644 index 000000000..cb18f2378 --- /dev/null +++ b/kvm/user/iotable.h @@ -0,0 +1,40 @@ +/* + * Kernel-based Virtual Machine test driver + * + * This test driver provides a simple way of testing kvm, without a full + * device model. + * + * Copyright (C) 2006 Qumranet + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#include <stdint.h> + +#define MAX_IO_TABLE 50 + +typedef int (io_table_handler_t)(void *, int, int, uint64_t, uint64_t *); + +struct io_table_entry +{ + uint64_t start; + uint64_t end; + io_table_handler_t *handler; + void *opaque; +}; + +struct io_table +{ + int nr_entries; + struct io_table_entry entries[MAX_IO_TABLE]; +}; + +struct io_table_entry *io_table_lookup(struct io_table *io_table, + uint64_t addr); +int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size, + io_table_handler_t *handler, void *opaque); diff --git a/kvm/user/kvmtrace.c b/kvm/user/kvmtrace.c new file mode 100644 index 000000000..de3c1897f --- /dev/null +++ b/kvm/user/kvmtrace.c @@ -0,0 +1,706 @@ +/* + * kvm tracing application + * + * This tool is used for collecting trace buffer data + * for kvm trace. + * + * Based on blktrace 0.99.3 + * + * Copyright (C) 2005 Jens Axboe <axboe@suse.de> + * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk> + * Copyright (C) 2008 Eric Liu <eric.e.liu@intel.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/statfs.h> +#include <sys/poll.h> +#include <sys/mman.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <getopt.h> +#include <errno.h> +#include <sched.h> + +#ifndef __user +#define __user +#endif +#include <linux/kvm.h> + +static char kvmtrace_version[] = "0.1"; + +/* + * You may want to increase this even more, if you are logging at a high + * rate and see skipped/missed events + */ +#define BUF_SIZE (512 * 1024) +#define BUF_NR (8) + +#define OFILE_BUF (128 * 1024) + +#define DEBUGFS_TYPE 0x64626720 + +#define max(a, b) ((a) > (b) ? (a) : (b)) + +#define S_OPTS "r:o:w:?Vb:n:D:" +static struct option l_opts[] = { + { + .name = "relay", + .has_arg = required_argument, + .flag = NULL, + .val = 'r' + }, + { + .name = "output", + .has_arg = required_argument, + .flag = NULL, + .val = 'o' + }, + { + .name = "stopwatch", + .has_arg = required_argument, + .flag = NULL, + .val = 'w' + }, + { + .name = "version", + .has_arg = no_argument, + .flag = NULL, + .val = 'V' + }, + { + .name = "buffer-size", + .has_arg = required_argument, + .flag = NULL, + .val = 'b' + }, + { + .name = "num-sub-buffers", + .has_arg = required_argument, + .flag = NULL, + .val = 'n' + }, + { + .name = "output-dir", + .has_arg = required_argument, + .flag = NULL, + .val = 'D' + }, + { + .name = NULL, + } +}; + +struct thread_information { + int cpu; + pthread_t thread; + + int fd; + char fn[MAXPATHLEN + 64]; + + FILE *ofile; + char *ofile_buffer; + + int (*get_subbuf)(struct thread_information *, unsigned int); + int (*read_data)(struct thread_information *, void *, unsigned int); + + unsigned long long data_read; + + struct kvm_trace_information *trace_info; + + int exited; + + /* + * mmap controlled output files + */ + unsigned long long fs_size; + unsigned long long fs_max_size; + unsigned long fs_off; + void *fs_buf; + unsigned long fs_buf_len; + +}; + +struct kvm_trace_information { + int fd; + volatile int trace_started; + unsigned long lost_records; + struct thread_information *threads; + unsigned long buf_size; + unsigned long buf_nr; +}; + +static struct kvm_trace_information trace_information; + +static int ncpus; +static char default_debugfs_path[] = "/sys/kernel/debug"; + +/* command line option globals */ +static char *debugfs_path; +static char *output_name; +static char *output_dir; +static int stop_watch; +static unsigned long buf_size = BUF_SIZE; +static unsigned long buf_nr = BUF_NR; +static unsigned int page_size; + +#define for_each_cpu_online(cpu) \ + for (cpu = 0; cpu < ncpus; cpu++) +#define for_each_tip(tip, i) \ + for (i = 0, tip = trace_information.threads; i < ncpus; i++, tip++) + +#define is_done() (*(volatile int *)(&done)) +static volatile int done; + +#define is_trace_stopped() (*(volatile int *)(&trace_stopped)) +static volatile int trace_stopped; + +static void exit_trace(int status); + +static void handle_sigint(__attribute__((__unused__)) int sig) +{ + ioctl(trace_information.fd, KVM_TRACE_PAUSE); + done = 1; +} + +static int get_lost_records() +{ + int fd; + char tmp[MAXPATHLEN + 64]; + + snprintf(tmp, sizeof(tmp), "%s/kvm/lost_records", debugfs_path); + fd = open(tmp, O_RDONLY); + if (fd < 0) { + /* + * this may be ok, if the kernel doesn't support dropped counts + */ + if (errno == ENOENT) + return 0; + + fprintf(stderr, "Couldn't open dropped file %s\n", tmp); + return -1; + } + + if (read(fd, tmp, sizeof(tmp)) < 0) { + perror(tmp); + close(fd); + return -1; + } + close(fd); + + return atoi(tmp); +} + +static void wait_for_data(struct thread_information *tip, int timeout) +{ + struct pollfd pfd = { .fd = tip->fd, .events = POLLIN }; + + while (!is_done()) { + if (poll(&pfd, 1, timeout) < 0) { + perror("poll"); + break; + } + if (pfd.revents & POLLIN) + break; + } +} + +static int read_data(struct thread_information *tip, void *buf, + unsigned int len) +{ + int ret = 0; + + do { + wait_for_data(tip, 100); + + ret = read(tip->fd, buf, len); + + if (!ret) + continue; + else if (ret > 0) + return ret; + else { + if (errno != EAGAIN) { + perror(tip->fn); + fprintf(stderr, "Thread %d failed read of %s\n", + tip->cpu, tip->fn); + break; + } + continue; + } + } while (!is_done()); + + return ret; + +} + +/* + * For file output, truncate and mmap the file appropriately + */ +static int mmap_subbuf(struct thread_information *tip, unsigned int maxlen) +{ + int ofd = fileno(tip->ofile); + int ret; + unsigned long nr; + unsigned long size; + + /* + * extend file, if we have to. use chunks of 16 subbuffers. + */ + if (tip->fs_off + maxlen > tip->fs_buf_len) { + if (tip->fs_buf) { + munlock(tip->fs_buf, tip->fs_buf_len); + munmap(tip->fs_buf, tip->fs_buf_len); + tip->fs_buf = NULL; + } + + tip->fs_off = tip->fs_size & (page_size - 1); + nr = max(16, tip->trace_info->buf_nr); + size = tip->trace_info->buf_size; + tip->fs_buf_len = (nr * size) - tip->fs_off; + tip->fs_max_size += tip->fs_buf_len; + + if (ftruncate(ofd, tip->fs_max_size) < 0) { + perror("ftruncate"); + return -1; + } + + tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE, + MAP_SHARED, ofd, tip->fs_size - tip->fs_off); + if (tip->fs_buf == MAP_FAILED) { + perror("mmap"); + return -1; + } + mlock(tip->fs_buf, tip->fs_buf_len); + } + + ret = tip->read_data(tip, tip->fs_buf + tip->fs_off, maxlen); + if (ret >= 0) { + tip->data_read += ret; + tip->fs_size += ret; + tip->fs_off += ret; + return 0; + } + + return -1; +} + +static void tip_ftrunc_final(struct thread_information *tip) +{ + /* + * truncate to right size and cleanup mmap + */ + if (tip->ofile) { + int ofd = fileno(tip->ofile); + + if (tip->fs_buf) + munmap(tip->fs_buf, tip->fs_buf_len); + + ftruncate(ofd, tip->fs_size); + } +} + +static void *thread_main(void *arg) +{ + struct thread_information *tip = arg; + pid_t pid = getpid(); + cpu_set_t cpu_mask; + + CPU_ZERO(&cpu_mask); + CPU_SET((tip->cpu), &cpu_mask); + + if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) { + perror("sched_setaffinity"); + exit_trace(1); + } + + snprintf(tip->fn, sizeof(tip->fn), "%s/kvm/trace%d", + debugfs_path, tip->cpu); + tip->fd = open(tip->fn, O_RDONLY); + if (tip->fd < 0) { + perror(tip->fn); + fprintf(stderr, "Thread %d failed open of %s\n", tip->cpu, + tip->fn); + exit_trace(1); + } + while (!is_done()) { + if (tip->get_subbuf(tip, tip->trace_info->buf_size) < 0) + break; + } + + /* + * trace is stopped, pull data until we get a short read + */ + while (tip->get_subbuf(tip, tip->trace_info->buf_size) > 0) + ; + + tip_ftrunc_final(tip); + tip->exited = 1; + return NULL; +} + +static int fill_ofname(struct thread_information *tip, char *dst) +{ + struct stat sb; + int len = 0; + + if (output_dir) + len = sprintf(dst, "%s/", output_dir); + else + len = sprintf(dst, "./"); + + if (stat(dst, &sb) < 0) { + if (errno != ENOENT) { + perror("stat"); + return 1; + } + if (mkdir(dst, 0755) < 0) { + perror(dst); + fprintf(stderr, "Can't make output dir\n"); + return 1; + } + } + + sprintf(dst + len, "%s.kvmtrace.%d", output_name, tip->cpu); + + return 0; +} + +static void fill_ops(struct thread_information *tip) +{ + tip->get_subbuf = mmap_subbuf; + tip->read_data = read_data; +} + +static void close_thread(struct thread_information *tip) +{ + if (tip->fd != -1) + close(tip->fd); + if (tip->ofile) + fclose(tip->ofile); + if (tip->ofile_buffer) + free(tip->ofile_buffer); + + tip->fd = -1; + tip->ofile = NULL; + tip->ofile_buffer = NULL; +} + +static int tip_open_output(struct thread_information *tip) +{ + int mode, vbuf_size; + char op[NAME_MAX]; + + if (fill_ofname(tip, op)) + return 1; + + tip->ofile = fopen(op, "w+"); + mode = _IOFBF; + vbuf_size = OFILE_BUF; + + if (tip->ofile == NULL) { + perror(op); + return 1; + } + + tip->ofile_buffer = malloc(vbuf_size); + if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) { + perror("setvbuf"); + close_thread(tip); + return 1; + } + + fill_ops(tip); + return 0; +} + +static int start_threads(int cpu) +{ + struct thread_information *tip; + + tip = trace_information.threads + cpu; + tip->cpu = cpu; + tip->trace_info = &trace_information; + tip->fd = -1; + + if (tip_open_output(tip)) + return 1; + + if (pthread_create(&tip->thread, NULL, thread_main, tip)) { + perror("pthread_create"); + close_thread(tip); + return 1; + } + + return 0; +} + +static void stop_threads() +{ + struct thread_information *tip; + unsigned long ret; + int i; + + for_each_tip(tip, i) { + if (tip->thread) + (void) pthread_join(tip->thread, (void *) &ret); + close_thread(tip); + } +} + +static int start_trace(void) +{ + int fd; + struct kvm_user_trace_setup kuts; + + fd = trace_information.fd = open("/dev/kvm", O_RDWR); + if (fd == -1) { + perror("/dev/kvm"); + return 1; + } + + memset(&kuts, 0, sizeof(kuts)); + kuts.buf_size = trace_information.buf_size = buf_size; + kuts.buf_nr = trace_information.buf_nr = buf_nr; + + if (ioctl(trace_information.fd , KVM_TRACE_ENABLE, &kuts) < 0) { + perror("KVM_TRACE_ENABLE"); + close(fd); + return 1; + } + trace_information.trace_started = 1; + + return 0; +} + +static void cleanup_trace(void) +{ + if (trace_information.fd == -1) + return; + + trace_information.lost_records = get_lost_records(); + + if (trace_information.trace_started) { + trace_information.trace_started = 0; + if (ioctl(trace_information.fd, KVM_TRACE_DISABLE) < 0) + perror("KVM_TRACE_DISABLE"); + } + + close(trace_information.fd); + trace_information.fd = -1; +} + +static void stop_all_traces(void) +{ + if (!is_trace_stopped()) { + trace_stopped = 1; + stop_threads(); + cleanup_trace(); + } +} + +static void exit_trace(int status) +{ + stop_all_traces(); + exit(status); +} + +static int start_kvm_trace(void) +{ + int i, size; + struct thread_information *tip; + + size = ncpus * sizeof(struct thread_information); + tip = malloc(size); + if (!tip) { + fprintf(stderr, "Out of memory, threads (%d)\n", size); + return 1; + } + memset(tip, 0, size); + trace_information.threads = tip; + + if (start_trace()) + return 1; + + for_each_cpu_online(i) { + if (start_threads(i)) { + fprintf(stderr, "Failed to start worker threads\n"); + break; + } + } + + if (i != ncpus) { + stop_threads(); + cleanup_trace(); + return 1; + } + + return 0; +} + +static void wait_for_threads(void) +{ + struct thread_information *tip; + int i, tips_running; + + do { + tips_running = 0; + usleep(100000); + + for_each_tip(tip, i) + tips_running += !tip->exited; + + } while (tips_running); +} + +static void show_stats(void) +{ + struct thread_information *tip; + unsigned long long data_read; + int i; + + data_read = 0; + for_each_tip(tip, i) { + printf(" CPU%3d: %8llu KiB data\n", + tip->cpu, (tip->data_read + 1023) >> 10); + data_read += tip->data_read; + } + + printf(" Total: lost %lu, %8llu KiB data\n", + trace_information.lost_records, (data_read + 1023) >> 10); + + if (trace_information.lost_records) + fprintf(stderr, "You have lost records, " + "consider using a larger buffer size (-b)\n"); +} + +static char usage_str[] = \ + "[ -r debugfs path ] [ -D output dir ] [ -b buffer size ]\n" \ + "[ -n number of buffers] [ -o <output file> ] [ -w time ] [ -V ]\n\n" \ + "\t-r Path to mounted debugfs, defaults to /sys/kernel/debug\n" \ + "\t-o File(s) to send output to\n" \ + "\t-D Directory to prepend to output file names\n" \ + "\t-w Stop after defined time, in seconds\n" \ + "\t-b Sub buffer size in KiB\n" \ + "\t-n Number of sub buffers\n" \ + "\t-V Print program version info\n\n"; + +static void show_usage(char *prog) +{ + fprintf(stderr, "Usage: %s %s %s", prog, kvmtrace_version, usage_str); + exit(EXIT_FAILURE); +} + +void parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) { + switch (c) { + case 'r': + debugfs_path = optarg; + break; + case 'o': + output_name = optarg; + break; + case 'w': + stop_watch = atoi(optarg); + if (stop_watch <= 0) { + fprintf(stderr, + "Invalid stopwatch value (%d secs)\n", + stop_watch); + exit(EXIT_FAILURE); + } + break; + case 'V': + printf("%s version %s\n", argv[0], kvmtrace_version); + exit(EXIT_SUCCESS); + case 'b': + buf_size = strtoul(optarg, NULL, 10); + if (buf_size <= 0 || buf_size > 16*1024) { + fprintf(stderr, + "Invalid buffer size (%lu)\n", + buf_size); + exit(EXIT_FAILURE); + } + buf_size <<= 10; + break; + case 'n': + buf_nr = strtoul(optarg, NULL, 10); + if (buf_nr <= 0) { + fprintf(stderr, + "Invalid buffer nr (%lu)\n", buf_nr); + exit(EXIT_FAILURE); + } + break; + case 'D': + output_dir = optarg; + break; + default: + show_usage(argv[0]); + } + } + + if (optind < argc || output_name == NULL) + show_usage(argv[0]); +} + +int main(int argc, char *argv[]) +{ + struct statfs st; + + parse_args(argc, argv); + + if (!debugfs_path) + debugfs_path = default_debugfs_path; + + if (statfs(debugfs_path, &st) < 0) { + perror("statfs"); + fprintf(stderr, "%s does not appear to be a valid path\n", + debugfs_path); + return 1; + } else if (st.f_type != (long) DEBUGFS_TYPE) { + fprintf(stderr, "%s does not appear to be a debug filesystem," + " please mount debugfs.\n", + debugfs_path); + return 1; + } + + page_size = getpagesize(); + + ncpus = sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 0) { + fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n"); + return 1; + } + + signal(SIGINT, handle_sigint); + signal(SIGHUP, handle_sigint); + signal(SIGTERM, handle_sigint); + signal(SIGALRM, handle_sigint); + signal(SIGPIPE, SIG_IGN); + + if (start_kvm_trace() != 0) + return 1; + + if (stop_watch) + alarm(stop_watch); + + wait_for_threads(); + stop_all_traces(); + show_stats(); + + return 0; +} diff --git a/kvm/user/kvmtrace_format b/kvm/user/kvmtrace_format new file mode 100755 index 000000000..6556475f7 --- /dev/null +++ b/kvm/user/kvmtrace_format @@ -0,0 +1,532 @@ +#!/usr/bin/env python + +# by Mark Williamson, (C) 2004 Intel Research Cambridge + +# Program for reformatting trace buffer output according to user-supplied rules + +import re, sys, string, signal, struct, os, getopt, operator + +PREFIX = '/usr' +DATADIR = os.path.join(PREFIX, 'share') +KVMDIR = os.path.join(DATADIR, 'kvm') +FORMATS_FILE = os.path.join(KVMDIR, 'formats') + +def usage(): + print >> sys.stderr, \ + "Usage: " + sys.argv[0] + """ defs-file + Parses trace data in binary format, as output by kvmtrace and + reformats it according to the rules in a file of definitions. The + rules in this file should have the format ({ and } show grouping + and are not part of the syntax): + + {event_id}{whitespace}{text format string} + + The textual format string may include format specifiers, such as: + %(ts)d, %(event)d, %(pid)d %(vcpu)d %(1)d, %(2)d, + %(3)d, %(4)d, %(5)d + [ the 'd' format specifier outputs in decimal, alternatively 'x' + will output in hexadecimal and 'o' will output in octal ] + + Which correspond to the event ID, timestamp counter, pid + , vcpu and the 5 data fields from the trace record. There should be + one such rule for each type of event. + Depending on your system and the volume of trace buffer data, + this script may not be able to keep up with the output of kvmtrace + if it is piped directly. In these circumstances you should have + kvmtrace output to a file for processing off-line. + + kvmtrace_format has the following additional switches + -s - if this switch is set additional trace statistics are + created and printed at the end of the output + """ + sys.exit(1) + +def read_defs(defs_file): + defs = {} + + fd = open(defs_file) + + reg = re.compile('(\S+)\s+(\S.*)') + + while True: + line = fd.readline() + if not line: + break + + if line[0] == '#' or line[0] == '\n': + continue + + m = reg.match(line) + + if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1) + + defs[str(eval(m.group(1)))] = m.group(2) + + return defs + +def sighand(x,y): + global interrupted + interrupted = 1 + +# ppc instruction decoding for event type 0x00020019 (PPC_INSTR) +# some globals for statistic summaries +stat_ppc_instr_mnemonic = {}; +stat_ppc_instr_spr = {}; +stat_ppc_instr_dcr = {}; +stat_ppc_instr_tlb = {}; + +def ppc_instr_print_summary(sortedlist, colname): + print "\n\n%14s + %10s" % (colname, "count") + print "%s" % (15*"-"+"+"+11*"-") + sum = 0 + for value, key in sortedlist: + sum += key + print "%14s | %10d" % (value, key) + print "%14s = %10d" % ("sum", sum) + + +def ppc_instr_summary(): + # don't print empty statistics + if stat_ppc_instr_mnemonic: + ppc_instr_print_summary(sorted(stat_ppc_instr_mnemonic.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic") + if stat_ppc_instr_spr: + ppc_instr_print_summary(sorted(stat_ppc_instr_spr.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-spr") + if stat_ppc_instr_dcr: + ppc_instr_print_summary(sorted(stat_ppc_instr_dcr.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-dcr") + if stat_ppc_instr_tlb: + ppc_instr_print_summary(sorted(stat_ppc_instr_tlb.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-tlb") + +def get_op(instr): + return (instr >> 26); + +def get_xop(instr): + return (instr >> 1) & 0x3ff; + +def get_sprn(instr): + return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0) + +def get_dcrn(instr): + return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0); + +def get_tlbwe_type(instr): + ws = (instr >> 11) & 0x1f; + if ws == 0: + return "PAGEID" + elif ws == 1: + return "XLAT" + elif ws == 2: + return "ATTRIB" + else: + return "UNKNOWN" + +def get_name(instr): + if get_op(instr)==3: + return "trap" + elif get_op(instr)==19: + if get_xop(instr) == 50: + return "rfi" + else: + return "unknown" + elif get_op(instr)==31: + if get_xop(instr) == 83: + return "mfmsr" + + elif get_xop(instr) == 87: + return "lbzx" + + elif get_xop(instr) == 131: + return "wrtee" + + elif get_xop(instr) == 146: + return "mtmsr" + + elif get_xop(instr) == 163: + return "wrteei" + + elif get_xop(instr) == 215: + return "stbx" + + elif get_xop(instr) == 247: + return "stbux" + + elif get_xop(instr) == 279: + return "lhzx" + + elif get_xop(instr) == 311: + return "lhzux" + + elif get_xop(instr) == 323: + return "mfdcr" + + elif get_xop(instr) == 339: + return "mfspr" + + elif get_xop(instr) == 407: + return "sthx" + + elif get_xop(instr) == 439: + return "sthux" + + elif get_xop(instr) == 451: + return "mtdcr" + + elif get_xop(instr) == 467: + return "mtspr" + + elif get_xop(instr) == 470: + return "dcbi" + + elif get_xop(instr) == 534: + return "lwbrx" + + elif get_xop(instr) == 566: + return "tlbsync" + + elif get_xop(instr) == 662: + return "stwbrx" + + elif get_xop(instr) == 978: + return "tlbwe" + + elif get_xop(instr) == 914: + return "tlbsx" + + elif get_xop(instr) == 790: + return "lhbrx" + + elif get_xop(instr) == 918: + return "sthbrx" + + elif get_xop(instr) == 966: + return "iccci" + + else: + return "unknown" + + elif get_op(instr) == 32: + return "lwz" + + elif get_op(instr) == 33: + return "lwzu" + + elif get_op(instr) == 34: + return "lbz" + + elif get_op(instr) == 35: + return "lbzu" + + elif get_op(instr) == 36: + return "stw" + + elif get_op(instr) == 37: + return "stwu" + + elif get_op(instr) == 38: + return "stb" + + elif get_op(instr) == 39: + return "stbu" + + elif get_op(instr) == 40: + return "lhz" + + elif get_op(instr) == 41: + return "lhzu" + + elif get_op(instr) == 44: + return "sth" + + elif get_op(instr) == 45: + return "sthu" + + else: + return "unknown" + +def get_sprn_name(sprn): + if sprn == 0x01a: + return "SRR0" + elif sprn == 0x01b: + return "SRR1" + elif sprn == 0x3b2: + return "MMUCR" + elif sprn == 0x030: + return "PID" + elif sprn == 0x03f: + return "IVPR" + elif sprn == 0x3b3: + return "CCR0" + elif sprn == 0x378: + return "CCR1" + elif sprn == 0x11f: + return "PVR" + elif sprn == 0x03d: + return "DEAR" + elif sprn == 0x03e: + return "ESR" + elif sprn == 0x134: + return "DBCR0" + elif sprn == 0x135: + return "DBCR1" + elif sprn == 0x11c: + return "TBWL" + elif sprn == 0x11d: + return "TBWU" + elif sprn == 0x016: + return "DEC" + elif sprn == 0x150: + return "TSR" + elif sprn == 0x154: + return "TCR" + elif sprn == 0x110: + return "SPRG0" + elif sprn == 0x111: + return "SPRG1" + elif sprn == 0x112: + return "SPRG2" + elif sprn == 0x113: + return "SPRG3" + elif sprn == 0x114: + return "SPRG4" + elif sprn == 0x115: + return "SPRG5" + elif sprn == 0x116: + return "SPRG6" + elif sprn == 0x117: + return "SPRG7" + elif sprn == 0x190: + return "IVOR0" + elif sprn == 0x191: + return "IVOR1" + elif sprn == 0x192: + return "IVOR2" + elif sprn == 0x193: + return "IVOR3" + elif sprn == 0x194: + return "IVOR4" + elif sprn == 0x195: + return "IVOR5" + elif sprn == 0x196: + return "IVOR6" + elif sprn == 0x197: + return "IVOR7" + elif sprn == 0x198: + return "IVOR8" + elif sprn == 0x199: + return "IVOR9" + elif sprn == 0x19a: + return "IVOR10" + elif sprn == 0x19b: + return "IVOR11" + elif sprn == 0x19c: + return "IVOR12" + elif sprn == 0x19d: + return "IVOR13" + elif sprn == 0x19e: + return "IVOR14" + elif sprn == 0x19f: + return "IVOR15" + else: + return "UNKNOWN" + +def get_special(instr): + name = get_name(instr); + if stat_ppc_instr_mnemonic.has_key(name): + stat_ppc_instr_mnemonic[name] += 1 + else: + stat_ppc_instr_mnemonic[name] = 1 + + if get_op(instr) == 31: + if (get_xop(instr) == 339) or (get_xop(instr) == 467): + sprn = get_sprn(instr); + sprn_name = get_sprn_name(sprn); + stat_idx = name+"-"+sprn_name + if stat_ppc_instr_spr.has_key(stat_idx): + stat_ppc_instr_spr[stat_idx] += 1 + else: + stat_ppc_instr_spr[stat_idx] = 1 + return ("- sprn 0x%03x %8s" % (sprn, sprn_name)) + elif (get_xop(instr) == 323 ) or (get_xop(instr) == 451): + dcrn = get_dcrn(instr); + stat_idx = name+"-"+("%04X"%dcrn) + if stat_ppc_instr_dcr.has_key(stat_idx): + stat_ppc_instr_dcr[stat_idx] += 1 + else: + stat_ppc_instr_dcr[stat_idx] = 1 + return ("- dcrn 0x%03x" % dcrn) + elif (get_xop(instr) == 978 ) or (get_xop(instr) == 451): + tlbwe_type = get_tlbwe_type(instr) + stat_idx = name+"-"+tlbwe_type + if stat_ppc_instr_tlb.has_key(stat_idx): + stat_ppc_instr_tlb[stat_idx] += 1 + else: + stat_ppc_instr_tlb[stat_idx] = 1 + return ("- ws -> %8s" % tlbwe_type) + return "" + +##### Main code + +summary = False + +try: + opts, arg = getopt.getopt(sys.argv[1:], "sc:" ) + for opt in opts: + if opt[0] == '-s' : summary = True + +except getopt.GetoptError: + usage() + +signal.signal(signal.SIGTERM, sighand) +signal.signal(signal.SIGHUP, sighand) +signal.signal(signal.SIGINT, sighand) + +interrupted = 0 + +if len(arg) > 0: + defs = read_defs(arg[0]) +else: + defs = read_defs(FORMATS_FILE) + +# structure of trace record (as output by kvmtrace): +# HDR(I) {TSC(Q)} D1(I) D2(I) D3(I) D4(I) D5(I) +# +# HDR consists of EVENT:28:, n_data:3:, ts_in:1: +# pid:32, vcpu_id:32 +# EVENT means Event ID +# n_data means number of data (like D1, D2, ...) +# ts_in means Timestamp data exists(1) or not(0). +# if ts_in == 0, TSC(Q) does not exists. +# +HDRREC = "<III" +TSCREC = "<Q" +D1REC = "<I" +D2REC = "<II" +D3REC = "<III" +D4REC = "<IIII" +D5REC = "<IIIII" +KMAGIC = "<I" + +last_ts = 0 + +i=0 + +while not interrupted: + try: + i=i+1 + + if i == 1: + line = sys.stdin.read(struct.calcsize(KMAGIC)) + if not line: + break + kmgc = struct.unpack(KMAGIC, line)[0] + + #firstly try to parse data file as little endian + # if "kvmtrace-metadata".kmagic != kmagic + # then data file must be big endian" + if kmgc != 0x12345678: + if kmgc != 0x78563412: + print >> sys.stderr, "Bad data file: magic number error." + break; + else: + HDRREC = ">III" + TSCREC = ">Q" + D1REC = ">I" + D2REC = ">II" + D3REC = ">III" + D4REC = ">IIII" + D5REC = ">IIIII" + continue + + line = sys.stdin.read(struct.calcsize(HDRREC)) + if not line: + break + (event, pid, vcpu_id) = struct.unpack(HDRREC, line) + + n_data = event >> 28 & 0x7 + ts_in = event >> 31 + + d1 = 0 + d2 = 0 + d3 = 0 + d4 = 0 + d5 = 0 + + ts = 0 + + if ts_in == 1: + line = sys.stdin.read(struct.calcsize(TSCREC)) + if not line: + break + ts = struct.unpack(TSCREC, line)[0] + if n_data == 1: + line = sys.stdin.read(struct.calcsize(D1REC)) + if not line: + break + d1 = struct.unpack(D1REC, line)[0] + if n_data == 2: + line = sys.stdin.read(struct.calcsize(D2REC)) + if not line: + break + (d1, d2) = struct.unpack(D2REC, line) + if n_data == 3: + line = sys.stdin.read(struct.calcsize(D3REC)) + if not line: + break + (d1, d2, d3) = struct.unpack(D3REC, line) + if n_data == 4: + line = sys.stdin.read(struct.calcsize(D4REC)) + if not line: + break + (d1, d2, d3, d4) = struct.unpack(D4REC, line) + if n_data == 5: + line = sys.stdin.read(struct.calcsize(D5REC)) + if not line: + break + (d1, d2, d3, d4, d5) = struct.unpack(D5REC, line) + + event &= 0x0fffffff + + # provide relative TSC + + if last_ts > 0 and ts_in == 1: + relts = ts - last_ts + else: + relts = 0 + + if ts_in == 1: + last_ts = ts + + args = {'ts' : ts, + 'event' : event, + 'relts': relts, + 'pid' : pid, + 'vcpu' : vcpu_id, + '1' : d1, + '2' : d2, + '3' : d3, + '4' : d4, + '5' : d5 } + + # some event types need more than just formats mapping they are if/elif + # chained here and the last default else is the mapping via formats + if event == 0x00020019: + pdata = (ts, relts, vcpu_id, pid, d1, d2, d3, get_name(d1), get_special(d1)) + print "%d (+%12d) PPC_INSTR vcpu = 0x%08x pid = 0x%08x [ instr = 0x%08x, pc = 0x%08x, emul = %01d, mnemonic = %8s %s" % pdata + else: + try: + if defs.has_key(str(event)): + print defs[str(event)] % args + else: + if defs.has_key(str(0)): print defs[str(0)] % args + except TypeError: + if defs.has_key(str(event)): + print defs[str(event)] + print args + else: + if defs.has_key(str(0)): + print defs[str(0)] + print args + + except IOError, struct.error: sys.exit() + +if summary: + ppc_instr_summary() diff --git a/kvm/user/main-ppc.c b/kvm/user/main-ppc.c new file mode 100644 index 000000000..5af59f846 --- /dev/null +++ b/kvm/user/main-ppc.c @@ -0,0 +1,383 @@ +/* + * Kernel-based Virtual Machine test driver + * + * This test driver provides a simple way of testing kvm, without a full + * device model. + * + * Copyright (C) 2006 Qumranet + * Copyright IBM Corp. 2008 + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * Hollis Blanchard <hollisb@us.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#define _GNU_SOURCE + +#include <libkvm.h> + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <semaphore.h> +#include <sys/types.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <pthread.h> +#include <sys/syscall.h> +#include <linux/unistd.h> +#include <getopt.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "iotable.h" + +static int gettid(void) +{ + return syscall(__NR_gettid); +} + +kvm_context_t kvm; + +#define IPI_SIGNAL (SIGRTMIN + 4) + +struct io_table mmio_table; + +static int ncpus = 1; +static sem_t exited_sem; +static __thread int vcpu; +static sigset_t kernel_sigmask; +static sigset_t ipi_sigmask; +static uint64_t memory_size = 128 * 1024 * 1024; + +struct vcpu_info { + pid_t tid; +}; + +struct vcpu_info *vcpus; + +/* Must match flat.lds linker script */ +#define VM_TEST_LOAD_ADDRESS 0x100000 + +static int test_debug(void *opaque, void *vcpu) +{ + printf("test_debug\n"); + return 0; +} + +static int test_halt(void *opaque, int vcpu) +{ + int n; + + sigwait(&ipi_sigmask, &n); + return 0; +} + +static int test_io_window(void *opaque) +{ + return 0; +} + +static int test_try_push_interrupts(void *opaque) +{ + return 0; +} + +static void test_post_kvm_run(void *opaque, void *vcpu) +{ +} + +static int test_pre_kvm_run(void *opaque, void *vcpu) +{ + return 0; +} + +static int mmio_handler(void *opaque, int len, int is_write, uint64_t offset, + uint64_t *data) +{ + int r = 0; + + switch (offset) { + case 0: /* putc */ + putc(*(char *)data, stdout); + fflush(stdout); + break; + case 1: /* exit */ + r = *(char *)data; + break; + default: + printf("%s: offset %"PRIx64" len %d data %"PRIx64"\n", + __func__, offset, len, *(uint64_t *)data); + r = -EINVAL; + } + + return r; +} + +static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len) +{ + struct io_table_entry *iodev; + +#if 0 + printf("%s: addr %"PRIx64" len %d\n", __func__, addr, len); +#endif + + iodev = io_table_lookup(&mmio_table, addr); + if (!iodev) { + printf("couldn't find device\n"); + return -ENODEV; + } + + return iodev->handler(iodev->opaque, len, 0, addr - iodev->start, + (uint64_t *)data); +} + +static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len) +{ + struct io_table_entry *iodev; + +#if 0 + printf("%s: addr %"PRIx64" len %d data %"PRIx64"\n", + __func__, addr, len, *(uint64_t *)data); +#endif + + iodev = io_table_lookup(&mmio_table, addr); + if (!iodev) { + printf("couldn't find device\n"); + return -ENODEV; + } + + return iodev->handler(iodev->opaque, len, 1, addr - iodev->start, + (uint64_t *)data); +} + +static int test_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data) +{ + printf("%s: dcrn %04X\n", __func__, dcrn); + *data = 0; + return 0; +} + +static int test_dcr_write(int vcpu, uint32_t dcrn, uint32_t data) +{ + printf("%s: dcrn %04X data %04X\n", __func__, dcrn, data); + return 0; +} + +static struct kvm_callbacks test_callbacks = { + .mmio_read = test_mem_read, + .mmio_write = test_mem_write, + .debug = test_debug, + .halt = test_halt, + .io_window = test_io_window, + .try_push_interrupts = test_try_push_interrupts, + .post_kvm_run = test_post_kvm_run, + .pre_kvm_run = test_pre_kvm_run, + .powerpc_dcr_read = test_dcr_read, + .powerpc_dcr_write = test_dcr_write, +}; + +static unsigned long load_file(void *mem, const char *fname, int inval_icache) +{ + ssize_t r; + int fd; + unsigned long bytes = 0; + + fd = open(fname, O_RDONLY); + if (fd == -1) { + perror("open"); + exit(1); + } + + while ((r = read(fd, mem, 4096)) != -1 && r != 0) { + mem += r; + bytes += r; + } + + if (r == -1) { + perror("read"); + printf("read %d bytes\n", bytes); + exit(1); + } + + return bytes; +} + +#define ICACHE_LINE_SIZE 32 + +void sync_caches(void *mem, unsigned long len) +{ + unsigned long i; + + for (i = 0; i < len; i += ICACHE_LINE_SIZE) + asm volatile ("dcbst %0, %1" : : "g"(mem), "r"(i)); + asm volatile ("sync"); + for (i = 0; i < len; i += ICACHE_LINE_SIZE) + asm volatile ("icbi %0, %1" : : "g"(mem), "r"(i)); + asm volatile ("sync; isync"); +} + +static void init_vcpu(int n) +{ + sigemptyset(&ipi_sigmask); + sigaddset(&ipi_sigmask, IPI_SIGNAL); + sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL); + sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask); + vcpus[n].tid = gettid(); + vcpu = n; + kvm_set_signal_mask(kvm, n, &kernel_sigmask); +} + +static void *do_create_vcpu(void *_n) +{ + struct kvm_regs regs; + int n = (long)_n; + + kvm_create_vcpu(kvm, n); + init_vcpu(n); + + kvm_get_regs(kvm, n, ®s); + regs.pc = VM_TEST_LOAD_ADDRESS; + kvm_set_regs(kvm, n, ®s); + + kvm_run(kvm, n, &vcpus[n]); + sem_post(&exited_sem); + return NULL; +} + +static void start_vcpu(int n) +{ + pthread_t thread; + + pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n); +} + +static void usage(const char *progname) +{ + fprintf(stderr, +"Usage: %s [OPTIONS] [bootstrap] flatfile\n" +"KVM test harness.\n" +"\n" +" -s, --smp=NUM create a VM with NUM virtual CPUs\n" +" -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n" +" can be used to change the unit (default: `M')\n" +" -h, --help display this help screen and exit\n" +"\n" +"Report bugs to <kvm-ppc@vger.kernel.org>.\n" + , progname); +} + +static void sig_ignore(int sig) +{ + write(1, "boo\n", 4); +} + +int main(int argc, char **argv) +{ + void *vm_mem; + unsigned long len; + int i; + const char *sopts = "s:phm:"; + struct option lopts[] = { + { "smp", 1, 0, 's' }, + { "memory", 1, 0, 'm' }, + { "help", 0, 0, 'h' }, + { 0 }, + }; + int opt_ind, ch; + int nb_args; + char *endptr; + + while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) { + switch (ch) { + case 's': + ncpus = atoi(optarg); + break; + case 'm': + memory_size = strtoull(optarg, &endptr, 0); + switch (*endptr) { + case 'G': case 'g': + memory_size <<= 30; + break; + case '\0': + case 'M': case 'm': + memory_size <<= 20; + break; + case 'K': case 'k': + memory_size <<= 10; + break; + default: + fprintf(stderr, + "Unrecongized memory suffix: %c\n", + *endptr); + exit(1); + } + if (memory_size == 0) { + fprintf(stderr, + "Invalid memory size: 0\n"); + exit(1); + } + break; + case 'h': + usage(argv[0]); + exit(0); + case '?': + default: + fprintf(stderr, + "Try `%s --help' for more information.\n", + argv[0]); + exit(1); + } + } + + nb_args = argc - optind; + if (nb_args < 1 || nb_args > 2) { + fprintf(stderr, + "Incorrect number of arguments.\n" + "Try `%s --help' for more information.\n", + argv[0]); + exit(1); + } + + signal(IPI_SIGNAL, sig_ignore); + + vcpus = calloc(ncpus, sizeof *vcpus); + if (!vcpus) { + fprintf(stderr, "calloc failed\n"); + return 1; + } + + kvm = kvm_init(&test_callbacks, 0); + if (!kvm) { + fprintf(stderr, "kvm_init failed\n"); + return 1; + } + if (kvm_create(kvm, memory_size, &vm_mem) < 0) { + kvm_finalize(kvm); + fprintf(stderr, "kvm_create failed\n"); + return 1; + } + + vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1); + + len = load_file(vm_mem + VM_TEST_LOAD_ADDRESS, argv[optind], 1); + sync_caches(vm_mem + VM_TEST_LOAD_ADDRESS, len); + + io_table_register(&mmio_table, 0xf0000000, 64, mmio_handler, NULL); + + sem_init(&exited_sem, 0, 0); + for (i = 0; i < ncpus; ++i) + start_vcpu(i); + /* Wait for all vcpus to exit. */ + for (i = 0; i < ncpus; ++i) + sem_wait(&exited_sem); + + return 0; +} diff --git a/kvm/user/main.c b/kvm/user/main.c new file mode 100644 index 000000000..1530ae2f4 --- /dev/null +++ b/kvm/user/main.c @@ -0,0 +1,611 @@ +/* + * Kernel-based Virtual Machine test driver + * + * This test driver provides a simple way of testing kvm, without a full + * device model. + * + * Copyright (C) 2006 Qumranet + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#define _GNU_SOURCE + +#include <libkvm.h> +#include "test/lib/x86/fake-apic.h" +#include "test/x86/ioram.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <semaphore.h> +#include <sys/types.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <pthread.h> +#include <sys/syscall.h> +#include <linux/unistd.h> +#include <getopt.h> +#include <stdbool.h> + +#include "iotable.h" + +static uint8_t ioram[IORAM_LEN]; + +static int gettid(void) +{ + return syscall(__NR_gettid); +} + +static int tkill(int pid, int sig) +{ + return syscall(__NR_tkill, pid, sig); +} + +kvm_context_t kvm; + +#define MAX_VCPUS 4 + +#define IPI_SIGNAL (SIGRTMIN + 4) + +static int ncpus = 1; +static sem_t init_sem; +static __thread int vcpu; +static int apic_ipi_vector = 0xff; +static sigset_t kernel_sigmask; +static sigset_t ipi_sigmask; +static uint64_t memory_size = 128 * 1024 * 1024; + +static struct io_table pio_table; + +struct vcpu_info { + int id; + pid_t tid; + sem_t sipi_sem; +}; + +struct vcpu_info *vcpus; + +static uint32_t apic_sipi_addr; + +static void apic_send_sipi(int vcpu) +{ + sem_post(&vcpus[vcpu].sipi_sem); +} + +static void apic_send_ipi(int vcpu) +{ + struct vcpu_info *v; + + if (vcpu < 0 || vcpu >= ncpus) + return; + v = &vcpus[vcpu]; + tkill(v->tid, IPI_SIGNAL); +} + +static int apic_io(void *opaque, int size, int is_write, + uint64_t addr, uint64_t *value) +{ + if (!is_write) + *value = -1u; + + switch (addr - APIC_BASE) { + case APIC_REG_NCPU: + if (!is_write) + *value = ncpus; + break; + case APIC_REG_ID: + if (!is_write) + *value = vcpu; + break; + case APIC_REG_SIPI_ADDR: + if (!is_write) + *value = apic_sipi_addr; + else + apic_sipi_addr = *value; + break; + case APIC_REG_SEND_SIPI: + if (is_write) + apic_send_sipi(*value); + break; + case APIC_REG_IPI_VECTOR: + if (!is_write) + *value = apic_ipi_vector; + else + apic_ipi_vector = *value; + break; + case APIC_REG_SEND_IPI: + if (is_write) + apic_send_ipi(*value); + break; + } + + return 0; +} + +static int apic_init(void) +{ + return io_table_register(&pio_table, APIC_BASE, + APIC_SIZE, apic_io, NULL); +} + +static int misc_io(void *opaque, int size, int is_write, + uint64_t addr, uint64_t *value) +{ + static int newline = 1; + + if (!is_write) + *value = -1; + + switch (addr) { + case 0xff: // irq injector + if (is_write) { + printf("injecting interrupt 0x%x\n", (uint8_t)*value); + kvm_inject_irq(kvm, 0, *value); + } + break; + case 0xf1: // serial + if (is_write) { + if (newline) + fputs("GUEST: ", stdout); + putchar(*value); + newline = *value == '\n'; + } + break; + case 0xd1: + if (!is_write) + *value = memory_size; + break; + case 0xf4: // exit + if (is_write) + exit(*value); + break; + } + + return 0; +} + +static int misc_init(void) +{ + int err; + + err = io_table_register(&pio_table, 0xff, 1, misc_io, NULL); + if (err < 0) + return err; + + err = io_table_register(&pio_table, 0xf1, 1, misc_io, NULL); + if (err < 0) + return err; + + err = io_table_register(&pio_table, 0xf4, 1, misc_io, NULL); + if (err < 0) + return err; + + return io_table_register(&pio_table, 0xd1, 1, misc_io, NULL); +} + +#define IRQCHIP_IO_BASE 0x2000 + +static int irqchip_io(void *opaque, int size, int is_write, + uint64_t addr, uint64_t *value) +{ + addr -= IRQCHIP_IO_BASE; + + if (is_write) { + kvm_set_irq_level(kvm, addr, *value, NULL); + } + return 0; +} + +static int test_inb(void *opaque, uint16_t addr, uint8_t *value) +{ + struct io_table_entry *entry; + + entry = io_table_lookup(&pio_table, addr); + if (entry) { + uint64_t val; + entry->handler(entry->opaque, 1, 0, addr, &val); + *value = val; + } else { + *value = -1; + printf("inb 0x%x\n", addr); + } + + return 0; +} + +static int test_inw(void *opaque, uint16_t addr, uint16_t *value) +{ + struct io_table_entry *entry; + + entry = io_table_lookup(&pio_table, addr); + if (entry) { + uint64_t val; + entry->handler(entry->opaque, 2, 0, addr, &val); + *value = val; + } else { + *value = -1; + printf("inw 0x%x\n", addr); + } + + return 0; +} + +static int test_inl(void *opaque, uint16_t addr, uint32_t *value) +{ + struct io_table_entry *entry; + + entry = io_table_lookup(&pio_table, addr); + if (entry) { + uint64_t val; + entry->handler(entry->opaque, 4, 0, addr, &val); + *value = val; + } else { + *value = -1; + printf("inl 0x%x\n", addr); + } + + return 0; +} + +static int test_outb(void *opaque, uint16_t addr, uint8_t value) +{ + struct io_table_entry *entry; + + entry = io_table_lookup(&pio_table, addr); + if (entry) { + uint64_t val = value; + entry->handler(entry->opaque, 1, 1, addr, &val); + } else + printf("outb $0x%x, 0x%x\n", value, addr); + + return 0; +} + +static int test_outw(void *opaque, uint16_t addr, uint16_t value) +{ + struct io_table_entry *entry; + + entry = io_table_lookup(&pio_table, addr); + if (entry) { + uint64_t val = value; + entry->handler(entry->opaque, 2, 1, addr, &val); + } else + printf("outw $0x%x, 0x%x\n", value, addr); + + return 0; +} + +static int test_outl(void *opaque, uint16_t addr, uint32_t value) +{ + struct io_table_entry *entry; + + entry = io_table_lookup(&pio_table, addr); + if (entry) { + uint64_t val = value; + entry->handler(entry->opaque, 4, 1, addr, &val); + } else + printf("outl $0x%x, 0x%x\n", value, addr); + + return 0; +} + +#ifdef KVM_CAP_SET_GUEST_DEBUG +static int test_debug(void *opaque, void *vcpu, + struct kvm_debug_exit_arch *arch_info) +{ + printf("test_debug\n"); + return 0; +} +#endif + +static int test_halt(void *opaque, int vcpu) +{ + int n; + + sigwait(&ipi_sigmask, &n); + kvm_inject_irq(kvm, vcpus[vcpu].id, apic_ipi_vector); + return 0; +} + +static int test_io_window(void *opaque) +{ + return 0; +} + +static int test_try_push_interrupts(void *opaque) +{ + return 0; +} + +#ifdef KVM_CAP_USER_NMI +static void test_push_nmi(void *opaque) +{ +} +#endif + +static void test_post_kvm_run(void *opaque, void *vcpu) +{ +} + +static int test_pre_kvm_run(void *opaque, void *vcpu) +{ + return 0; +} + +static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len) +{ + if (addr < IORAM_BASE_PHYS || addr + len > IORAM_BASE_PHYS + IORAM_LEN) + return 1; + memcpy(data, ioram + addr - IORAM_BASE_PHYS, len); + return 0; +} + +static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len) +{ + if (addr < IORAM_BASE_PHYS || addr + len > IORAM_BASE_PHYS + IORAM_LEN) + return 1; + memcpy(ioram + addr - IORAM_BASE_PHYS, data, len); + return 0; +} + +static int test_shutdown(void *opaque, void *env) +{ + printf("shutdown\n"); + kvm_show_regs(kvm, 0); + exit(1); + return 1; +} + +static struct kvm_callbacks test_callbacks = { + .inb = test_inb, + .inw = test_inw, + .inl = test_inl, + .outb = test_outb, + .outw = test_outw, + .outl = test_outl, + .mmio_read = test_mem_read, + .mmio_write = test_mem_write, +#ifdef KVM_CAP_SET_GUEST_DEBUG + .debug = test_debug, +#endif + .halt = test_halt, + .io_window = test_io_window, + .try_push_interrupts = test_try_push_interrupts, +#ifdef KVM_CAP_USER_NMI + .push_nmi = test_push_nmi, +#endif + .post_kvm_run = test_post_kvm_run, + .pre_kvm_run = test_pre_kvm_run, + .shutdown = test_shutdown, +}; + +static void load_file(void *mem, const char *fname) +{ + int r; + int fd; + + fd = open(fname, O_RDONLY); + if (fd == -1) { + perror("open"); + exit(1); + } + while ((r = read(fd, mem, 4096)) != -1 && r != 0) + mem += r; + if (r == -1) { + perror("read"); + exit(1); + } +} + +static void enter_32(kvm_context_t kvm) +{ + struct kvm_regs regs = { + .rsp = 0x80000, /* 512KB */ + .rip = 0x100000, /* 1MB */ + .rflags = 2, + }; + struct kvm_sregs sregs = { + .cs = { 0, -1u, 8, 11, 1, 0, 1, 1, 0, 1, 0, 0 }, + .ds = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, + .es = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, + .fs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, + .gs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, + .ss = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, + + .tr = { 0, 10000, 24, 11, 1, 0, 0, 0, 0, 0, 0, 0 }, + .ldt = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + .gdt = { 0, 0 }, + .idt = { 0, 0 }, + .cr0 = 0x37, + .cr3 = 0, + .cr4 = 0, + .efer = 0, + .apic_base = 0, + .interrupt_bitmap = { 0 }, + }; + + kvm_set_regs(kvm, 0, ®s); + kvm_set_sregs(kvm, 0, &sregs); +} + +static void init_vcpu(int n) +{ + sigemptyset(&ipi_sigmask); + sigaddset(&ipi_sigmask, IPI_SIGNAL); + sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL); + sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask); + vcpus[n].id = n; + vcpus[n].tid = gettid(); + vcpu = n; + kvm_set_signal_mask(kvm, n, &kernel_sigmask); + sem_post(&init_sem); +} + +static void *do_create_vcpu(void *_n) +{ + int n = (long)_n; + struct kvm_regs regs; + + kvm_create_vcpu(kvm, n); + init_vcpu(n); + sem_wait(&vcpus[n].sipi_sem); + kvm_get_regs(kvm, n, ®s); + regs.rip = apic_sipi_addr; + kvm_set_regs(kvm, n, ®s); + kvm_run(kvm, n, &vcpus[n]); + return NULL; +} + +static void start_vcpu(int n) +{ + pthread_t thread; + + sem_init(&vcpus[n].sipi_sem, 0, 0); + pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n); +} + +static void usage(const char *progname) +{ + fprintf(stderr, +"Usage: %s [OPTIONS] [bootstrap] flatfile\n" +"KVM test harness.\n" +"\n" +" -s, --smp=NUM create a VM with NUM virtual CPUs\n" +" -p, --protected-mode start VM in protected mode\n" +" -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n" +" can be used to change the unit (default: `M')\n" +" -h, --help display this help screen and exit\n" +"\n" +"Report bugs to <kvm@vger.kernel.org>.\n" + , progname); +} + +static void sig_ignore(int sig) +{ + write(1, "boo\n", 4); +} + +int main(int argc, char **argv) +{ + void *vm_mem; + int i; + const char *sopts = "s:phm:"; + struct option lopts[] = { + { "smp", 1, 0, 's' }, + { "protected-mode", 0, 0, 'p' }, + { "memory", 1, 0, 'm' }, + { "help", 0, 0, 'h' }, + { 0 }, + }; + int opt_ind, ch; + bool enter_protected_mode = false; + int nb_args; + char *endptr; + + while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) { + switch (ch) { + case 's': + ncpus = atoi(optarg); + break; + case 'p': + enter_protected_mode = true; + break; + case 'm': + memory_size = strtoull(optarg, &endptr, 0); + switch (*endptr) { + case 'G': case 'g': + memory_size <<= 30; + break; + case '\0': + case 'M': case 'm': + memory_size <<= 20; + break; + case 'K': case 'k': + memory_size <<= 10; + break; + default: + fprintf(stderr, + "Unrecongized memory suffix: %c\n", + *endptr); + exit(1); + } + if (memory_size == 0) { + fprintf(stderr, + "Invalid memory size: 0\n"); + exit(1); + } + break; + case 'h': + usage(argv[0]); + exit(0); + case '?': + default: + fprintf(stderr, + "Try `%s --help' for more information.\n", + argv[0]); + exit(1); + } + } + + nb_args = argc - optind; + if (nb_args < 1 || nb_args > 2) { + fprintf(stderr, + "Incorrect number of arguments.\n" + "Try `%s --help' for more information.\n", + argv[0]); + exit(1); + } + + signal(IPI_SIGNAL, sig_ignore); + + vcpus = calloc(ncpus, sizeof *vcpus); + if (!vcpus) { + fprintf(stderr, "calloc failed\n"); + return 1; + } + + kvm = kvm_init(&test_callbacks, 0); + if (!kvm) { + fprintf(stderr, "kvm_init failed\n"); + return 1; + } + if (kvm_create(kvm, memory_size, &vm_mem) < 0) { + kvm_finalize(kvm); + fprintf(stderr, "kvm_create failed\n"); + return 1; + } + + vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1); + + if (enter_protected_mode) + enter_32(kvm); + else + load_file(vm_mem + 0xf0000, argv[optind]); + + if (nb_args > 1) + load_file(vm_mem + 0x100000, argv[optind + 1]); + + apic_init(); + misc_init(); + + io_table_register(&pio_table, IRQCHIP_IO_BASE, 0x20, irqchip_io, NULL); + + sem_init(&init_sem, 0, 0); + for (i = 0; i < ncpus; ++i) + start_vcpu(i); + for (i = 0; i < ncpus; ++i) + sem_wait(&init_sem); + + kvm_run(kvm, 0, &vcpus[0]); + + return 0; +} diff --git a/kvm/user/test/lib/libcflat.h b/kvm/user/test/lib/libcflat.h new file mode 100644 index 000000000..1da4013f5 --- /dev/null +++ b/kvm/user/test/lib/libcflat.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#ifndef __LIBCFLAT_H +#define __LIBCFLAT_H + +#include <stdarg.h> + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned u32; +typedef unsigned long ulong; +typedef unsigned long long u64; + +extern void exit(int code); +extern void panic(char *fmt, ...); + +extern unsigned long strlen(const char *buf); +extern char *strcat(char *dest, const char *src); + +extern int printf(const char *fmt, ...); +extern int vsnprintf(char *buf, int size, const char *fmt, va_list va); + +extern void puts(const char *s); + +#endif diff --git a/kvm/user/test/lib/panic.c b/kvm/user/test/lib/panic.c new file mode 100644 index 000000000..6e0b29ebe --- /dev/null +++ b/kvm/user/test/lib/panic.c @@ -0,0 +1,13 @@ +#include "libcflat.h" + +void panic(char *fmt, ...) +{ + va_list va; + char buf[2000]; + + va_start(va, fmt); + vsnprintf(buf, sizeof(buf), fmt, va); + va_end(va); + puts(buf); + exit(-1); +} diff --git a/kvm/user/test/lib/powerpc/44x/map.c b/kvm/user/test/lib/powerpc/44x/map.c new file mode 100644 index 000000000..113434d2f --- /dev/null +++ b/kvm/user/test/lib/powerpc/44x/map.c @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#include "libcflat.h" + +#define TLB_SIZE 64 + +extern void tlbwe(unsigned int index, + unsigned char tid, + unsigned int word0, + unsigned int word1, + unsigned int word2); + +unsigned int next_free_index; + +#define PAGE_SHIFT 12 +#define PAGE_MASK (~((1<<PAGE_SHIFT)-1)) + +#define V (1<<9) + +void map(unsigned long vaddr, unsigned long paddr) +{ + unsigned int w0, w1, w2; + + /* We don't install exception handlers, so we can't handle TLB misses, + * so we can't loop around and overwrite entry 0. */ + if (next_free_index++ >= TLB_SIZE) + panic("TLB overflow"); + + w0 = (vaddr & PAGE_MASK) | V; + w1 = paddr & PAGE_MASK; + w2 = 0x3; + + tlbwe(next_free_index, 0, w0, w1, w2); +} diff --git a/kvm/user/test/lib/powerpc/44x/timebase.S b/kvm/user/test/lib/powerpc/44x/timebase.S new file mode 100644 index 000000000..385904da3 --- /dev/null +++ b/kvm/user/test/lib/powerpc/44x/timebase.S @@ -0,0 +1,28 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +/* unsigned long long mftb(void); */ +.global mftb +mftb: + mftbu r5 + mftbl r4 + mftbu r3 + cmpw r3, r5 + bne mftb + blr diff --git a/kvm/user/test/lib/powerpc/44x/timebase.h b/kvm/user/test/lib/powerpc/44x/timebase.h new file mode 100644 index 000000000..ce85347bd --- /dev/null +++ b/kvm/user/test/lib/powerpc/44x/timebase.h @@ -0,0 +1,25 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#ifndef __TIMEBASE_H__ +#define __TIMEBASE_H__ + +unsigned long long mftb(void); + +#endif /* __TIMEBASE_H__ */ diff --git a/kvm/user/test/lib/powerpc/44x/tlbwe.S b/kvm/user/test/lib/powerpc/44x/tlbwe.S new file mode 100644 index 000000000..3790374eb --- /dev/null +++ b/kvm/user/test/lib/powerpc/44x/tlbwe.S @@ -0,0 +1,29 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#define SPRN_MMUCR 0x3b2 + +/* tlbwe(uint index, uint8_t tid, uint word0, uint word1, uint word2) */ +.global tlbwe +tlbwe: + mtspr SPRN_MMUCR, r4 + tlbwe r5, r3, 0 + tlbwe r6, r3, 1 + tlbwe r7, r3, 2 + blr diff --git a/kvm/user/test/lib/powerpc/io.c b/kvm/user/test/lib/powerpc/io.c new file mode 100644 index 000000000..8bd239521 --- /dev/null +++ b/kvm/user/test/lib/powerpc/io.c @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#include "libcflat.h" + +#define BASE 0xf0000000 +#define _putc ((volatile char *)(BASE)) +#define _exit ((volatile char *)(BASE+1)) + +void puts(const char *s) +{ + while (*s != '\0') + *_putc = *s++; +} + +void exit(int code) +{ + *_exit = code; +} diff --git a/kvm/user/test/lib/printf.c b/kvm/user/test/lib/printf.c new file mode 100644 index 000000000..3bb9e3d74 --- /dev/null +++ b/kvm/user/test/lib/printf.c @@ -0,0 +1,179 @@ +#include "libcflat.h" + +typedef struct pstream { + char *buffer; + int remain; + int added; +} pstream_t; + +static void addchar(pstream_t *p, char c) +{ + if (p->remain) { + *p->buffer++ = c; + --p->remain; + } + ++p->added; +} + +void print_str(pstream_t *p, const char *s) +{ + while (*s) + addchar(p, *s++); +} + +static char digits[16] = "0123456789abcdef"; + +void print_int(pstream_t *ps, long long n, int base) +{ + char buf[sizeof(long) * 3 + 2], *p = buf; + int s = 0, i; + + if (n < 0) { + n = -n; + s = 1; + } + + while (n) { + *p++ = digits[n % base]; + n /= base; + } + + if (s) + *p++ = '-'; + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print_str(ps, buf); +} + +void print_unsigned(pstream_t *ps, unsigned long long n, int base) +{ + char buf[sizeof(long) * 3 + 1], *p = buf; + int i; + + while (n) { + *p++ = digits[n % base]; + n /= base; + } + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print_str(ps, buf); +} + +int vsnprintf(char *buf, int size, const char *fmt, va_list va) +{ + pstream_t s; + + s.buffer = buf; + s.remain = size - 1; + s.added = 0; + while (*fmt) { + char f = *fmt++; + int nlong = 0; + + if (f != '%') { + addchar(&s, f); + continue; + } + morefmt: + f = *fmt++; + switch (f) { + case '%': + addchar(&s, '%'); + break; + case '\0': + --fmt; + break; + case 'l': + ++nlong; + goto morefmt; + case 'd': + switch (nlong) { + case 0: + print_int(&s, va_arg(va, int), 10); + break; + case 1: + print_int(&s, va_arg(va, long), 10); + break; + default: + print_int(&s, va_arg(va, long long), 10); + break; + } + break; + case 'x': + switch (nlong) { + case 0: + print_unsigned(&s, va_arg(va, unsigned), 16); + break; + case 1: + print_unsigned(&s, va_arg(va, unsigned long), 16); + break; + default: + print_unsigned(&s, va_arg(va, unsigned long long), 16); + break; + } + break; + case 'p': + print_str(&s, "0x"); + print_unsigned(&s, (unsigned long)va_arg(va, void *), 16); + break; + case 's': + print_str(&s, va_arg(va, const char *)); + break; + default: + addchar(&s, f); + break; + } + } + *s.buffer = 0; + ++s.added; + return s.added; +} + + +int snprintf(char *buf, int size, const char *fmt, ...) +{ + va_list va; + int r; + + va_start(va, fmt); + r = vsnprintf(buf, size, fmt, va); + va_end(va); + return r; +} + +int printf(const char *fmt, ...) +{ + va_list va; + char buf[2000]; + int r; + + va_start(va, fmt); + r = vsnprintf(buf, sizeof buf, fmt, va); + va_end(va); + puts(buf); + return r; +} diff --git a/kvm/user/test/lib/string.c b/kvm/user/test/lib/string.c new file mode 100644 index 000000000..42be94697 --- /dev/null +++ b/kvm/user/test/lib/string.c @@ -0,0 +1,21 @@ +#include "libcflat.h" + +unsigned long strlen(const char *buf) +{ + unsigned long len = 0; + + while (*buf++) + ++len; + return len; +} + +char *strcat(char *dest, const char *src) +{ + char *p = dest; + + while (*p) + ++p; + while ((*p++ = *src++) != 0) + ; + return dest; +} diff --git a/kvm/user/test/lib/x86/apic-defs.h b/kvm/user/test/lib/x86/apic-defs.h new file mode 100644 index 000000000..c061e3d4a --- /dev/null +++ b/kvm/user/test/lib/x86/apic-defs.h @@ -0,0 +1,133 @@ +#ifndef _ASM_X86_APICDEF_H +#define _ASM_X86_APICDEF_H + +/* + * Constants for various Intel APICs. (local APIC, IOAPIC, etc.) + * + * Alan Cox <Alan.Cox@linux.org>, 1995. + * Ingo Molnar <mingo@redhat.com>, 1999, 2000 + */ + +#define APIC_DEFAULT_PHYS_BASE 0xfee00000 + +#define APIC_ID 0x20 + +#define APIC_LVR 0x30 +#define APIC_LVR_MASK 0xFF00FF +#define GET_APIC_VERSION(x) ((x) & 0xFFu) +#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu) +#ifdef CONFIG_X86_32 +# define APIC_INTEGRATED(x) ((x) & 0xF0u) +#else +# define APIC_INTEGRATED(x) (1) +#endif +#define APIC_XAPIC(x) ((x) >= 0x14) +#define APIC_TASKPRI 0x80 +#define APIC_TPRI_MASK 0xFFu +#define APIC_ARBPRI 0x90 +#define APIC_ARBPRI_MASK 0xFFu +#define APIC_PROCPRI 0xA0 +#define APIC_EOI 0xB0 +#define APIC_EIO_ACK 0x0 +#define APIC_RRR 0xC0 +#define APIC_LDR 0xD0 +#define APIC_LDR_MASK (0xFFu << 24) +#define GET_APIC_LOGICAL_ID(x) (((x) >> 24) & 0xFFu) +#define SET_APIC_LOGICAL_ID(x) (((x) << 24)) +#define APIC_ALL_CPUS 0xFFu +#define APIC_DFR 0xE0 +#define APIC_DFR_CLUSTER 0x0FFFFFFFul +#define APIC_DFR_FLAT 0xFFFFFFFFul +#define APIC_SPIV 0xF0 +#define APIC_SPIV_FOCUS_DISABLED (1 << 9) +#define APIC_SPIV_APIC_ENABLED (1 << 8) +#define APIC_ISR 0x100 +#define APIC_ISR_NR 0x8 /* Number of 32 bit ISR registers. */ +#define APIC_TMR 0x180 +#define APIC_IRR 0x200 +#define APIC_ESR 0x280 +#define APIC_ESR_SEND_CS 0x00001 +#define APIC_ESR_RECV_CS 0x00002 +#define APIC_ESR_SEND_ACC 0x00004 +#define APIC_ESR_RECV_ACC 0x00008 +#define APIC_ESR_SENDILL 0x00020 +#define APIC_ESR_RECVILL 0x00040 +#define APIC_ESR_ILLREGA 0x00080 +#define APIC_ICR 0x300 +#define APIC_DEST_SELF 0x40000 +#define APIC_DEST_ALLINC 0x80000 +#define APIC_DEST_ALLBUT 0xC0000 +#define APIC_ICR_RR_MASK 0x30000 +#define APIC_ICR_RR_INVALID 0x00000 +#define APIC_ICR_RR_INPROG 0x10000 +#define APIC_ICR_RR_VALID 0x20000 +#define APIC_INT_LEVELTRIG 0x08000 +#define APIC_INT_ASSERT 0x04000 +#define APIC_ICR_BUSY 0x01000 +#define APIC_DEST_LOGICAL 0x00800 +#define APIC_DEST_PHYSICAL 0x00000 +#define APIC_DM_FIXED 0x00000 +#define APIC_DM_LOWEST 0x00100 +#define APIC_DM_SMI 0x00200 +#define APIC_DM_REMRD 0x00300 +#define APIC_DM_NMI 0x00400 +#define APIC_DM_INIT 0x00500 +#define APIC_DM_STARTUP 0x00600 +#define APIC_DM_EXTINT 0x00700 +#define APIC_VECTOR_MASK 0x000FF +#define APIC_ICR2 0x310 +#define GET_APIC_DEST_FIELD(x) (((x) >> 24) & 0xFF) +#define SET_APIC_DEST_FIELD(x) ((x) << 24) +#define APIC_LVTT 0x320 +#define APIC_LVTTHMR 0x330 +#define APIC_LVTPC 0x340 +#define APIC_LVT0 0x350 +#define APIC_LVT_TIMER_BASE_MASK (0x3 << 18) +#define GET_APIC_TIMER_BASE(x) (((x) >> 18) & 0x3) +#define SET_APIC_TIMER_BASE(x) (((x) << 18)) +#define APIC_TIMER_BASE_CLKIN 0x0 +#define APIC_TIMER_BASE_TMBASE 0x1 +#define APIC_TIMER_BASE_DIV 0x2 +#define APIC_LVT_TIMER_PERIODIC (1 << 17) +#define APIC_LVT_MASKED (1 << 16) +#define APIC_LVT_LEVEL_TRIGGER (1 << 15) +#define APIC_LVT_REMOTE_IRR (1 << 14) +#define APIC_INPUT_POLARITY (1 << 13) +#define APIC_SEND_PENDING (1 << 12) +#define APIC_MODE_MASK 0x700 +#define GET_APIC_DELIVERY_MODE(x) (((x) >> 8) & 0x7) +#define SET_APIC_DELIVERY_MODE(x, y) (((x) & ~0x700) | ((y) << 8)) +#define APIC_MODE_FIXED 0x0 +#define APIC_MODE_NMI 0x4 +#define APIC_MODE_EXTINT 0x7 +#define APIC_LVT1 0x360 +#define APIC_LVTERR 0x370 +#define APIC_TMICT 0x380 +#define APIC_TMCCT 0x390 +#define APIC_TDCR 0x3E0 +#define APIC_SELF_IPI 0x3F0 +#define APIC_TDR_DIV_TMBASE (1 << 2) +#define APIC_TDR_DIV_1 0xB +#define APIC_TDR_DIV_2 0x0 +#define APIC_TDR_DIV_4 0x1 +#define APIC_TDR_DIV_8 0x2 +#define APIC_TDR_DIV_16 0x3 +#define APIC_TDR_DIV_32 0x8 +#define APIC_TDR_DIV_64 0x9 +#define APIC_TDR_DIV_128 0xA +#define APIC_EILVT0 0x500 +#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */ +#define APIC_EILVT_NR_AMD_10H 4 +#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF) +#define APIC_EILVT_MSG_FIX 0x0 +#define APIC_EILVT_MSG_SMI 0x2 +#define APIC_EILVT_MSG_NMI 0x4 +#define APIC_EILVT_MSG_EXT 0x7 +#define APIC_EILVT_MASKED (1 << 16) +#define APIC_EILVT1 0x510 +#define APIC_EILVT2 0x520 +#define APIC_EILVT3 0x530 + +#define APIC_BASE_MSR 0x800 + +#endif /* _ASM_X86_APICDEF_H */ diff --git a/kvm/user/test/lib/x86/apic.c b/kvm/user/test/lib/x86/apic.c new file mode 100644 index 000000000..7bb98ed34 --- /dev/null +++ b/kvm/user/test/lib/x86/apic.c @@ -0,0 +1,143 @@ +#include "libcflat.h" +#include "apic.h" + +static void *g_apic = (void *)0xfee00000; +static void *g_ioapic = (void *)0xfec00000; + +struct apic_ops { + u32 (*reg_read)(unsigned reg); + void (*reg_write)(unsigned reg, u32 val); + void (*icr_write)(u32 val, u32 dest); + u32 (*id)(void); +}; + +static void outb(unsigned char data, unsigned short port) +{ + asm volatile ("out %0, %1" : : "a"(data), "d"(port)); +} + +static u32 xapic_read(unsigned reg) +{ + return *(volatile u32 *)(g_apic + reg); +} + +static void xapic_write(unsigned reg, u32 val) +{ + *(volatile u32 *)(g_apic + reg) = val; +} + +static void xapic_icr_write(u32 val, u32 dest) +{ + while (xapic_read(APIC_ICR) & APIC_ICR_BUSY) + ; + xapic_write(APIC_ICR2, dest << 24); + xapic_write(APIC_ICR, val); +} + +static uint32_t xapic_id(void) +{ + return xapic_read(APIC_ID) >> 24; +} + +static const struct apic_ops xapic_ops = { + .reg_read = xapic_read, + .reg_write = xapic_write, + .icr_write = xapic_icr_write, + .id = xapic_id, +}; + +static const struct apic_ops *apic_ops = &xapic_ops; + +static u32 x2apic_read(unsigned reg) +{ + unsigned a, d; + + asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(APIC_BASE_MSR + reg/16)); + return a | (u64)d << 32; +} + +static void x2apic_write(unsigned reg, u32 val) +{ + asm volatile ("wrmsr" : : "a"(val), "d"(0), "c"(APIC_BASE_MSR + reg/16)); +} + +static void x2apic_icr_write(u32 val, u32 dest) +{ + asm volatile ("wrmsr" : : "a"(val), "d"(dest), + "c"(APIC_BASE_MSR + APIC_ICR/16)); +} + +static uint32_t x2apic_id(void) +{ + return xapic_read(APIC_ID); +} + +static const struct apic_ops x2apic_ops = { + .reg_read = x2apic_read, + .reg_write = x2apic_write, + .icr_write = x2apic_icr_write, + .id = x2apic_id, +}; + +u32 apic_read(unsigned reg) +{ + return apic_ops->reg_read(reg); +} + +void apic_write(unsigned reg, u32 val) +{ + apic_ops->reg_write(reg, val); +} + +void apic_icr_write(u32 val, u32 dest) +{ + apic_ops->icr_write(val, dest); +} + +uint32_t apic_id(void) +{ + return apic_ops->id(); +} + +#define MSR_APIC_BASE 0x0000001b + +int enable_x2apic(void) +{ + unsigned a, b, c, d; + + asm ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1)); + + if (c & (1 << 21)) { + asm ("rdmsr" : "=a"(a), "=d"(d) : "c"(MSR_APIC_BASE)); + a |= 1 << 10; + asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_APIC_BASE)); + apic_ops = &x2apic_ops; + return 1; + } else { + return 0; + } +} + +void ioapic_write_reg(unsigned reg, u32 value) +{ + *(volatile u32 *)g_ioapic = reg; + *(volatile u32 *)(g_ioapic + 0x10) = value; +} + +void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e) +{ + ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]); + ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]); +} + +void enable_apic(void) +{ + printf("enabling apic\n"); + xapic_write(0xf0, 0x1ff); /* spurious vector register */ +} + +void mask_pic_interrupts(void) +{ + outb(0xff, 0x21); + outb(0xff, 0xa1); +} diff --git a/kvm/user/test/lib/x86/apic.h b/kvm/user/test/lib/x86/apic.h new file mode 100644 index 000000000..e325e9aae --- /dev/null +++ b/kvm/user/test/lib/x86/apic.h @@ -0,0 +1,34 @@ +#ifndef CFLAT_APIC_H +#define CFLAT_APIC_H + +#include <stdint.h> +#include "apic-defs.h" + +typedef struct { + uint8_t vector; + uint8_t delivery_mode:3; + uint8_t dest_mode:1; + uint8_t delivery_status:1; + uint8_t polarity:1; + uint8_t remote_irr:1; + uint8_t trig_mode:1; + uint8_t mask:1; + uint8_t reserve:7; + uint8_t reserved[4]; + uint8_t dest_id; +} ioapic_redir_entry_t; + +void mask_pic_interrupts(void); + +void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e); +void ioapic_write_reg(unsigned reg, uint32_t value); + +void enable_apic(void); +uint32_t apic_read(unsigned reg); +void apic_write(unsigned reg, uint32_t val); +void apic_icr_write(uint32_t val, uint32_t dest); +uint32_t apic_id(void); + +int enable_x2apic(void); + +#endif diff --git a/kvm/user/test/lib/x86/fake-apic.h b/kvm/user/test/lib/x86/fake-apic.h new file mode 100644 index 000000000..eed63baef --- /dev/null +++ b/kvm/user/test/lib/x86/fake-apic.h @@ -0,0 +1,14 @@ +#ifndef SILLY_APIC_H +#define SILLY_APIC_H + +#define APIC_BASE 0x1000 +#define APIC_SIZE 0x100 + +#define APIC_REG_NCPU 0x00 +#define APIC_REG_ID 0x04 +#define APIC_REG_SIPI_ADDR 0x08 +#define APIC_REG_SEND_SIPI 0x0c +#define APIC_REG_IPI_VECTOR 0x10 +#define APIC_REG_SEND_IPI 0x14 + +#endif diff --git a/kvm/user/test/lib/x86/fwcfg.c b/kvm/user/test/lib/x86/fwcfg.c new file mode 100644 index 000000000..2cf7cecdd --- /dev/null +++ b/kvm/user/test/lib/x86/fwcfg.c @@ -0,0 +1,40 @@ +#include "fwcfg.h" + +uint64_t fwcfg_get_u(uint16_t index, int bytes) +{ + uint64_t r = 0; + uint8_t b; + int i; + + asm volatile ("out %0, %1" : : "a"(index), "d"((uint16_t)BIOS_CFG_IOPORT)); + for (i = 0; i < bytes; ++i) { + asm volatile ("in %1, %0" : "=a"(b) : "d"((uint16_t)(BIOS_CFG_IOPORT + 1))); + r |= (uint64_t)b << (i * 8); + } + return r; +} + +uint8_t fwcfg_get_u8(unsigned index) +{ + return fwcfg_get_u(index, 1); +} + +uint16_t fwcfg_get_u16(unsigned index) +{ + return fwcfg_get_u(index, 2); +} + +uint32_t fwcfg_get_u32(unsigned index) +{ + return fwcfg_get_u(index, 4); +} + +uint64_t fwcfg_get_u64(unsigned index) +{ + return fwcfg_get_u(index, 8); +} + +unsigned fwcfg_get_nb_cpus(void) +{ + return fwcfg_get_u16(FW_CFG_NB_CPUS); +} diff --git a/kvm/user/test/lib/x86/fwcfg.h b/kvm/user/test/lib/x86/fwcfg.h new file mode 100644 index 000000000..e0836ca4d --- /dev/null +++ b/kvm/user/test/lib/x86/fwcfg.h @@ -0,0 +1,44 @@ +#ifndef FWCFG_H +#define FWCFG_H + +#include <stdint.h> + +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_MAX_ENTRY 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) + +#define FW_CFG_INVALID 0xffff + +#define BIOS_CFG_IOPORT 0x510 + +#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) +#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) +#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) + +uint8_t fwcfg_get_u8(unsigned index); +uint16_t fwcfg_get_u16(unsigned index); +uint32_t fwcfg_get_u32(unsigned index); +uint64_t fwcfg_get_u64(unsigned index); + +unsigned fwcfg_get_nb_cpus(void); + +#endif + diff --git a/kvm/user/test/lib/x86/io.c b/kvm/user/test/lib/x86/io.c new file mode 100644 index 000000000..894f398b1 --- /dev/null +++ b/kvm/user/test/lib/x86/io.c @@ -0,0 +1,23 @@ +#include "libcflat.h" +#include "smp.h" + +static struct spinlock lock; + +static void print_serial(const char *buf) +{ + unsigned long len = strlen(buf); + + asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1)); +} + +void puts(const char *s) +{ + spin_lock(&lock); + print_serial(s); + spin_unlock(&lock); +} + +void exit(int code) +{ + asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4)); +} diff --git a/kvm/user/test/lib/x86/smp.c b/kvm/user/test/lib/x86/smp.c new file mode 100644 index 000000000..241f7551e --- /dev/null +++ b/kvm/user/test/lib/x86/smp.c @@ -0,0 +1,128 @@ + +#include <libcflat.h> +#include "smp.h" +#include "apic.h" +#include "fwcfg.h" + +#define IPI_VECTOR 0x20 + +static struct spinlock ipi_lock; +static void (*ipi_function)(void *data); +static void *ipi_data; +static volatile int ipi_done; + +static __attribute__((used)) void ipi() +{ + ipi_function(ipi_data); + apic_write(APIC_EOI, 0); + ipi_done = 1; +} + +asm ( + "ipi_entry: \n" + " call ipi \n" +#ifndef __x86_64__ + " iret" +#else + " iretq" +#endif + ); + + +static void set_ipi_descriptor(void (*ipi_entry)(void)) +{ + unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2); + unsigned short cs; + unsigned long ipi = (unsigned long)ipi_entry; + + asm ("mov %%cs, %0" : "=r"(cs)); + desc[0] = ipi; + desc[1] = cs; + desc[2] = 0x8e00; + desc[3] = ipi >> 16; +#ifdef __x86_64__ + desc[4] = ipi >> 32; + desc[5] = ipi >> 48; + desc[6] = 0; + desc[7] = 0; +#endif +} + +void spin_lock(struct spinlock *lock) +{ + int v = 1; + + do { + asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v)); + } while (v); + asm volatile ("" : : : "memory"); +} + +void spin_unlock(struct spinlock *lock) +{ + asm volatile ("" : : : "memory"); + lock->v = 0; +} + +int cpu_count(void) +{ + return fwcfg_get_nb_cpus(); +} + +int smp_id(void) +{ + unsigned id; + + asm ("mov %%gs:0, %0" : "=r"(id)); + return id; +} + +static void setup_smp_id(void *data) +{ + asm ("mov %0, %%gs:0" : : "r"(apic_id()) : "memory"); +} + +static void __on_cpu(int cpu, void (*function)(void *data), void *data, + int wait) +{ + spin_lock(&ipi_lock); + if (cpu == smp_id()) + function(data); + else { + ipi_done = 0; + ipi_function = function; + ipi_data = data; + apic_icr_write(APIC_INT_ASSERT | APIC_DEST_PHYSICAL | APIC_DM_FIXED + | IPI_VECTOR, + cpu); + if (wait) { + while (!ipi_done) + ; + } + } + spin_unlock(&ipi_lock); +} + +void on_cpu(int cpu, void (*function)(void *data), void *data) +{ + __on_cpu(cpu, function, data, 1); +} + +void on_cpu_async(int cpu, void (*function)(void *data), void *data) +{ + __on_cpu(cpu, function, data, 0); +} + + +void smp_init(void) +{ + int i; + void ipi_entry(void); + + set_ipi_descriptor(ipi_entry); + + setup_smp_id(0); + for (i = 1; i < cpu_count(); ++i) + on_cpu(i, setup_smp_id, 0); + +} diff --git a/kvm/user/test/lib/x86/smp.h b/kvm/user/test/lib/x86/smp.h new file mode 100644 index 000000000..c2e73501c --- /dev/null +++ b/kvm/user/test/lib/x86/smp.h @@ -0,0 +1,17 @@ +#ifndef __SMP_H +#define __SMP_H + +struct spinlock { + int v; +}; + +void smp_init(void); + +int cpu_count(void); +int smp_id(void); +void on_cpu(int cpu, void (*function)(void *data), void *data); +void on_cpu_async(int cpu, void (*function)(void *data), void *data); +void spin_lock(struct spinlock *lock); +void spin_unlock(struct spinlock *lock); + +#endif diff --git a/kvm/user/test/powerpc/44x/tlbsx.S b/kvm/user/test/powerpc/44x/tlbsx.S new file mode 100644 index 000000000..b15874b18 --- /dev/null +++ b/kvm/user/test/powerpc/44x/tlbsx.S @@ -0,0 +1,33 @@ +#define SPRN_MMUCR 0x3b2 + +#define TLBWORD0 0x10000210 +#define TLBWORD1 0x10000000 +#define TLBWORD2 0x00000003 + +.global _start +_start: + li r4, 0 + mtspr SPRN_MMUCR, r4 + + li r3, 23 + + lis r4, TLBWORD0@h + ori r4, r4, TLBWORD0@l + tlbwe r4, r3, 0 + + lis r4, TLBWORD1@h + ori r4, r4, TLBWORD1@l + tlbwe r4, r3, 1 + + lis r4, TLBWORD2@h + ori r4, r4, TLBWORD2@l + tlbwe r4, r3, 2 + + lis r4, 0x1000 + tlbsx r5, r4, r0 + cmpwi r5, 23 + beq good + trap + +good: + b . diff --git a/kvm/user/test/powerpc/44x/tlbwe.S b/kvm/user/test/powerpc/44x/tlbwe.S new file mode 100644 index 000000000..ec6ef5c57 --- /dev/null +++ b/kvm/user/test/powerpc/44x/tlbwe.S @@ -0,0 +1,27 @@ +#define SPRN_MMUCR 0x3b2 + +/* Create a mapping at 4MB */ +#define TLBWORD0 0x00400210 +#define TLBWORD1 0x00400000 +#define TLBWORD2 0x00000003 + +.global _start +_start: + li r4, 0 + mtspr SPRN_MMUCR, r4 + + li r3, 23 + + lis r4, TLBWORD0@h + ori r4, r4, TLBWORD0@l + tlbwe r4, r3, 0 + + lis r4, TLBWORD1@h + ori r4, r4, TLBWORD1@l + tlbwe r4, r3, 1 + + lis r4, TLBWORD2@h + ori r4, r4, TLBWORD2@l + tlbwe r4, r3, 2 + + b . diff --git a/kvm/user/test/powerpc/44x/tlbwe_16KB.S b/kvm/user/test/powerpc/44x/tlbwe_16KB.S new file mode 100644 index 000000000..1bd10bf17 --- /dev/null +++ b/kvm/user/test/powerpc/44x/tlbwe_16KB.S @@ -0,0 +1,35 @@ +#define SPRN_MMUCR 0x3b2 + +/* 16KB mapping at 4MB */ +#define TLBWORD0 0x00400220 +#define TLBWORD1 0x00400000 +#define TLBWORD2 0x00000003 + +.global _start +_start: + li r4, 0 + mtspr SPRN_MMUCR, r4 + + li r3, 5 + + lis r4, TLBWORD0@h + ori r4, r4, TLBWORD0@l + tlbwe r4, r3, 0 + + lis r4, TLBWORD1@h + ori r4, r4, TLBWORD1@l + tlbwe r4, r3, 1 + + lis r4, TLBWORD2@h + ori r4, r4, TLBWORD2@l + tlbwe r4, r3, 2 + + /* load from 4MB */ + lis r3, 0x0040 + lwz r4, 0(r3) + + /* load from 4MB+8KB */ + ori r3, r3, 0x2000 + lwz r4, 0(r3) + + b . diff --git a/kvm/user/test/powerpc/44x/tlbwe_hole.S b/kvm/user/test/powerpc/44x/tlbwe_hole.S new file mode 100644 index 000000000..5efd30357 --- /dev/null +++ b/kvm/user/test/powerpc/44x/tlbwe_hole.S @@ -0,0 +1,27 @@ +#define SPRN_MMUCR 0x3b2 + +/* Try to map real address 1GB. */ +#define TLBWORD0 0x40000210 +#define TLBWORD1 0x40000000 +#define TLBWORD2 0x00000003 + +.global _start +_start: + li r4, 0 + mtspr SPRN_MMUCR, r4 + + li r3, 23 + + lis r4, TLBWORD0@h + ori r4, r4, TLBWORD0@l + tlbwe r4, r3, 0 + + lis r4, TLBWORD1@h + ori r4, r4, TLBWORD1@l + tlbwe r4, r3, 1 + + lis r4, TLBWORD2@h + ori r4, r4, TLBWORD2@l + tlbwe r4, r3, 2 + + b . diff --git a/kvm/user/test/powerpc/cstart.S b/kvm/user/test/powerpc/cstart.S new file mode 100644 index 000000000..70a0e9fcd --- /dev/null +++ b/kvm/user/test/powerpc/cstart.S @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation; + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#define OUTPUT_VADDR 0xf0000000 +#define OUTPUT_PADDR 0xf0000000 + +.globl _start +_start: + /* In the future we might need to assign a stack and zero BSS here. */ + + /* Map the debug page 1:1. */ + lis r3, OUTPUT_VADDR@h + ori r3, r3, OUTPUT_VADDR@l + lis r4, OUTPUT_PADDR@h + ori r4, r4, OUTPUT_PADDR@l + bl map + + /* Call main() and pass return code to exit(). */ + bl main + bl exit + + b . diff --git a/kvm/user/test/powerpc/exit.c b/kvm/user/test/powerpc/exit.c new file mode 100644 index 000000000..804ee04d9 --- /dev/null +++ b/kvm/user/test/powerpc/exit.c @@ -0,0 +1,23 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation; + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +int main(void) +{ + return 1; +} diff --git a/kvm/user/test/powerpc/helloworld.c b/kvm/user/test/powerpc/helloworld.c new file mode 100644 index 000000000..f8630f7c5 --- /dev/null +++ b/kvm/user/test/powerpc/helloworld.c @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation; + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Deepa Srinivasan <deepas@us.ibm.com> + */ + +#include "libcflat.h" + +int main() +{ + printf("Hello World\n"); + + return 1; +} diff --git a/kvm/user/test/powerpc/io.S b/kvm/user/test/powerpc/io.S new file mode 100644 index 000000000..97567cb6c --- /dev/null +++ b/kvm/user/test/powerpc/io.S @@ -0,0 +1,32 @@ +#define SPRN_MMUCR 0x3b2 + +#define TLBWORD0 0xf0000210 +#define TLBWORD1 0xf0000000 +#define TLBWORD2 0x00000003 + +.global _start +_start: + li r4, 0 + mtspr SPRN_MMUCR, r4 + + li r3, 2 + + lis r4, TLBWORD0@h + ori r4, r4, TLBWORD0@l + tlbwe r4, r3, 0 + + lis r4, TLBWORD1@h + ori r4, r4, TLBWORD1@l + tlbwe r4, r3, 1 + + lis r4, TLBWORD2@h + ori r4, r4, TLBWORD2@l + tlbwe r4, r3, 2 + + lis r3, 0xf000 + lis r4, 0x1234 + ori r4, r4, 0x5678 + stb r4, 0(r3) + lbz r5, 0(r3) + + b . diff --git a/kvm/user/test/powerpc/spin.S b/kvm/user/test/powerpc/spin.S new file mode 100644 index 000000000..4406641c2 --- /dev/null +++ b/kvm/user/test/powerpc/spin.S @@ -0,0 +1,4 @@ + +.global _start +_start: + b . diff --git a/kvm/user/test/powerpc/sprg.S b/kvm/user/test/powerpc/sprg.S new file mode 100644 index 000000000..d0414a480 --- /dev/null +++ b/kvm/user/test/powerpc/sprg.S @@ -0,0 +1,7 @@ + +.global _start +_start: + li r3, 42 + mtsprg 0, r3 + mfsprg r4, 0 + b . diff --git a/kvm/user/test/x86/access.c b/kvm/user/test/x86/access.c new file mode 100644 index 000000000..5eadff824 --- /dev/null +++ b/kvm/user/test/x86/access.c @@ -0,0 +1,604 @@ + +#include "libcflat.h" + +#define smp_id() 0 + +#define true 1 +#define false 0 + +typedef unsigned long pt_element_t; + +#define PAGE_SIZE ((pt_element_t)4096) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK)) +#define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21)) + +#define PT_PRESENT_MASK ((pt_element_t)1 << 0) +#define PT_WRITABLE_MASK ((pt_element_t)1 << 1) +#define PT_USER_MASK ((pt_element_t)1 << 2) +#define PT_ACCESSED_MASK ((pt_element_t)1 << 5) +#define PT_DIRTY_MASK ((pt_element_t)1 << 6) +#define PT_PSE_MASK ((pt_element_t)1 << 7) +#define PT_NX_MASK ((pt_element_t)1 << 63) + +#define CR0_WP_MASK (1UL << 16) + +#define PFERR_PRESENT_MASK (1U << 0) +#define PFERR_WRITE_MASK (1U << 1) +#define PFERR_USER_MASK (1U << 2) +#define PFERR_RESERVED_MASK (1U << 3) +#define PFERR_FETCH_MASK (1U << 4) + +#define MSR_EFER 0xc0000080 +#define EFER_NX_MASK (1ull << 11) + +/* + * page table access check tests + */ + +enum { + AC_PTE_PRESENT, + AC_PTE_WRITABLE, + AC_PTE_USER, + AC_PTE_ACCESSED, + AC_PTE_DIRTY, + AC_PTE_NX, + AC_PTE_BIT51, + + AC_PDE_PRESENT, + AC_PDE_WRITABLE, + AC_PDE_USER, + AC_PDE_ACCESSED, + AC_PDE_DIRTY, + AC_PDE_PSE, + AC_PDE_NX, + AC_PDE_BIT51, + + AC_ACCESS_USER, + AC_ACCESS_WRITE, + AC_ACCESS_FETCH, + AC_ACCESS_TWICE, + // AC_ACCESS_PTE, + + AC_CPU_EFER_NX, + AC_CPU_CR0_WP, + + NR_AC_FLAGS +}; + +const char *ac_names[] = { + [AC_PTE_PRESENT] = "pte.p", + [AC_PTE_ACCESSED] = "pte.a", + [AC_PTE_WRITABLE] = "pte.rw", + [AC_PTE_USER] = "pte.user", + [AC_PTE_DIRTY] = "pte.d", + [AC_PTE_NX] = "pte.nx", + [AC_PTE_BIT51] = "pte.51", + [AC_PDE_PRESENT] = "pde.p", + [AC_PDE_ACCESSED] = "pde.a", + [AC_PDE_WRITABLE] = "pde.rw", + [AC_PDE_USER] = "pde.user", + [AC_PDE_DIRTY] = "pde.d", + [AC_PDE_PSE] = "pde.pse", + [AC_PDE_NX] = "pde.nx", + [AC_PDE_BIT51] = "pde.51", + [AC_ACCESS_WRITE] = "write", + [AC_ACCESS_USER] = "user", + [AC_ACCESS_FETCH] = "fetch", + [AC_ACCESS_TWICE] = "twice", + [AC_CPU_EFER_NX] = "efer.nx", + [AC_CPU_CR0_WP] = "cr0.wp", +}; + +static inline void *va(pt_element_t phys) +{ + return (void *)phys; +} + +static unsigned long read_cr0() +{ + unsigned long cr0; + + asm volatile ("mov %%cr0, %0" : "=r"(cr0)); + + return cr0; +} + +static void write_cr0(unsigned long cr0) +{ + asm volatile ("mov %0, %%cr0" : : "r"(cr0)); +} + +typedef struct { + unsigned short offset0; + unsigned short selector; + unsigned short ist : 3; + unsigned short : 5; + unsigned short type : 4; + unsigned short : 1; + unsigned short dpl : 2; + unsigned short p : 1; + unsigned short offset1; + unsigned offset2; + unsigned reserved; +} idt_entry_t; + +typedef struct { + unsigned flags[NR_AC_FLAGS]; + void *virt; + pt_element_t phys; + pt_element_t pt_pool; + unsigned pt_pool_size; + unsigned pt_pool_current; + pt_element_t *ptep; + pt_element_t expected_pte; + pt_element_t *pdep; + pt_element_t expected_pde; + int expected_fault; + unsigned expected_error; + idt_entry_t idt[256]; +} ac_test_t; + +typedef struct { + unsigned short limit; + unsigned long linear_addr; +} __attribute__((packed)) descriptor_table_t; + +void lidt(idt_entry_t *idt, int nentries) +{ + descriptor_table_t dt; + + dt.limit = nentries * sizeof(*idt) - 1; + dt.linear_addr = (unsigned long)idt; + asm volatile ("lidt %0" : : "m"(dt)); +} + +void memset(void *a, unsigned char v, int n) +{ + unsigned char *x = a; + + while (n--) + *x++ = v; +} + +unsigned short read_cs() +{ + unsigned short r; + + asm volatile ("mov %%cs, %0" : "=r"(r)); + return r; +} + +unsigned long long rdmsr(unsigned index) +{ + unsigned a, d; + + asm volatile("rdmsr" : "=a"(a), "=d"(d) : "c"(index)); + return ((unsigned long long)d << 32) | a; +} + +void wrmsr(unsigned index, unsigned long long val) +{ + unsigned a = val, d = val >> 32; + + asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(index)); +} + +void set_idt_entry(idt_entry_t *e, void *addr, int dpl) +{ + memset(e, 0, sizeof *e); + e->offset0 = (unsigned long)addr; + e->selector = read_cs(); + e->ist = 0; + e->type = 14; + e->dpl = dpl; + e->p = 1; + e->offset1 = (unsigned long)addr >> 16; + e->offset2 = (unsigned long)addr >> 32; +} + +void set_cr0_wp(int wp) +{ + unsigned long cr0 = read_cr0(); + + cr0 &= ~CR0_WP_MASK; + if (wp) + cr0 |= CR0_WP_MASK; + write_cr0(cr0); +} + +void set_efer_nx(int nx) +{ + unsigned long long efer; + + efer = rdmsr(MSR_EFER); + efer &= ~EFER_NX_MASK; + if (nx) + efer |= EFER_NX_MASK; + wrmsr(MSR_EFER, efer); +} + + +void ac_test_init(ac_test_t *at) +{ + wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK); + set_cr0_wp(1); + for (int i = 0; i < NR_AC_FLAGS; ++i) + at->flags[i] = 0; + at->virt = (void *)(0x123400000000 + 16 * smp_id()); + at->phys = 32 * 1024 * 1024; + at->pt_pool = 33 * 1024 * 1024; + at->pt_pool_size = 120 * 1024 * 1024 - at->pt_pool; + at->pt_pool_current = 0; + memset(at->idt, 0, sizeof at->idt); + lidt(at->idt, 256); + extern char page_fault, kernel_entry; + set_idt_entry(&at->idt[14], &page_fault, 0); + set_idt_entry(&at->idt[0x20], &kernel_entry, 3); +} + +int ac_test_bump_one(ac_test_t *at) +{ + for (int i = 0; i < NR_AC_FLAGS; ++i) + if (!at->flags[i]) { + at->flags[i] = 1; + return 1; + } else + at->flags[i] = 0; + return 0; +} + +_Bool ac_test_legal(ac_test_t *at) +{ + if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_ACCESS_WRITE]) + return false; + return true; +} + +int ac_test_bump(ac_test_t *at) +{ + int ret; + + ret = ac_test_bump_one(at); + while (ret && !ac_test_legal(at)) + ret = ac_test_bump_one(at); + return ret; +} + +unsigned long read_cr3() +{ + unsigned long cr3; + + asm volatile ("mov %%cr3, %0" : "=r"(cr3)); + return cr3; +} + +void invlpg(void *addr) +{ + asm volatile ("invlpg (%0)" : : "r"(addr)); +} + +pt_element_t ac_test_alloc_pt(ac_test_t *at) +{ + pt_element_t ret = at->pt_pool + at->pt_pool_current; + at->pt_pool_current += PAGE_SIZE; + return ret; +} + +_Bool ac_test_enough_room(ac_test_t *at) +{ + return at->pt_pool_current + 4 * PAGE_SIZE <= at->pt_pool_size; +} + +void ac_test_reset_pt_pool(ac_test_t *at) +{ + at->pt_pool_current = 0; +} + +void ac_test_setup_pte(ac_test_t *at) +{ + unsigned long root = read_cr3(); + int pde_valid, pte_valid; + + if (!ac_test_enough_room(at)) + ac_test_reset_pt_pool(at); + + at->ptep = 0; + for (int i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) { + pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK); + unsigned index = ((unsigned long)at->virt >> (12 + (i-1) * 9)) & 511; + pt_element_t pte = 0; + switch (i) { + case 4: + case 3: + pte = vroot[index]; + pte = ac_test_alloc_pt(at) | PT_PRESENT_MASK; + pte |= PT_WRITABLE_MASK | PT_USER_MASK; + break; + case 2: + if (!at->flags[AC_PDE_PSE]) + pte = ac_test_alloc_pt(at); + else { + pte = at->phys & PT_PSE_BASE_ADDR_MASK; + pte |= PT_PSE_MASK; + } + if (at->flags[AC_PDE_PRESENT]) + pte |= PT_PRESENT_MASK; + if (at->flags[AC_PDE_WRITABLE]) + pte |= PT_WRITABLE_MASK; + if (at->flags[AC_PDE_USER]) + pte |= PT_USER_MASK; + if (at->flags[AC_PDE_ACCESSED]) + pte |= PT_ACCESSED_MASK; + if (at->flags[AC_PDE_DIRTY]) + pte |= PT_DIRTY_MASK; + if (at->flags[AC_PDE_NX]) + pte |= PT_NX_MASK; + if (at->flags[AC_PDE_BIT51]) + pte |= 1ull << 51; + at->pdep = &vroot[index]; + break; + case 1: + pte = at->phys & PT_BASE_ADDR_MASK; + if (at->flags[AC_PTE_PRESENT]) + pte |= PT_PRESENT_MASK; + if (at->flags[AC_PTE_WRITABLE]) + pte |= PT_WRITABLE_MASK; + if (at->flags[AC_PTE_USER]) + pte |= PT_USER_MASK; + if (at->flags[AC_PTE_ACCESSED]) + pte |= PT_ACCESSED_MASK; + if (at->flags[AC_PTE_DIRTY]) + pte |= PT_DIRTY_MASK; + if (at->flags[AC_PTE_NX]) + pte |= PT_NX_MASK; + if (at->flags[AC_PTE_BIT51]) + pte |= 1ull << 51; + at->ptep = &vroot[index]; + break; + } + vroot[index] = pte; + root = vroot[index]; + } + invlpg(at->virt); + if (at->ptep) + at->expected_pte = *at->ptep; + at->expected_pde = *at->pdep; + at->expected_fault = 0; + at->expected_error = PFERR_PRESENT_MASK; + + pde_valid = at->flags[AC_PDE_PRESENT] + && !at->flags[AC_PDE_BIT51] + && !(at->flags[AC_PDE_NX] && !at->flags[AC_CPU_EFER_NX]); + pte_valid = pde_valid + && at->flags[AC_PTE_PRESENT] + && !at->flags[AC_PTE_BIT51] + && !(at->flags[AC_PTE_NX] && !at->flags[AC_CPU_EFER_NX]); + if (at->flags[AC_ACCESS_TWICE]) { + if (pde_valid) { + at->expected_pde |= PT_ACCESSED_MASK; + if (pte_valid) + at->expected_pte |= PT_ACCESSED_MASK; + } + } + + if (at->flags[AC_ACCESS_USER]) + at->expected_error |= PFERR_USER_MASK; + + if (at->flags[AC_ACCESS_WRITE]) + at->expected_error |= PFERR_WRITE_MASK; + + if (at->flags[AC_ACCESS_FETCH]) + at->expected_error |= PFERR_FETCH_MASK; + + if (!at->flags[AC_PDE_PRESENT]) { + at->expected_fault = 1; + at->expected_error &= ~PFERR_PRESENT_MASK; + } else if (!pde_valid) { + at->expected_fault = 1; + at->expected_error |= PFERR_RESERVED_MASK; + } + + if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PDE_USER]) + at->expected_fault = 1; + + if (at->flags[AC_ACCESS_WRITE] + && !at->flags[AC_PDE_WRITABLE] + && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) + at->expected_fault = 1; + + if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_NX]) + at->expected_fault = 1; + + if (at->expected_fault) + goto fault; + + at->expected_pde |= PT_ACCESSED_MASK; + + if (at->flags[AC_PDE_PSE]) { + if (at->flags[AC_ACCESS_WRITE]) + at->expected_pde |= PT_DIRTY_MASK; + goto no_pte; + } + + if (!at->flags[AC_PTE_PRESENT]) { + at->expected_fault = 1; + at->expected_error &= ~PFERR_PRESENT_MASK; + } else if (!pte_valid) { + at->expected_fault = 1; + at->expected_error |= PFERR_RESERVED_MASK; + } + + if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PTE_USER]) + at->expected_fault = 1; + + if (at->flags[AC_ACCESS_WRITE] + && !at->flags[AC_PTE_WRITABLE] + && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) + at->expected_fault = 1; + + if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PTE_NX]) + at->expected_fault = 1; + + if (at->expected_fault) + goto fault; + + at->expected_pte |= PT_ACCESSED_MASK; + if (at->flags[AC_ACCESS_WRITE]) + at->expected_pte |= PT_DIRTY_MASK; + +no_pte: +fault: + ; +} + +int ac_test_do_access(ac_test_t *at) +{ + static unsigned unique = 42; + int fault = 0; + unsigned e; + static unsigned char user_stack[4096]; + unsigned long rsp; + + ++unique; + + *((unsigned char *)at->phys) = 0xc3; /* ret */ + + unsigned r = unique; + set_cr0_wp(at->flags[AC_CPU_CR0_WP]); + set_efer_nx(at->flags[AC_CPU_EFER_NX]); + + if (at->flags[AC_ACCESS_TWICE]) { + asm volatile ( + "mov $fixed2, %%rsi \n\t" + "mov (%[addr]), %[reg] \n\t" + "fixed2:" + : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e) + : [addr]"r"(at->virt) + : "rsi" + ); + fault = 0; + } + + asm volatile ("mov $fixed1, %%rsi \n\t" + "mov %%rsp, %%rdx \n\t" + "cmp $0, %[user] \n\t" + "jz do_access \n\t" + "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax \n\t" + "pushq %[user_ds] \n\t" + "pushq %[user_stack_top] \n\t" + "pushfq \n\t" + "pushq %[user_cs] \n\t" + "pushq $do_access \n\t" + "iretq \n" + "do_access: \n\t" + "cmp $0, %[fetch] \n\t" + "jnz 2f \n\t" + "cmp $0, %[write] \n\t" + "jnz 1f \n\t" + "mov (%[addr]), %[reg] \n\t" + "jmp done \n\t" + "1: mov %[reg], (%[addr]) \n\t" + "jmp done \n\t" + "2: call *%[addr] \n\t" + "done: \n" + "fixed1: \n" + "int %[kernel_entry_vector] \n\t" + "back_to_kernel:" + : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp) + : [addr]"r"(at->virt), + [write]"r"(at->flags[AC_ACCESS_WRITE]), + [user]"r"(at->flags[AC_ACCESS_USER]), + [fetch]"r"(at->flags[AC_ACCESS_FETCH]), + [user_ds]"i"(32+3), + [user_cs]"i"(24+3), + [user_stack_top]"r"(user_stack + sizeof user_stack), + [kernel_entry_vector]"i"(0x20) + : "rsi"); + + asm volatile (".section .text.pf \n\t" + "page_fault: \n\t" + "pop %rbx \n\t" + "mov %rsi, (%rsp) \n\t" + "movl $1, %eax \n\t" + "iretq \n\t" + ".section .text"); + + asm volatile (".section .text.entry \n\t" + "kernel_entry: \n\t" + "mov %rdx, %rsp \n\t" + "jmp back_to_kernel \n\t" + ".section .text"); + + if (fault && !at->expected_fault) { + printf("FAIL: unexpected fault\n"); + return 0; + } + if (!fault && at->expected_fault) { + printf("FAIL: unexpected access\n"); + return 0; + } + if (fault && e != at->expected_error) { + printf("FAIL: error code %x expected %x\n", e, at->expected_error); + return 0; + } + if (at->ptep && *at->ptep != at->expected_pte) { + printf("FAIL: pte %x expected %x\n", *at->ptep, at->expected_pte); + return 0; + } + + if (*at->pdep != at->expected_pde) { + printf("FAIL: pde %x expected %x\n", *at->pdep, at->expected_pde); + return 0; + } + + printf("PASS\n"); + return 1; +} + +int ac_test_exec(ac_test_t *at) +{ + int r; + char line[5000]; + + *line = 0; + strcat(line, "test"); + for (int i = 0; i < NR_AC_FLAGS; ++i) + if (at->flags[i]) { + strcat(line, " "); + strcat(line, ac_names[i]); + } + strcat(line, ": "); + printf("%s", line); + ac_test_setup_pte(at); + r = ac_test_do_access(at); + return r; +} + +int ac_test_run(void) +{ + static ac_test_t at; + int tests, successes; + + printf("run\n"); + tests = successes = 0; + ac_test_init(&at); + do { + ++tests; + successes += ac_test_exec(&at); + } while (ac_test_bump(&at)); + + printf("\n%d tests, %d failures\n", tests, tests - successes); + + return successes == tests; +} + +int main() +{ + int r; + + printf("starting test\n\n"); + r = ac_test_run(); + return r ? 0 : 1; +} diff --git a/kvm/user/test/x86/apic.c b/kvm/user/test/x86/apic.c new file mode 100644 index 000000000..b6718ec08 --- /dev/null +++ b/kvm/user/test/x86/apic.c @@ -0,0 +1,317 @@ +#include "libcflat.h" +#include "apic.h" +#include "vm.h" + +typedef struct { + unsigned short offset0; + unsigned short selector; + unsigned short ist : 3; + unsigned short : 5; + unsigned short type : 4; + unsigned short : 1; + unsigned short dpl : 2; + unsigned short p : 1; + unsigned short offset1; +#ifdef __x86_64__ + unsigned offset2; + unsigned reserved; +#endif +} idt_entry_t; + +typedef struct { + ulong regs[sizeof(ulong)*2]; + ulong func; + ulong rip; + ulong cs; + ulong rflags; +} isr_regs_t; + +#ifdef __x86_64__ +# define R "r" +#else +# define R "e" +#endif + +extern char isr_entry_point[]; + +asm ( + "isr_entry_point: \n" +#ifdef __x86_64__ + "push %r15 \n\t" + "push %r14 \n\t" + "push %r13 \n\t" + "push %r12 \n\t" + "push %r11 \n\t" + "push %r10 \n\t" + "push %r9 \n\t" + "push %r8 \n\t" +#endif + "push %"R "di \n\t" + "push %"R "si \n\t" + "push %"R "bp \n\t" + "push %"R "sp \n\t" + "push %"R "bx \n\t" + "push %"R "dx \n\t" + "push %"R "cx \n\t" + "push %"R "ax \n\t" +#ifdef __x86_64__ + "mov %rsp, %rdi \n\t" + "callq *8*16(%rsp) \n\t" +#else + "push %esp \n\t" + "calll *4+4*8(%esp) \n\t" + "add $4, %esp \n\t" +#endif + "pop %"R "ax \n\t" + "pop %"R "cx \n\t" + "pop %"R "dx \n\t" + "pop %"R "bx \n\t" + "pop %"R "bp \n\t" + "pop %"R "bp \n\t" + "pop %"R "si \n\t" + "pop %"R "di \n\t" +#ifdef __x86_64__ + "pop %r8 \n\t" + "pop %r9 \n\t" + "pop %r10 \n\t" + "pop %r11 \n\t" + "pop %r12 \n\t" + "pop %r13 \n\t" + "pop %r14 \n\t" + "pop %r15 \n\t" +#endif +#ifdef __x86_64__ + "add $8, %rsp \n\t" + "iretq \n\t" +#else + "add $4, %esp \n\t" + "iretl \n\t" +#endif + ); + +static idt_entry_t idt[256]; + +static int g_fail; +static int g_tests; + +static void outb(unsigned char data, unsigned short port) +{ + asm volatile ("out %0, %1" : : "a"(data), "d"(port)); +} + +static void report(const char *msg, int pass) +{ + ++g_tests; + printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); + if (!pass) + ++g_fail; +} + +static void test_lapic_existence(void) +{ + u32 lvr; + + lvr = apic_read(APIC_LVR); + printf("apic version: %x\n", lvr); + report("apic existence", (u16)lvr == 0x14); +} + +#define MSR_APIC_BASE 0x0000001b + +void test_enable_x2apic(void) +{ + if (enable_x2apic()) { + printf("x2apic enabled\n"); + } else { + printf("x2apic not detected\n"); + } +} + +static u16 read_cs(void) +{ + u16 v; + + asm("mov %%cs, %0" : "=rm"(v)); + return v; +} + +static void init_idt(void) +{ + struct { + u16 limit; + ulong idt; + } __attribute__((packed)) idt_ptr = { + sizeof(idt_entry_t) * 256 - 1, + (ulong)&idt, + }; + + asm volatile("lidt %0" : : "m"(idt_ptr)); +} + +static void set_idt_entry(unsigned vec, void (*func)(isr_regs_t *regs)) +{ + u8 *thunk = vmalloc(50); + ulong ptr = (ulong)thunk; + idt_entry_t ent = { + .offset0 = ptr, + .selector = read_cs(), + .ist = 0, + .type = 14, + .dpl = 0, + .p = 1, + .offset1 = ptr >> 16, +#ifdef __x86_64__ + .offset2 = ptr >> 32, +#endif + }; +#ifdef __x86_64__ + /* sub $8, %rsp */ + *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08; + /* mov $func_low, %(rsp) */ + *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24; + *(u32 *)thunk = (ulong)func; thunk += 4; + /* mov $func_high, %(rsp+4) */ + *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04; + *(u32 *)thunk = (ulong)func >> 32; thunk += 4; + /* jmp isr_entry_point */ + *thunk ++ = 0xe9; + *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); +#else + /* push $func */ + *thunk++ = 0x68; + *(u32 *)thunk = (ulong)func; + /* jmp isr_entry_point */ + *thunk ++ = 0xe9; + *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); +#endif + idt[vec] = ent; +} + +static void irq_disable(void) +{ + asm volatile("cli"); +} + +static void irq_enable(void) +{ + asm volatile("sti"); +} + +static void eoi(void) +{ + apic_write(APIC_EOI, 0); +} + +static int ipi_count; + +static void self_ipi_isr(isr_regs_t *regs) +{ + ++ipi_count; + eoi(); +} + +static void test_self_ipi(void) +{ + int vec = 0xf1; + + set_idt_entry(vec, self_ipi_isr); + irq_enable(); + apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, + 0); + asm volatile ("nop"); + report("self ipi", ipi_count == 1); +} + +static void set_ioapic_redir(unsigned line, unsigned vec) +{ + ioapic_redir_entry_t e = { + .vector = vec, + .delivery_mode = 0, + .trig_mode = 0, + }; + + ioapic_write_redir(line, e); +} + +static void set_irq_line(unsigned line, int val) +{ + asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); +} + +static void toggle_irq_line(unsigned line) +{ + set_irq_line(line, 1); + set_irq_line(line, 0); +} + +static int g_isr_77; + +static void ioapic_isr_77(isr_regs_t *regs) +{ + ++g_isr_77; + eoi(); +} + +static void test_ioapic_intr(void) +{ + set_idt_entry(0x77, ioapic_isr_77); + set_ioapic_redir(0x10, 0x77); + toggle_irq_line(0x10); + asm volatile ("nop"); + report("ioapic interrupt", g_isr_77 == 1); +} + +static int g_78, g_66, g_66_after_78; +static ulong g_66_rip, g_78_rip; + +static void ioapic_isr_78(isr_regs_t *regs) +{ + ++g_78; + g_78_rip = regs->rip; + eoi(); +} + +static void ioapic_isr_66(isr_regs_t *regs) +{ + ++g_66; + if (g_78) + ++g_66_after_78; + g_66_rip = regs->rip; + eoi(); +} + +static void test_ioapic_simultaneous(void) +{ + set_idt_entry(0x78, ioapic_isr_78); + set_idt_entry(0x66, ioapic_isr_66); + set_ioapic_redir(0x10, 0x78); + set_ioapic_redir(0x11, 0x66); + irq_disable(); + toggle_irq_line(0x11); + toggle_irq_line(0x10); + irq_enable(); + asm volatile ("nop"); + report("ioapic simultaneous interrupt", + g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); +} + +int main() +{ + setup_vm(); + + test_lapic_existence(); + + mask_pic_interrupts(); + enable_apic(); + test_enable_x2apic(); + init_idt(); + + test_self_ipi(); + + test_ioapic_intr(); + test_ioapic_simultaneous(); + + printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); + + return g_fail != 0; +} diff --git a/kvm/user/test/x86/bootstrap.S b/kvm/user/test/x86/bootstrap.S new file mode 100644 index 000000000..e32fea90c --- /dev/null +++ b/kvm/user/test/x86/bootstrap.S @@ -0,0 +1,137 @@ +/* + * minimal bootstrap to set up flat 32-bit protected mode + */ + +#include "fake-apic.h" + +bstart = 0xf0000 + +.code16 + +stack_top = 0x1000 +cpu_up = 0x1000 +cpu_up_pmode = 0x1004 + +pmode_stack_start = 0x10000 +pmode_stack_shift = 16 +pmode_stack_size = (1 << pmode_stack_shift) + +ipi_vec = 0xf0 + +start: + mov $stack_top, %sp + call smp_init + + cs lidtl idt_desc + cs lgdtl gdt_desc + mov %cr0, %eax + or $1, %eax + mov %eax, %cr0 + ljmpl $8, $pmode + bstart + +smp_init: + mov $ipi_vec, %eax + mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx + out %eax, %dx + movw $ap_switch_to_pmode, ipi_vec*4 + movw %cs, %ax + mov %ax, ipi_vec*4+2 + mov $sipi, %eax + mov $(APIC_BASE + APIC_REG_SIPI_ADDR), %dx + outl %eax, %dx + mov $(APIC_BASE + APIC_REG_NCPU), %dx + inl %dx, %eax + mov %eax, %ecx + mov $1, %esi +smp_loop: + cmp %esi, %ecx + jbe smp_done + mov %esi, %eax + mov $(APIC_BASE + APIC_REG_SEND_SIPI), %dx + outl %eax, %dx +wait_for_cpu: + cmp cpu_up, %esi + jne wait_for_cpu + mov %esi, %eax + mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx + out %eax, %dx +wait_for_cpu_pmode: + cmp cpu_up_pmode, %esi + jne wait_for_cpu_pmode + + inc %esi + jmp smp_loop +smp_done: + ret + +sipi: + mov $(APIC_BASE + APIC_REG_ID), %dx + inl %dx, %eax + mov %eax, cpu_up + shl $12, %eax + addl $stack_top, %eax + movl %eax, %esp + sti + nop +1: hlt + jmp 1b + +ap_switch_to_pmode: + cs lidtl idt_desc + cs lgdtl gdt_desc + mov %cr0, %eax + or $1, %eax + mov %eax, %cr0 + ljmpl $8, $ap_pmode + bstart + +.code32 +ap_pmode: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov $(APIC_BASE + APIC_REG_ID), %dx + in %dx, %eax + mov %eax, cpu_up_pmode + shl $pmode_stack_shift, %eax + lea pmode_stack_start + pmode_stack_size(%eax), %esp + sti + nop +ap_pmode_wait: + hlt + jmp ap_pmode_wait + +pmode: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov $pmode_stack_start + pmode_stack_size, %esp + ljmp $8, $0x100000 + +.align 16 + +idt_desc: + .word 8*256-1 + .long 0 + +gdt_desc: + .word gdt_end - gdt - 1 + .long gdt + bstart + +.align 16 + +gdt: + .quad 0 + .quad 0x00cf9b000000ffff // flat 32-bit code segment + .quad 0x00cf93000000ffff // flat 32-bit data segment +gdt_end: + +. = 0xfff0 + .code16 + ljmp $0xf000, $start +.align 65536 diff --git a/kvm/user/test/x86/cstart.S b/kvm/user/test/x86/cstart.S new file mode 100644 index 000000000..0471b92e7 --- /dev/null +++ b/kvm/user/test/x86/cstart.S @@ -0,0 +1,19 @@ + + +.bss + +.section .init + +mb_magic = 0x1BADB002 +mb_flags = 0x0 + + # multiboot header + .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) + +.globl start +start: + call main + push %eax + call exit + + diff --git a/kvm/user/test/x86/cstart64.S b/kvm/user/test/x86/cstart64.S new file mode 100644 index 000000000..f1a9d0955 --- /dev/null +++ b/kvm/user/test/x86/cstart64.S @@ -0,0 +1,225 @@ + +#include "apic-defs.h" + +.globl boot_idt +boot_idt = 0 + +ipi_vector = 0x20 + +max_cpus = 4 + +.bss + + . = . + 4096 * max_cpus + .align 16 +stacktop: + + . = . + 4096 + .align 16 +ring0stacktop: + +.data + +.align 4096 +ptl2: +i = 0 + .rept 512 * 4 + .quad 0x1e7 | (i << 21) + i = i + 1 + .endr + +.align 4096 +ptl3: + .quad ptl2 + 7 + 0 * 4096 + .quad ptl2 + 7 + 1 * 4096 + .quad ptl2 + 7 + 2 * 4096 + .quad ptl2 + 7 + 3 * 4096 + +.align 4096 +ptl4: + .quad ptl3 + 7 + +.align 4096 + +gdt64_desc: + .word gdt64_end - gdt64 - 1 + .quad gdt64 + +gdt64: + .quad 0 + .quad 0x00af9b000000ffff // 64-bit code segment + .quad 0x00cf93000000ffff // 64-bit data segment + .quad 0x00affb000000ffff // 64-bit code segment (user) + .quad 0x00cff3000000ffff // 64-bit data segment (user) +tss_descr: + .rept max_cpus + .quad 0x000089000000ffff // 64-bit avail tss + .quad 0 // tss high addr + .endr +gdt64_end: + +i = 0 +tss: + .rept max_cpus + .long 0 + .quad ring0stacktop - i * 4096 + .quad 0, 0, 0 + .quad 0, 0, 0, 0, 0, 0, 0, 0 + .long 0, 0, 0 +i = i + 1 + .endr +tss_end: + +.section .init + +.code32 + +mb_magic = 0x1BADB002 +mb_flags = 0x0 + + # multiboot header + .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) + +MSR_GS_BASE = 0xc0000101 + +.macro setup_percpu_area + lea -4096(%esp), %eax + mov $0, %edx + mov $MSR_GS_BASE, %ecx + wrmsr +.endm + +.globl start +start: + mov $stacktop, %esp + setup_percpu_area + call prepare_64 + jmpl $8, $start64 + +prepare_64: + lgdt gdt64_desc + + mov %cr4, %eax + bts $5, %eax // pae + mov %eax, %cr4 + + mov $ptl4, %eax + mov %eax, %cr3 + +efer = 0xc0000080 + mov $efer, %ecx + rdmsr + bts $8, %eax + wrmsr + + mov %cr0, %eax + bts $0, %eax + bts $31, %eax + mov %eax, %cr0 + ret + +smp_stacktop: .long 0xa0000 + +.align 16 + +gdt32: + .quad 0 + .quad 0x00cf9b000000ffff // flat 32-bit code segment + .quad 0x00cf93000000ffff // flat 32-bit data segment +gdt32_end: + +.code16 +sipi_entry: + mov %cr0, %eax + or $1, %eax + mov %eax, %cr0 + lgdtl gdt32_descr - sipi_entry + ljmpl $8, $ap_start32 + +gdt32_descr: + .word gdt32_end - gdt32 - 1 + .long gdt32 + +sipi_end: + +.code32 +ap_start32: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov $-4096, %esp + lock/xaddl %esp, smp_stacktop + setup_percpu_area + call prepare_64 + ljmpl $8, $ap_start64 + +.code64 +ap_start64: + call load_tss + call enable_apic + call enable_x2apic + sti + nop + lock incw cpu_online_count + +1: hlt + jmp 1b + +start64: + call load_tss + call mask_pic_interrupts + call enable_apic + call smp_init + call enable_x2apic + call main + mov %eax, %edi + call exit + +idt_descr: + .word 16 * 256 - 1 + .quad boot_idt + +load_tss: + lidtq idt_descr + mov $0, %eax + mov %ax, %ss + mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax + mov (%rax), %eax + shr $24, %eax + mov %eax, %ebx + shl $4, %ebx + mov $((tss_end - tss) / max_cpus), %edx + imul %edx + add $tss, %rax + mov %ax, tss_descr+2(%rbx) + shr $16, %rax + mov %al, tss_descr+4(%rbx) + shr $8, %rax + mov %al, tss_descr+7(%rbx) + shr $8, %rax + mov %eax, tss_descr+8(%rbx) + lea tss_descr-gdt64(%rbx), %rax + ltr %ax + ret + +smp_init: + cld + lea sipi_entry, %rsi + xor %rdi, %rdi + mov $(sipi_end - sipi_entry), %rcx + rep/movsb + mov $APIC_DEFAULT_PHYS_BASE, %eax + movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT | APIC_INT_ASSERT), APIC_ICR(%rax) + movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT), APIC_ICR(%rax) + movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_STARTUP), APIC_ICR(%rax) + call fwcfg_get_nb_cpus +1: pause + cmpw %ax, cpu_online_count + jne 1b +smp_init_done: + ret + +cpu_online_count: .word 1 diff --git a/kvm/user/test/x86/emulator.c b/kvm/user/test/x86/emulator.c new file mode 100644 index 000000000..c6adbb5fd --- /dev/null +++ b/kvm/user/test/x86/emulator.c @@ -0,0 +1,258 @@ +#include "ioram.h" +#include "vm.h" +#include "libcflat.h" + +#define memset __builtin_memset + +int fails, tests; + +void report(const char *name, int result) +{ + ++tests; + if (result) + printf("PASS: %s\n", name); + else { + printf("FAIL: %s\n", name); + ++fails; + } +} + +void test_cmps(void *mem) +{ + unsigned char *m1 = mem, *m2 = mem + 1024; + unsigned char m3[1024]; + void *rsi, *rdi; + long rcx, tmp; + + for (int i = 0; i < 100; ++i) + m1[i] = m2[i] = m3[i] = i; + for (int i = 100; i < 200; ++i) + m1[i] = (m3[i] = m2[i] = i) + 1; + + rsi = m1; rdi = m3; rcx = 30; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsb" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpsb (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30); + + rsi = m1; rdi = m3; rcx = 15; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsw" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpsw (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30); + + rsi = m1; rdi = m3; rcx = 7; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsl" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpll (1)", rcx == 0 && rsi == m1 + 28 && rdi == m3 + 28); + + rsi = m1; rdi = m3; rcx = 4; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsq" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpsq (1)", rcx == 0 && rsi == m1 + 32 && rdi == m3 + 32); + + rsi = m1; rdi = m3; rcx = 130; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsb" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpsb (2)", + rcx == 29 && rsi == m1 + 101 && rdi == m3 + 101); + + rsi = m1; rdi = m3; rcx = 65; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsw" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpsw (2)", + rcx == 14 && rsi == m1 + 102 && rdi == m3 + 102); + + rsi = m1; rdi = m3; rcx = 32; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsl" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpll (2)", + rcx == 6 && rsi == m1 + 104 && rdi == m3 + 104); + + rsi = m1; rdi = m3; rcx = 16; + asm volatile("xor %[tmp], %[tmp] \n\t" + "repe/cmpsq" + : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) + : : "cc"); + report("repe/cmpsq (2)", + rcx == 3 && rsi == m1 + 104 && rdi == m3 + 104); + +} + +void test_cr8(void) +{ + unsigned long src, dst; + + dst = 777; + src = 3; + asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]" + : [dst]"+r"(dst), [src]"+r"(src)); + report("mov %cr8", dst == 3 && src == 3); +} + +void test_push(void *mem) +{ + unsigned long tmp; + unsigned long *stack_top = mem + 4096; + unsigned long *new_stack_top; + unsigned long memw = 0x123456789abcdeful; + + memset(mem, 0x55, (void *)stack_top - mem); + + asm volatile("mov %%rsp, %[tmp] \n\t" + "mov %[stack_top], %%rsp \n\t" + "pushq $-7 \n\t" + "pushq %[reg] \n\t" + "pushq (%[mem]) \n\t" + "pushq $-7070707 \n\t" + "mov %%rsp, %[new_stack_top] \n\t" + "mov %[tmp], %%rsp" + : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top) + : [stack_top]"r"(stack_top), + [reg]"r"(-17l), [mem]"r"(&memw) + : "memory"); + + report("push $imm8", stack_top[-1] == -7ul); + report("push %reg", stack_top[-2] == -17ul); + report("push mem", stack_top[-3] == 0x123456789abcdeful); + report("push $imm", stack_top[-4] == -7070707); +} + +void test_pop(void *mem) +{ + unsigned long tmp; + unsigned long *stack_top = mem + 4096; + unsigned long memw = 0x123456789abcdeful; + static unsigned long tmp2; + + memset(mem, 0x55, (void *)stack_top - mem); + + asm volatile("pushq %[val] \n\t" + "popq (%[mem])" + : : [val]"m"(memw), [mem]"r"(mem) : "memory"); + report("pop mem", *(unsigned long *)mem == memw); + + memw = 7 - memw; + asm volatile("mov %%rsp, %[tmp] \n\t" + "mov %[stack_top], %%rsp \n\t" + "pushq %[val] \n\t" + "popq %[tmp2] \n\t" + "mov %[tmp], %%rsp" + : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2) + : [val]"r"(memw), [stack_top]"r"(stack_top) + : "memory"); + report("pop mem (2)", tmp2 == memw); + + memw = 129443 - memw; + asm volatile("mov %%rsp, %[tmp] \n\t" + "mov %[stack_top], %%rsp \n\t" + "pushq %[val] \n\t" + "popq %[tmp2] \n\t" + "mov %[tmp], %%rsp" + : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2) + : [val]"r"(memw), [stack_top]"r"(stack_top) + : "memory"); + report("pop reg", tmp2 == memw); + + asm volatile("mov %%rsp, %[tmp] \n\t" + "mov %[stack_top], %%rsp \n\t" + "push $1f \n\t" + "ret \n\t" + "2: jmp 2b \n\t" + "1: mov %[tmp], %%rsp" + : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top) + : "memory"); + report("ret", 1); +} + +unsigned long read_cr0(void) +{ + unsigned long cr0; + + asm volatile ("mov %%cr0, %0" : "=r"(cr0)); + return cr0; +} + +void test_smsw(void) +{ + char mem[16]; + unsigned short msw, msw_orig, *pmsw; + int i, zero; + + msw_orig = read_cr0(); + + asm("smsw %0" : "=r"(msw)); + report("smsw (1)", msw == msw_orig); + + memset(mem, 0, 16); + pmsw = (void *)mem; + asm("smsw %0" : "=m"(pmsw[4])); + zero = 1; + for (i = 0; i < 8; ++i) + if (i != 4 && pmsw[i]) + zero = 0; + report("smsw (2)", msw == pmsw[4] && zero); +} + +void test_lmsw(void) +{ + char mem[16]; + unsigned short msw, *pmsw; + unsigned long cr0; + + cr0 = read_cr0(); + + msw = cr0 ^ 8; + asm("lmsw %0" : : "r"(msw)); + printf("before %lx after %lx\n", cr0, read_cr0()); + report("lmsw (1)", (cr0 ^ read_cr0()) == 8); + + pmsw = (void *)mem; + *pmsw = cr0; + asm("lmsw %0" : : "m"(*pmsw)); + printf("before %lx after %lx\n", cr0, read_cr0()); + report("lmsw (2)", cr0 == read_cr0()); +} + +int main() +{ + void *mem; + unsigned long t1, t2; + + setup_vm(); + mem = vmap(IORAM_BASE_PHYS, IORAM_LEN); + + // test mov reg, r/m and mov r/m, reg + t1 = 0x123456789abcdef; + asm volatile("mov %[t1], (%[mem]) \n\t" + "mov (%[mem]), %[t2]" + : [t2]"=r"(t2) + : [t1]"r"(t1), [mem]"r"(mem) + : "memory"); + report("mov reg, r/m (1)", t2 == 0x123456789abcdef); + + test_cmps(mem); + + test_push(mem); + test_pop(mem); + + test_cr8(); + + test_smsw(); + test_lmsw(); + + printf("\nSUMMARY: %d tests, %d failures\n", tests, fails); + return fails ? 1 : 0; +} diff --git a/kvm/user/test/x86/exit.c b/kvm/user/test/x86/exit.c new file mode 100644 index 000000000..8903621b1 --- /dev/null +++ b/kvm/user/test/x86/exit.c @@ -0,0 +1,7 @@ +#include "runtime.h" + +void exit(unsigned code) +{ + asm volatile("out %al, %dx" : : "a"(code), "d"(0xf4)); + asm volatile("cli; hlt"); +} diff --git a/kvm/user/test/x86/hypercall.c b/kvm/user/test/x86/hypercall.c new file mode 100644 index 000000000..95120a23b --- /dev/null +++ b/kvm/user/test/x86/hypercall.c @@ -0,0 +1,31 @@ +#include "libcflat.h" + +#define KVM_HYPERCALL_INTEL ".byte 0x0f,0x01,0xc1" +#define KVM_HYPERCALL_AMD ".byte 0x0f,0x01,0xd9" + +static inline long kvm_hypercall0_intel(unsigned int nr) +{ + long ret; + asm volatile(KVM_HYPERCALL_INTEL + : "=a"(ret) + : "a"(nr)); + return ret; +} + +static inline long kvm_hypercall0_amd(unsigned int nr) +{ + long ret; + asm volatile(KVM_HYPERCALL_AMD + : "=a"(ret) + : "a"(nr)); + return ret; +} + +int main(int ac, char **av) +{ + kvm_hypercall0_intel(-1u); + printf("Hypercall via VMCALL: OK\n"); + kvm_hypercall0_amd(-1u); + printf("Hypercall via VMMCALL: OK\n"); + return 0; +} diff --git a/kvm/user/test/x86/ioram.h b/kvm/user/test/x86/ioram.h new file mode 100644 index 000000000..2938142b3 --- /dev/null +++ b/kvm/user/test/x86/ioram.h @@ -0,0 +1,7 @@ +#ifndef __IO_RAM_H +#define __IO_RAM_H + +#define IORAM_BASE_PHYS 0xff000000UL +#define IORAM_LEN 0x10000UL + +#endif diff --git a/kvm/user/test/x86/memtest1.S b/kvm/user/test/x86/memtest1.S new file mode 100644 index 000000000..3821e867c --- /dev/null +++ b/kvm/user/test/x86/memtest1.S @@ -0,0 +1,44 @@ +.text + +start: + mov $0x1000,%r8 + mov $0x0a,%ecx + +init_page: + dec %ecx + jne no_io + mov $0x0,%al + out %al,$0x80 + mov $0x0a,%ecx + +no_io: + mov %r8,(%r8) + add $0x1000,%r8 + cmp $0x8000000,%r8 + jne init_page + mov $0x1000,%r8 + mov $0x0a,%ecx + +test_loop: + dec %ecx + jne no_io2 + mov $0x0,%al + out %al,$0x80 + mov $0x0a,%ecx + +no_io2: + mov (%r8),%r9 + cmp %r8,%r9 + jne err + add $0x1000,%r8 + cmp $0x8000000,%r8 + jne test_loop + mov $0x1000,%r8 + jmp test_loop + +err: + mov $0xffffffffffffffff,%r12 + mov $0xffffffffffffffff,%r13 + mov $0x0,%al + out %al,$0x80 + jmp err diff --git a/kvm/user/test/x86/msr.c b/kvm/user/test/x86/msr.c new file mode 100644 index 000000000..92102fa47 --- /dev/null +++ b/kvm/user/test/x86/msr.c @@ -0,0 +1,52 @@ +/* msr tests */ + +#include "libcflat.h" + +#define MSR_KERNEL_GS_BASE 0xc0000102 /* SwapGS GS shadow */ + +int nr_passed, nr_tests; + +#ifdef __x86_64__ +static void report(const char *name, int passed) +{ + ++nr_tests; + if (passed) + ++nr_passed; + printf("%s: %s\n", name, passed ? "PASS" : "FAIL"); +} + +static void wrmsr(unsigned index, unsigned long long value) +{ + asm volatile ("wrmsr" : : "c"(index), "A"(value)); +} + +static unsigned long long rdmsr(unsigned index) +{ + unsigned long long value; + + asm volatile ("rdmsr" : "=A"(value) : "c"(index)); + + return value; +} +#endif + +static void test_kernel_gs_base(void) +{ +#ifdef __x86_64__ + unsigned long long v1 = 0x123456789abcdef, v2; + + wrmsr(MSR_KERNEL_GS_BASE, v1); + v2 = rdmsr(MSR_KERNEL_GS_BASE); + report("MSR_KERNEL_GS_BASE", v1 == v2); +#endif +} + +int main(int ac, char **av) +{ + test_kernel_gs_base(); + + printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed); + + return nr_passed == nr_tests ? 0 : 1; +} + diff --git a/kvm/user/test/x86/port80.c b/kvm/user/test/x86/port80.c new file mode 100644 index 000000000..522c1a4dc --- /dev/null +++ b/kvm/user/test/x86/port80.c @@ -0,0 +1,12 @@ +#include "libcflat.h" + +int main() +{ + int i; + + printf("begining port 0x80 write test\n"); + for (i = 0; i < 10000000; ++i) + asm volatile("outb %al, $0x80"); + printf("done\n"); + return 0; +} diff --git a/kvm/user/test/x86/print.S b/kvm/user/test/x86/print.S new file mode 100644 index 000000000..c1b1c0d7c --- /dev/null +++ b/kvm/user/test/x86/print.S @@ -0,0 +1,31 @@ + +#include "print.h" + +#define PSEUDO_SERIAL_PORT 0xf1 + + +.text + PRINT "boo" + hlt +1: jmp 1b + +.globl print +print: + push %rax + push %rsi + push %rdx + + mov %rdi, %rsi + mov $(PSEUDO_SERIAL_PORT), %edx + +putchar: + cmpb $0, (%rsi) + jz done + outsb + jmp putchar +done: + + pop %rdx + pop %rsi + pop %rax + ret diff --git a/kvm/user/test/x86/print.h b/kvm/user/test/x86/print.h new file mode 100644 index 000000000..d5bd2f997 --- /dev/null +++ b/kvm/user/test/x86/print.h @@ -0,0 +1,19 @@ +#ifndef PRINT_H +#define PRINT_H + +.macro PRINT text + +.data + +333: .asciz "\text\n" + +.previous + + push %rdi + lea 333b, %rdi + call print + pop %rdi + +.endm + +#endif diff --git a/kvm/user/test/x86/realmode.c b/kvm/user/test/x86/realmode.c new file mode 100644 index 000000000..9bf6cb00e --- /dev/null +++ b/kvm/user/test/x86/realmode.c @@ -0,0 +1,624 @@ +asm(".code16gcc"); + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned u32; +typedef unsigned long long u64; + +void test_function(void); + +asm( + "test_function: \n\t" + "mov $0x1234, %eax \n\t" + "ret" + ); + +static int strlen(const char *str) +{ + int n; + + for (n = 0; *str; ++str) + ++n; + return n; +} + +static void print_serial(const char *buf) +{ + unsigned long len = strlen(buf); + + asm volatile ("cld; addr32/rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1)); +} + +static void exit(int code) +{ + asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4)); +} + +struct regs { + u32 eax, ebx, ecx, edx; + u32 esi, edi, esp, ebp; + u32 eip, eflags; +}; + +static u64 gdt[] = { + 0, + 0x00cf9b000000ffffull, // flat 32-bit code segment + 0x00cf93000000ffffull, // flat 32-bit data segment +}; + +static struct { + u16 limit; + void *base; +} __attribute__((packed)) gdt_descr = { + sizeof(gdt) - 1, + gdt, +}; + +static void exec_in_big_real_mode(const struct regs *inregs, + struct regs *outregs, + const u8 *insn, int insn_len) +{ + unsigned long tmp; + static struct regs save; + int i; + extern u8 test_insn[], test_insn_end[]; + + for (i = 0; i < insn_len; ++i) + test_insn[i] = insn[i]; + for (; i < test_insn_end - test_insn; ++i) + test_insn[i] = 0x90; // nop + + save = *inregs; + asm volatile( + "lgdtl %[gdt_descr] \n\t" + "mov %%cr0, %[tmp] \n\t" + "or $1, %[tmp] \n\t" + "mov %[tmp], %%cr0 \n\t" + "mov %[bigseg], %%gs \n\t" + "and $-2, %[tmp] \n\t" + "mov %[tmp], %%cr0 \n\t" + + "xchg %%eax, %[save]+0 \n\t" + "xchg %%ebx, %[save]+4 \n\t" + "xchg %%ecx, %[save]+8 \n\t" + "xchg %%edx, %[save]+12 \n\t" + "xchg %%esi, %[save]+16 \n\t" + "xchg %%edi, %[save]+20 \n\t" + "xchg %%esp, %[save]+24 \n\t" + "xchg %%ebp, %[save]+28 \n\t" + + "test_insn: . = . + 16\n\t" + "test_insn_end: \n\t" + + "xchg %%eax, %[save]+0 \n\t" + "xchg %%ebx, %[save]+4 \n\t" + "xchg %%ecx, %[save]+8 \n\t" + "xchg %%edx, %[save]+12 \n\t" + "xchg %%esi, %[save]+16 \n\t" + "xchg %%edi, %[save]+20 \n\t" + "xchg %%esp, %[save]+24 \n\t" + "xchg %%ebp, %[save]+28 \n\t" + + /* Save EFLAGS in outregs*/ + "pushfl \n\t" + "popl %[save]+36 \n\t" + + "xor %[tmp], %[tmp] \n\t" + "mov %[tmp], %%gs \n\t" + : [tmp]"=&r"(tmp), [save]"+m"(save) + : [gdt_descr]"m"(gdt_descr), [bigseg]"r"((short)16) + : "cc", "memory" + ); + *outregs = save; +} + +#define R_AX 1 +#define R_BX 2 +#define R_CX 4 +#define R_DX 8 +#define R_SI 16 +#define R_DI 32 +#define R_SP 64 +#define R_BP 128 + +int regs_equal(const struct regs *r1, const struct regs *r2, int ignore) +{ + const u32 *p1 = &r1->eax, *p2 = &r2->eax; // yuck + int i; + + for (i = 0; i < 8; ++i) + if (!(ignore & (1 << i)) && p1[i] != p2[i]) + return 0; + return 1; +} + +#define MK_INSN(name, str) \ + asm ( \ + ".pushsection \".text\" \n\t" \ + "insn_" #name ": " str " \n\t" \ + "insn_" #name "_end: \n\t" \ + ".popsection \n\t" \ + ); \ + extern u8 insn_##name[], insn_##name##_end[] + +void test_shld(void) +{ + struct regs inregs = { .eax = 0xbe, .edx = 0xef000000 }, outregs; + MK_INSN(shld_test, "shld $8,%edx,%eax\n\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_shld_test, + insn_shld_test_end - insn_shld_test); + if (outregs.eax != 0xbeef) + print_serial("shld: failure\n"); + else + print_serial("shld: success\n"); +} + +void test_mov_imm(void) +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(mov_r32_imm_1, "mov $1234567890, %eax"); + MK_INSN(mov_r16_imm_1, "mov $1234, %ax"); + MK_INSN(mov_r8_imm_1, "mov $0x12, %ah"); + MK_INSN(mov_r8_imm_2, "mov $0x34, %al"); + MK_INSN(mov_r8_imm_3, "mov $0x12, %ah\n\t" "mov $0x34, %al\n\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_mov_r16_imm_1, + insn_mov_r16_imm_1_end - insn_mov_r16_imm_1); + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 1234) + print_serial("mov test 1: FAIL\n"); + + /* test mov $imm, %eax */ + exec_in_big_real_mode(&inregs, &outregs, + insn_mov_r32_imm_1, + insn_mov_r32_imm_1_end - insn_mov_r32_imm_1); + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 1234567890) + print_serial("mov test 2: FAIL\n"); + + /* test mov $imm, %al/%ah */ + exec_in_big_real_mode(&inregs, &outregs, + insn_mov_r8_imm_1, + insn_mov_r8_imm_1_end - insn_mov_r8_imm_1); + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1200) + print_serial("mov test 3: FAIL\n"); + exec_in_big_real_mode(&inregs, &outregs, + insn_mov_r8_imm_2, + insn_mov_r8_imm_2_end - insn_mov_r8_imm_2); + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x34) + print_serial("mov test 4: FAIL\n"); + exec_in_big_real_mode(&inregs, &outregs, + insn_mov_r8_imm_3, + insn_mov_r8_imm_3_end - insn_mov_r8_imm_3); + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) + print_serial("mov test 5: FAIL\n"); +} + +void test_cmp_imm(void) +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(cmp_test1, "mov $0x34, %al\n\t" + "cmp $0x34, %al\n\t"); + MK_INSN(cmp_test2, "mov $0x34, %al\n\t" + "cmp $0x39, %al\n\t"); + MK_INSN(cmp_test3, "mov $0x34, %al\n\t" + "cmp $0x24, %al\n\t"); + + /* test cmp imm8 with AL */ + /* ZF: (bit 6) Zero Flag becomes 1 if an operation results + * in a 0 writeback, or 0 register + */ + exec_in_big_real_mode(&inregs, &outregs, + insn_cmp_test1, + insn_cmp_test1_end - insn_cmp_test1); + if ((outregs.eflags & (1<<6)) != (1<<6)) + print_serial("cmp test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_cmp_test2, + insn_cmp_test2_end - insn_cmp_test2); + if ((outregs.eflags & (1<<6)) != 0) + print_serial("cmp test 2: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_cmp_test3, + insn_cmp_test3_end - insn_cmp_test3); + if ((outregs.eflags & (1<<6)) != 0) + print_serial("cmp test 3: FAIL\n"); +} + +void test_add_imm(void) +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(add_test1, "mov $0x43211234, %eax \n\t" + "add $0x12344321, %eax \n\t"); + MK_INSN(add_test2, "mov $0x12, %eax \n\t" + "add $0x21, %al\n\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_add_test1, + insn_add_test1_end - insn_add_test1); + if (outregs.eax != 0x55555555) + print_serial("add test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_add_test2, + insn_add_test2_end - insn_add_test2); + if (outregs.eax != 0x33) + print_serial("add test 2: FAIL\n"); +} + +void test_eflags_insn(void) +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(clc, "clc"); + MK_INSN(cli, "cli"); + MK_INSN(sti, "sti"); + MK_INSN(cld, "cld"); + MK_INSN(std, "std"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_clc, + insn_clc_end - insn_clc); + if (outregs.eflags & 1) + print_serial("clc test: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_cli, + insn_cli_end - insn_cli); + if (outregs.eflags & (1 << 9)) + print_serial("cli test: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_sti, + insn_sti_end - insn_sti); + if (!(outregs.eflags & (1 << 9))) + print_serial("sti test: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_cld, + insn_cld_end - insn_cld); + if (outregs.eflags & (1 << 10)) + print_serial("cld test: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_std, + insn_std_end - insn_std); + if (!(outregs.eflags & (1 << 10))) + print_serial("std test: FAIL\n"); +} + +void test_io(void) +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(io_test1, "mov $0xff, %al \n\t" + "out %al, $0x10 \n\t" + "in $0x10, %al \n\t"); + MK_INSN(io_test2, "mov $0xffff, %ax \n\t" + "out %ax, $0x10 \n\t" + "in $0x10, %ax \n\t"); + MK_INSN(io_test3, "mov $0xffffffff, %eax \n\t" + "out %eax, $0x10 \n\t" + "in $0x10, %eax \n\t"); + MK_INSN(io_test4, "mov $0x10, %dx \n\t" + "mov $0xff, %al \n\t" + "out %al, %dx \n\t" + "in %dx, %al \n\t"); + MK_INSN(io_test5, "mov $0x10, %dx \n\t" + "mov $0xffff, %ax \n\t" + "out %ax, %dx \n\t" + "in %dx, %ax \n\t"); + MK_INSN(io_test6, "mov $0x10, %dx \n\t" + "mov $0xffffffff, %eax \n\t" + "out %eax, %dx \n\t" + "in %dx, %eax \n\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_io_test1, + insn_io_test1_end - insn_io_test1); + + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xff) + print_serial("I/O test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_io_test2, + insn_io_test2_end - insn_io_test2); + + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xffff) + print_serial("I/O test 2: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_io_test3, + insn_io_test3_end - insn_io_test3); + + if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xffffffff) + print_serial("I/O test 3: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_io_test4, + insn_io_test4_end - insn_io_test4); + + if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xff) + print_serial("I/O test 4: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_io_test5, + insn_io_test5_end - insn_io_test5); + + if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xffff) + print_serial("I/O test 5: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_io_test6, + insn_io_test6_end - insn_io_test6); + + if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xffffffff) + print_serial("I/O test 6: FAIL\n"); + +} + +void test_call(void) +{ + struct regs inregs = { 0 }, outregs; + u32 esp[16]; + + inregs.esp = (u32)esp; + + MK_INSN(call1, "mov $test_function, %eax \n\t" + "call *%eax\n\t"); + MK_INSN(call_near1, "jmp 2f\n\t" + "1: mov $0x1234, %eax\n\t" + "ret\n\t" + "2: call 1b\t"); + MK_INSN(call_near2, "call 1f\n\t" + "jmp 2f\n\t" + "1: mov $0x1234, %eax\n\t" + "ret\n\t" + "2:\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_call1, + insn_call1_end - insn_call1); + if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) + print_serial("Call Test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_call_near1, insn_call_near1_end - insn_call_near1); + if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) + print_serial("Call near Test 1: FAIL\n"); + exec_in_big_real_mode(&inregs, &outregs, + insn_call_near2, insn_call_near2_end - insn_call_near2); + if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) + print_serial("Call near Test 2: FAIL\n"); +} + +void test_jcc_short(void) +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(jnz_short1, "jnz 1f\n\t" + "mov $0x1234, %eax\n\t" + "1:\n\t"); + MK_INSN(jnz_short2, "1:\n\t" + "cmp $0x1234, %eax\n\t" + "mov $0x1234, %eax\n\t" + "jnz 1b\n\t"); + MK_INSN(jmp_short1, "jmp 1f\n\t" + "mov $0x1234, %eax\n\t" + "1:\n\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_jnz_short1, insn_jnz_short1_end - insn_jnz_short1); + if(!regs_equal(&inregs, &outregs, 0)) + print_serial("JNZ sort Test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_jnz_short2, insn_jnz_short2_end - insn_jnz_short2); + if(!regs_equal(&inregs, &outregs, R_AX) || !(outregs.eflags & (1 << 6))) + print_serial("JNZ sort Test 2: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_jmp_short1, insn_jmp_short1_end - insn_jmp_short1); + if(!regs_equal(&inregs, &outregs, 0)) + print_serial("JMP sort Test 1: FAIL\n"); +} + +void test_jcc_near(void) +{ + struct regs inregs = { 0 }, outregs; + /* encode near jmp manually. gas will not do it if offsets < 127 byte */ + MK_INSN(jnz_near1, ".byte 0x0f, 0x85, 0x06, 0x00\n\t" + "mov $0x1234, %eax\n\t"); + MK_INSN(jnz_near2, "cmp $0x1234, %eax\n\t" + "mov $0x1234, %eax\n\t" + ".byte 0x0f, 0x85, 0xf0, 0xff\n\t"); + MK_INSN(jmp_near1, ".byte 0xE9, 0x06, 0x00\n\t" + "mov $0x1234, %eax\n\t"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_jnz_near1, insn_jnz_near1_end - insn_jnz_near1); + if(!regs_equal(&inregs, &outregs, 0)) + print_serial("JNZ near Test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_jnz_near2, insn_jnz_near2_end - insn_jnz_near2); + if(!regs_equal(&inregs, &outregs, R_AX) || !(outregs.eflags & (1 << 6))) + print_serial("JNZ near Test 2: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_jmp_near1, insn_jmp_near1_end - insn_jmp_near1); + if(!regs_equal(&inregs, &outregs, 0)) + print_serial("JMP near Test 1: FAIL\n"); +} + +void test_long_jmp() +{ + struct regs inregs = { 0 }, outregs; + u32 esp[16]; + + inregs.esp = (u32)esp; + MK_INSN(long_jmp, "call 1f\n\t" + "jmp 2f\n\t" + "1: jmp $0, $test_function\n\t" + "2:\n\t"); + exec_in_big_real_mode(&inregs, &outregs, + insn_long_jmp, + insn_long_jmp_end - insn_long_jmp); + if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) + print_serial("Long JMP Test: FAIL\n"); +} +void test_push_pop() +{ + struct regs inregs = { 0 }, outregs; + MK_INSN(push32, "mov $0x12345678, %eax\n\t" + "push %eax\n\t" + "pop %ebx\n\t"); + MK_INSN(push16, "mov $0x1234, %ax\n\t" + "push %ax\n\t" + "pop %bx\n\t"); + + MK_INSN(push_es, "mov $0x231, %bx\n\t" //Just write a dummy value to see if it gets overwritten + "mov $0x123, %ax\n\t" + "mov %ax, %es\n\t" + "push %es\n\t" + "pop %bx \n\t" + ); + MK_INSN(pop_es, "push %ax\n\t" + "pop %es\n\t" + "mov %es, %bx\n\t" + ); + MK_INSN(push_pop_ss, "push %ss\n\t" + "pushw %ax\n\t" + "popw %ss\n\t" + "mov %ss, %bx\n\t" + "pop %ss\n\t" + ); + MK_INSN(push_pop_fs, "push %fs\n\t" + "pushl %eax\n\t" + "popl %fs\n\t" + "mov %fs, %ebx\n\t" + "pop %fs\n\t" + ); + + exec_in_big_real_mode(&inregs, &outregs, + insn_push32, + insn_push32_end - insn_push32); + if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.eax != outregs.ebx || outregs.eax != 0x12345678) + print_serial("Push/Pop Test 1: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_push16, + insn_push16_end - insn_push16); + + if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.eax != outregs.ebx || outregs.eax != 0x1234) + print_serial("Push/Pop Test 2: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_push_es, + insn_push_es_end - insn_push_es); + if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax || outregs.eax != 0x123) + print_serial("Push/Pop Test 3: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_pop_es, + insn_pop_es_end - insn_pop_es); + + if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax) + print_serial("Push/Pop Test 4: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_push_pop_ss, + insn_push_pop_ss_end - insn_push_pop_ss); + + if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax) + print_serial("Push/Pop Test 5: FAIL\n"); + + exec_in_big_real_mode(&inregs, &outregs, + insn_push_pop_fs, + insn_push_pop_fs_end - insn_push_pop_fs); + + if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax) + print_serial("Push/Pop Test 6: FAIL\n"); +} + +void test_null(void) +{ + struct regs inregs = { 0 }, outregs; + exec_in_big_real_mode(&inregs, &outregs, 0, 0); + if (!regs_equal(&inregs, &outregs, 0)) + print_serial("null test: FAIL\n"); +} + +void realmode_start(void) +{ + test_null(); + + test_shld(); + test_push_pop(); + test_mov_imm(); + test_cmp_imm(); + test_add_imm(); + test_io(); + test_eflags_insn(); + test_jcc_short(); + test_jcc_near(); + /* test_call() uses short jump so call it after testing jcc */ + test_call(); + /* long jmp test uses call near so test it after testing call */ + test_long_jmp(); + + exit(0); +} + +unsigned long long r_gdt[] = { 0, 0x9b000000ffff, 0x93000000ffff }; + +struct __attribute__((packed)) { + unsigned short limit; + void *base; +} r_gdt_descr = { sizeof(r_gdt) - 1, &r_gdt }; + +asm( + ".section .init \n\t" + + ".code32 \n\t" + + "mb_magic = 0x1BADB002 \n\t" + "mb_flags = 0x0 \n\t" + + "# multiboot header \n\t" + ".long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) \n\t" + + ".globl start \n\t" + ".data \n\t" + ". = . + 4096 \n\t" + "stacktop: \n\t" + + ".text \n\t" + "start: \n\t" + "lgdt r_gdt_descr \n\t" + "ljmp $8, $1f; 1: \n\t" + ".code16gcc \n\t" + "mov $16, %eax \n\t" + "mov %ax, %ds \n\t" + "mov %ax, %es \n\t" + "mov %ax, %fs \n\t" + "mov %ax, %gs \n\t" + "mov %ax, %ss \n\t" + "mov %cr0, %eax \n\t" + "btc $0, %eax \n\t" + "mov %eax, %cr0 \n\t" + "ljmp $0, $realmode_entry \n\t" + + "realmode_entry: \n\t" + + "xor %ax, %ax \n\t" + "mov %ax, %ds \n\t" + "mov %ax, %es \n\t" + "mov %ax, %ss \n\t" + "mov %ax, %fs \n\t" + "mov %ax, %gs \n\t" + "mov $stacktop, %sp\n\t" + "ljmp $0, $realmode_start \n\t" + + ".code16gcc \n\t" + ); diff --git a/kvm/user/test/x86/realmode.lds b/kvm/user/test/x86/realmode.lds new file mode 100644 index 000000000..c7386b8e7 --- /dev/null +++ b/kvm/user/test/x86/realmode.lds @@ -0,0 +1,12 @@ +SECTIONS +{ + . = 16K; + stext = .; + .text : { *(.init) *(.text) } + . = ALIGN(4K); + .data : { *(.data) *(.rodata*) } + . = ALIGN(16); + .bss : { *(.bss) } + edata = .; +} + diff --git a/kvm/user/test/x86/runtime.h b/kvm/user/test/x86/runtime.h new file mode 100644 index 000000000..4b4c30292 --- /dev/null +++ b/kvm/user/test/x86/runtime.h @@ -0,0 +1,6 @@ +#ifndef H_RUNTIME +#define H_RUNTIME + +void exit(unsigned code) __attribute__((__noreturn__)); + +#endif diff --git a/kvm/user/test/x86/sieve.c b/kvm/user/test/x86/sieve.c new file mode 100644 index 000000000..a707b92dc --- /dev/null +++ b/kvm/user/test/x86/sieve.c @@ -0,0 +1,89 @@ +#include "vm.h" + +void print(const char *text); + +void printi(int n) +{ + char buf[10], *p = buf; + int s = 0, i; + + if (n < 0) { + n = -n; + s = 1; + } + + while (n) { + *p++ = '0' + n % 10; + n /= 10; + } + + if (s) + *p++ = '-'; + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print(buf); +} + +int sieve(char* data, int size) +{ + int i, j, r = 0; + + for (i = 0; i < size; ++i) + data[i] = 1; + + data[0] = data[1] = 0; + + for (i = 2; i < size; ++i) + if (data[i]) { + ++r; + for (j = i*2; j < size; j += i) + data[j] = 0; + } + return r; +} + +void test_sieve(const char *msg, char *data, int size) +{ + int r; + + print(msg); + print(": "); + r = sieve(data, size); + printi(r); + print("\n"); +} + +#define STATIC_SIZE 1000000 +#define VSIZE 100000000 +char static_data[STATIC_SIZE]; + +int main() +{ + void *v; + int i; + + print("starting sieve\n"); + test_sieve("static", static_data, STATIC_SIZE); + setup_vm(); + print("mapped: "); + test_sieve("mapped", static_data, STATIC_SIZE); + for (i = 0; i < 30; ++i) { + v = vmalloc(VSIZE); + test_sieve("virtual", v, VSIZE); + vfree(v); + } + + return 0; +} diff --git a/kvm/user/test/x86/simple.S b/kvm/user/test/x86/simple.S new file mode 100644 index 000000000..f3c844fc7 --- /dev/null +++ b/kvm/user/test/x86/simple.S @@ -0,0 +1,13 @@ + + .text + + mov $0, %al + mov $10000, %ebx +1: + mov %rbx, %rcx +2: + loop 2b + out %al, $0x80 + inc %al + add $10000, %rbx + jmp 1b diff --git a/kvm/user/test/x86/smptest.c b/kvm/user/test/x86/smptest.c new file mode 100644 index 000000000..37805999b --- /dev/null +++ b/kvm/user/test/x86/smptest.c @@ -0,0 +1,25 @@ +#include "libcflat.h" +#include "smp.h" + +static void ipi_test(void *data) +{ + int n = (long)data; + + printf("ipi called, cpu %d\n", n); + if (n != smp_id()) + printf("but wrong cpu %d\n", smp_id()); +} + +int main() +{ + int ncpus; + int i; + + smp_init(); + + ncpus = cpu_count(); + printf("found %d cpus\n", ncpus); + for (i = 0; i < ncpus; ++i) + on_cpu(i, ipi_test, (void *)(long)i); + return 0; +} diff --git a/kvm/user/test/x86/stringio.S b/kvm/user/test/x86/stringio.S new file mode 100644 index 000000000..31ddc479f --- /dev/null +++ b/kvm/user/test/x86/stringio.S @@ -0,0 +1,31 @@ + +.data + +.macro str name, value + +\name : .long 1f-2f +2: .ascii "\value" +1: +.endm + + str "forward", "forward" + str "backward", "backward" + +.text + + + cld + movl forward, %ecx + lea 4+forward, %rsi + movw $1, %dx + rep outsb + + std + movl backward, %ecx + lea 4+backward-1(%rcx), %rsi + movw $2, %dx + rep outsb + + hlt + + diff --git a/kvm/user/test/x86/test32.S b/kvm/user/test/x86/test32.S new file mode 100644 index 000000000..a2e0fd7a2 --- /dev/null +++ b/kvm/user/test/x86/test32.S @@ -0,0 +1,8 @@ +.code32 + +.text + +1: + mov $0x12, %al + out %al, $0x80 + jmp 1b diff --git a/kvm/user/test/x86/tsc.c b/kvm/user/test/x86/tsc.c new file mode 100644 index 000000000..394a9c6b8 --- /dev/null +++ b/kvm/user/test/x86/tsc.c @@ -0,0 +1,38 @@ +#include "libcflat.h" + +u64 rdtsc(void) +{ + unsigned a, d; + + asm volatile("rdtsc" : "=a"(a), "=d"(d)); + return a | (u64)d << 32; +} + +void wrtsc(u64 tsc) +{ + unsigned a = tsc, d = tsc >> 32; + + asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(0x10)); +} + +void test_wrtsc(u64 t1) +{ + u64 t2; + + wrtsc(t1); + t2 = rdtsc(); + printf("rdtsc after wrtsc(%lld): %lld\n", t1, t2); +} + +int main() +{ + u64 t1, t2; + + t1 = rdtsc(); + t2 = rdtsc(); + printf("rdtsc latency %lld\n", (unsigned)(t2 - t1)); + + test_wrtsc(0); + test_wrtsc(100000000000ull); + return 0; +} diff --git a/kvm/user/test/x86/vm.c b/kvm/user/test/x86/vm.c new file mode 100644 index 000000000..ec9c1458a --- /dev/null +++ b/kvm/user/test/x86/vm.c @@ -0,0 +1,271 @@ + +#include "vm.h" + +void print(const char *s); + +#define PAGE_SIZE 4096ul +#define LARGE_PAGE_SIZE (512 * PAGE_SIZE) + +static void *free = 0; +static void *vfree_top = 0; + +static unsigned long virt_to_phys(const void *virt) +{ + return (unsigned long)virt; +} + +static void *phys_to_virt(unsigned long phys) +{ + return (void *)phys; +} + +void *memset(void *data, int c, unsigned long len) +{ + char *s = data; + + while (len--) + *s++ = c; + + return data; +} + +static void free_memory(void *mem, unsigned long size) +{ + while (size >= PAGE_SIZE) { + *(void **)mem = free; + free = mem; + mem += PAGE_SIZE; + size -= PAGE_SIZE; + } +} + +void *alloc_page() +{ + void *p; + + if (!free) + return 0; + + p = free; + free = *(void **)free; + + return p; +} + +void free_page(void *page) +{ + *(void **)page = free; + free = page; +} + +extern char edata; +static unsigned long end_of_memory; + +#define PTE_PRESENT (1ull << 0) +#define PTE_PSE (1ull << 7) +#define PTE_WRITE (1ull << 1) +#define PTE_ADDR (0xffffffffff000ull) + +static void install_pte(unsigned long *cr3, + int pte_level, + void *virt, + unsigned long pte) +{ + int level; + unsigned long *pt = cr3; + unsigned offset; + + for (level = 4; level > pte_level; --level) { + offset = ((unsigned long)virt >> ((level-1) * 9 + 12)) & 511; + if (!(pt[offset] & PTE_PRESENT)) { + unsigned long *new_pt = alloc_page(); + memset(new_pt, 0, PAGE_SIZE); + pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE; + } + pt = phys_to_virt(pt[offset] & 0xffffffffff000ull); + } + offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511; + pt[offset] = pte; +} + +static unsigned long get_pte(unsigned long *cr3, void *virt) +{ + int level; + unsigned long *pt = cr3, pte; + unsigned offset; + + for (level = 4; level > 1; --level) { + offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511; + pte = pt[offset]; + if (!(pte & PTE_PRESENT)) + return 0; + if (level == 2 && (pte & PTE_PSE)) + return pte; + pt = phys_to_virt(pte & 0xffffffffff000ull); + } + offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511; + pte = pt[offset]; + return pte; +} + +static void install_large_page(unsigned long *cr3, + unsigned long phys, + void *virt) +{ + install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE); +} + +static void install_page(unsigned long *cr3, + unsigned long phys, + void *virt) +{ + install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE); +} + +static inline void load_cr3(unsigned long cr3) +{ + asm ( "mov %0, %%cr3" : : "r"(cr3) ); +} + +static inline unsigned long read_cr3() +{ + unsigned long cr3; + + asm volatile ( "mov %%cr3, %0" : "=r"(cr3) ); + return cr3; +} + +static inline void load_cr0(unsigned long cr0) +{ + asm volatile ( "mov %0, %%cr0" : : "r"(cr0) ); +} + +static inline unsigned long read_cr0() +{ + unsigned long cr0; + + asm volatile ( "mov %%cr0, %0" : "=r"(cr0) ); + return cr0; +} + +static inline void load_cr4(unsigned long cr4) +{ + asm volatile ( "mov %0, %%cr4" : : "r"(cr4) ); +} + +static inline unsigned long read_cr4() +{ + unsigned long cr4; + + asm volatile ( "mov %%cr4, %0" : "=r"(cr4) ); + return cr4; +} + +struct gdt_table_descr +{ + unsigned short len; + unsigned long *table; +} __attribute__((packed)); + +static inline void load_gdt(unsigned long *table, int nent) +{ + struct gdt_table_descr descr; + + descr.len = nent * 8 - 1; + descr.table = table; + asm volatile ( "lgdt %0" : : "m"(descr) ); +} + +#define SEG_CS_32 8 +#define SEG_CS_64 16 + +struct ljmp { + void *ofs; + unsigned short seg; +}; + +static void setup_mmu(unsigned long len) +{ + unsigned long *cr3 = alloc_page(); + unsigned long phys = 0; + + if (len < (1ul << 32)) + len = 1ul << 32; /* map mmio 1:1 */ + + memset(cr3, 0, PAGE_SIZE); + while (phys + LARGE_PAGE_SIZE <= len) { + install_large_page(cr3, phys, (void *)phys); + phys += LARGE_PAGE_SIZE; + } + while (phys + PAGE_SIZE <= len) { + install_page(cr3, phys, (void *)phys); + phys += PAGE_SIZE; + } + + load_cr3(virt_to_phys(cr3)); + print("paging enabled\n"); +} + +static unsigned int inl(unsigned short port) +{ + unsigned int val; + asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port)); + return val; +} + +void setup_vm() +{ + end_of_memory = inl(0xd1); + free_memory(&edata, end_of_memory - (unsigned long)&edata); + setup_mmu(end_of_memory); +} + +void *vmalloc(unsigned long size) +{ + void *mem, *p; + unsigned pages; + + size += sizeof(unsigned long); + + size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + vfree_top -= size; + mem = p = vfree_top; + pages = size / PAGE_SIZE; + while (pages--) { + install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p); + p += PAGE_SIZE; + } + *(unsigned long *)mem = size; + mem += sizeof(unsigned long); + return mem; +} + +void vfree(void *mem) +{ + unsigned long size = ((unsigned long *)mem)[-1]; + + while (size) { + free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR)); + mem += PAGE_SIZE; + size -= PAGE_SIZE; + } +} + +void *vmap(unsigned long long phys, unsigned long size) +{ + void *mem, *p; + unsigned pages; + + size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + vfree_top -= size; + phys &= ~(unsigned long long)(PAGE_SIZE - 1); + + mem = p = vfree_top; + pages = size / PAGE_SIZE; + while (pages--) { + install_page(phys_to_virt(read_cr3()), phys, p); + phys += PAGE_SIZE; + p += PAGE_SIZE; + } + return mem; +} diff --git a/kvm/user/test/x86/vm.h b/kvm/user/test/x86/vm.h new file mode 100644 index 000000000..0a481f133 --- /dev/null +++ b/kvm/user/test/x86/vm.h @@ -0,0 +1,10 @@ +#ifndef VM_H +#define VM_H + +void setup_vm(); + +void *vmalloc(unsigned long size); +void vfree(void *mem); +void *vmap(unsigned long long phys, unsigned long size); + +#endif diff --git a/kvm/user/test/x86/vmexit.c b/kvm/user/test/x86/vmexit.c new file mode 100644 index 000000000..c3a01e0b5 --- /dev/null +++ b/kvm/user/test/x86/vmexit.c @@ -0,0 +1,164 @@ + +#include "libcflat.h" +#include "smp.h" + +static inline unsigned long long rdtsc() +{ + long long r; + +#ifdef __x86_64__ + unsigned a, d; + + asm volatile ("rdtsc" : "=a"(a), "=d"(d)); + r = a | ((long long)d << 32); +#else + asm volatile ("rdtsc" : "=A"(r)); +#endif + return r; +} + +static unsigned int inl(unsigned short port) +{ + unsigned int val; + asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port)); + return val; +} + +#define GOAL (1ull << 30) + +#ifdef __x86_64__ +# define R "r" +#else +# define R "e" +#endif + +static void cpuid(void) +{ + asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx" + : : : "eax", "ecx", "edx"); +} + +static void vmcall(void) +{ + unsigned long a = 0, b, c, d; + + asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d)); +} + +static void mov_from_cr8(void) +{ + unsigned long cr8; + + asm volatile ("mov %%cr8, %0" : "=r"(cr8)); +} + +static void mov_to_cr8(void) +{ + unsigned long cr8 = 0; + + asm volatile ("mov %0, %%cr8" : : "r"(cr8)); +} + +static int is_smp(void) +{ + return cpu_count() > 1; +} + +static void nop(void *junk) +{ +} + +static void ipi(void) +{ + on_cpu(1, nop, 0); +} + +static void ipi_halt(void) +{ + unsigned long long t; + + on_cpu(1, nop, 0); + t = rdtsc() + 2000; + while (rdtsc() < t) + ; +} + +static void inl_pmtimer(void) +{ + inl(0xb008); +} + +static struct test { + void (*func)(void); + const char *name; + int (*valid)(void); + int parallel; +} tests[] = { + { cpuid, "cpuid", .parallel = 1, }, + { vmcall, "vmcall", .parallel = 1, }, + { mov_from_cr8, "mov_from_cr8", .parallel = 1, }, + { mov_to_cr8, "mov_to_cr8" , .parallel = 1, }, + { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, }, + { ipi, "ipi", is_smp, .parallel = 0, }, + { ipi_halt, "ipi+halt", is_smp, .parallel = 0, }, +}; + +unsigned iterations; +volatile int nr_cpus_done; + +static void run_test(void *_func) +{ + int i; + void (*func)(void) = _func; + + for (i = 0; i < iterations; ++i) + func(); + + nr_cpus_done++; +} + +static void do_test(struct test *test) +{ + int i; + unsigned long long t1, t2; + void (*func)(void) = test->func; + + iterations = 32; + + if (test->valid && !test->valid()) { + printf("%s (skipped)\n", test->name); + return; + } + + do { + iterations *= 2; + t1 = rdtsc(); + + if (!test->parallel) { + for (i = 0; i < iterations; ++i) + func(); + } else { + nr_cpus_done = 0; + for (i = cpu_count(); i > 0; i--) + on_cpu_async(i-1, run_test, func); + while (nr_cpus_done < cpu_count()) + ; + } + t2 = rdtsc(); + } while ((t2 - t1) < GOAL); + printf("%s %d\n", test->name, (int)((t2 - t1) / iterations)); +} + +#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof((_x)[0])) + +int main(void) +{ + int i; + + smp_init(); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + do_test(&tests[i]); + + return 0; +} diff --git a/kvm/vgabios/.cvsignore b/kvm/vgabios/.cvsignore new file mode 100644 index 000000000..1df04b726 --- /dev/null +++ b/kvm/vgabios/.cvsignore @@ -0,0 +1 @@ +vbetables.h diff --git a/kvm/vgabios/BUGS b/kvm/vgabios/BUGS new file mode 100644 index 000000000..785f4dc37 --- /dev/null +++ b/kvm/vgabios/BUGS @@ -0,0 +1,3 @@ +Not all the functions have been implemented yet. + +Please report any bugs to <info@vruppert.de> diff --git a/kvm/vgabios/COPYING b/kvm/vgabios/COPYING new file mode 100644 index 000000000..223ede7de --- /dev/null +++ b/kvm/vgabios/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/kvm/vgabios/ChangeLog b/kvm/vgabios/ChangeLog new file mode 100644 index 000000000..75be5bdda --- /dev/null +++ b/kvm/vgabios/ChangeLog @@ -0,0 +1,1264 @@ +2008-05-11 08:40 vruppert + + * biossums.c (1.6): + + - fixed a warning + +2008-03-02 08:47 vruppert + + * vbe.c (1.60): + + - added debug message for unsupported VBE modes + +2008-02-24 09:18 vruppert + + * vbe.c (1.59): + + - in LFB modes the number of banks must be set to 1 + +2008-01-27 10:44 vruppert + + * Makefile (1.21), biossums.c (1.5), vgabios.c (1.67): + + - added PCI data structure for the Cirrus VGABIOS images + - added support for the PCI data structure in biossums + - updated year in copyright + +2008-01-26 11:46 vruppert + + * BUGS (1.4), Makefile (1.20), README (1.14), TODO (1.13), vbe_display_api.txt (1.14): + + - whitespace cleanup + +2006-11-26 10:43 vruppert + + * Makefile (1.19): + + - disable the generation of linemarkers by the preprocessor, since the latest + versions of bcc don't like them + +2006-09-02 13:15 vruppert + + * biossums.c (1.4): + + - the biossums utility no longer modifies VGABIOS images with proper checksum + and size + +2006-08-19 14:28 vruppert + + * Changelog (1.26), README (1.13), TODO (1.12): + + - updates for 0.6a release + +2006-08-19 09:39 vruppert + + * vbe.c (1.58): + + - improved VGA compatible setup for VBE modes (disable CGA and Hercules + compatible memory layout) + +2006-08-18 20:39 vruppert + + * vbe.c (1.57): + + - improved VGA compatible setup for >=8bpp VBE modes (CRTC doubleword mode and + GRDC shift register setting added) + - now using symbolic name for CRTC address register + +2006-08-15 20:42 vruppert + + * vbe.c (1.56), vbetables-gen.c (1.4): + + - init 4bpp VBE modes by a temporary switch to VGA mode 0x6A + - all 4bpp VBE modes now enabled + +2006-08-14 20:24 vruppert + + * vbe.c (1.55): + + - VGA compatible setup for VBE modes improved (Bochs hack can be removed now) + +2006-08-12 07:51 vruppert + + * .cvsignore (1.1): + + - .cvsignore added for auto-generated file + +2006-08-12 07:47 vruppert + + * vbe.c (1.54), vbe.h (1.27), vbe_display_api.txt (1.13), vbetables-gen.c (1.3): + + - cleaned up VBE memory size definitions (removed duplicate defines, main + definition now in vbetables-gen.c) + +2006-08-09 21:28 vruppert + + * vbetables.h (1.30): + + - removed auto-generated file + +2006-08-09 21:26 vruppert + + * vbe.c (1.53), vbe.h (1.26), vbe_display_api.txt (1.12), vbetables-gen.c (1.2), + vbetables.h (1.29): + + - VBE video memory increased to 8 MB + - VBE dispi ID changed to B0C4 + - documentation update + +2006-07-11 08:03 vruppert + + * Makefile (1.18), vbetables-gen.c (1.1), vbetables.h (1.28): + + - generate vbetables.h dynamicly + * initial patch from the qemu project by Fabrice Bellard + * only add modes that fit in video memory (still 4 MB) + * several other fixes (e.g. 4 bpp specific stuff, number of pages) + +2006-07-10 07:47 vruppert + + * vgabios.c (1.66): + + - biosfn_scroll(): check variable 'i' for underflowing when scrolling downwards + to avoid screen corruption + +2006-07-10 07:47 vruppert + + * vbe.c (1.52): + + - VBE set bank functions failure handling added + - VBE get/set logical scan line length fixes for the 4bpp mode + +2006-07-08 13:27 vruppert + + * vbe.c (1.51), vbetables.h (1.27): + + - added special case for the 4 bpp when setting VBE display start + - VBE mode table fixes + +2006-07-07 13:30 vruppert + + * clext.c (1.12): + + - bank pointer must be set to 0 after a mode set + +2006-06-21 16:58 vruppert + + * vbe.c (1.50), vbetables.h (1.26): + + - improved VBE display capabilities check (X resulution checked now) + - removed obsolete defines (LFB always available, always generate dynamic list) + - CR/LF to LF fixes + +2006-06-18 15:22 vruppert + + * clext.c (1.11), vbe.c (1.49), vbe.h (1.25), vbetables.h (1.25), vgabios.c + (1.65): + + - applied patch from the qemu project (Fabrice Bellard) + * Cirrus SVGA now supports the "no clear" bit when switching to Cirrus or + VESA mode + * Bochs VBE protected mode interface improved + * save/restore video state support for Bochs VBE and standard VGA added + * Bochs VBE prepared for more modi + +2006-03-25 10:19 vruppert + + * clext.c (1.10), vgabios.c (1.64), vgatables.h (1.10): + + - applied patch from Fabrice Bellard + * added minimal support for the video parameter table (VPT) + * added Cirrus SVGA mode 0x7b (1600x1200x8) + +2005-12-26 19:50 vruppert + + * vbe.c (1.48), vgabios.c (1.63): + + - Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com) + +2005-12-26 19:50 vruppert + + * biossums.c (1.3): + + - biossums utility now supports VGABIOS sizes up to 64 kBytes + +2005-09-21 18:45 vruppert + + * vgatables.h (1.9): + + - mode 0x11: all color planes must be enabled in this 2-color VGA mode + +2005-08-30 18:41 vruppert + + * biossums.c (1.2): + + - missing license text added in biossums.c + +2005-07-02 18:39 vruppert + + * vgabios.c (1.62): + + - BIOS configuration word usually reports initial mode 80x25 color text + - vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the + cursor row value + +2005-05-24 16:50 vruppert + + * vbe.c (1.47), vgabios.c (1.61): + + - output to the vgabios info port can be disabled now. It is still enabled by + default and always possible in debug mode. (based on a patch from Alex Beregszaszi) + +2005-05-20 16:06 vruppert + + * vbe.c (1.46), vgabios.c (1.60): + + - fixed return value for the default case in the VBE section (non-debug mode) + - removed unused macros HALT and PANIC_PORT + +2005-03-07 20:39 vruppert + + * README (1.9): + + - updates for 0.5a release + +2005-03-06 13:06 vruppert + + * Makefile (1.17): + + - vgabios files with cirrus support added to release target + +2005-03-06 12:24 vruppert + + * Makefile (1.16): + + - cross compilation support added (patch from Alex Beregszaszi) + +2005-03-05 13:03 vruppert + + * BUGS (1.3), README (1.8), TODO (1.11): + + - documentation updates + +2004-12-04 15:26 vruppert + + * VGABIOS-lgpl-latest.bin (1.61), VGABIOS-lgpl-latest.cirrus.bin + (1.13), VGABIOS-lgpl-latest.cirrus.debug.bin (1.13), + VGABIOS-lgpl-latest.debug.bin (1.61), clext.c (1.9): + + - Cirrus extension: support for 1280x1024x15 and 1280x1024x16 modes added (patch + from Fabrice Bellard) + +2004-08-08 16:53 vruppert + + * VGABIOS-lgpl-latest.bin (1.60), VGABIOS-lgpl-latest.cirrus.bin (1.12), + VGABIOS-lgpl-latest.cirrus.debug.bin (1.12), + VGABIOS-lgpl-latest.debug.bin (1.60), clext.c (1.8): + + - use single bank mode for VBE + - enable 16k granularity for VBE only + +2004-07-30 19:33 vruppert + + * VGABIOS-lgpl-latest.bin (1.59), VGABIOS-lgpl-latest.cirrus.bin (1.11), + VGABIOS-lgpl-latest.cirrus.debug.bin (1.11), + VGABIOS-lgpl-latest.debug.bin (1.59), clext.c (1.7): + + - cirrus init: set standard vga mode and reset bitblt + +2004-07-22 18:38 vruppert + + * VGABIOS-lgpl-latest.bin (1.58), VGABIOS-lgpl-latest.cirrus.bin (1.10), + VGABIOS-lgpl-latest.cirrus.debug.bin (1.10), + VGABIOS-lgpl-latest.debug.bin (1.58), clext.c (1.6), vbe.c (1.45), + vbetables.h (1.24): + + - cirrus extension: tables for mode 1280x1024x8 added + - vbe: dispi_set_xres() and dispi_set_virt_width() now modify vga compatible + registers + - vbe: mode list entry for mode 800x600x4 fixed + +2004-07-18 20:23 vruppert + + * VGABIOS-lgpl-latest.bin (1.57), VGABIOS-lgpl-latest.cirrus.bin (1.9), + VGABIOS-lgpl-latest.cirrus.debug.bin (1.9), + VGABIOS-lgpl-latest.debug.bin (1.57), vgabios.c (1.59), vgatables.h (1.8): + + - disable CRTC write protection before setting new values + - CRTC line for mode 0x6a fixed + +2004-07-07 16:08 vruppert + + * Makefile (1.15), VGABIOS-lgpl-latest.bin (1.56), + VGABIOS-lgpl-latest.cirrus.bin (1.8), VGABIOS-lgpl-latest.cirrus.debug.bin (1.8), + VGABIOS-lgpl-latest.debug.bin (1.56), biossums.c (1.1), clext.c (1.5): + + - biossums utility for the Bochs BIOS adapted for the LGPL'd VGABIOS + - VESA3 PMINFO checksum calculated in the source + - 24 bpp mode entries fixed (patch from Fabrice Bellard) + +2004-06-25 18:28 vruppert + + * VGABIOS-lgpl-latest.cirrus.bin (1.7), VGABIOS-lgpl-latest.cirrus.debug.bin (1.7), + clext.c (1.4): + + - 4MB memory probe added (patch from Fabrice Bellard) + +2004-06-25 17:31 vruppert + + * VGABIOS-lgpl-latest.bin (1.55), VGABIOS-lgpl-latest.cirrus.bin (1.6), + VGABIOS-lgpl-latest.cirrus.debug.bin (1.6), + VGABIOS-lgpl-latest.debug.bin (1.55), clext.c (1.3): + + - fixed value of sequencer reset register in cirrus mode table + - fixed possible overflow error if cirrus start address is >256k + +2004-06-23 21:11 vruppert + + * VGABIOS-lgpl-latest.bin (1.54), VGABIOS-lgpl-latest.cirrus.bin (1.5), + VGABIOS-lgpl-latest.cirrus.debug.bin (1.5), + VGABIOS-lgpl-latest.debug.bin (1.54), clext.c (1.2): + + - applied new patch for the cirrus extension from suzu + * enable VESA LFB support if a Cirrus PCI adapter is detected + * prepared VBE3 protected mode info block (test case required) + - added VBE functions 4F06h and 4F07h + - some bugfixes + +2004-06-17 18:57 vruppert + + * Makefile (1.14), VGABIOS-lgpl-latest.bin (1.53), + VGABIOS-lgpl-latest.cirrus.bin (1.2), VGABIOS-lgpl-latest.cirrus.debug.bin (1.2), + VGABIOS-lgpl-latest.debug.bin (1.53): + + - fixed makefile targets for the binaries with cirrus extension + +2004-06-16 21:11 vruppert + + * Makefile (1.13), VGABIOS-lgpl-latest.bin (1.52), + VGABIOS-lgpl-latest.cirrus.bin (1.1), VGABIOS-lgpl-latest.cirrus.debug.bin (1.1), + VGABIOS-lgpl-latest.debug.bin (1.52), clext.c (1.1), vgabios.c (1.58): + + - applied suzu's cirrus extension patch. Cirrus SVGA detection, most of the + cirrus-specific modes and some basic VBE features are present now. + +2004-05-31 21:15 vruppert + + * VGABIOS-lgpl-latest.bin (1.51), VGABIOS-lgpl-latest.debug.bin (1.51), + vgabios.c (1.57): + + - write character in planar graphics modes: sequencer map mask must be 0x0f and + bit operation must be 'replace' if bit 7 of attribute is clear + - read/write pixel in planar graphics modes: bit mask setup simplified + +2004-05-11 18:08 vruppert + + * VGABIOS-lgpl-latest.bin (1.50), VGABIOS-lgpl-latest.debug.bin (1.50), + vgabios.c (1.56): + + - biosfn_select_vert_res rewritten in assembler + - scroll text in planar graphics modes: attribute for blank line fixed + - write character in planar graphics modes: graphics controller values fixed + +2004-05-09 20:32 vruppert + + * VGABIOS-lgpl-latest.bin (1.49), VGABIOS-lgpl-latest.debug.bin (1.49), + vbe.c (1.44), vbe.h (1.24), vgabios.c (1.55): + + - VBE init code and some dispi ioport functions rewritten in assembler + - text scroll functions for CGA graphics modes added + - scroll text in graphics modes: attribute for blank line fixed + +2004-05-08 16:06 vruppert + + * BUGS (1.2), README (1.7), TODO (1.10), VGABIOS-lgpl-latest.bin (1.48), + VGABIOS-lgpl-latest.debug.bin (1.48), vbe.c (1.43), vbe.h (1.23), + vbe_display_api.txt (1.11), vgabios.c (1.54): + + - VBE internal functions dispi_set_enable and dispi_set_bank now called both from C + and asm code + - VBE function 0x03 rewritten in assembler + - VBE function 0x08 cleaned up + - text output and scroll functions for graphics modes rewritten using case + structures + - documentation and comments updated + +2004-05-06 21:18 vruppert + + * VGABIOS-lgpl-latest.bin (1.47), VGABIOS-lgpl-latest.debug.bin (1.47), + vbe.c (1.42), vbe.h (1.22), vgabios.c (1.53): + + - VBE functions 0x05, 0x06, 0x07 and some dispi ioport functions rewritten in + assembler + - VBE functions 0x06 and 0x07: get functions now supported, 15 bpp bug fixed + +2004-05-05 19:24 vruppert + + * VGABIOS-lgpl-latest.bin (1.46), VGABIOS-lgpl-latest.debug.bin (1.46), + vbe.c (1.41), vbe.h (1.21), vbe_display_api.txt (1.10), vgabios.c (1.52): + + - 8 bit DAC capability flag set + - vbe_biosfn_set_get_dac_palette_format implemented + - VBE api description updated + - C definitions from header files now used assembler code + +2004-05-02 17:27 vruppert + + * VGABIOS-lgpl-latest.bin (1.45), VGABIOS-lgpl-latest.debug.bin (1.45), + vgabios.c (1.51): + + - text scroll functions for PLANAR1/PLANAR4 graphics modes added + - function biosfn_get_ega_info rewritten in assembler + - read/write graphics pixel functions rewritten using a case structure + +2004-05-01 16:03 vruppert + + * VGABIOS-lgpl-latest.bin (1.44), VGABIOS-lgpl-latest.debug.bin (1.44), + vgabios.c (1.50): + + - biosfn_enable_cursor_emulation rewritten in assembler + - remap of the cursor shape depends on modeset control bit 0 + - text output in PLANAR4 modes now supports attribute bit 7 (XOR with background) + +2004-04-25 20:13 vruppert + + * VGABIOS-lgpl-latest.bin (1.43), VGABIOS-lgpl-latest.debug.bin (1.43), + vgabios.c (1.49), vgatables.h (1.7): + + - table entries for vga mode 0x0f fixed (PLANAR2 exists on EGA only) + - function release_font_access now supports the monochrome text mode + - PLANAR1 modes now supported in text output functions and read/write pixel + - function AH=0x12/BL=0x32 rewritten in assembler + +2004-04-25 08:45 vruppert + + * VGABIOS-lgpl-latest.bin (1.42), VGABIOS-lgpl-latest.debug.bin (1.42), + vgabios.c (1.48): + + - block address calculation in font functions fixed + - functions AX=0x1103, AH=0x12/BL=0x31 and AH=0x12/BL=0x33 rewritten in assembler + +2004-04-24 09:59 vruppert + + * VGABIOS-lgpl-latest.bin (1.41), VGABIOS-lgpl-latest.debug.bin (1.41), + vgabios.c (1.47): + + - read/write graphics pixel for PLANAR4 modes added + - CGA specific functions (group AH = 0x0B) implemented + +2004-04-23 14:34 vruppert + + * VGABIOS-lgpl-latest.bin (1.40), VGABIOS-lgpl-latest.debug.bin (1.40), + vgabios.c (1.46): + + - remaining palette and dac read/write functions (except gray scale summing) + rewritten in assembler + +2004-04-18 13:43 vruppert + + * VGABIOS-lgpl-latest.bin (1.39), VGABIOS-lgpl-latest.debug.bin (1.39), + vgabios.c (1.45): + + - some palette and dac read/write functions rewritten in assembler + - main int10 debug message now works with assembler functions, too + +2004-04-18 09:15 japj + + * vbe.c (1.40): + + updated my email address + put vgabios url in the bios copyright string + (instead of my old email address) + +2004-04-17 07:18 vruppert + + * VGABIOS-lgpl-latest.bin (1.38), VGABIOS-lgpl-latest.debug.bin (1.38), + vgabios.c (1.44): + + - biosfn_set_video_mode: don't load DAC registers if default palette loading is + disabled. Perform gray scale summing if enabled. + - biosfn_perform_gray_scale_summing: switch between DAC read and write mode is + required to make this function work. Maximum DAC value always set to 0x3f. + +2004-04-08 17:50 vruppert + + * VGABIOS-lgpl-latest.bin (1.37), VGABIOS-lgpl-latest.debug.bin (1.37), + vgabios.c (1.43): + + - write character function for the LINEAR8 mode + - get_font_access() and release_font_access() rewritten in assembler + - fixed wrong variable name in the init code + +2004-04-06 19:31 vruppert + + * VGABIOS-lgpl-latest.bin (1.36), VGABIOS-lgpl-latest.debug.bin (1.36), + vgabios.c (1.42): + + - init functions rewitten in assembler + - function biosfn_set_display_code rewritten in assembler + +2004-04-05 19:40 vruppert + + * VGABIOS-lgpl-latest.bin (1.35), VGABIOS-lgpl-latest.debug.bin (1.35), + vgabios.c (1.41): + + - functions biosfn_get_video_mode() and biosfn_read_display_code() rewritten + in assembler + +2004-04-04 18:20 vruppert + + * VGABIOS-lgpl-latest.bin (1.34), VGABIOS-lgpl-latest.debug.bin (1.34), + vgabios.c (1.40): + + - write character function for CGA modes added + - read/write graphics pixel for CGA and LINEAR8 modes added + +2004-02-23 21:08 vruppert + + * VGABIOS-lgpl-latest.bin (1.33), VGABIOS-lgpl-latest.debug.bin (1.33), + vbe.c (1.39): + + - dispi_get_max_bpp(): restore the original value of the vbe enable register + +2004-02-22 14:17 vruppert + + * README (1.6), vbe.c (1.38), vbe.h (1.20), vbe_display_api.txt (1.9), + VGABIOS-lgpl-latest.bin (1.32), VGABIOS-lgpl-latest.debug.bin (1.32): + + - new function dispi_get_max_bpp() returns the bpp capabilities of the Bochs gui + - create the mode list depending on the supported bpp capability + - unused stuff removed + - documentation updated + +2004-02-21 18:20 vruppert + + * vbe.c (1.37), vbe.h (1.19), vbetables.h (1.23), + VGABIOS-lgpl-latest.bin (1.31), VGABIOS-lgpl-latest.debug.bin (1.31): + + - dynamicly genarated vbe mode_info list works now + +2003-11-17 21:04 vruppert + + * vbe.c (1.36), vbetables.h (1.22), vgabios.c (1.39), vgatables.h (1.6), + VGABIOS-lgpl-latest.bin (1.30), VGABIOS-lgpl-latest.debug.bin (1.30): + + - new VBE presence flag stored at unused BDA address 0xB9 + - VBE init code rewritten + - added BIOS TTY flag for VBE mode 0x0102 (TODO: scrolling) + - vgabios_init_func: load and activate text font already done by set_video_mode + - function biosfn_get_all_palette_reg() fixed + +2003-11-06 00:26 cbothamy + + * README (1.5): + + - add changes for 0.4c release + +2003-11-06 00:22 cbothamy + + * VGABIOS-lgpl-latest.bin (1.29), VGABIOS-lgpl-latest.debug.bin + (1.29): + + - compile vgabios.c rev1.38 + +2003-11-06 00:21 cbothamy + + * vgabios.c (1.38): + + - activate char table after loading it when setting a text video + mode + +2003-11-06 00:19 cbothamy + + * Makefile (1.12): + + - when making a release, remove unwanted files first, and exclude + CVS from the tarball + +2003-11-04 22:50 cbothamy + + * ChangeLog (1.20, v0_4b): + + - update ChangeLog for 0.4b release + +2003-11-04 22:49 cbothamy + + * README (1.4, v0_4b): + + - update Changes for 0.4b release + +2003-11-04 20:26 vruppert + + * vgabios.c (1.37), VGABIOS-lgpl-latest.bin (1.28), + VGABIOS-lgpl-latest.debug.bin (1.28) (utags: v0_4b): + + - biosfn_get_font_info(): character height must be returned in CX + +2003-11-03 21:57 vruppert + + * vbe.c (1.35, v0_4b), vgabios.c (1.36), VGABIOS-lgpl-latest.bin + (1.27), VGABIOS-lgpl-latest.debug.bin (1.27): + + - the 'noclearmem' flag is not stored in the 'current video mode' + register (0040h:0049h) - VBE also stores the 'noclear' flag in + the 'video control' register (0040h:0087h) + +2003-10-05 10:06 vruppert + + * vbe.h (1.18, v0_4b), vbe_display_api.txt (1.8, v0_4b), + VGABIOS-lgpl-latest.bin (1.26), VGABIOS-lgpl-latest.debug.bin + (1.26): + + - changed VBE i/o registers to 0x01CE/CF (suggestion from Daniel + Gimpelevich) + +2003-08-18 18:38 vruppert + + * VGABIOS-lgpl-latest.bin (1.25), VGABIOS-lgpl-latest.debug.bin + (1.25), vgabios.c (1.35): + + - wrong offsets to the character tables (INT 0x1F/0x43) fixed + (underscore added) - functions accessing the CRT controller + optimized using a local variable 'crtc_addr' + +2003-08-17 15:46 cbothamy + + * ChangeLog (1.19, v0_4a): + + - ChangeLog is now automatically generated by running "cvs2cl -r + -t -P -S" - update ChangeLog for 0.4a release + +2003-08-17 15:44 cbothamy + + * README (1.3, v0_4a): + + - added the old ChangeLog in the HOSTORY section of the README + file - update History for 0.4a release, with a summary of Changes + +2003-08-17 15:24 cbothamy + + * Makefile (1.11, v0_4b, v0_4a): + + - fix Makefile for "release" target + +2003-08-16 01:49 cbothamy + + * Makefile (1.10), README (1.2), VGABIOS-lgpl-latest.bin (1.24, + v0_4a), VGABIOS-lgpl-latest.debug.bin (1.24, v0_4a), vgabios.c + (1.34, v0_4a): + + - update the Makefile for releases - remove references to old + plex86 website - update the Makefile so it build + VGABIOS-lgpl-latest.bin and VGABIOS-lgpl-latest.debug.bin + +2003-08-07 18:17 vruppert + + * VGABIOS-lgpl-latest.bin (1.23), VGABIOS-lgpl-latest.debug.bin + (1.23): + + - current VBE mode now stored in BDA (unused address 0xBA) + +2003-08-07 17:54 vruppert + + * vbe.c (1.34), vgatables.h (1.5, v0_4b) (utags: v0_4a): + + - current VBE mode now stored in BDA (unused address 0xBA) + +2003-07-20 18:05 vruppert + + * vgabios.c (1.33), VGABIOS-lgpl-latest.bin (1.22), + VGABIOS-lgpl-latest.debug.bin (1.22): + + - fixed a few functions accessing the attribute controller + +2003-07-19 09:33 vruppert + + * vgabios.c (1.32), VGABIOS-lgpl-latest.bin (1.21), + VGABIOS-lgpl-latest.debug.bin (1.21): + + - re-enable video after programming the attribute controller - + biosfn_set_all_palette_reg(): number of palette registers fixed + +2003-07-16 22:32 vruppert + + * ChangeLog (1.18), vbe.c (1.33), vbe.h (1.17, v0_4a), + vbe_display_api.txt (1.7, v0_4a), vgabios.c (1.31), + VGABIOS-lgpl-latest.bin (1.20), VGABIOS-lgpl-latest.debug.bin + (1.20): + + - LFB flag now stored in the register VBE_DISPI_INDEX_ENABLE - + release date in Changelog fixed - release date of VBE BIOS 0.6 + was the same as VGA BIOS 0.3b - year changed in copyright + messages + +2003-07-15 12:40 vruppert + + * VGABIOS-lgpl-latest.bin (1.19), VGABIOS-lgpl-latest.debug.bin + (1.19): + + - new function dispi_get_bpp() - function + vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp - + number of image pages of all VBE modes fixed + +2003-07-15 12:35 vruppert + + * vbe.c (1.32), vbetables.h (1.21, v0_4b, v0_4a): + + - new function dispi_get_bpp() - function + vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp - + number of image pages of all VBE modes fixed + +2003-07-14 19:45 vruppert + + * vbe_display_api.txt (1.6): + + - description of VBE_DISPI_ interface 0xb0c2 added + +2003-07-10 19:07 vruppert + + * vbe.c (1.31), vbetables.h (1.20), VGABIOS-lgpl-latest.bin (1.18), + VGABIOS-lgpl-latest.debug.bin (1.18): + + - 15 bpp VBE modes added - "Bochs own" mode 0x142 (640x480x32bpp) + added + +2003-07-01 19:00 vruppert + + * vbe.c (1.30), vbe.h (1.16), vbetables.h (1.19), + VGABIOS-lgpl-latest.bin (1.17), VGABIOS-lgpl-latest.debug.bin + (1.17): + + - VBE preserve display memory feature implemented - VBE mode + entries 0x117 and 0x118 added + +2003-06-30 21:27 vruppert + + * vbe.c (1.29), vbe.h (1.15), vbetables.h (1.18), + VGABIOS-lgpl-latest.bin (1.16), VGABIOS-lgpl-latest.debug.bin + (1.16): + + - VBE mode info blocks of modes with >8bpp enabled - VBE modes + with 24 bpp: bytes per scanline fixed - vbe_biosfn_set_mode() now + supports >8bpp - VBE will be enabled with new VBE_DISPI_ID2 + (0xB0C2) + +2003-06-29 12:53 vruppert + + * vbetables.h (1.17), VGABIOS-lgpl-latest.bin (1.15), + VGABIOS-lgpl-latest.debug.bin (1.15): + + - duplicate lines with VBE_MODE_ATTRIBUTE_GRAPHICS_MODE removed - + VBE mode info items of currently unsupported modes fixed + +2003-06-15 21:19 vruppert + + * vgabios.c (1.30), VGABIOS-lgpl-latest.bin (1.14), + VGABIOS-lgpl-latest.debug.bin (1.14): + + - function write_gfx_char() rewritten + +2003-04-26 09:27 vruppert + + * VGABIOS-lgpl-latest.debug.bin (1.13): + + - added missing VBE function dispi_get_bank() - added missing + return codes for VBE function 4F05h - memory size is always + reported in VBE function 4F00h - fixed scan line length for VBE + mode 0102h - fixed function set_active_page() for graphics modes + - fixed the page sizes of some VGA modes + +2003-04-26 09:22 vruppert + + * vbe.c (1.28), vbetables.h (1.16), vgabios.c (1.29), vgatables.h + (1.4), VGABIOS-lgpl-latest.bin (1.13): + + - added missing VBE function dispi_get_bank() - added missing + return codes for VBE function 4F05h - memory size is always + reported in VBE function 4F00h - fixed scan line length for VBE + mode 0102h - fixed function set_active_page() for graphics modes + - fixed the page sizes of some VGA modes + +2003-04-20 09:51 vruppert + + * vgabios.c (1.28), vgatables.h (1.3), VGABIOS-lgpl-latest.bin + (1.12), VGABIOS-lgpl-latest.debug.bin (1.12): + + - function write_gfx_char() now supports different font sizes - + some entries of the static functionality table fixed + +2003-04-18 09:23 vruppert + + * vbe.c (1.27), vbe.h (1.14), vbetables.h (1.15): + + - applied patch #1331 * new function dispi_set_bank_farcall() + * VBE mode info item WinFuncPtr points to the new function if the + flag VBE_WINDOW_ATTRIBUTE_RELOCATABLE is set * flag + VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE added + +2003-02-11 20:17 vruppert + + * VGABIOS-lgpl-latest.bin (1.11), VGABIOS-lgpl-latest.debug.bin + (1.11), vbe.c (1.26), vbetables.h (1.14): + + - VBE mode search rewritten * improved function + mode_info_find_mode() is now used by the VBE functions 0x4F01 + and 0x4F02 * removed all mode list entries with the LFB bit + set. LFB detection is now present in the function + mode_info_find_mode() + +2003-02-09 20:59 vruppert + + * VGABIOS-lgpl-latest.bin (1.10), VGABIOS-lgpl-latest.debug.bin + (1.10), vgabios.c (1.27): + + - function write_gfx_char(): memory address now calculated in + this function; background color is always black - function + biosfn_write_char_attr(): the count parameter is now used in + graphics modes too - function biosfn_write_char_only() works + the same way as function biosfn_write_char_attr() in graphics + mode - copying charmap data optimized using memcpyb() + +2003-02-09 11:36 vruppert + + * VGABIOS-lgpl-latest.bin (1.9), VGABIOS-lgpl-latest.debug.bin + (1.9): + + - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA + modes with the LFB flag set removed from the list (Linux doesn't + like mode numbers > 0x07ff) + +2003-02-09 11:02 vruppert + + * vbe.c (1.25), vbe.h (1.13), vbetables.h (1.13): + + - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA + modes with the LFB flag set removed from the list (Linux doesn't + like mode numbers > 0x07ff) + +2003-02-08 13:04 vruppert + + * vbe.c (1.24), vgabios.c (1.26): + + - vbe_biosfn_return_current_mode() now returns the active + standard VGA mode TODO: return VESA mode if enabled - + biosfn_set_video_mode() now clears the screen in CGA mode + correctly - write character functions are now working in all + PLANAR4 graphics modes - added stubs for unimplemented features + in graphics modes + +2003-02-04 22:19 vruppert + + * VGABIOS-lgpl-latest.bin (1.8), VGABIOS-lgpl-latest.debug.bin + (1.8): + + - set video mode: clear vga memory in graphics mode - set video + mode: load default font in text mode - write character + implemented for graphics mode 0x12 + +2003-02-04 22:06 vruppert + + * vgabios.c (1.25): + + - set video mode: clear vga memory in graphics mode - set video + mode: load default font in text mode - write character + implemented for graphics mode 0x12 + +2003-01-21 19:30 vruppert + + * vgabios.c (1.24): + + - remap the cursor size if the char height is > 8 and the new + values are < 8 + +2003-01-20 18:24 cbothamy + + * Makefile (1.9): + + - fix so make -j2 does not overwrite temp files + +2003-01-19 12:35 vruppert + + * vgabios.c (1.23): + + - function set_scan_lines() recalculates the number of rows and + the page size - new values for char height, text rows and page + size are stored in the BIOS data segment - asm helper function + idiv_u added + +2003-01-15 18:49 cbothamy + + * VGABIOS-lgpl-latest.bin (1.7), VGABIOS-lgpl-latest.debug.bin + (1.7): + + - compile vgabios rev 1.22 + +2003-01-15 18:49 cbothamy + + * vgabios.c (1.22): + + - fix bug found by ams : a 8bits index value was compared to + 0x100 in some cases in biosfn_set_all_dac_reg, + biosfn_read_all_dac_reg, biosfn_perform_gray_scale_summing + +2003-01-15 17:34 cbothamy + + * Makefile (1.8): + + - fix symbol table file names, discovered by ams + +2003-01-04 21:20 vruppert + + * VGABIOS-lgpl-latest.bin (1.6), VGABIOS-lgpl-latest.debug.bin + (1.6), vgabios.c (1.21): + + - biosfn_set_video_mode(): reset attribute controller flip-flop + before setting up the controller's registers (bug found with + amidiag) + +2003-01-04 09:50 vruppert + + * vbe.c (1.23): + + - VBE function 0x00 returns VBE 1.x compatible information if no + VBE signature is present + +2003-01-01 12:44 vruppert + + * VGABIOS-lgpl-latest.bin (1.5), VGABIOS-lgpl-latest.debug.bin + (1.5): + + - SVGA mode 0x6A (800x600x4) added to the list of graphics modes + +2002-12-31 18:07 vruppert + + * vgatables.h (1.2): + + - SVGA mode 0x6A (800x600x4) added to the list of graphics modes + +2002-11-23 10:38 cbothamy + + * ChangeLog (1.17, v0_3b): + + - fix changelog for 0.3b release + +2002-10-20 17:12 vruppert + + * VGABIOS-lgpl-latest.bin (1.4), VGABIOS-lgpl-latest.debug.bin + (1.4), vgabios.c (1.20) (utags: v0_3b): + + - new function set_scan_lines() for the font size change (patch + from Hartmut Birr) - cursor shape start and end must be updated + in set_scan_lines() - set_scan_lines() is called by the functions + 0x1110, 0x1111, 0x1112 and 0x1114 after copying the font data + +2002-10-04 08:20 vruppert + + * VGABIOS-lgpl-latest.bin (1.3), VGABIOS-lgpl-latest.debug.bin + (1.3), vgabios.c (1.19): + + - biosfn_set_single_dac_reg(): the red value is stored in DH + +2002-09-19 19:05 cbothamy + + * VGABIOS-lgpl-latest.bin (1.2), VGABIOS-lgpl-latest.debug.bin + (1.2): + + - updated with latest changes + +2002-09-19 19:03 cbothamy + + * ChangeLog (1.16), Makefile (1.7, v0_3b), vbe.c (1.22, v0_3b), + vgabios.c (1.18), vgabios.h (1.3, v0_4b, v0_4a, v0_3b): + + - updated the Makefile - removed display of copyrights. - + changed the Copyright string to "LGPL VGABios developers" + +2002-09-08 21:14 vruppert + + * vgabios.c (1.17): + + - set the cursor shape depending on the current font height - + clear BL before calling int 0x10 function 0x1103 in + vgabios_init_func + +2002-08-23 22:58 cbothamy + + * vbe.c (1.21), vbetables.h (1.12, v0_3b): + + - added lfb-mode numbers (patch from mathis) + +2002-07-21 21:57 japj + + * vbe.c (1.20), vgabios.c (1.16): + + gcc2/3 preprocessing fix + +2002-05-18 16:55 cbothamy + + * vgabios.c (1.15): + + - include patch from Volker that adds some text font functions + +2002-05-01 23:13 japj + + * VGABIOS-lgpl-latest.bin (1.1), VGABIOS-lgpl-latest.debug.bin + (1.1): + + adding latest bin & debug bin of the vgabios + +2002-04-29 14:50 japj + + * ChangeLog (1.15), vbe.c (1.19), vbe.h (1.12, v0_3b), vbetables.h + (1.11), vgabios.c (1.14): + + - applying hw scrolling/multibuffering patch + +2002-04-25 21:59 japj + + * Makefile (1.6), vbe.c (1.18), vgabios.c (1.13): + + - reverting #asm/##asm & endasm patch (does not work with with + cygwin) + +2002-04-19 19:38 japj + + * Makefile (1.5), vbe.c (1.17), vgabios.c (1.12): + + - fixing preprocessing of vgabios with latest gcc (from Mandrake + 8.2) + +2002-04-08 23:44 japj + + * ChangeLog (1.14), vbe_display_api.txt (1.5, v0_3b): + + - preparing docs for new DISPI interface (for hardware scrolling) + +2002-04-03 19:06 japj + + * ChangeLog (1.13), TODO (1.9, v0_4b, v0_4a, v0_3b), vbe.c (1.16): + + - defaulting LFB on + updated changelog & todo + +2002-04-03 00:38 cbothamy + + * vbe.c (1.15), vgabios.c (1.11): + + - changed the logging ports to 0x500 -> 0x502 + +2002-03-14 17:54 japj + + * vbe.c (1.14): + + - vbetables.h is dependant upon some defines (VBE_HAVE_LFB), so + put the include *after* the define + +2002-03-13 21:47 japj + + * ChangeLog (1.12), TODO (1.8), vbe.c (1.13), vbetables.h (1.10), + vgabios.c (1.10): + + - made LFB dependant upon define - not implement vbe functions + return failure - updated todo & docs for things after bochs 1.4 + +2002-03-13 19:46 japj + + * vbe.h (1.11), vbe_display_api.txt (1.4): + + - added max video memory + documented what is in the 0xb0c0 + interface + +2002-03-12 02:33 cbothamy + + * ChangeLog (1.11), Makefile (1.4): + + - updated for 0.3a. Merged vgabios.bin and vbebios.bin + +2002-03-10 21:36 japj + + * ChangeLog (1.10), vbetables.h (1.9): + + - added LFB modes for testing with vbe-lfb patch in Bochs + +2002-03-10 17:42 japj + + * vbe.c (1.12, v0_3a): + + - show people when they do NOT have VBE support available + +2002-03-10 17:36 japj + + * TODO (1.7, v0_3a), vbe.c (1.11), vbe.h (1.10, v0_3a), vgabios.c + (1.9, v0_3a): + + - cleanup of vbe internal functions (set 8bpp mode is now + dependant on ModeInfo content instead of hardcoded functions) + +2002-03-10 17:20 cbothamy + + * ChangeLog (1.9, v0_3a), TODO (1.6): + + - updated for 0.3a + +2002-03-10 17:19 cbothamy + + * vbe.c (1.10), vbe.h (1.9): + + - added vbe_has_vbe_display function that detects an attached vbe + display + +2002-03-10 17:12 cbothamy + + * vgabios.c (1.8): + + - vbe calls are done only if a vbe display is detected + +2002-03-10 11:25 japj + + * vbe.h (1.8), vbe_display_api.txt (1.3, v0_3a): + + - preparing for LFB support + +2002-03-09 14:25 japj + + * vgabios.c (1.7): + + - fixing initial cursor shape to _ instead of - + +2002-03-08 23:08 japj + + * ChangeLog (1.8), TODO (1.5), vbe.c (1.9), vbe.h (1.7), vgabios.c + (1.6): + + - updating vbe code to new API + +2002-03-08 21:48 japj + + * vbe.c (1.8), vbe.h (1.6), vbetables.h (1.8, v0_3a): + + - updating vbe code with #defines from API + +2002-03-08 21:31 japj + + * vbe_display_api.txt (1.2): + + - adding some text about how banks work + +2002-03-08 21:09 japj + + * ChangeLog (1.7), vbe_display_api.txt (1.1): + + - adding vbe_display_api documentation + +2002-03-07 21:36 japj + + * ChangeLog (1.6), vbe.c (1.7), vbetables.h (1.7): + + - added 1024x768xbpp support - some more cleanups/comments + +2002-03-06 21:55 japj + + * ChangeLog (1.5), TODO (1.4), vbe.c (1.6), vbetables.h (1.6), + vgabios.c (1.5): + + - updated changelog with new modi - added 640x480x8 (Mandrake + Installer can use this!) - added pre VBE2 compatible 'detection' + - fixed problem when normal vga set mode wouldn't disable vbe + mode + +2002-03-06 20:59 japj + + * TODO (1.3), vbe.c (1.5), vbe.h (1.5), vbetables.h (1.5), + vgabios.c (1.4): + + - adding 640x400x8 and 800x600x8 vbe support (this depends + HEAVILY on my bochs vga code patch - japj) + +2002-03-06 18:00 japj + + * vbe.c (1.4), vbe.h (1.4), vbetables.h (1.4): + + - implemented banked & lfb support for 320x200x8bpp (some fixes + for vbetest program not displaying anything) + +2002-03-05 20:25 japj + + * Makefile (1.3, v0_3a): + + for vbe debug bios: - print debugging information in assembly + output - print source code in assembly output + +2002-03-01 19:39 japj + + * ChangeLog (1.4), TODO (1.2), vbe.c (1.3), vbe.h (1.3), + vbetables.h (1.3): + + - added vbe support for 320x200x8 using the standard vgamode + (0x13) + +2002-02-19 00:29 japj + + * ChangeLog (1.3): + + - updating ChangeLog with lfbprof + +2002-02-18 23:26 japj + + * tests/lfbprof/: lfbprof.c (1.2), lfbprof.h (1.2) (utags: v0_3a, + v0_3b, v0_4a, v0_4b): + + - fixed unsigned short for mode list (-1 != 0xffff otherwise) - + fixed LfbMapRealPointer macro mask problem (some modes were + skipped) - added some extra 'debugging' printf's + +2002-02-18 23:07 japj + + * tests/lfbprof/: Makefile (1.1, v0_4b, v0_4a, v0_3b, v0_3a), + lfbprof.c (1.1), lfbprof.h (1.1): + + - Adding lfbprof testprogram (for vbe testing purposes) It + needs to be compiled with the Watcom C Compiler + +2002-02-18 18:48 japj + + * vbe.c (1.2), vbe.h (1.2): + + - cosmetic updates to vbe.c/h + added bunch of FIXMEs for work + that needs to be done + +2002-02-18 18:34 japj + + * vbetables.h (1.2): + + - cosmetic updates in vbetables.h + +2002-02-18 18:32 japj + + * ChangeLog (1.2): + + updated changelog with merge of vbebios 0.2 + +2002-02-18 18:07 japj + + * vgabios.c (1.3): + + - small cosmetic cleanup in vgabios vbe code + added FIXMEs + +2002-02-18 17:55 japj + + * Makefile (1.2), dataseghack (1.2, v0_4b, v0_4a, v0_3b, v0_3a), + vbe.c (1.1), vbe.h (1.1), vbetables.h (1.1), vgabios.c (1.2), + vgabios.h (1.2, v0_3a): + + - merging with vbebios 0.2 release + +2002-02-18 11:31 cbothamy + + * BUGS (1.1, v0_4b, v0_4a, v0_3b, v0_3a), COPYING (1.1, v0_4b, + v0_4a, v0_3b, v0_3a), ChangeLog (1.1), Makefile (1.1), Notes + (1.1, v0_4b, v0_4a, v0_3b, v0_3a), README (1.1, v0_3b, v0_3a), + TODO (1.1), dataseghack (1.1), vgabios.c (1.1), vgabios.h (1.1), + vgafonts.h (1.1, v0_4b, v0_4a, v0_3b, v0_3a), vgatables.h (1.1, + v0_3b, v0_3a), tests/testbios.c (1.1, v0_4b, v0_4a, v0_3b, + v0_3a): + + - initial import + diff --git a/kvm/vgabios/Makefile b/kvm/vgabios/Makefile new file mode 100644 index 000000000..00e8c6687 --- /dev/null +++ b/kvm/vgabios/Makefile @@ -0,0 +1,87 @@ +SHELL = /bin/sh + +CC = gcc +CFLAGS = -g -O2 -Wall -Wstrict-prototypes +LDFLAGS = + +GCC = gcc +BCC = bcc +AS86 = as86 + +RELEASE = `pwd | sed "s-.*/--"` +RELDATE = `date '+%d %b %Y'` +RELVERS = `pwd | sed "s-.*/--" | sed "s/vgabios//" | sed "s/-//"` + +VGABIOS_DATE = "-DVGABIOS_DATE=\"$(RELDATE)\"" + +all: bios cirrus-bios + + +bios: biossums vgabios.bin vgabios.debug.bin + +cirrus-bios: vgabios-cirrus.bin vgabios-cirrus.debug.bin + +clean: + /bin/rm -f biossums vbetables-gen vbetables.h *.o *.s *.ld86 \ + temp.awk.* vgabios*.orig _vgabios_* _vgabios-debug_* core vgabios*.bin vgabios*.txt $(RELEASE).bin *.bak + +dist-clean: clean + +release: + VGABIOS_VERS=\"-DVGABIOS_VERS=\\\"$(RELVERS)\\\"\" make bios cirrus-bios + /bin/rm -f *.o *.s *.ld86 \ + temp.awk.* vgabios.*.orig _vgabios_.*.c core *.bak .#* + cp VGABIOS-lgpl-latest.bin ../$(RELEASE).bin + cp VGABIOS-lgpl-latest.debug.bin ../$(RELEASE).debug.bin + cp VGABIOS-lgpl-latest.cirrus.bin ../$(RELEASE).cirrus.bin + cp VGABIOS-lgpl-latest.cirrus.debug.bin ../$(RELEASE).cirrus.debug.bin + tar czvf ../$(RELEASE).tgz --exclude CVS -C .. $(RELEASE)/ + +vgabios.bin: vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h + $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE $(VGABIOS_DATE) > _vgabios_.c + $(BCC) -o vgabios.s -C-c -D__i86__ -S -0 _vgabios_.c + sed -e 's/^\.text//' -e 's/^\.data//' vgabios.s > _vgabios_.s + $(AS86) _vgabios_.s -b vgabios.bin -u -w- -g -0 -j -O -l vgabios.txt + rm -f _vgabios_.s _vgabios_.c vgabios.s + mv vgabios.bin VGABIOS-lgpl-latest.bin + ./biossums VGABIOS-lgpl-latest.bin + ls -l VGABIOS-lgpl-latest.bin + +vgabios.debug.bin: vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h + $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE -DDEBUG $(VGABIOS_DATE) > _vgabios-debug_.c + $(BCC) -o vgabios-debug.s -C-c -D__i86__ -S -0 _vgabios-debug_.c + sed -e 's/^\.text//' -e 's/^\.data//' vgabios-debug.s > _vgabios-debug_.s + $(AS86) _vgabios-debug_.s -b vgabios.debug.bin -u -w- -g -0 -j -O -l vgabios.debug.txt + rm -f _vgabios-debug_.s _vgabios-debug_.c vgabios-debug.s + mv vgabios.debug.bin VGABIOS-lgpl-latest.debug.bin + ./biossums VGABIOS-lgpl-latest.debug.bin + ls -l VGABIOS-lgpl-latest.debug.bin + +vgabios-cirrus.bin: vgabios.c vgabios.h vgafonts.h vgatables.h clext.c + $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus_.c + $(BCC) -o vgabios-cirrus.s -C-c -D__i86__ -S -0 _vgabios-cirrus_.c + sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus.s > _vgabios-cirrus_.s + $(AS86) _vgabios-cirrus_.s -b vgabios-cirrus.bin -u -w- -g -0 -j -O -l vgabios.cirrus.txt + rm -f _vgabios-cirrus_.s _vgabios-cirrus_.c vgabios-cirrus.s + mv vgabios-cirrus.bin VGABIOS-lgpl-latest.cirrus.bin + ./biossums VGABIOS-lgpl-latest.cirrus.bin + ls -l VGABIOS-lgpl-latest.cirrus.bin + +vgabios-cirrus.debug.bin: vgabios.c vgabios.h vgafonts.h vgatables.h clext.c + $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DCIRRUS_DEBUG -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus-debug_.c + $(BCC) -o vgabios-cirrus-debug.s -C-c -D__i86__ -S -0 _vgabios-cirrus-debug_.c + sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus-debug.s > _vgabios-cirrus-debug_.s + $(AS86) _vgabios-cirrus-debug_.s -b vgabios.cirrus.debug.bin -u -w- -g -0 -j -O -l vgabios.cirrus.debug.txt + rm -f _vgabios-cirrus-debug_.s _vgabios-cirrus-debug_.c vgabios-cirrus-debug.s + mv vgabios.cirrus.debug.bin VGABIOS-lgpl-latest.cirrus.debug.bin + ./biossums VGABIOS-lgpl-latest.cirrus.debug.bin + ls -l VGABIOS-lgpl-latest.cirrus.debug.bin + +biossums: biossums.c + $(CC) -o biossums biossums.c + +vbetables-gen: vbetables-gen.c + $(CC) -o vbetables-gen vbetables-gen.c + +vbetables.h: vbetables-gen + ./vbetables-gen > $@ diff --git a/kvm/vgabios/Notes b/kvm/vgabios/Notes new file mode 100644 index 000000000..d5b708dc7 --- /dev/null +++ b/kvm/vgabios/Notes @@ -0,0 +1,11 @@ +Development notes +----------------- + +- need to split video init function + 1. set bios variables + 2. do the real init with io based on bios variables + +- characters format switching will set the bios + variables and call function #2 above + +- need to rework the tables as explained in Interrupt list diff --git a/kvm/vgabios/README b/kvm/vgabios/README new file mode 100644 index 000000000..90141d426 --- /dev/null +++ b/kvm/vgabios/README @@ -0,0 +1,219 @@ +Plex86/Bochs VGABios +-------------------- + +The goal of this project is to have a LGPL'd Video Bios in plex86, +Bochs and qemu. +This VGA Bios is very specific to the emulated VGA card. +It is NOT meant to drive a physical vga card. + + +Cirrus SVGA extension +--------------------- + +The Cirrus SVGA extension is designed for the Cirrus emulation in Bochs and +qemu. The initial patch for the Cirrus extension has been written by Makoto +Suzuki (suzu). + + +Install +------- +To compile the VGA Bios you will need : +- gcc +- bcc +- as86 +- ld86 + +Untar the archive, and type make. You should get a "VGABIOS-lgpl-latest.bin" +file. Alternatively, you can use the binary file "VGABIOS-lgpl-latest.bin", +i have compiled for you. + +Edit your plex86/bochs conf file, and modify the load-rom command in the +VGA BIOS section, to point to the new vgabios image file. + + +Debugging +--------- +You can get a very basic debugging system: messages printed by the vgabios. +You have to register the "unmapped" device driver in plex86 or bochs, and make +sure it grabs port 0xfff0. + +Comment the #undef DEBUG at the beginning of vgabios.c. +You can then use the "printf" function in the bios. + + +Testing +------- +Look at the "testvga.c" file in the archive. This is a minimal Turbo C 2.0 +source file that calls a few int10 functions. Feel free to modify it to suit +your needs. + + +Copyright and License +--------------------- +This program has been written by Christophe Bothamy +It is protected by the GNU Lesser Public License, which you should +have received a copy of along with this package. + + +Reverse Engineering +------------------- +The VGA Bios has been written without reverse-engineering any existing Bios. + + +Acknowledgment +-------------- +The source code contains code ripped from rombios.c of plex86, written +by Kevin Lawton <kevin2001@yahoo.com> + +The source code contains fonts from fntcol16.zip (c) by Joseph Gil avalable at : +ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip +These fonts are public domain + +The source code is based on information taken from : +- Kevin Lawton's vga card emulation for bochs/plex86 +- Ralf Brown's interrupts list avalaible at + http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html +- Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/ +- Michael Abrash's Graphics Programming Black Book +- Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" + edited by sybex +- DOSEMU 1.0.1 source code for several tables values and formulas + + +Feedback +-------- +Please report any bugs, comments, patches for this VGA Bios to info@vruppert.de +You can find the latest release at : http://www.nongnu.org/vgabios/ +For any information on bochs, visit the website http://bochs.sourceforge.net/ +For any information on qemu, visit the website http://fabrice.bellard.free.fr/qemu/ + + +History +------- +vgabios-0.6b : May 30 2008 + - Volker + . added PCI data structure for the Cirrus VGABIOS images + . minor bugfixes in biossums utility, VBE support and makefile + +vgabios-0.6a : Aug 19 2006 + - Volker + . added minimal support for the video parameter table (VPT) + . Cirrus SVGA now supports the "no clear" bit in Cirrus and VESA mode + . Bochs VBE protected mode interface improved + . save/restore video state support for Bochs VBE and standard VGA added + . generate vbetables.h dynamicly + . VBE video memory increased to 8 MB (VBE dispi ID changed to B0C4) + . lots of 4bpp VBE fixes (all 4bpp VBE modes now enabled) + . VGA compatible setup for VBE modes added + +vgabios-0.5d : Dec 29 2005 + - Volker + . Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com) + . biossums utility now supports VGABIOS sizes up to 64 kBytes + . VGA mode 0x11: all color planes must be enabled in this 2-color VGA mode + +vgabios-0.5c : Jul 07 2005 + - Volker + . BIOS configuration word usually reports initial mode 80x25 color text + . vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the + cursor row value + +vgabios-0.5b : May 24 2005 + - Volker + . fixed return value for the default case in the VBE section (non-debug mode) + . removed unused stuff + +vgabios-0.5a : Mar 07 2005 + - Volker + . Cirrus SVGA extension (initial patches from Makoto Suzuki, improvements + from Fabrice Bellard) + . vgabios image size is now exactly 32k with a checksum + . a lot of vgabios and vbe functions rewritten in assembler + . dynamicly generated VBE mode info list + . write character function for CGA and LINEAR8 modes + . read/write graphics pixel for some graphics modes + . text scroll feature for some graphics modes + . VBE 8-bit DAC support + +vgabios-0.4c : Nov 06 2003 + - Christophe + . fix font problem on initial screen of NT4 Loader + +vgabios-0.4b : Nov 04 2003 + - Volker + . fix offset of character tables + . optimizations of CRT controller accesses + . VBE i/o registers changed to 0x01CE/CF + (suggestion from Daniel Gimpelevich) + . "noclear" flag stored in BIOS area + . fix character height returned by get_font_info function + +vgabios-0.4a : Aug 17 2003 + - Volker + . VBE mode search rewritten (VBE modes with LFB bit removed) + . many bugfixes and optimizations + . write character function implemented for graphics modes + . support for 15bpp, 16bpp, 24bpp and 32bpp VBE modes added + . SVGA mode 0x6A added + . VBE modes 0x102, 0x117, 0x118 and 0x142 (Bochs specific) + +vgabios-0.3b : Nov 23 2002 + - Christophe + . added lfb-mode numbers (patch from mathis) + . updated the Makefile + . removed display of copyrights. + . changed the Copyright string to "LGPL VGABios developers" + - Volker + . set the cursor shape depending on the current font height + . clear BL before calling int 0x10 function 0x1103 in vgabios_init_func + . added some text font functions + - Jeroen + . Forced to new DISPI (0xb0c1) interface (requires latest bochs vbe code) + . Added multibuffering support + . Added new DISPI interface for: virt width, height, x offset, y offset + . Added LFB modes (to be used with the vbe-lfb patch in bochs) + see VBE_HAVE_LFB in vbe.c (currently default enabled) + . updated TODO & docs for changes after bochs 1.4 + +vgabios-0.3a : Mar 10 2002 + - Christophe + . Fixed bug in function ah=13 + - Jeroen + . updated vbebios implementation to new api + . added vbe_display_api documentation + . added 640x400x8, 640x480x8, 800x600x8, 1024x768 + (>640x480 needs a special bochs patch atm) + . added 320x200x8 vbe support (uses the standard 320x200x8 vga mode to + display, this allows for testing & having something on screen as well, + at least until bochs host side display is up & running) + . adding lfbprof (vbe) testprogram (+some small fixes to it) + . merging with vbebios 0.2 + +vgabios-0.2b : Nov 19 2001 + - Christophe + . Fixed bug in function ah=13 + +vgabios-0.2a : Nov 09 2001 + - Christophe + . Included bugfix from techt@pikeonline.net about grayscale summing + . Added the "IBM" string at org 0x1e as Bart Oldeman suggested + . Fixed DS and ES that where inverted in the int10 parameters list! + . The following have been implemented : + - function ax=1a00, ax=1a01, ah=1b + - function ax=1130 + . Added debug messages for unimplemented/unknown functions + Must be compiled with DEBUG defined. The output is trapped + by the unknown-ioport driver of plex/bochs (port 0xfff0 is used) + +vgabios-0.1a : May 8 2001 + - Christophe + . First release. The work has been focused only on text mode. + . The following have been implemented : + - inits + - int 10 handler + - functions ah=00, ah=01, ah=02, ah=03, ah=05, ah=06, ah=07, ah=08 + ah=09, ah=0a, ah=0e, ah=0f, ax=1000, ax=1001, ax=1002, ax=1003 + ax=1007, ax=1008, ax=1009, ax=1010, ax=1012, ax=1013, ax=1015 + ax=1017, ax=1018, ax=1019, ax=101a, ax=101b, ah=12 bl=10, + ah=12 bl=30, ah=12 bl=31, ah=12 bl=32, ah=12 bl=33, ah=12 bl=34 + ah=13 diff --git a/kvm/vgabios/TODO b/kvm/vgabios/TODO new file mode 100644 index 000000000..b08ee4b77 --- /dev/null +++ b/kvm/vgabios/TODO @@ -0,0 +1,26 @@ +Short term : +------------ + +General + - Fix init mode (ah=00). Should use more BIOS variables + - Add new functionalities and modify static functionality table + - Performance : 16 bits IO + +v0.7 + - Implement the remaining functions (don't know if all are needed): + - chargen ax=1120, ax=1121, ax=1122, ax=1123, ax=1124 + - display switch interface ah=12 bl=35 + - video refresh control ah=12 bl=36 + - Graphic modes + +v1.0 + - Bugfixes + + +================================================================================================= +VBE: +---- +Long term: +- have plex86 host side display interface +- have text io functions in vbe mode + diff --git a/kvm/vgabios/biossums.c b/kvm/vgabios/biossums.c new file mode 100644 index 000000000..d5816f420 --- /dev/null +++ b/kvm/vgabios/biossums.c @@ -0,0 +1,282 @@ +/* biossums.c --- written by Eike W. for the Bochs BIOS */ +/* adapted for the LGPL'd VGABIOS by vruppert */ + +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +typedef unsigned char byte; + +void check( int value, char* message ); + +#define MAX_BIOS_DATA 0x10000 + +long chksum_bios_get_offset( byte* data, long offset ); +byte chksum_bios_calc_value( byte* data, long offset ); +byte chksum_bios_get_value( byte* data, long offset ); +void chksum_bios_set_value( byte* data, long offset, byte value ); + +#define PMID_LEN 20 +#define PMID_CHKSUM 19 + +long chksum_pmid_get_offset( byte* data, long offset ); +byte chksum_pmid_calc_value( byte* data, long offset ); +byte chksum_pmid_get_value( byte* data, long offset ); +void chksum_pmid_set_value( byte* data, long offset, byte value ); + +#define PCIR_LEN 24 + +long chksum_pcir_get_offset( byte* data, long offset ); + + +byte bios_data[MAX_BIOS_DATA]; +long bios_len; + + +int main(int argc, char* argv[]) +{ + FILE* stream; + long offset, tmp_offset, pcir_offset; + byte bios_len_byte, cur_val = 0, new_val = 0; + int hits, modified; + + if (argc != 2) { + printf( "Error. Need a file-name as an argument.\n" ); + exit( EXIT_FAILURE ); + } + + if ((stream = fopen(argv[1], "rb")) == NULL) { + printf("Error opening %s for reading.\n", argv[1]); + exit(EXIT_FAILURE); + } + memset(bios_data, 0, MAX_BIOS_DATA); + bios_len = fread(bios_data, 1, MAX_BIOS_DATA, stream); + if (bios_len > MAX_BIOS_DATA) { + printf("Error reading max. 65536 Bytes from %s.\n", argv[1]); + fclose(stream); + exit(EXIT_FAILURE); + } + fclose(stream); + modified = 0; + if (bios_len < 0x8000) { + bios_len = 0x8000; + modified = 1; + } else if ((bios_len & 0x1FF) != 0) { + bios_len = (bios_len + 0x200) & ~0x1FF; + modified = 1; + } + bios_len_byte = (byte)(bios_len / 512); + if (bios_len_byte != bios_data[2]) { + if (modified == 0) { + bios_len += 0x200; + } + bios_data[2] = (byte)(bios_len / 512); + modified = 1; + } + + hits = 0; + offset = 0L; + while( (tmp_offset = chksum_pmid_get_offset( bios_data, offset )) != -1L ) { + offset = tmp_offset; + cur_val = chksum_pmid_get_value( bios_data, offset ); + new_val = chksum_pmid_calc_value( bios_data, offset ); + printf( "\nPMID entry at: 0x%4lX\n", offset ); + printf( "Current checksum: 0x%02X\n", cur_val ); + printf( "Calculated checksum: 0x%02X ", new_val ); + hits++; + } + if ((hits == 1) && (cur_val != new_val)) { + printf("Setting checksum."); + chksum_pmid_set_value( bios_data, offset, new_val ); + if (modified == 0) { + bios_len += 0x200; + bios_data[2]++; + } + modified = 1; + } + if (hits >= 2) { + printf( "Multiple PMID entries! No checksum set." ); + } + if (hits) { + printf("\n"); + } + + offset = 0L; + pcir_offset = chksum_pcir_get_offset( bios_data, offset ); + if (pcir_offset != -1L) { + if (bios_data[pcir_offset + 16] != bios_data[2]) { + bios_data[pcir_offset + 16] = bios_data[2]; + if (modified == 0) { + bios_len += 0x200; + bios_data[2]++; + bios_data[pcir_offset + 16]++; + } + modified = 1; + } + } + + offset = 0L; + do { + offset = chksum_bios_get_offset(bios_data, offset); + cur_val = chksum_bios_get_value(bios_data, offset); + new_val = chksum_bios_calc_value(bios_data, offset); + if ((cur_val != new_val) && (modified == 0)) { + bios_len += 0x200; + bios_data[2]++; + if (pcir_offset != -1L) { + bios_data[pcir_offset + 16]++; + } + modified = 1; + } else { + printf("\nBios checksum at: 0x%4lX\n", offset); + printf("Current checksum: 0x%02X\n", cur_val); + printf("Calculated checksum: 0x%02X ", new_val); + if (cur_val != new_val) { + printf("Setting checksum."); + chksum_bios_set_value(bios_data, offset, new_val); + cur_val = new_val; + modified = 1; + } + printf( "\n" ); + } + } while (cur_val != new_val); + + if (modified == 1) { + if ((stream = fopen( argv[1], "wb")) == NULL) { + printf("Error opening %s for writing.\n", argv[1]); + exit(EXIT_FAILURE); + } + if (fwrite(bios_data, 1, bios_len, stream) < bios_len) { + printf("Error writing %d KBytes to %s.\n", bios_len / 1024, argv[1]); + fclose(stream); + exit(EXIT_FAILURE); + } + fclose(stream); + } + + return (EXIT_SUCCESS); +} + + +void check( int okay, char* message ) { + + if( !okay ) { + printf( "\n\nError. %s.\n", message ); + exit( EXIT_FAILURE ); + } +} + + +long chksum_bios_get_offset( byte* data, long offset ) { + + return (bios_len - 1); +} + + +byte chksum_bios_calc_value( byte* data, long offset ) { + + int i; + byte sum; + + sum = 0; + for( i = 0; i < offset; i++ ) { + sum = sum + *( data + i ); + } + sum = -sum; /* iso ensures -s + s == 0 on unsigned types */ + return( sum ); +} + + +byte chksum_bios_get_value( byte* data, long offset ) { + + return( *( data + offset ) ); +} + + +void chksum_bios_set_value( byte* data, long offset, byte value ) { + + *( data + offset ) = value; +} + + +byte chksum_pmid_calc_value( byte* data, long offset ) { + + int i; + int len; + byte sum; + + len = PMID_LEN; + check((offset + len) <= (bios_len - 1), "PMID entry length out of bounds" ); + sum = 0; + for( i = 0; i < len; i++ ) { + if( i != PMID_CHKSUM ) { + sum = sum + *( data + offset + i ); + } + } + sum = -sum; + return( sum ); +} + + +long chksum_pmid_get_offset( byte* data, long offset ) { + + long result = -1L; + + while ((offset + PMID_LEN) < (bios_len - 1)) { + offset = offset + 1; + if( *( data + offset + 0 ) == 'P' && \ + *( data + offset + 1 ) == 'M' && \ + *( data + offset + 2 ) == 'I' && \ + *( data + offset + 3 ) == 'D' ) { + result = offset; + break; + } + } + return( result ); +} + + +byte chksum_pmid_get_value( byte* data, long offset ) { + + check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" ); + return( *( data + offset + PMID_CHKSUM ) ); +} + + +void chksum_pmid_set_value( byte* data, long offset, byte value ) { + + check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" ); + *( data + offset + PMID_CHKSUM ) = value; +} + + +long chksum_pcir_get_offset( byte* data, long offset ) { + + long result = -1L; + + while ((offset + PCIR_LEN) < (bios_len - 1)) { + offset = offset + 1; + if( *( data + offset + 0 ) == 'P' && \ + *( data + offset + 1 ) == 'C' && \ + *( data + offset + 2 ) == 'I' && \ + *( data + offset + 3 ) == 'R' ) { + result = offset; + break; + } + } + return( result ); +} diff --git a/kvm/vgabios/clext.c b/kvm/vgabios/clext.c new file mode 100644 index 000000000..c7a2ad0ef --- /dev/null +++ b/kvm/vgabios/clext.c @@ -0,0 +1,1688 @@ +// +// QEMU Cirrus CLGD 54xx VGABIOS Extension. +// +// Copyright (c) 2004 Makoto Suzuki (suzu) +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +//#define CIRRUS_VESA3_PMINFO +#ifdef VBE +#undef CIRRUS_VESA3_PMINFO +#endif + +#define PM_BIOSMEM_CURRENT_MODE 0x449 +#define PM_BIOSMEM_CRTC_ADDRESS 0x463 +#define PM_BIOSMEM_VBE_MODE 0x4BA + +typedef struct +{ + /* + 0 */ + unsigned short mode; + unsigned short width; + unsigned short height; + unsigned short depth; + /* + 8 */ + unsigned short hidden_dac; /* 0x3c6 */ + unsigned short *seq; /* 0x3c4 */ + unsigned short *graph; /* 0x3ce */ + unsigned short *crtc; /* 0x3d4 */ + /* +16 */ + unsigned char bitsperpixel; + unsigned char vesacolortype; + unsigned char vesaredmask; + unsigned char vesaredpos; + unsigned char vesagreenmask; + unsigned char vesagreenpos; + unsigned char vesabluemask; + unsigned char vesabluepos; + /* +24 */ + unsigned char vesareservedmask; + unsigned char vesareservedpos; +} cirrus_mode_t; +#define CIRRUS_MODE_SIZE 26 + + +/* For VESA BIOS 3.0 */ +#define CIRRUS_PM16INFO_SIZE 20 + +/* VGA */ +unsigned short cseq_vga[] = {0x0007,0xffff}; +unsigned short cgraph_vga[] = {0x0009,0x000a,0x000b,0xffff}; +unsigned short ccrtc_vga[] = {0x001a,0x001b,0x001d,0xffff}; + +/* extensions */ +unsigned short cgraph_svgacolor[] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x4005,0x0506,0x0f07,0xff08, +0x0009,0x000a,0x000b, +0xffff +}; +/* 640x480x8 */ +unsigned short cseq_640x480x8[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, +0x580b,0x580c,0x580d,0x580e, +0x0412,0x0013,0x2017, +0x331b,0x331c,0x331d,0x331e, +0xffff +}; +unsigned short ccrtc_640x480x8[] = { +0x2c11, +0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, +0x4009,0x000c,0x000d, +0xea10,0xdf12,0x5013,0x4014,0xdf15,0x0b16,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; +/* 640x480x16 */ +unsigned short cseq_640x480x16[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, +0x580b,0x580c,0x580d,0x580e, +0x0412,0x0013,0x2017, +0x331b,0x331c,0x331d,0x331e, +0xffff +}; +unsigned short ccrtc_640x480x16[] = { +0x2c11, +0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, +0x4009,0x000c,0x000d, +0xea10,0xdf12,0xa013,0x4014,0xdf15,0x0b16,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; +/* 640x480x24 */ +unsigned short cseq_640x480x24[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, +0x580b,0x580c,0x580d,0x580e, +0x0412,0x0013,0x2017, +0x331b,0x331c,0x331d,0x331e, +0xffff +}; +unsigned short ccrtc_640x480x24[] = { +0x2c11, +0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, +0x4009,0x000c,0x000d, +0xea10,0xdf12,0x0013,0x4014,0xdf15,0x0b16,0xc317,0xff18, +0x001a,0x321b,0x001d, +0xffff +}; +/* 800x600x8 */ +unsigned short cseq_800x600x8[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, +0x230b,0x230c,0x230d,0x230e, +0x0412,0x0013,0x2017, +0x141b,0x141c,0x141d,0x141e, +0xffff +}; +unsigned short ccrtc_800x600x8[] = { +0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, +0x6009,0x000c,0x000d, +0x7d10,0x5712,0x6413,0x4014,0x5715,0x9816,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; +/* 800x600x16 */ +unsigned short cseq_800x600x16[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, +0x230b,0x230c,0x230d,0x230e, +0x0412,0x0013,0x2017, +0x141b,0x141c,0x141d,0x141e, +0xffff +}; +unsigned short ccrtc_800x600x16[] = { +0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, +0x6009,0x000c,0x000d, +0x7d10,0x5712,0xc813,0x4014,0x5715,0x9816,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; +/* 800x600x24 */ +unsigned short cseq_800x600x24[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, +0x230b,0x230c,0x230d,0x230e, +0x0412,0x0013,0x2017, +0x141b,0x141c,0x141d,0x141e, +0xffff +}; +unsigned short ccrtc_800x600x24[] = { +0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, +0x6009,0x000c,0x000d, +0x7d10,0x5712,0x2c13,0x4014,0x5715,0x9816,0xc317,0xff18, +0x001a,0x321b,0x001d, +0xffff +}; +/* 1024x768x8 */ +unsigned short cseq_1024x768x8[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, +0x760b,0x760c,0x760d,0x760e, +0x0412,0x0013,0x2017, +0x341b,0x341c,0x341d,0x341e, +0xffff +}; +unsigned short ccrtc_1024x768x8[] = { +0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, +0x6009,0x000c,0x000d, +0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; +/* 1024x768x16 */ +unsigned short cseq_1024x768x16[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, +0x760b,0x760c,0x760d,0x760e, +0x0412,0x0013,0x2017, +0x341b,0x341c,0x341d,0x341e, +0xffff +}; +unsigned short ccrtc_1024x768x16[] = { +0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, +0x6009,0x000c,0x000d, +0x0310,0xff12,0x0013,0x4014,0xff15,0x2416,0xc317,0xff18, +0x001a,0x321b,0x001d, +0xffff +}; +/* 1024x768x24 */ +unsigned short cseq_1024x768x24[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, +0x760b,0x760c,0x760d,0x760e, +0x0412,0x0013,0x2017, +0x341b,0x341c,0x341d,0x341e, +0xffff +}; +unsigned short ccrtc_1024x768x24[] = { +0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, +0x6009,0x000c,0x000d, +0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18, +0x001a,0x321b,0x001d, +0xffff +}; +/* 1280x1024x8 */ +unsigned short cseq_1280x1024x8[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, +0x760b,0x760c,0x760d,0x760e, +0x0412,0x0013,0x2017, +0x341b,0x341c,0x341d,0x341e, +0xffff +}; +unsigned short ccrtc_1280x1024x8[] = { +0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, +0x6009,0x000c,0x000d, +0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; +/* 1280x1024x16 */ +unsigned short cseq_1280x1024x16[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, +0x760b,0x760c,0x760d,0x760e, +0x0412,0x0013,0x2017, +0x341b,0x341c,0x341d,0x341e, +0xffff +}; +unsigned short ccrtc_1280x1024x16[] = { +0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, +0x6009,0x000c,0x000d, +0x0310,0xff12,0x4013,0x4014,0xff15,0x2416,0xc317,0xff18, +0x001a,0x321b,0x001d, +0xffff +}; + +/* 1600x1200x8 */ +unsigned short cseq_1600x1200x8[] = { +0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, +0x760b,0x760c,0x760d,0x760e, +0x0412,0x0013,0x2017, +0x341b,0x341c,0x341d,0x341e, +0xffff +}; +unsigned short ccrtc_1600x1200x8[] = { +0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, +0x6009,0x000c,0x000d, +0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18, +0x001a,0x221b,0x001d, +0xffff +}; + +cirrus_mode_t cirrus_modes[] = +{ + {0x5f,640,480,8,0x00, + cseq_640x480x8,cgraph_svgacolor,ccrtc_640x480x8,8, + 4,0,0,0,0,0,0,0,0}, + {0x64,640,480,16,0xe1, + cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16, + 6,5,11,6,5,5,0,0,0}, + {0x66,640,480,15,0xf0, + cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16, + 6,5,10,5,5,5,0,1,15}, + {0x71,640,480,24,0xe5, + cseq_640x480x24,cgraph_svgacolor,ccrtc_640x480x24,24, + 6,8,16,8,8,8,0,0,0}, + + {0x5c,800,600,8,0x00, + cseq_800x600x8,cgraph_svgacolor,ccrtc_800x600x8,8, + 4,0,0,0,0,0,0,0,0}, + {0x65,800,600,16,0xe1, + cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16, + 6,5,11,6,5,5,0,0,0}, + {0x67,800,600,15,0xf0, + cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16, + 6,5,10,5,5,5,0,1,15}, + + {0x60,1024,768,8,0x00, + cseq_1024x768x8,cgraph_svgacolor,ccrtc_1024x768x8,8, + 4,0,0,0,0,0,0,0,0}, + {0x74,1024,768,16,0xe1, + cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16, + 6,5,11,6,5,5,0,0,0}, + {0x68,1024,768,15,0xf0, + cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16, + 6,5,10,5,5,5,0,1,15}, + + {0x78,800,600,24,0xe5, + cseq_800x600x24,cgraph_svgacolor,ccrtc_800x600x24,24, + 6,8,16,8,8,8,0,0,0}, + {0x79,1024,768,24,0xe5, + cseq_1024x768x24,cgraph_svgacolor,ccrtc_1024x768x24,24, + 6,8,16,8,8,8,0,0,0}, + + {0x6d,1280,1024,8,0x00, + cseq_1280x1024x8,cgraph_svgacolor,ccrtc_1280x1024x8,8, + 4,0,0,0,0,0,0,0,0}, + {0x69,1280,1024,15,0xf0, + cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16, + 6,5,10,5,5,5,0,1,15}, + {0x75,1280,1024,16,0xe1, + cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16, + 6,5,11,6,5,5,0,0,0}, + + {0x7b,1600,1200,8,0x00, + cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8,8, + 4,0,0,0,0,0,0,0,0}, + + {0xfe,0,0,0,0,cseq_vga,cgraph_vga,ccrtc_vga,0, + 0xff,0,0,0,0,0,0,0,0}, + {0xff,0,0,0,0,0,0,0,0, + 0xff,0,0,0,0,0,0,0,0}, +}; + +unsigned char cirrus_id_table[] = { + // 5430 + 0xA0, 0x32, + // 5446 + 0xB8, 0x39, + + 0xff, 0xff +}; + + +unsigned short cirrus_vesa_modelist[] = { +// 640x480x8 + 0x101, 0x5f, +// 640x480x15 + 0x110, 0x66, +// 640x480x16 + 0x111, 0x64, +// 640x480x24 + 0x112, 0x71, +// 800x600x8 + 0x103, 0x5c, +// 800x600x15 + 0x113, 0x67, +// 800x600x16 + 0x114, 0x65, +// 800x600x24 + 0x115, 0x78, +// 1024x768x8 + 0x105, 0x60, +// 1024x768x15 + 0x116, 0x68, +// 1024x768x16 + 0x117, 0x74, +// 1024x768x24 + 0x118, 0x79, +// 1280x1024x8 + 0x107, 0x6d, +// 1280x1024x15 + 0x119, 0x69, +// 1280x1024x16 + 0x11a, 0x75, +// invalid + 0xffff,0xffff +}; + + +ASM_START + +cirrus_installed: +.ascii "cirrus-compatible VGA is detected" +.byte 0x0d,0x0a +.byte 0x0d,0x0a,0x00 + +cirrus_not_installed: +.ascii "cirrus-compatible VGA is not detected" +.byte 0x0d,0x0a +.byte 0x0d,0x0a,0x00 + +cirrus_vesa_vendorname: +cirrus_vesa_productname: +cirrus_vesa_oemname: +.ascii "VGABIOS Cirrus extension" +.byte 0 +cirrus_vesa_productrevision: +.ascii "1.0" +.byte 0 + +cirrus_init: + call cirrus_check + jnz no_cirrus + SET_INT_VECTOR(0x10, #0xC000, #cirrus_int10_handler) + mov al, #0x0f ; memory setup + mov dx, #0x3C4 + out dx, al + inc dx + in al, dx + and al, #0x18 + mov ah, al + mov al, #0x0a + dec dx + out dx, ax + mov ax, #0x0007 ; set vga mode + out dx, ax + mov ax, #0x0431 ; reset bitblt + mov dx, #0x3CE + out dx, ax + mov ax, #0x0031 + out dx, ax +no_cirrus: + ret + +cirrus_display_info: + push ds + push si + push cs + pop ds + call cirrus_check + mov si, #cirrus_not_installed + jnz cirrus_msgnotinstalled + mov si, #cirrus_installed + +cirrus_msgnotinstalled: + call _display_string + pop si + pop ds + ret + +cirrus_check: + push ax + push dx + mov ax, #0x9206 + mov dx, #0x3C4 + out dx, ax + inc dx + in al, dx + cmp al, #0x12 + pop dx + pop ax + ret + + +cirrus_int10_handler: + pushf + push bp + cmp ah, #0x00 ;; set video mode + jz cirrus_set_video_mode + cmp ah, #0x12 ;; cirrus extension + jz cirrus_extbios + cmp ah, #0x4F ;; VESA extension + jz cirrus_vesa + +cirrus_unhandled: + pop bp + popf + jmp vgabios_int10_handler + +cirrus_return: +#ifdef CIRRUS_DEBUG + call cirrus_debug_dump +#endif + pop bp + popf + iret + +cirrus_set_video_mode: +#ifdef CIRRUS_DEBUG + call cirrus_debug_dump +#endif + push si + push ax + push bx + push ds +#ifdef CIRRUS_VESA3_PMINFO + db 0x2e ;; cs: + mov si, [cirrus_vesa_sel0000_data] +#else + xor si, si +#endif + mov ds, si + xor bx, bx + mov [PM_BIOSMEM_VBE_MODE], bx + pop ds + pop bx + call cirrus_get_modeentry + jnc cirrus_set_video_mode_extended + mov al, #0xfe + call cirrus_get_modeentry_nomask + call cirrus_switch_mode + pop ax + pop si + jmp cirrus_unhandled + +cirrus_extbios: +#ifdef CIRRUS_DEBUG + call cirrus_debug_dump +#endif + cmp bl, #0x80 + jb cirrus_unhandled + cmp bl, #0xAF + ja cirrus_unhandled + push bx + and bx, #0x7F + shl bx, 1 + db 0x2e ;; cs: + mov bp, cirrus_extbios_handlers[bx] + pop bx + push #cirrus_return + push bp + ret + +cirrus_vesa: +#ifdef CIRRUS_DEBUG + call cirrus_debug_dump +#endif + cmp al, #0x10 + ja cirrus_vesa_not_handled + push bx + xor bx, bx + mov bl, al + shl bx, 1 + db 0x2e ;; cs: + mov bp, cirrus_vesa_handlers[bx] + pop bx + push #cirrus_return + push bp + ret + +cirrus_vesa_not_handled: + mov ax, #0x014F ;; not implemented + jmp cirrus_return + +#ifdef CIRRUS_DEBUG +cirrus_debug_dump: + push es + push ds + pusha + push cs + pop ds + call _cirrus_debugmsg + popa + pop ds + pop es + ret +#endif + +cirrus_set_video_mode_extended: + call cirrus_switch_mode + pop ax ;; mode + test al, #0x80 + jnz cirrus_set_video_mode_extended_1 + push ax + mov ax, #0xffff ; set to 0xff to keep win 2K happy + call cirrus_clear_vram + pop ax +cirrus_set_video_mode_extended_1: + and al, #0x7f + + push ds +#ifdef CIRRUS_VESA3_PMINFO + db 0x2e ;; cs: + mov si, [cirrus_vesa_sel0000_data] +#else + xor si, si +#endif + mov ds, si + mov [PM_BIOSMEM_CURRENT_MODE], al + pop ds + + mov al, #0x20 + + pop si + jmp cirrus_return + +cirrus_vesa_pmbios_init: + retf +cirrus_vesa_pmbios_entry: + pushf + push bp + cmp ah, #0x4F + jnz cirrus_vesa_pmbios_unimplemented + cmp al, #0x0F + ja cirrus_vesa_pmbios_unimplemented + push bx + xor bx, bx + mov bl, al + shl bx, 1 + db 0x2e ;; cs: + mov bp, cirrus_vesa_handlers[bx] + pop bx + push #cirrus_vesa_pmbios_return + push bp + ret +cirrus_vesa_pmbios_unimplemented: + mov ax, #0x014F +cirrus_vesa_pmbios_return: + pop bp + popf + retf + +; in si:mode table +cirrus_switch_mode: + push ds + push bx + push dx + push cs + pop ds + + mov bx, [si+10] ;; seq + mov dx, #0x3c4 + mov ax, #0x1206 + out dx, ax ;; Unlock cirrus special + call cirrus_switch_mode_setregs + + mov bx, [si+12] ;; graph + mov dx, #0x3ce + call cirrus_switch_mode_setregs + + mov bx, [si+14] ;; crtc + call cirrus_get_crtc + call cirrus_switch_mode_setregs + + mov dx, #0x3c6 + mov al, #0x00 + out dx, al + in al, dx + in al, dx + in al, dx + in al, dx + mov al, [si+8] ;; hidden dac + out dx, al + mov al, #0xff + out dx, al + + mov al, #0x00 + mov bl, [si+17] ;; memory model + or bl, bl + jz is_text_mode + mov al, #0x01 + cmp bl, #0x03 + jnz is_text_mode + or al, #0x40 +is_text_mode: + mov bl, #0x10 + call biosfn_get_single_palette_reg + and bh, #0xfe + or bh, al + call biosfn_set_single_palette_reg + + pop dx + pop bx + pop ds + ret + +cirrus_enable_16k_granularity: + push ax + push dx + mov dx, #0x3ce + mov al, #0x0b + out dx, al + inc dx + in al, dx + or al, #0x20 ;; enable 16k + out dx, al + pop dx + pop ax + ret + +cirrus_switch_mode_setregs: +csms_1: + mov ax, [bx] + cmp ax, #0xffff + jz csms_2 + out dx, ax + add bx, #0x2 + jmp csms_1 +csms_2: + ret + +cirrus_extbios_80h: + push dx + call cirrus_get_crtc + mov al, #0x27 + out dx, al + inc dx + in al, dx + mov bx, #_cirrus_id_table +c80h_1: + db 0x2e ;; cs: + mov ah, [bx] + cmp ah, al + jz c80h_2 + cmp ah, #0xff + jz c80h_2 + inc bx + inc bx + jmp c80h_1 +c80h_2: + db 0x2e ;; cs: + mov al, 0x1[bx] + pop dx + mov ah, #0x00 + xor bx, bx + ret + +cirrus_extbios_81h: + mov ax, #0x100 ;; XXX + ret +cirrus_extbios_82h: + push dx + call cirrus_get_crtc + xor ax, ax + mov al, #0x27 + out dx, al + inc dx + in al, dx + and al, #0x03 + mov ah, #0xAF + pop dx + ret + +cirrus_extbios_85h: + push cx + push dx + mov dx, #0x3C4 + mov al, #0x0f ;; get DRAM band width + out dx, al + inc dx + in al, dx + ;; al = 4 << bandwidth + mov cl, al + shr cl, #0x03 + and cl, #0x03 + cmp cl, #0x03 + je c85h2 + mov al, #0x04 + shl al, cl + jmp c85h3 +c85h2: +;; 4MB or 2MB + and al, #0x80 + mov al, #0x20 ;; 2 MB + je c85h3 + mov al, #0x40 ;; 4 MB +c85h3: + pop dx + pop cx + ret + +cirrus_extbios_9Ah: + mov ax, #0x4060 + mov cx, #0x1132 + ret + +cirrus_extbios_A0h: + call cirrus_get_modeentry + mov ah, #0x01 + sbb ah, #0x00 + mov bx, cirrus_extbios_A0h_callback + mov si, #0xffff + mov di, bx + mov ds, bx + mov es, bx + ret + +cirrus_extbios_A0h_callback: + ;; fatal: not implemented yet + cli + hlt + retf + +cirrus_extbios_A1h: + mov bx, #0x0E00 ;; IBM 8512/8513, color + ret + +cirrus_extbios_A2h: + mov al, #0x07 ;; HSync 31.5 - 64.0 kHz + ret + +cirrus_extbios_AEh: + mov al, #0x01 ;; High Refresh 75Hz + ret + +cirrus_extbios_unimplemented: + ret + +cirrus_vesa_00h: + push ds + push si + mov bp, di + push es + pop ds + cld + mov ax, [di] + cmp ax, #0x4256 ;; VB + jnz cv00_1 + mov ax, [di+2] + cmp ax, #0x3245 ;; E2 + jnz cv00_1 + ;; VBE2 + lea di, 0x14[bp] + mov ax, #0x0100 ;; soft ver. + stosw + mov ax, # cirrus_vesa_vendorname + stosw + mov ax, cs + stosw + mov ax, # cirrus_vesa_productname + stosw + mov ax, cs + stosw + mov ax, # cirrus_vesa_productrevision + stosw + mov ax, cs + stosw +cv00_1: + mov di, bp + mov ax, #0x4556 ;; VE + stosw + mov ax, #0x4153 ;; SA + stosw + mov ax, #0x0200 ;; v2.00 + stosw + mov ax, # cirrus_vesa_oemname + stosw + mov ax, cs + stosw + xor ax, ax ;; caps + stosw + stosw + lea ax, 0x40[bp] + stosw + mov ax, es + stosw + call cirrus_extbios_85h ;; vram in 64k + mov ah, #0x00 + stosw + + push cs + pop ds + lea di, 0x40[bp] + mov si, #_cirrus_vesa_modelist +cv00_2: + lodsw + stosw + add si, #2 + cmp ax, #0xffff + jnz cv00_2 + + mov ax, #0x004F + mov di, bp + pop si + pop ds + ret + +cirrus_vesa_01h: + mov ax, cx + and ax, #0x3fff + call cirrus_vesamode_to_mode + cmp ax, #0xffff + jnz cirrus_vesa_01h_1 + jmp cirrus_vesa_unimplemented +cirrus_vesa_01h_1: + push ds + push si + push cx + push dx + push bx + mov bp, di + cld + push cs + pop ds + call cirrus_get_modeentry_nomask + + push di + xor ax, ax + mov cx, #0x80 + rep + stosw ;; clear buffer + pop di + + mov ax, #0x003b ;; mode + stosw + mov ax, #0x0007 ;; attr + stosw + mov ax, #0x0010 ;; granularity =16K + stosw + mov ax, #0x0040 ;; size =64K + stosw + mov ax, #0xA000 ;; segment A + stosw + xor ax, ax ;; no segment B + stosw + mov ax, #cirrus_vesa_05h_farentry + stosw + mov ax, cs + stosw + call cirrus_get_line_offset_entry + stosw ;; bytes per scan line + mov ax, [si+2] ;; width + stosw + mov ax, [si+4] ;; height + stosw + mov ax, #0x08 + stosb + mov ax, #0x10 + stosb + mov al, #1 ;; count of planes + stosb + mov al, [si+6] ;; bpp + stosb + mov al, #0x1 ;; XXX number of banks + stosb + mov al, [si+17] + stosb ;; memory model + mov al, #0x0 ;; XXX size of bank in K + stosb + call cirrus_get_line_offset_entry + mov bx, [si+4] + mul bx ;; dx:ax=vramdisp + or ax, ax + jz cirrus_vesa_01h_3 + inc dx +cirrus_vesa_01h_3: + call cirrus_extbios_85h ;; al=vram in 64k + mov ah, #0x00 + mov cx, dx + xor dx, dx + div cx + dec ax + stosb ;; number of image pages = vramtotal/vramdisp-1 + mov al, #0x00 + stosb + + ;; v1.2+ stuffs + push si + add si, #18 + movsw + movsw + movsw + movsw + pop si + + mov ah, [si+16] + mov al, #0x0 + sub ah, #9 + rcl al, #1 ; bit 0=palette flag + stosb ;; direct screen mode info + + ;; v2.0+ stuffs + ;; 32-bit LFB address + xor ax, ax + stosw + call cirrus_get_lfb_addr + stosw + or ax, ax + jz cirrus_vesa_01h_4 + push di + mov di, bp + db 0x26 ;; es: + mov ax, [di] + or ax, #0x0080 ;; mode bit 7:LFB + stosw + pop di +cirrus_vesa_01h_4: + + xor ax, ax + stosw ; reserved + stosw ; reserved + stosw ; reserved + + mov ax, #0x004F + mov di, bp + pop bx + pop dx + pop cx + pop si + pop ds + + test cx, #0x4000 ;; LFB flag + jz cirrus_vesa_01h_5 + push cx + db 0x26 ;; es: + mov cx, [di] + cmp cx, #0x0080 ;; is LFB supported? + jnz cirrus_vesa_01h_6 + mov ax, #0x014F ;; error - no LFB +cirrus_vesa_01h_6: + pop cx +cirrus_vesa_01h_5: + ret + +cirrus_vesa_02h: + ;; XXX support CRTC registers + test bx, #0x3e00 + jnz cirrus_vesa_02h_2 ;; unknown flags + mov ax, bx + and ax, #0x1ff ;; bit 8-0 mode + cmp ax, #0x100 ;; legacy VGA mode + jb cirrus_vesa_02h_legacy + call cirrus_vesamode_to_mode + cmp ax, #0xffff + jnz cirrus_vesa_02h_1 +cirrus_vesa_02h_2: + jmp cirrus_vesa_unimplemented +cirrus_vesa_02h_legacy: +#ifdef CIRRUS_VESA3_PMINFO + db 0x2e ;; cs: + cmp byte ptr [cirrus_vesa_is_protected_mode], #0 + jnz cirrus_vesa_02h_2 +#endif // CIRRUS_VESA3_PMINFO + int #0x10 + mov ax, #0x004F + ret +cirrus_vesa_02h_1: + push si + push ax + call cirrus_get_modeentry_nomask + call cirrus_switch_mode + test bx, #0x4000 ;; LFB + jnz cirrus_vesa_02h_3 + call cirrus_enable_16k_granularity +cirrus_vesa_02h_3: + test bx, #0x8000 ;; no clear + jnz cirrus_vesa_02h_4 + push ax + xor ax,ax + call cirrus_clear_vram + pop ax +cirrus_vesa_02h_4: + pop ax + push ds +#ifdef CIRRUS_VESA3_PMINFO + db 0x2e ;; cs: + mov si, [cirrus_vesa_sel0000_data] +#else + xor si, si +#endif + mov ds, si + mov [PM_BIOSMEM_CURRENT_MODE], al + mov [PM_BIOSMEM_VBE_MODE], bx + pop ds + pop si + mov ax, #0x004F + ret + +cirrus_vesa_03h: + push ds +#ifdef CIRRUS_VESA3_PMINFO + db 0x2e ;; cs: + mov ax, [cirrus_vesa_sel0000_data] +#else + xor ax, ax +#endif + mov ds, ax + mov bx, # PM_BIOSMEM_VBE_MODE + mov ax, [bx] + mov bx, ax + test bx, bx + jnz cirrus_vesa_03h_1 + mov bx, # PM_BIOSMEM_CURRENT_MODE + mov al, [bx] + mov bl, al + xor bh, bh +cirrus_vesa_03h_1: + mov ax, #0x004f + pop ds + ret + +cirrus_vesa_05h_farentry: + call cirrus_vesa_05h + retf + +cirrus_vesa_05h: + cmp bl, #0x01 + ja cirrus_vesa_05h_1 + cmp bh, #0x00 + jz cirrus_vesa_05h_setmempage + cmp bh, #0x01 + jz cirrus_vesa_05h_getmempage +cirrus_vesa_05h_1: + jmp cirrus_vesa_unimplemented +cirrus_vesa_05h_setmempage: + or dh, dh ; address must be < 0x100 + jnz cirrus_vesa_05h_1 + push dx + mov al, bl ;; bl=bank number + add al, #0x09 + mov ah, dl ;; dx=window address in granularity + mov dx, #0x3ce + out dx, ax + pop dx + mov ax, #0x004F + ret +cirrus_vesa_05h_getmempage: + mov al, bl ;; bl=bank number + add al, #0x09 + mov dx, #0x3ce + out dx, al + inc dx + in al, dx + xor dx, dx + mov dl, al ;; dx=window address in granularity + mov ax, #0x004F + ret + +cirrus_vesa_06h: + mov ax, cx + cmp bl, #0x01 + je cirrus_vesa_06h_3 + cmp bl, #0x02 + je cirrus_vesa_06h_2 + jb cirrus_vesa_06h_1 + mov ax, #0x0100 + ret +cirrus_vesa_06h_1: + call cirrus_get_bpp_bytes + mov bl, al + xor bh, bh + mov ax, cx + mul bx +cirrus_vesa_06h_2: + call cirrus_set_line_offset +cirrus_vesa_06h_3: + call cirrus_get_bpp_bytes + mov bl, al + xor bh, bh + xor dx, dx + call cirrus_get_line_offset + push ax + div bx + mov cx, ax + pop bx + call cirrus_extbios_85h ;; al=vram in 64k + xor dx, dx + mov dl, al + xor ax, ax + div bx + mov dx, ax + mov ax, #0x004f + ret + +cirrus_vesa_07h: + cmp bl, #0x80 + je cirrus_vesa_07h_1 + cmp bl, #0x01 + je cirrus_vesa_07h_2 + jb cirrus_vesa_07h_1 + mov ax, #0x0100 + ret +cirrus_vesa_07h_1: + push dx + call cirrus_get_bpp_bytes + mov bl, al + xor bh, bh + mov ax, cx + mul bx + pop bx + push ax + call cirrus_get_line_offset + mul bx + pop bx + add ax, bx + jnc cirrus_vesa_07h_3 + inc dx +cirrus_vesa_07h_3: + push dx + and dx, #0x0003 + mov bx, #0x04 + div bx + pop dx + shr dx, #2 + call cirrus_set_start_addr + mov ax, #0x004f + ret +cirrus_vesa_07h_2: + call cirrus_get_start_addr + shl dx, #2 + push dx + mov bx, #0x04 + mul bx + pop bx + or dx, bx + push ax + call cirrus_get_line_offset + mov bx, ax + pop ax + div bx + push ax + push dx + call cirrus_get_bpp_bytes + mov bl, al + xor bh, bh + pop ax + xor dx, dx + div bx + mov cx, ax + pop dx + mov ax, #0x004f + ret + +cirrus_vesa_10h: + cmp bl, #0x00 + jne cirrus_vesa_10h_01 + mov bx, #0x0f30 + mov ax, #0x004f + ret +cirrus_vesa_10h_01: + cmp bl, #0x01 + jne cirrus_vesa_10h_02 + push dx + push ds + mov dx, #0x40 + mov ds, dx + mov [0xb9], bh + pop ds + pop dx + mov ax, #0x004f + ret +cirrus_vesa_10h_02: + cmp bl, #0x02 + jne cirrus_vesa_unimplemented + push dx + push ds + mov dx, #0x40 + mov ds, dx + mov bh, [0xb9] + pop ds + pop dx + mov ax, #0x004f + ret + +cirrus_vesa_unimplemented: + mov ax, #0x014F ;; not implemented + ret + + +;; in ax:vesamode, out ax:cirrusmode +cirrus_vesamode_to_mode: + push ds + push cx + push si + push cs + pop ds + mov cx, #0xffff + mov si, #_cirrus_vesa_modelist +cvtm_1: + cmp [si],ax + jz cvtm_2 + cmp [si],cx + jz cvtm_2 + add si, #4 + jmp cvtm_1 +cvtm_2: + mov ax,[si+2] + pop si + pop cx + pop ds + ret + + ; cirrus_get_crtc + ;; NOTE - may be called in protected mode +cirrus_get_crtc: + push ds + push ax + mov dx, #0x3cc + in al, dx + and al, #0x01 + shl al, #5 + mov dx, #0x3b4 + add dl, al + pop ax + pop ds + ret + +;; in - al:mode, out - cflag:result, si:table, ax:destroyed +cirrus_get_modeentry: + and al, #0x7f +cirrus_get_modeentry_nomask: + mov si, #_cirrus_modes +cgm_1: + db 0x2e ;; cs: + mov ah, [si] + cmp al, ah + jz cgm_2 + cmp ah, #0xff + jz cgm_4 + add si, # CIRRUS_MODE_SIZE + jmp cgm_1 +cgm_4: + xor si, si + stc ;; video mode is not supported + jmp cgm_3 +cgm_2: + clc ;; video mode is supported +cgm_3: + ret + + ; get LFB address + ; out - ax:LFB address (high 16 bit) + ;; NOTE - may be called in protected mode +cirrus_get_lfb_addr: + push cx + push dx + push eax + xor cx, cx + mov dl, #0x00 + call cirrus_pci_read + cmp ax, #0xffff + jz cirrus_get_lfb_addr_5 + cirrus_get_lfb_addr_3: + mov dl, #0x00 + call cirrus_pci_read + cmp ax, #0x1013 ;; cirrus + jz cirrus_get_lfb_addr_4 + add cx, #0x8 + cmp cx, #0x200 ;; search bus #0 and #1 + jb cirrus_get_lfb_addr_3 + cirrus_get_lfb_addr_5: + xor dx, dx ;; no LFB + jmp cirrus_get_lfb_addr_6 + cirrus_get_lfb_addr_4: + mov dl, #0x10 ;; I/O space #0 + call cirrus_pci_read + test ax, #0xfff1 + jnz cirrus_get_lfb_addr_5 + shr eax, #16 + mov dx, ax ;; LFB address + cirrus_get_lfb_addr_6: + pop eax + mov ax, dx + pop dx + pop cx + ret + +cirrus_pci_read: + mov eax, #0x00800000 + mov ax, cx + shl eax, #8 + mov al, dl + mov dx, #0xcf8 + out dx, eax + add dl, #4 + in eax, dx + ret + +;; out - al:bytes per pixel +cirrus_get_bpp_bytes: + push dx + mov dx, #0x03c4 + mov al, #0x07 + out dx, al + inc dx + in al, dx + and al, #0x0e + cmp al, #0x06 + jne cirrus_get_bpp_bytes_1 + and al, #0x02 +cirrus_get_bpp_bytes_1: + shr al, #1 + cmp al, #0x04 + je cirrus_get_bpp_bytes_2 + inc al +cirrus_get_bpp_bytes_2: + pop dx + ret + +;; in - ax: new line offset +cirrus_set_line_offset: + shr ax, #3 + push ax + call cirrus_get_crtc + mov al, #0x13 + out dx, al + inc dx + pop ax + out dx, al + dec dx + mov al, #0x1b + out dx, al + inc dx + shl ah, #4 + in al, dx + and al, #ef + or al, ah + out dx, al + ret + +;; out - ax: active line offset +cirrus_get_line_offset: + push dx + push bx + call cirrus_get_crtc + mov al, #0x13 + out dx, al + inc dx + in al, dx + mov bl, al + dec dx + mov al, #0x1b + out dx, al + inc dx + in al, dx + mov ah, al + shr ah, #4 + and ah, #0x01 + mov al, bl + shl ax, #3 + pop bx + pop dx + ret + +;; in - si: table +;; out - ax: line offset for mode +cirrus_get_line_offset_entry: + push bx + mov bx, [si+14] ;; crtc table + push bx +offset_loop1: + mov ax, [bx] + cmp al, #0x13 + je offset_found1 + inc bx + inc bx + jnz offset_loop1 +offset_found1: + xor al, al + shr ax, #5 + pop bx + push ax +offset_loop2: + mov ax, [bx] + cmp al, #0x1b + je offset_found2 + inc bx + inc bx + jnz offset_loop2 +offset_found2: + pop bx + and ax, #0x1000 + shr ax, #1 + or ax, bx + pop bx + ret + +;; in - new address in DX:AX +cirrus_set_start_addr: + push bx + push dx + push ax + call cirrus_get_crtc + mov al, #0x0d + out dx, al + inc dx + pop ax + out dx, al + dec dx + mov al, #0x0c + out dx, al + inc dx + mov al, ah + out dx, al + dec dx + mov al, #0x1d + out dx, al + inc dx + in al, dx + and al, #0x7f + pop bx + mov ah, bl + shl bl, #4 + and bl, #0x80 + or al, bl + out dx, al + dec dx + mov bl, ah + and ah, #0x01 + shl bl, #1 + and bl, #0x0c + or ah, bl + mov al, #0x1b + out dx, al + inc dx + in al, dx + and al, #0xf2 + or al, ah + out dx, al + pop bx + ret + +;; out - current address in DX:AX +cirrus_get_start_addr: + push bx + call cirrus_get_crtc + mov al, #0x0c + out dx, al + inc dx + in al, dx + mov ah, al + dec dx + mov al, #0x0d + out dx, al + inc dx + in al, dx + push ax + dec dx + mov al, #0x1b + out dx, al + inc dx + in al, dx + dec dx + mov bl, al + and al, #0x01 + and bl, #0x0c + shr bl, #1 + or bl, al + mov al, #0x1d + out dx, al + inc dx + in al, dx + and al, #0x80 + shr al, #4 + or bl, al + mov dl, bl + xor dh, dh + pop ax + pop bx + ret + +cirrus_clear_vram: + pusha + push es + mov si, ax + + call cirrus_enable_16k_granularity + call cirrus_extbios_85h + shl al, #2 + mov bl, al + xor ah,ah +cirrus_clear_vram_1: + mov al, #0x09 + mov dx, #0x3ce + out dx, ax + push ax + mov cx, #0xa000 + mov es, cx + xor di, di + mov ax, si + mov cx, #8192 + cld + rep + stosw + pop ax + inc ah + cmp ah, bl + jne cirrus_clear_vram_1 + + xor ah,ah + mov dx, #0x3ce + out dx, ax + + pop es + popa + ret + +cirrus_extbios_handlers: + ;; 80h + dw cirrus_extbios_80h + dw cirrus_extbios_81h + dw cirrus_extbios_82h + dw cirrus_extbios_unimplemented + ;; 84h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_85h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; 88h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; 8Ch + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; 90h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; 94h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; 98h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_9Ah + dw cirrus_extbios_unimplemented + ;; 9Ch + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; A0h + dw cirrus_extbios_A0h + dw cirrus_extbios_A1h + dw cirrus_extbios_A2h + dw cirrus_extbios_unimplemented + ;; A4h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; A8h + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + ;; ACh + dw cirrus_extbios_unimplemented + dw cirrus_extbios_unimplemented + dw cirrus_extbios_AEh + dw cirrus_extbios_unimplemented + +cirrus_vesa_handlers: + ;; 00h + dw cirrus_vesa_00h + dw cirrus_vesa_01h + dw cirrus_vesa_02h + dw cirrus_vesa_03h + ;; 04h + dw cirrus_vesa_unimplemented + dw cirrus_vesa_05h + dw cirrus_vesa_06h + dw cirrus_vesa_07h + ;; 08h + dw cirrus_vesa_unimplemented + dw cirrus_vesa_unimplemented + dw cirrus_vesa_unimplemented + dw cirrus_vesa_unimplemented + ;; 0Ch + dw cirrus_vesa_unimplemented + dw cirrus_vesa_unimplemented + dw cirrus_vesa_unimplemented + dw cirrus_vesa_unimplemented + ;; 10h + dw cirrus_vesa_10h + + +ASM_END + +#ifdef CIRRUS_VESA3_PMINFO +ASM_START +cirrus_vesa_pminfo: + /* + 0 */ + .byte 0x50,0x4d,0x49,0x44 ;; signature[4] + /* + 4 */ + dw cirrus_vesa_pmbios_entry ;; entry_bios + dw cirrus_vesa_pmbios_init ;; entry_init + /* + 8 */ +cirrus_vesa_sel0000_data: + dw 0x0000 ;; sel_00000 +cirrus_vesa_selA000_data: + dw 0xA000 ;; sel_A0000 + /* +12 */ +cirrus_vesa_selB000_data: + dw 0xB000 ;; sel_B0000 +cirrus_vesa_selB800_data: + dw 0xB800 ;; sel_B8000 + /* +16 */ +cirrus_vesa_selC000_data: + dw 0xC000 ;; sel_C0000 +cirrus_vesa_is_protected_mode: + ;; protected mode flag and checksum + dw (~((0xf2 + (cirrus_vesa_pmbios_entry >> 8) + (cirrus_vesa_pmbios_entry) \ + + (cirrus_vesa_pmbios_init >> 8) + (cirrus_vesa_pmbios_init)) & 0xff) << 8) + 0x01 +ASM_END +#endif // CIRRUS_VESA3_PMINFO + + +#ifdef CIRRUS_DEBUG +static void cirrus_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS) + Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS; +{ + if((GET_AH()!=0x0E)&&(GET_AH()!=0x02)&&(GET_AH()!=0x09)&&(AX!=0x4F05)) + printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX); +} +#endif diff --git a/kvm/vgabios/dataseghack b/kvm/vgabios/dataseghack new file mode 100755 index 000000000..02a2d4c52 --- /dev/null +++ b/kvm/vgabios/dataseghack @@ -0,0 +1,23 @@ +#!/bin/bash + +awk \ + 'BEGIN { }\ + /^\.text/,/DATA_SEG_DEFS_HERE/ { print }\ + END { }'\ + $1 > temp.awk.1 + +awk \ + 'BEGIN { i = 0; last = "hello" }\ + /BLOCK_STRINGS_BEGIN/,/^\.bss/ { if ( i > 1 ) { print last } last = $0; i = i + 1 }\ + END { }'\ + $1 > temp.awk.2 + +awk \ + 'BEGIN { }\ + /DATA_SEG_DEFS_HERE/,/BLOCK_STRINGS_BEGIN/ { print }\ + END { }'\ + $1 > temp.awk.3 + +cp $1 $1.orig +cat temp.awk.1 temp.awk.2 temp.awk.3 | sed -e 's/^\.data//' -e 's/^\.bss//' -e 's/^\.text//' > $1 +/bin/rm -f temp.awk.1 temp.awk.2 temp.awk.3 $1.orig diff --git a/kvm/vgabios/tests/lfbprof/Makefile b/kvm/vgabios/tests/lfbprof/Makefile new file mode 100644 index 000000000..7c42e38b0 --- /dev/null +++ b/kvm/vgabios/tests/lfbprof/Makefile @@ -0,0 +1,5 @@ +# Very simple makefile for LFBPROF.C using Watcom C++ 10.0a with DOS4GW + +lfbprof.exe: lfbprof.c lfbprof.h + wcl386 -zq -s -d2 lfbprof.c + diff --git a/kvm/vgabios/tests/lfbprof/lfbprof.c b/kvm/vgabios/tests/lfbprof/lfbprof.c new file mode 100644 index 000000000..df37452e8 --- /dev/null +++ b/kvm/vgabios/tests/lfbprof/lfbprof.c @@ -0,0 +1,594 @@ +/**************************************************************************** +* +* VBE 2.0 Linear Framebuffer Profiler +* By Kendall Bennett and Brian Hook +* +* Filename: LFBPROF.C +* Language: ANSI C +* Environment: Watcom C/C++ 10.0a with DOS4GW +* +* Description: Simple program to profile the speed of screen clearing +* and full screen BitBlt operations using a VESA VBE 2.0 +* linear framebuffer from 32 bit protected mode. +* +* For simplicity, this program only supports 256 color +* SuperVGA video modes that support a linear framebuffer. +* +* +* 2002/02/18: Jeroen Janssen <japj at xs4all dot nl> +* - fixed unsigned short for mode list (-1 != 0xffff otherwise) +* - fixed LfbMapRealPointer macro mask problem (some modes were skipped) +* +****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <conio.h> +#include <dos.h> +#include "lfbprof.h" + +/*---------------------------- Global Variables ---------------------------*/ + +int VESABuf_len = 1024; /* Length of VESABuf */ +int VESABuf_sel = 0; /* Selector for VESABuf */ +int VESABuf_rseg; /* Real mode segment of VESABuf */ +unsigned short modeList[50]; /* List of available VBE modes */ +float clearsPerSec; /* Number of clears per second */ +float clearsMbPerSec; /* Memory transfer for clears */ +float bitBltsPerSec; /* Number of BitBlt's per second */ +float bitBltsMbPerSec; /* Memory transfer for bitblt's */ +int xres,yres; /* Video mode resolution */ +int bytesperline; /* Bytes per scanline for mode */ +long imageSize; /* Length of the video image */ +char *LFBPtr; /* Pointer to linear framebuffer */ + +/*------------------------- DPMI interface routines -----------------------*/ + +void DPMI_allocRealSeg(int size,int *sel,int *r_seg) +/**************************************************************************** +* +* Function: DPMI_allocRealSeg +* Parameters: size - Size of memory block to allocate +* sel - Place to return protected mode selector +* r_seg - Place to return real mode segment +* +* Description: Allocates a block of real mode memory using DPMI services. +* This routine returns both a protected mode selector and +* real mode segment for accessing the memory block. +* +****************************************************************************/ +{ + union REGS r; + + r.w.ax = 0x100; /* DPMI allocate DOS memory */ + r.w.bx = (size + 0xF) >> 4; /* number of paragraphs */ + int386(0x31, &r, &r); + if (r.w.cflag) + FatalError("DPMI_allocRealSeg failed!"); + *sel = r.w.dx; /* Protected mode selector */ + *r_seg = r.w.ax; /* Real mode segment */ +} + +void DPMI_freeRealSeg(unsigned sel) +/**************************************************************************** +* +* Function: DPMI_allocRealSeg +* Parameters: sel - Protected mode selector of block to free +* +* Description: Frees a block of real mode memory. +* +****************************************************************************/ +{ + union REGS r; + + r.w.ax = 0x101; /* DPMI free DOS memory */ + r.w.dx = sel; /* DX := selector from 0x100 */ + int386(0x31, &r, &r); +} + +typedef struct { + long edi; + long esi; + long ebp; + long reserved; + long ebx; + long edx; + long ecx; + long eax; + short flags; + short es,ds,fs,gs,ip,cs,sp,ss; + } _RMREGS; + +#define IN(reg) rmregs.e##reg = in->x.reg +#define OUT(reg) out->x.reg = rmregs.e##reg + +int DPMI_int86(int intno, RMREGS *in, RMREGS *out) +/**************************************************************************** +* +* Function: DPMI_int86 +* Parameters: intno - Interrupt number to issue +* in - Pointer to structure for input registers +* out - Pointer to structure for output registers +* Returns: Value returned by interrupt in AX +* +* Description: Issues a real mode interrupt using DPMI services. +* +****************************************************************************/ +{ + _RMREGS rmregs; + union REGS r; + struct SREGS sr; + + memset(&rmregs, 0, sizeof(rmregs)); + IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di); + + segread(&sr); + r.w.ax = 0x300; /* DPMI issue real interrupt */ + r.h.bl = intno; + r.h.bh = 0; + r.w.cx = 0; + sr.es = sr.ds; + r.x.edi = (unsigned)&rmregs; + int386x(0x31, &r, &r, &sr); /* Issue the interrupt */ + + OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di); + out->x.cflag = rmregs.flags & 0x1; + return out->x.ax; +} + +int DPMI_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs) +/**************************************************************************** +* +* Function: DPMI_int86 +* Parameters: intno - Interrupt number to issue +* in - Pointer to structure for input registers +* out - Pointer to structure for output registers +* sregs - Values to load into segment registers +* Returns: Value returned by interrupt in AX +* +* Description: Issues a real mode interrupt using DPMI services. +* +****************************************************************************/ +{ + _RMREGS rmregs; + union REGS r; + struct SREGS sr; + + memset(&rmregs, 0, sizeof(rmregs)); + IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di); + rmregs.es = sregs->es; + rmregs.ds = sregs->ds; + + segread(&sr); + r.w.ax = 0x300; /* DPMI issue real interrupt */ + r.h.bl = intno; + r.h.bh = 0; + r.w.cx = 0; + sr.es = sr.ds; + r.x.edi = (unsigned)&rmregs; + int386x(0x31, &r, &r, &sr); /* Issue the interrupt */ + + OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di); + sregs->es = rmregs.es; + sregs->cs = rmregs.cs; + sregs->ss = rmregs.ss; + sregs->ds = rmregs.ds; + out->x.cflag = rmregs.flags & 0x1; + return out->x.ax; +} + +int DPMI_allocSelector(void) +/**************************************************************************** +* +* Function: DPMI_allocSelector +* Returns: Newly allocated protected mode selector +* +* Description: Allocates a new protected mode selector using DPMI +* services. This selector has a base address and limit of 0. +* +****************************************************************************/ +{ + int sel; + union REGS r; + + r.w.ax = 0; /* DPMI allocate selector */ + r.w.cx = 1; /* Allocate a single selector */ + int386(0x31, &r, &r); + if (r.x.cflag) + FatalError("DPMI_allocSelector() failed!"); + sel = r.w.ax; + + r.w.ax = 9; /* DPMI set access rights */ + r.w.bx = sel; + r.w.cx = 0x8092; /* 32 bit page granular */ + int386(0x31, &r, &r); + return sel; +} + +long DPMI_mapPhysicalToLinear(long physAddr,long limit) +/**************************************************************************** +* +* Function: DPMI_mapPhysicalToLinear +* Parameters: physAddr - Physical memory address to map +* limit - Length-1 of physical memory region to map +* Returns: Starting linear address for mapped memory +* +* Description: Maps a section of physical memory into the linear address +* space of a process using DPMI calls. Note that this linear +* address cannot be used directly, but must be used as the +* base address for a selector. +* +****************************************************************************/ +{ + union REGS r; + + r.w.ax = 0x800; /* DPMI map physical to linear */ + r.w.bx = physAddr >> 16; + r.w.cx = physAddr & 0xFFFF; + r.w.si = limit >> 16; + r.w.di = limit & 0xFFFF; + int386(0x31, &r, &r); + if (r.x.cflag) + FatalError("DPMI_mapPhysicalToLinear() failed!"); + return ((long)r.w.bx << 16) + r.w.cx; +} + +void DPMI_setSelectorBase(int sel,long linAddr) +/**************************************************************************** +* +* Function: DPMI_setSelectorBase +* Parameters: sel - Selector to change base address for +* linAddr - Linear address used for new base address +* +* Description: Sets the base address for the specified selector. +* +****************************************************************************/ +{ + union REGS r; + + r.w.ax = 7; /* DPMI set selector base address */ + r.w.bx = sel; + r.w.cx = linAddr >> 16; + r.w.dx = linAddr & 0xFFFF; + int386(0x31, &r, &r); + if (r.x.cflag) + FatalError("DPMI_setSelectorBase() failed!"); +} + +void DPMI_setSelectorLimit(int sel,long limit) +/**************************************************************************** +* +* Function: DPMI_setSelectorLimit +* Parameters: sel - Selector to change limit for +* limit - Limit-1 for the selector +* +* Description: Sets the memory limit for the specified selector. +* +****************************************************************************/ +{ + union REGS r; + + r.w.ax = 8; /* DPMI set selector limit */ + r.w.bx = sel; + r.w.cx = limit >> 16; + r.w.dx = limit & 0xFFFF; + int386(0x31, &r, &r); + if (r.x.cflag) + FatalError("DPMI_setSelectorLimit() failed!"); +} + +/*-------------------------- VBE Interface routines -----------------------*/ + +void FatalError(char *msg) +{ + fprintf(stderr,"%s\n", msg); + exit(1); +} + +static void ExitVBEBuf(void) +{ + DPMI_freeRealSeg(VESABuf_sel); +} + +void VBE_initRMBuf(void) +/**************************************************************************** +* +* Function: VBE_initRMBuf +* Description: Initialises the VBE transfer buffer in real mode memory. +* This routine is called by the VESAVBE module every time +* it needs to use the transfer buffer, so we simply allocate +* it once and then return. +* +****************************************************************************/ +{ + if (!VESABuf_sel) { + DPMI_allocRealSeg(VESABuf_len, &VESABuf_sel, &VESABuf_rseg); + atexit(ExitVBEBuf); + } +} + +void VBE_callESDI(RMREGS *regs, void *buffer, int size) +/**************************************************************************** +* +* Function: VBE_callESDI +* Parameters: regs - Registers to load when calling VBE +* buffer - Buffer to copy VBE info block to +* size - Size of buffer to fill +* +* Description: Calls the VESA VBE and passes in a buffer for the VBE to +* store information in, which is then copied into the users +* buffer space. This works in protected mode as the buffer +* passed to the VESA VBE is allocated in conventional +* memory, and is then copied into the users memory block. +* +****************************************************************************/ +{ + RMSREGS sregs; + + VBE_initRMBuf(); + sregs.es = VESABuf_rseg; + regs->x.di = 0; + _fmemcpy(MK_FP(VESABuf_sel,0),buffer,size); + DPMI_int86x(0x10, regs, regs, &sregs); + _fmemcpy(buffer,MK_FP(VESABuf_sel,0),size); +} + +int VBE_detect(void) +/**************************************************************************** +* +* Function: VBE_detect +* Parameters: vgaInfo - Place to store the VGA information block +* Returns: VBE version number, or 0 if not detected. +* +* Description: Detects if a VESA VBE is out there and functioning +* correctly. If we detect a VBE interface we return the +* VGAInfoBlock returned by the VBE and the VBE version number. +* +****************************************************************************/ +{ + RMREGS regs; + unsigned short *p1,*p2; + VBE_vgaInfo vgaInfo; + + /* Put 'VBE2' into the signature area so that the VBE 2.0 BIOS knows + * that we have passed a 512 byte extended block to it, and wish + * the extended information to be filled in. + */ + strncpy(vgaInfo.VESASignature,"VBE2",4); + + /* Get the SuperVGA Information block */ + regs.x.ax = 0x4F00; + VBE_callESDI(®s, &vgaInfo, sizeof(VBE_vgaInfo)); + if (regs.x.ax != 0x004F) + return 0; + if (strncmp(vgaInfo.VESASignature,"VESA",4) != 0) + return 0; + + /* Now that we have detected a VBE interface, copy the list of available + * video modes into our local buffer. We *must* copy this mode list, + * since the VBE will build the mode list in the VBE_vgaInfo buffer + * that we have passed, so the next call to the VBE will trash the + * list of modes. + */ + printf("videomodeptr %x\n",vgaInfo.VideoModePtr); + p1 = LfbMapRealPointer(vgaInfo.VideoModePtr); + p2 = modeList; + while (*p1 != -1) + { + printf("found mode %x\n",*p1); + *p2++ = *p1++; + } + *p2 = -1; + return vgaInfo.VESAVersion; +} + +int VBE_getModeInfo(int mode,VBE_modeInfo *modeInfo) +/**************************************************************************** +* +* Function: VBE_getModeInfo +* Parameters: mode - VBE mode to get information for +* modeInfo - Place to store VBE mode information +* Returns: 1 on success, 0 if function failed. +* +* Description: Obtains information about a specific video mode from the +* VBE. You should use this function to find the video mode +* you wish to set, as the new VBE 2.0 mode numbers may be +* completely arbitrary. +* +****************************************************************************/ +{ + RMREGS regs; + + regs.x.ax = 0x4F01; /* Get mode information */ + regs.x.cx = mode; + VBE_callESDI(®s, modeInfo, sizeof(VBE_modeInfo)); + if (regs.x.ax != 0x004F) + return 0; + if ((modeInfo->ModeAttributes & vbeMdAvailable) == 0) + return 0; + return 1; +} + +void VBE_setVideoMode(int mode) +/**************************************************************************** +* +* Function: VBE_setVideoMode +* Parameters: mode - VBE mode number to initialise +* +****************************************************************************/ +{ + RMREGS regs; + regs.x.ax = 0x4F02; + regs.x.bx = mode; + DPMI_int86(0x10,®s,®s); +} + +/*-------------------- Application specific routines ----------------------*/ + +void *GetPtrToLFB(long physAddr) +/**************************************************************************** +* +* Function: GetPtrToLFB +* Parameters: physAddr - Physical memory address of linear framebuffer +* Returns: Far pointer to the linear framebuffer memory +* +****************************************************************************/ +{ + int sel; + long linAddr,limit = (4096 * 1024) - 1; + +// sel = DPMI_allocSelector(); + linAddr = DPMI_mapPhysicalToLinear(physAddr,limit); +// DPMI_setSelectorBase(sel,linAddr); +// DPMI_setSelectorLimit(sel,limit); +// return MK_FP(sel,0); + return (void*)linAddr; +} + +void AvailableModes(void) +/**************************************************************************** +* +* Function: AvailableModes +* +* Description: Display a list of available LFB mode resolutions. +* +****************************************************************************/ +{ + unsigned short *p; + VBE_modeInfo modeInfo; + + printf("Usage: LFBPROF <xres> <yres>\n\n"); + printf("Available 256 color video modes:\n"); + for (p = modeList; *p != -1; p++) { + if (VBE_getModeInfo(*p, &modeInfo)) { + /* Filter out only 8 bit linear framebuffer modes */ + if ((modeInfo.ModeAttributes & vbeMdLinear) == 0) + continue; + if (modeInfo.MemoryModel != vbeMemPK + || modeInfo.BitsPerPixel != 8 + || modeInfo.NumberOfPlanes != 1) + continue; + printf(" %4d x %4d %d bits per pixel\n", + modeInfo.XResolution, modeInfo.YResolution, + modeInfo.BitsPerPixel); + } + } + exit(1); +} + +void InitGraphics(int x,int y) +/**************************************************************************** +* +* Function: InitGraphics +* Parameters: x,y - Requested video mode resolution +* +* Description: Initialise the specified video mode. We search through +* the list of available video modes for one that matches +* the resolution and color depth are are looking for. +* +****************************************************************************/ +{ + unsigned short *p; + VBE_modeInfo modeInfo; + printf("InitGraphics\n"); + + for (p = modeList; *p != -1; p++) { + if (VBE_getModeInfo(*p, &modeInfo)) { + /* Filter out only 8 bit linear framebuffer modes */ + if ((modeInfo.ModeAttributes & vbeMdLinear) == 0) + continue; + if (modeInfo.MemoryModel != vbeMemPK + || modeInfo.BitsPerPixel != 8 + || modeInfo.NumberOfPlanes != 1) + continue; + if (modeInfo.XResolution != x || modeInfo.YResolution != y) + continue; + xres = x; + yres = y; + bytesperline = modeInfo.BytesPerScanLine; + imageSize = bytesperline * yres; + VBE_setVideoMode(*p | vbeUseLFB); + LFBPtr = GetPtrToLFB(modeInfo.PhysBasePtr); + return; + } + } + printf("Valid video mode not found\n"); + exit(1); +} + +void EndGraphics(void) +/**************************************************************************** +* +* Function: EndGraphics +* +* Description: Restores text mode. +* +****************************************************************************/ +{ + RMREGS regs; + printf("EndGraphics\n"); + regs.x.ax = 0x3; + DPMI_int86(0x10, ®s, ®s); +} + +void ProfileMode(void) +/**************************************************************************** +* +* Function: ProfileMode +* +* Description: Profiles framebuffer performance for simple screen clearing +* and for copying from system memory to video memory (BitBlt). +* This routine thrashes the CPU cache by cycling through +* enough system memory buffers to invalidate the entire +* CPU external cache before re-using the first memory buffer +* again. +* +****************************************************************************/ +{ + int i,numClears,numBlts,maxImages; + long startTicks,endTicks; + void *image[10],*dst; + printf("ProfileMode\n"); + + /* Profile screen clearing operation */ + startTicks = LfbGetTicks(); + numClears = 0; + while ((LfbGetTicks() - startTicks) < 182) + LfbMemset(LFBPtr,numClears++,imageSize); + endTicks = LfbGetTicks(); + clearsPerSec = numClears / ((endTicks - startTicks) * 0.054925); + clearsMbPerSec = (clearsPerSec * imageSize) / 1048576.0; + + /* Profile system memory to video memory copies */ + maxImages = ((512 * 1024U) / imageSize) + 2; + for (i = 0; i < maxImages; i++) { + image[i] = malloc(imageSize); + if (image[i] == NULL) + FatalError("Not enough memory to profile BitBlt!"); + memset(image[i],i+1,imageSize); + } + startTicks = LfbGetTicks(); + numBlts = 0; + while ((LfbGetTicks() - startTicks) < 182) + LfbMemcpy(LFBPtr,image[numBlts++ % maxImages],imageSize); + endTicks = LfbGetTicks(); + bitBltsPerSec = numBlts / ((endTicks - startTicks) * 0.054925); + bitBltsMbPerSec = (bitBltsPerSec * imageSize) / 1048576.0; +} + +void main(int argc, char *argv[]) +{ + if (VBE_detect() < 0x200) + FatalError("This program requires VBE 2.0; Please install UniVBE 5.1."); + if (argc != 3) + AvailableModes(); /* Display available modes */ + + InitGraphics(atoi(argv[1]),atoi(argv[2])); /* Start graphics */ + ProfileMode(); /* Profile the video mode */ + EndGraphics(); /* Restore text mode */ + + printf("Profiling results for %dx%d 8 bits per pixel.\n",xres,yres); + printf("%3.2f clears/s, %2.2f Mb/s\n", clearsPerSec, clearsMbPerSec); + printf("%3.2f bitBlt/s, %2.2f Mb/s\n", bitBltsPerSec, bitBltsMbPerSec); +} diff --git a/kvm/vgabios/tests/lfbprof/lfbprof.h b/kvm/vgabios/tests/lfbprof/lfbprof.h new file mode 100644 index 000000000..bae0e09b1 --- /dev/null +++ b/kvm/vgabios/tests/lfbprof/lfbprof.h @@ -0,0 +1,149 @@ +/**************************************************************************** +* +* VBE 2.0 Linear Framebuffer Profiler +* By Kendall Bennett and Brian Hook +* +* Filename: LFBPROF.H +* Language: ANSI C +* Environment: Watcom C/C++ 10.0a with DOS4GW +* +* Description: Header file for the LFBPROF.C progam. +* +****************************************************************************/ + +#ifndef __LFBPROF_H +#define __LFBPROF_H + +/*---------------------- Macros and type definitions ----------------------*/ + +#pragma pack(1) + +/* SuperVGA information block */ + +typedef struct { + char VESASignature[4]; /* 'VESA' 4 byte signature */ + short VESAVersion; /* VBE version number */ + long OemStringPtr; /* Pointer to OEM string */ + long Capabilities; /* Capabilities of video card */ + long VideoModePtr; /* Pointer to supported modes */ + short TotalMemory; /* Number of 64kb memory blocks */ + + /* VBE 2.0 extensions */ + + short OemSoftwareRev; /* OEM Software revision number */ + long OemVendorNamePtr; /* Pointer to Vendor Name string */ + long OemProductNamePtr; /* Pointer to Product Name string */ + long OemProductRevPtr; /* Pointer to Product Revision str */ + char reserved[222]; /* Pad to 256 byte block size */ + char OemDATA[256]; /* Scratch pad for OEM data */ + } VBE_vgaInfo; + +/* SuperVGA mode information block */ + +typedef struct { + short ModeAttributes; /* Mode attributes */ + char WinAAttributes; /* Window A attributes */ + char WinBAttributes; /* Window B attributes */ + short WinGranularity; /* Window granularity in k */ + short WinSize; /* Window size in k */ + short WinASegment; /* Window A segment */ + short WinBSegment; /* Window B segment */ + long WinFuncPtr; /* Pointer to window function */ + short BytesPerScanLine; /* Bytes per scanline */ + short XResolution; /* Horizontal resolution */ + short YResolution; /* Vertical resolution */ + char XCharSize; /* Character cell width */ + char YCharSize; /* Character cell height */ + char NumberOfPlanes; /* Number of memory planes */ + char BitsPerPixel; /* Bits per pixel */ + char NumberOfBanks; /* Number of CGA style banks */ + char MemoryModel; /* Memory model type */ + char BankSize; /* Size of CGA style banks */ + char NumberOfImagePages; /* Number of images pages */ + char res1; /* Reserved */ + char RedMaskSize; /* Size of direct color red mask */ + char RedFieldPosition; /* Bit posn of lsb of red mask */ + char GreenMaskSize; /* Size of direct color green mask */ + char GreenFieldPosition; /* Bit posn of lsb of green mask */ + char BlueMaskSize; /* Size of direct color blue mask */ + char BlueFieldPosition; /* Bit posn of lsb of blue mask */ + char RsvdMaskSize; /* Size of direct color res mask */ + char RsvdFieldPosition; /* Bit posn of lsb of res mask */ + char DirectColorModeInfo; /* Direct color mode attributes */ + + /* VBE 2.0 extensions */ + + long PhysBasePtr; /* Physical address for linear buf */ + long OffScreenMemOffset; /* Pointer to start of offscreen mem*/ + short OffScreenMemSize; /* Amount of offscreen mem in 1K's */ + char res2[206]; /* Pad to 256 byte block size */ + } VBE_modeInfo; + +#define vbeMemPK 4 /* Packed Pixel memory model */ +#define vbeUseLFB 0x4000 /* Enable linear framebuffer mode */ + +/* Flags for the mode attributes returned by VBE_getModeInfo. If + * vbeMdNonBanked is set to 1 and vbeMdLinear is also set to 1, then only + * the linear framebuffer mode is available. + */ + +#define vbeMdAvailable 0x0001 /* Video mode is available */ +#define vbeMdColorMode 0x0008 /* Mode is a color video mode */ +#define vbeMdGraphMode 0x0010 /* Mode is a graphics mode */ +#define vbeMdNonBanked 0x0040 /* Banked mode is not supported */ +#define vbeMdLinear 0x0080 /* Linear mode supported */ + +/* Structures for issuing real mode interrupts with DPMI */ + +struct _RMWORDREGS { + unsigned short ax, bx, cx, dx, si, di, cflag; + }; + +struct _RMBYTEREGS { + unsigned char al, ah, bl, bh, cl, ch, dl, dh; + }; + +typedef union { + struct _RMWORDREGS x; + struct _RMBYTEREGS h; + } RMREGS; + +typedef struct { + unsigned short es; + unsigned short cs; + unsigned short ss; + unsigned short ds; + } RMSREGS; + +/* Inline assembler block fill/move routines */ + +void LfbMemset(void *p,int c,int n); +#pragma aux LfbMemset = \ + "shr ecx,2" \ + "xor eax,eax" \ + "mov al,bl" \ + "shl ebx,8" \ + "or ax,bx" \ + "mov ebx,eax" \ + "shl ebx,16" \ + "or eax,ebx" \ + "rep stosd" \ + parm [edi] [ebx] [ecx]; + +void LfbMemcpy(void *dst,void *src,int n); +#pragma aux LfbMemcpy = \ + "shr ecx,2" \ + "rep movsd" \ + parm [edi] [esi] [ecx]; + +/* Map a real mode pointer into address space */ + +#define LfbMapRealPointer(p) (void*)(((unsigned)((p) & 0xFFFF0000) >> 12) + ((p) & 0xFFFF)) + +/* Get the current timer tick count */ + +#define LfbGetTicks() *((long*)0x46C) + +#pragma pack() + +#endif /* __LFBPROF_H */ diff --git a/kvm/vgabios/tests/testbios.c b/kvm/vgabios/tests/testbios.c new file mode 100644 index 000000000..99da5a65f --- /dev/null +++ b/kvm/vgabios/tests/testbios.c @@ -0,0 +1,353 @@ +/*
+ This is a little turbo C program that executes
+ several int10, and let you inspect the content
+ of the vgabios area
+
+ It is used to test the behavior of the vgabios
+*/
+
+#include <stdio.h>
+#include <dos.h>
+#include <conio.h>
+
+
+typedef unsigned char Bit8u;
+typedef unsigned short Bit16u;
+
+typedef struct
+{Bit8u initial;
+ Bit8u current;
+ Bit16u nbcols;
+ Bit16u regen;
+ Bit16u start;
+ Bit16u curpos[8];
+ Bit8u curtyp;
+ Bit8u curpage;
+ Bit16u crtc;
+ Bit16u msr;
+ Bit16u cgapal;
+ Bit8u nbrows;
+ Bit16u cheight;
+ Bit8u ctl;
+ Bit8u switches;
+ Bit8u modeset;
+ Bit8u dcc;
+ Bit16u vsseg;
+ Bit16u vsoffset;
+} BIOSAREA;
+
+void int10ax0003(struct REGPACK *regs)
+{
+ regs->r_ax=0x0003;
+ intr(0x10,regs);
+}
+
+void int10ax02(struct REGPACK *regs)
+{
+ regs->r_ax=0x0200;
+ regs->r_bx=0x0000;
+ regs->r_dx=0x1710;
+ intr(0x10,regs);
+ printf("We are now at 24/17");
+}
+
+void int10ax03(struct REGPACK *regs)
+{
+ regs->r_ax=0x0300;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+ printf("\nCursor is ax%04x cx%04x dx%04x\n",regs->r_ax,regs->r_cx,regs->r_dx);
+}
+
+void int10ax0501(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0e61;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+ printf("We are now on page 2");
+}
+
+void int10ax0602(struct REGPACK *regs)
+{
+ regs->r_ax=0x0602;
+ regs->r_bx=0x0700;
+ regs->r_cx=0x0101;
+ regs->r_dx=0x0a0a;
+ intr(0x10,regs);
+ printf("Scrolled 2 up");
+}
+
+void int10ax0702(struct REGPACK *regs)
+{
+ regs->r_ax=0x0702;
+ regs->r_bx=0x0700;
+ regs->r_cx=0x0101;
+ regs->r_dx=0x0a0a;
+ intr(0x10,regs);
+ printf("Scrolled 2 down");
+}
+
+void int10ax08(struct REGPACK *regs)
+{
+ regs->r_ax=0x0800;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+}
+
+void int10ax09(struct REGPACK *regs)
+{
+ char attr;
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ for(attr=0;attr<16;attr++)
+ {printf("%02x ",attr);
+ regs->r_ax=0x0961+attr;
+ regs->r_bx=0x0100+attr;
+ regs->r_cx=0x0016;
+ intr(0x10,regs);
+ printf("\n");
+ }
+}
+
+void int10ax0a(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0a62;
+ regs->r_bx=0x0101;
+ regs->r_cx=0x0016;
+ intr(0x10,regs);
+}
+
+void int10ax0f(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0f00;
+ intr(0x10,regs);
+}
+
+void int10ax1b(struct REGPACK *regs)
+{unsigned char table[64];
+ unsigned char far *ptable;
+ int i;
+
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x1b00;
+ regs->r_bx=0x0000;
+ ptable=&table;
+ regs->r_es=FP_SEG(ptable);
+ regs->r_di=FP_OFF(ptable);
+ printf("Read state info in %04x:%04x\n",regs->r_es,regs->r_di);
+ intr(0x10,regs);
+
+ for(i=0;i<64;i++)
+ {if(i%16==0)printf("\n%02x ",i);
+ printf("%02x ",table[i]);
+ }
+ printf("\n");
+}
+
+static unsigned char var[64];
+
+void int10ax13(struct REGPACK *regs)
+{unsigned char far *pvar;
+
+ pvar=&var;
+
+ regs->r_ax=0x1300;
+ regs->r_bx=0x000b;
+ regs->r_dx=0x1010;
+ regs->r_cx=0x0002;
+ regs->r_es=FP_SEG(pvar);
+ regs->r_bp=FP_OFF(pvar);
+ pokeb(regs->r_es,regs->r_bp,'t');
+ pokeb(regs->r_es,regs->r_bp+1,'b');
+ printf("Writing from %04x:%04x\n",regs->r_es,regs->r_bp);
+ intr(0x10,regs);
+
+}
+
+void switch_50(struct REGPACK *regs)
+{
+ regs->r_ax=0x1202;
+ regs->r_bx=0x3000;
+ intr(0x10,regs);
+ regs->r_ax=0x0003;
+ intr(0x10,regs);
+ regs->r_ax=0x1112;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+}
+
+char exec_function(struct REGPACK *regs)
+{char c;
+
+ printf("--- Functions --------------------\n");
+ printf("a. int10 ax0003\t");
+ printf("b. int10 ax02\t");
+ printf("c. int10 ax03\t");
+ printf("d. int10 ax0501\n");
+ printf("e. int10 ax0602\t");
+ printf("f. int10 ax0702\t");
+ printf("g. int10 ax08\t");
+ printf("h. int10 ax09\t");
+ printf("i. int10 ax0a\n");
+ printf("j. int10 ax0f\t");
+ printf("k. int10 ax1b\t");
+ printf("l. int10 ax13\n");
+ printf("q. Quit\t");
+ printf("r. switch to 50 lines\n");
+ c=getche();
+
+ switch(c)
+ {case 'a':
+ int10ax0003(regs);
+ break;
+ case 'b':
+ int10ax02(regs);
+ break;
+ case 'c':
+ int10ax03(regs);
+ break;
+ case 'd':
+ int10ax0501(regs);
+ break;
+ case 'e':
+ int10ax0602(regs);
+ break;
+ case 'f':
+ int10ax0702(regs);
+ break;
+ case 'g':
+ int10ax08(regs);
+ break;
+ case 'h':
+ int10ax09(regs);
+ break;
+ case 'i':
+ int10ax0a(regs);
+ break;
+ case 'j':
+ int10ax0f(regs);
+ break;
+ case 'k':
+ int10ax1b(regs);
+ break;
+ case 'l':
+ int10ax13(regs);
+ break;
+ case 'q':
+ break;
+ case 'r':
+ switch_50(regs);
+ break;
+ default:
+ printf("No such function!\n");
+ }
+
+ if(c=='q')return 1;
+ while(kbhit()==0);
+ c=getch();
+
+ return 0;
+}
+
+void read_bios_area(BIOSAREA *biosarea)
+{
+ biosarea->initial=peekb(0x40,0x10);
+ biosarea->current=peekb(0x40,0x49);
+ biosarea->nbcols=peek(0x40,0x4a);
+ biosarea->regen=peek(0x40,0x4c);
+ biosarea->start=peek(0x40,0x4e);
+ biosarea->curpos[0]=peek(0x40,0x50);
+ biosarea->curpos[1]=peek(0x40,0x52);
+ biosarea->curpos[2]=peek(0x40,0x54);
+ biosarea->curpos[3]=peek(0x40,0x56);
+ biosarea->curpos[4]=peek(0x40,0x58);
+ biosarea->curpos[5]=peek(0x40,0x5a);
+ biosarea->curpos[6]=peek(0x40,0x5c);
+ biosarea->curpos[7]=peek(0x40,0x5e);
+ biosarea->curtyp=peek(0x40,0x60);
+ biosarea->curpage=peekb(0x40,0x62);
+ biosarea->crtc=peek(0x40,0x63);
+ biosarea->msr=peekb(0x40,0x65);
+ biosarea->cgapal=peekb(0x40,0x66);
+ biosarea->nbrows=peekb(0x40,0x84);
+ biosarea->cheight=peek(0x40,0x85);
+ biosarea->ctl=peekb(0x40,0x87);
+ biosarea->switches=peekb(0x40,0x88);
+ biosarea->modeset=peekb(0x40,0x89);
+ biosarea->dcc=peekb(0x40,0x8a);
+ biosarea->vsseg=peek(0x40,0xa8);
+ biosarea->vsoffset=peek(0x40,0xaa);
+}
+
+void show_bios_area(BIOSAREA *biosarea)
+{
+ printf("--- BIOS area --------------------\n");
+ printf("initial : %02x\t",biosarea->initial);
+ printf("current : %02x\t",biosarea->current);
+ printf("nbcols : %04x\t",biosarea->nbcols);
+ printf("regen : %04x\t",biosarea->regen);
+ printf("start : %04x\n",biosarea->start);
+ printf("curpos : %04x %04x %04x %04x %04x %04x %04x %04x\n",
+ biosarea->curpos[0], biosarea->curpos[1], biosarea->curpos[2], biosarea->curpos[3],
+ biosarea->curpos[4], biosarea->curpos[5], biosarea->curpos[6], biosarea->curpos[7]);
+ printf("curtyp : %04x\t",biosarea->curtyp);
+ printf("curpage : %02x\t",biosarea->curpage);
+ printf("crtc : %04x\t",biosarea->crtc);
+ printf("msr : %04x\n",biosarea->msr);
+ printf("cgapal : %04x\t",biosarea->cgapal);
+ printf("nbrows-1: %02x\t",biosarea->nbrows);
+ printf("cheight : %04x\t",biosarea->cheight);
+ printf("ctl : %02x\n",biosarea->ctl);
+ printf("switches: %02x\t",biosarea->switches);
+ printf("modeset : %02x\t",biosarea->modeset);
+ printf("dcc : %02x\t",biosarea->dcc);
+ printf("vs : %04x:%04x\n",biosarea->vsseg,biosarea->vsoffset);
+}
+
+void show_regs(struct REGPACK *regs)
+{
+ printf("--- Registers --------------------\n");
+ printf("ax %04x\t",regs->r_ax);
+ printf("bx %04x\t",regs->r_bx);
+ printf("cx %04x\t",regs->r_cx);
+ printf("dx %04x\t",regs->r_dx);
+ printf("ds %04x\t",regs->r_ds);
+ printf("si %04x\t",regs->r_si);
+ printf("es %04x\t",regs->r_es);
+ printf("di %04x\n",regs->r_di);
+}
+
+void reset_videomode()
+{
+ struct REGPACK regs;
+
+ regs.r_ax=0x0003;
+ intr(0x10,®s);
+}
+
+void main()
+{
+
+ BIOSAREA biosarea;
+ struct REGPACK regs;
+
+ directvideo=0;
+
+ while(1)
+ {
+ read_bios_area(&biosarea);
+
+ reset_videomode();
+ show_bios_area(&biosarea);
+ show_regs(®s);
+
+ if(exec_function(®s)!=0)break;
+ }
+}
diff --git a/kvm/vgabios/vbe.c b/kvm/vgabios/vbe.c new file mode 100644 index 000000000..6173ca033 --- /dev/null +++ b/kvm/vgabios/vbe.c @@ -0,0 +1,1432 @@ +// ============================================================================================ +// +// Copyright (C) 2002 Jeroen Janssen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ============================================================================================ +// +// This VBE is part of the VGA Bios specific to the plex86/bochs Emulated VGA card. +// You can NOT drive any physical vga card with it. +// +// ============================================================================================ +// +// This VBE Bios is based on information taken from : +// - VESA BIOS EXTENSION (VBE) Core Functions Standard Version 3.0 located at www.vesa.org +// +// ============================================================================================ + + +// defines available + +// disable VESA/VBE2 check in vbe info +//#define VBE2_NO_VESA_CHECK + + +#include "vbe.h" +#include "vbetables.h" + +#define VBE_TOTAL_VIDEO_MEMORY_DIV_64K (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB*1024/64) + +// The current OEM Software Revision of this VBE Bios +#define VBE_OEM_SOFTWARE_REV 0x0002; + +extern char vbebios_copyright; +extern char vbebios_vendor_name; +extern char vbebios_product_name; +extern char vbebios_product_revision; + +ASM_START +// FIXME: 'merge' these (c) etc strings with the vgabios.c strings? +_vbebios_copyright: +.ascii "Bochs/Plex86 VBE(C) 2003 http://savannah.nongnu.org/projects/vgabios/" +.byte 0x00 + +_vbebios_vendor_name: +.ascii "Bochs/Plex86 Developers" +.byte 0x00 + +_vbebios_product_name: +.ascii "Bochs/Plex86 VBE Adapter" +.byte 0x00 + +_vbebios_product_revision: +.ascii "$Id$" +.byte 0x00 + +_vbebios_info_string: +.ascii "Bochs VBE Display Adapter enabled" +.byte 0x0a,0x0d +.byte 0x0a,0x0d +.byte 0x00 + +_no_vbebios_info_string: +.ascii "NO Bochs VBE Support available!" +.byte 0x0a,0x0d +.byte 0x0a,0x0d +.byte 0x00 + +#if defined(USE_BX_INFO) || defined(DEBUG) +msg_vbe_init: +.ascii "VBE Bios $Id$" +.byte 0x0a,0x0d, 0x00 +#endif + + .align 2 +vesa_pm_start: + dw vesa_pm_set_window - vesa_pm_start + dw vesa_pm_set_display_start - vesa_pm_start + dw vesa_pm_unimplemented - vesa_pm_start + dw vesa_pm_io_ports_table - vesa_pm_start +vesa_pm_io_ports_table: + dw VBE_DISPI_IOPORT_INDEX + dw VBE_DISPI_IOPORT_INDEX + 1 + dw VBE_DISPI_IOPORT_DATA + dw VBE_DISPI_IOPORT_DATA + 1 + dw 0xffff + dw 0xffff + + USE32 +vesa_pm_set_window: + cmp bx, #0x00 + je vesa_pm_set_display_window1 + mov ax, #0x0100 + ret +vesa_pm_set_display_window1: + mov ax, dx + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BANK + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + in ax, dx + pop dx + cmp dx, ax + jne illegal_window + mov ax, #0x004f + ret +illegal_window: + mov ax, #0x014f + ret + +vesa_pm_set_display_start: + cmp bl, #0x80 + je vesa_pm_set_display_start1 + cmp bl, #0x00 + je vesa_pm_set_display_start1 + mov ax, #0x0100 + ret +vesa_pm_set_display_start1: +; convert offset to (X, Y) coordinate +; (would be simpler to change Bochs VBE API...) + push eax + push ecx + push edx + push esi + push edi + shl edx, #16 + and ecx, #0xffff + or ecx, edx + shl ecx, #2 + mov eax, ecx + + push eax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + movzx ecx, ax + + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BPP + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + movzx esi, ax + pop eax + + cmp esi, #4 + jz bpp4_mode + add esi, #7 + shr esi, #3 + imul ecx, esi + xor edx, edx + div ecx + mov edi, eax + mov eax, edx + xor edx, edx + div esi + jmp set_xy_regs + +bpp4_mode: + shr ecx, #1 + xor edx, edx + div ecx + mov edi, eax + mov eax, edx + shl eax, #1 + +set_xy_regs: + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_X_OFFSET + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + + mov ax, di + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_Y_OFFSET + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + + pop edi + pop esi + pop edx + pop ecx + pop eax + mov ax, #0x004f + ret + +vesa_pm_unimplemented: + mov ax, #0x014f + ret + USE16 +vesa_pm_end: + +; DISPI ioport functions + +dispi_get_id: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_ID + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret + +dispi_set_id: + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_ID + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + ret +ASM_END + +static void dispi_set_xres(xres) + Bit16u xres; +{ +ASM_START + push bp + mov bp, sp + push ax + push dx + + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_XRES + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + mov ax, 4[bp] ; xres + out dx, ax + + pop dx + pop ax + pop bp +ASM_END +} + +static void dispi_set_yres(yres) + Bit16u yres; +{ + outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_YRES); + outw(VBE_DISPI_IOPORT_DATA,yres); +} + +static void dispi_set_bpp(bpp) + Bit16u bpp; +{ + outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_BPP); + outw(VBE_DISPI_IOPORT_DATA,bpp); +} + +ASM_START +; AL = bits per pixel / AH = bytes per pixel +dispi_get_bpp: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BPP + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + mov ah, al + shr ah, 3 + test al, #0x07 + jz get_bpp_noinc + inc ah +get_bpp_noinc: + pop dx + ret + +; get display capabilities + +_dispi_get_max_xres: + push dx + push bx + call dispi_get_enable + mov bx, ax + or ax, # VBE_DISPI_GETCAPS + call _dispi_set_enable + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_XRES + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + push ax + mov ax, bx + call _dispi_set_enable + pop ax + pop bx + pop dx + ret + +_dispi_get_max_bpp: + push dx + push bx + call dispi_get_enable + mov bx, ax + or ax, # VBE_DISPI_GETCAPS + call _dispi_set_enable + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BPP + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + push ax + mov ax, bx + call _dispi_set_enable + pop ax + pop bx + pop dx + ret + +_dispi_set_enable: + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_ENABLE + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + ret + +dispi_get_enable: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_ENABLE + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret + +_dispi_set_bank: + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BANK + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + ret + +dispi_get_bank: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BANK + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret +ASM_END + +static void dispi_set_bank_farcall() +{ +ASM_START + cmp bx,#0x0100 + je dispi_set_bank_farcall_get + or bx,bx + jnz dispi_set_bank_farcall_error + mov ax,dx + push dx + push ax + mov ax,# VBE_DISPI_INDEX_BANK + mov dx,# VBE_DISPI_IOPORT_INDEX + out dx,ax + pop ax + mov dx,# VBE_DISPI_IOPORT_DATA + out dx,ax + in ax,dx + pop dx + cmp dx,ax + jne dispi_set_bank_farcall_error + mov ax, #0x004f + retf +dispi_set_bank_farcall_get: + mov ax,# VBE_DISPI_INDEX_BANK + mov dx,# VBE_DISPI_IOPORT_INDEX + out dx,ax + mov dx,# VBE_DISPI_IOPORT_DATA + in ax,dx + mov dx,ax + retf +dispi_set_bank_farcall_error: + mov ax,#0x014F + retf +ASM_END +} + +ASM_START +dispi_set_x_offset: + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_X_OFFSET + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + ret + +dispi_get_x_offset: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_X_OFFSET + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret + +dispi_set_y_offset: + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_Y_OFFSET + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + ret + +dispi_get_y_offset: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_Y_OFFSET + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret + +vga_set_virt_width: + push ax + push bx + push dx + mov bx, ax + call dispi_get_bpp + cmp al, #0x04 + ja set_width_svga + shr bx, #1 +set_width_svga: + shr bx, #3 + mov dx, # VGAREG_VGA_CRTC_ADDRESS + mov ah, bl + mov al, #0x13 + out dx, ax + pop dx + pop bx + pop ax + ret + +dispi_set_virt_width: + call vga_set_virt_width + push dx + push ax + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH + out dx, ax + pop ax + mov dx, # VBE_DISPI_IOPORT_DATA + out dx, ax + pop dx + ret + +dispi_get_virt_width: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret + +dispi_get_virt_height: + push dx + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_VIRT_HEIGHT + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + pop dx + ret + +_vga_compat_setup: + push ax + push dx + + ; set CRT X resolution + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_XRES + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + push ax + mov dx, # VGAREG_VGA_CRTC_ADDRESS + mov ax, #0x0011 + out dx, ax + pop ax + push ax + shr ax, #3 + dec ax + mov ah, al + mov al, #0x01 + out dx, ax + pop ax + call vga_set_virt_width + + ; set CRT Y resolution + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_YRES + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + dec ax + push ax + mov dx, # VGAREG_VGA_CRTC_ADDRESS + mov ah, al + mov al, #0x12 + out dx, ax + pop ax + mov al, #0x07 + out dx, al + inc dx + in al, dx + and al, #0xbd + test ah, #0x01 + jz bit8_clear + or al, #0x02 +bit8_clear: + test ah, #0x02 + jz bit9_clear + or al, #0x40 +bit9_clear: + out dx, al + + ; other settings + mov dx, # VGAREG_VGA_CRTC_ADDRESS + mov ax, #0x0009 + out dx, ax + mov al, #0x17 + out dx, al + mov dx, # VGAREG_VGA_CRTC_DATA + in al, dx + or al, #0x03 + out dx, al + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x10 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + or al, #0x01 + mov dx, # VGAREG_ACTL_ADDRESS + out dx, al + mov al, #0x20 + out dx, al + mov dx, # VGAREG_GRDC_ADDRESS + mov ax, #0x0506 + out dx, ax + mov dx, # VGAREG_SEQU_ADDRESS + mov ax, #0x0f02 + out dx, ax + + ; settings for >= 8bpp + mov dx, # VBE_DISPI_IOPORT_INDEX + mov ax, # VBE_DISPI_INDEX_BPP + out dx, ax + mov dx, # VBE_DISPI_IOPORT_DATA + in ax, dx + cmp al, #0x08 + jb vga_compat_end + mov dx, # VGAREG_VGA_CRTC_ADDRESS + mov al, #0x14 + out dx, al + mov dx, # VGAREG_VGA_CRTC_DATA + in al, dx + or al, #0x40 + out dx, al + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x10 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + or al, #0x40 + mov dx, # VGAREG_ACTL_ADDRESS + out dx, al + mov al, #0x20 + out dx, al + mov dx, # VGAREG_SEQU_ADDRESS + mov al, #0x04 + out dx, al + mov dx, # VGAREG_SEQU_DATA + in al, dx + or al, #0x08 + out dx, al + mov dx, # VGAREG_GRDC_ADDRESS + mov al, #0x05 + out dx, al + mov dx, # VGAREG_GRDC_DATA + in al, dx + and al, #0x9f + or al, #0x40 + out dx, al + +vga_compat_end: + pop dx + pop ax +ASM_END + + +// ModeInfo helper function +static ModeInfoListItem* mode_info_find_mode(mode, using_lfb) + Bit16u mode; Boolean using_lfb; +{ + ModeInfoListItem *cur_info=&mode_info_list; + + while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST) + { + if (cur_info->mode == mode) + { + if (!using_lfb) + { + return cur_info; + } + else if (cur_info->info.ModeAttributes & VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE) + { + return cur_info; + } + else + { + cur_info++; + } + } + else + { + cur_info++; + } + } + + return 0; +} + +ASM_START + +; Has VBE display - Returns true if VBE display detected + +_vbe_has_vbe_display: + push ds + push bx + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_VBE_FLAG + mov al, [bx] + and al, #0x01 + xor ah, ah + pop bx + pop ds + ret + +; VBE Init - Initialise the Vesa Bios Extension Code +; This function does a sanity check on the host side display code interface. + +vbe_init: + mov ax, # VBE_DISPI_ID0 + call dispi_set_id + call dispi_get_id + cmp ax, # VBE_DISPI_ID0 + jne no_vbe_interface + push ds + push bx + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_VBE_FLAG + mov al, #0x01 + mov [bx], al + pop bx + pop ds + mov ax, # VBE_DISPI_ID4 + call dispi_set_id +no_vbe_interface: +#if defined(USE_BX_INFO) || defined(DEBUG) + mov bx, #msg_vbe_init + push bx + call _printf + inc sp + inc sp +#endif + ret + +; VBE Display Info - Display information on screen about the VBE + +vbe_display_info: + call _vbe_has_vbe_display + test ax, ax + jz no_vbe_flag + mov ax, #0xc000 + mov ds, ax + mov si, #_vbebios_info_string + jmp _display_string +no_vbe_flag: + mov ax, #0xc000 + mov ds, ax + mov si, #_no_vbebios_info_string + jmp _display_string +ASM_END + +/** Function 00h - Return VBE Controller Information + * + * Input: + * AX = 4F00h + * ES:DI = Pointer to buffer in which to place VbeInfoBlock structure + * (VbeSignature should be VBE2 when VBE 2.0 information is desired and + * the info block is 512 bytes in size) + * Output: + * AX = VBE Return Status + * + */ +void vbe_biosfn_return_controller_information(AX, ES, DI) +Bit16u *AX;Bit16u ES;Bit16u DI; +{ + Bit16u ss=get_SS(); + VbeInfoBlock vbe_info_block; + Bit16u status; + Bit16u result; + Bit16u vbe2_info; + Bit16u cur_mode=0; + Bit16u cur_ptr=34; + ModeInfoListItem *cur_info=&mode_info_list; + + status = read_word(ss, AX); + +#ifdef DEBUG + printf("VBE vbe_biosfn_return_vbe_info ES%x DI%x AX%x\n",ES,DI,status); +#endif + + vbe2_info = 0; +#ifdef VBE2_NO_VESA_CHECK +#else + // get vbe_info_block into local variable + memcpyb(ss, &vbe_info_block, ES, DI, sizeof(vbe_info_block)); + + // check for VBE2 signature + if (((vbe_info_block.VbeSignature[0] == 'V') && + (vbe_info_block.VbeSignature[1] == 'B') && + (vbe_info_block.VbeSignature[2] == 'E') && + (vbe_info_block.VbeSignature[3] == '2')) || + + ((vbe_info_block.VbeSignature[0] == 'V') && + (vbe_info_block.VbeSignature[1] == 'E') && + (vbe_info_block.VbeSignature[2] == 'S') && + (vbe_info_block.VbeSignature[3] == 'A')) ) + { + vbe2_info = 1; +#ifdef DEBUG + printf("VBE correct VESA/VBE2 signature found\n"); +#endif + } +#endif + + // VBE Signature + vbe_info_block.VbeSignature[0] = 'V'; + vbe_info_block.VbeSignature[1] = 'E'; + vbe_info_block.VbeSignature[2] = 'S'; + vbe_info_block.VbeSignature[3] = 'A'; + + // VBE Version supported + vbe_info_block.VbeVersion = 0x0200; + + // OEM String + vbe_info_block.OemStringPtr_Seg = 0xc000; + vbe_info_block.OemStringPtr_Off = &vbebios_copyright; + + // Capabilities + vbe_info_block.Capabilities[0] = VBE_CAPABILITY_8BIT_DAC; + vbe_info_block.Capabilities[1] = 0; + vbe_info_block.Capabilities[2] = 0; + vbe_info_block.Capabilities[3] = 0; + + // VBE Video Mode Pointer (dynamicly generated from the mode_info_list) + vbe_info_block.VideoModePtr_Seg= ES ; + vbe_info_block.VideoModePtr_Off= DI + 34; + + // VBE Total Memory (in 64b blocks) + vbe_info_block.TotalMemory = VBE_TOTAL_VIDEO_MEMORY_DIV_64K; + + if (vbe2_info) + { + // OEM Stuff + vbe_info_block.OemSoftwareRev = VBE_OEM_SOFTWARE_REV; + vbe_info_block.OemVendorNamePtr_Seg = 0xc000; + vbe_info_block.OemVendorNamePtr_Off = &vbebios_vendor_name; + vbe_info_block.OemProductNamePtr_Seg = 0xc000; + vbe_info_block.OemProductNamePtr_Off = &vbebios_product_name; + vbe_info_block.OemProductRevPtr_Seg = 0xc000; + vbe_info_block.OemProductRevPtr_Off = &vbebios_product_revision; + + // copy updates in vbe_info_block back + memcpyb(ES, DI, ss, &vbe_info_block, sizeof(vbe_info_block)); + } + else + { + // copy updates in vbe_info_block back (VBE 1.x compatibility) + memcpyb(ES, DI, ss, &vbe_info_block, 256); + } + + do + { + if ((cur_info->info.XResolution <= dispi_get_max_xres()) && + (cur_info->info.BitsPerPixel <= dispi_get_max_bpp())) { +#ifdef DEBUG + printf("VBE found mode %x => %x\n", cur_info->mode,cur_mode); +#endif + write_word(ES, DI + cur_ptr, cur_info->mode); + cur_mode++; + cur_ptr+=2; + } else { +#ifdef DEBUG + printf("VBE mode %x (xres=%x / bpp=%02x) not supported by display\n", cur_info->mode,cur_info->info.XResolution,cur_info->info.BitsPerPixel); +#endif + } + cur_info++; + } while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST); + + // Add vesa mode list terminator + write_word(ES, DI + cur_ptr, cur_info->mode); + + result = 0x4f; + + write_word(ss, AX, result); +} + + +/** Function 01h - Return VBE Mode Information + * + * Input: + * AX = 4F01h + * CX = Mode Number + * ES:DI = Pointer to buffer in which to place ModeInfoBlock structure + * Output: + * AX = VBE Return Status + * + */ +void vbe_biosfn_return_mode_information(AX, CX, ES, DI) +Bit16u *AX;Bit16u CX; Bit16u ES;Bit16u DI; +{ + Bit16u result=0x0100; + Bit16u ss=get_SS(); + ModeInfoBlock info; + ModeInfoListItem *cur_info; + Boolean using_lfb; + +#ifdef DEBUG + printf("VBE vbe_biosfn_return_mode_information ES%x DI%x CX%x\n",ES,DI,CX); +#endif + + using_lfb=((CX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER); + + CX = (CX & 0x1ff); + + cur_info = mode_info_find_mode(CX, using_lfb, &cur_info); + + if (cur_info != 0) + { +#ifdef DEBUG + printf("VBE found mode %x\n",CX); +#endif + memsetb(ss, &info, 0, sizeof(ModeInfoBlock)); + memcpyb(ss, &info, 0xc000, &(cur_info->info), sizeof(ModeInfoBlockCompact)); + if (using_lfb) { + info.NumberOfBanks = 1; + } + if (info.WinAAttributes & VBE_WINDOW_ATTRIBUTE_RELOCATABLE) { + info.WinFuncPtr = 0xC0000000UL; + *(Bit16u *)&(info.WinFuncPtr) = (Bit16u)(dispi_set_bank_farcall); + } + + result = 0x4f; + } + else + { +#ifdef DEBUG + printf("VBE *NOT* found mode %x\n",CX); +#endif + result = 0x100; + } + + if (result == 0x4f) + { + // copy updates in mode_info_block back + memcpyb(ES, DI, ss, &info, sizeof(info)); + } + + write_word(ss, AX, result); +} + +/** Function 02h - Set VBE Mode + * + * Input: + * AX = 4F02h + * BX = Desired Mode to set + * ES:DI = Pointer to CRTCInfoBlock structure + * Output: + * AX = VBE Return Status + * + */ +void vbe_biosfn_set_mode(AX, BX, ES, DI) +Bit16u *AX;Bit16u BX; Bit16u ES;Bit16u DI; +{ + Bit16u ss = get_SS(); + Bit16u result; + ModeInfoListItem *cur_info; + Boolean using_lfb; + Bit8u no_clear; + Bit8u lfb_flag; + + using_lfb=((BX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER); + lfb_flag=using_lfb?VBE_DISPI_LFB_ENABLED:0; + no_clear=((BX & VBE_MODE_PRESERVE_DISPLAY_MEMORY) == VBE_MODE_PRESERVE_DISPLAY_MEMORY)?VBE_DISPI_NOCLEARMEM:0; + + BX = (BX & 0x1ff); + + //result=read_word(ss,AX); + + // check for non vesa mode + if (BX<VBE_MODE_VESA_DEFINED) + { + Bit8u mode; + + dispi_set_enable(VBE_DISPI_DISABLED); + // call the vgabios in order to set the video mode + // this allows for going back to textmode with a VBE call (some applications expect that to work) + + mode=(BX & 0xff); + biosfn_set_video_mode(mode); + result = 0x4f; + } + + cur_info = mode_info_find_mode(BX, using_lfb, &cur_info); + + if (cur_info != 0) + { +#ifdef DEBUG + printf("VBE found mode %x, setting:\n", BX); + printf("\txres%x yres%x bpp%x\n", + cur_info->info.XResolution, + cur_info->info.YResolution, + cur_info->info.BitsPerPixel); +#endif + + // first disable current mode (when switching between vesa modi) + dispi_set_enable(VBE_DISPI_DISABLED); + + if (cur_info->info.BitsPerPixel == 4) + { + biosfn_set_video_mode(0x6a); + } + + dispi_set_bpp(cur_info->info.BitsPerPixel); + dispi_set_xres(cur_info->info.XResolution); + dispi_set_yres(cur_info->info.YResolution); + dispi_set_bank(0); + dispi_set_enable(VBE_DISPI_ENABLED | no_clear | lfb_flag); + vga_compat_setup(); + + write_word(BIOSMEM_SEG,BIOSMEM_VBE_MODE,BX); + write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60 | no_clear)); + + result = 0x4f; + } + else + { +#ifdef DEBUG + printf("VBE *NOT* found mode %x\n" , BX); +#endif + result = 0x100; + + // FIXME: redirect non VBE modi to normal VGA bios operation + // (switch back to VGA mode + if (BX == 3) + result = 0x4f; + } + + write_word(ss, AX, result); +} + +/** Function 03h - Return Current VBE Mode + * + * Input: + * AX = 4F03h + * Output: + * AX = VBE Return Status + * BX = Current VBE Mode + * + */ +ASM_START +vbe_biosfn_return_current_mode: + push ds + mov ax, # BIOSMEM_SEG + mov ds, ax + call dispi_get_enable + and ax, # VBE_DISPI_ENABLED + jz no_vbe_mode + mov bx, # BIOSMEM_VBE_MODE + mov ax, [bx] + mov bx, ax + jnz vbe_03_ok +no_vbe_mode: + mov bx, # BIOSMEM_CURRENT_MODE + mov al, [bx] + mov bl, al + xor bh, bh +vbe_03_ok: + mov ax, #0x004f + pop ds + ret +ASM_END + + +Bit16u vbe_biosfn_read_video_state_size() +{ + return 9 * 2; +} + +void vbe_biosfn_save_video_state(ES, BX) + Bit16u ES; Bit16u BX; +{ + Bit16u enable, i; + + outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE); + enable = inw(VBE_DISPI_IOPORT_DATA); + write_word(ES, BX, enable); + BX += 2; + if (!(enable & VBE_DISPI_ENABLED)) + return; + for(i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) { + if (i != VBE_DISPI_INDEX_ENABLE) { + outw(VBE_DISPI_IOPORT_INDEX, i); + write_word(ES, BX, inw(VBE_DISPI_IOPORT_DATA)); + BX += 2; + } + } +} + + +void vbe_biosfn_restore_video_state(ES, BX) + Bit16u ES; Bit16u BX; +{ + Bit16u enable, i; + + enable = read_word(ES, BX); + BX += 2; + + if (!(enable & VBE_DISPI_ENABLED)) { + outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE); + outw(VBE_DISPI_IOPORT_DATA, enable); + } else { + outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES); + outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); + BX += 2; + outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES); + outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); + BX += 2; + outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP); + outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); + BX += 2; + outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE); + outw(VBE_DISPI_IOPORT_DATA, enable); + + for(i = VBE_DISPI_INDEX_BANK; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) { + outw(VBE_DISPI_IOPORT_INDEX, i); + outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); + BX += 2; + } + } +} + +/** Function 04h - Save/Restore State + * + * Input: + * AX = 4F04h + * DL = 00h Return Save/Restore State buffer size + * 01h Save State + * 02h Restore State + * CX = Requested states + * ES:BX = Pointer to buffer (if DL <> 00h) + * Output: + * AX = VBE Return Status + * BX = Number of 64-byte blocks to hold the state buffer (if DL=00h) + * + */ +void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX) +Bit16u *AX; Bit16u CX; Bit16u DX; Bit16u ES; Bit16u *BX; +{ + Bit16u ss=get_SS(); + Bit16u result, val; + + result = 0x4f; + switch(GET_DL()) { + case 0x00: + val = biosfn_read_video_state_size2(CX); +#ifdef DEBUG + printf("VGA state size=%x\n", val); +#endif + if (CX & 8) + val += vbe_biosfn_read_video_state_size(); + write_word(ss, BX, val); + break; + case 0x01: + val = read_word(ss, BX); + val = biosfn_save_video_state(CX, ES, val); +#ifdef DEBUG + printf("VGA save_state offset=%x\n", val); +#endif + if (CX & 8) + vbe_biosfn_save_video_state(ES, val); + break; + case 0x02: + val = read_word(ss, BX); + val = biosfn_restore_video_state(CX, ES, val); +#ifdef DEBUG + printf("VGA restore_state offset=%x\n", val); +#endif + if (CX & 8) + vbe_biosfn_restore_video_state(ES, val); + break; + default: + // function failed + result = 0x100; + break; + } + write_word(ss, AX, result); +} + +/** Function 05h - Display Window Control + * + * Input: + * AX = 4F05h + * (16-bit) BH = 00h Set memory window + * = 01h Get memory window + * BL = Window number + * = 00h Window A + * = 01h Window B + * DX = Window number in video memory in window + * granularity units (Set Memory Window only) + * Note: + * If this function is called while in a linear frame buffer mode, + * this function must fail with completion code AH=03h + * + * Output: + * AX = VBE Return Status + * DX = Window number in window granularity units + * (Get Memory Window only) + */ +ASM_START +vbe_biosfn_display_window_control: + cmp bl, #0x00 + jne vbe_05_failed + cmp bh, #0x01 + je get_display_window + jb set_display_window + mov ax, #0x0100 + ret +set_display_window: + mov ax, dx + call _dispi_set_bank + call dispi_get_bank + cmp ax, dx + jne vbe_05_failed + mov ax, #0x004f + ret +get_display_window: + call dispi_get_bank + mov dx, ax + mov ax, #0x004f + ret +vbe_05_failed: + mov ax, #0x014f + ret +ASM_END + + +/** Function 06h - Set/Get Logical Scan Line Length + * + * Input: + * AX = 4F06h + * BL = 00h Set Scan Line Length in Pixels + * = 01h Get Scan Line Length + * = 02h Set Scan Line Length in Bytes + * = 03h Get Maximum Scan Line Length + * CX = If BL=00h Desired Width in Pixels + * If BL=02h Desired Width in Bytes + * (Ignored for Get Functions) + * + * Output: + * AX = VBE Return Status + * BX = Bytes Per Scan Line + * CX = Actual Pixels Per Scan Line + * (truncated to nearest complete pixel) + * DX = Maximum Number of Scan Lines + */ +ASM_START +vbe_biosfn_set_get_logical_scan_line_length: + mov ax, cx + cmp bl, #0x01 + je get_logical_scan_line_length + cmp bl, #0x02 + je set_logical_scan_line_bytes + jb set_logical_scan_line_pixels + mov ax, #0x0100 + ret +set_logical_scan_line_bytes: + push ax + call dispi_get_bpp + xor bh, bh + mov bl, ah + or bl, bl + jnz no_4bpp_1 + shl ax, #3 + mov bl, #1 +no_4bpp_1: + xor dx, dx + pop ax + div bx +set_logical_scan_line_pixels: + call dispi_set_virt_width +get_logical_scan_line_length: + call dispi_get_bpp + xor bh, bh + mov bl, ah + call dispi_get_virt_width + mov cx, ax + or bl, bl + jnz no_4bpp_2 + shr ax, #3 + mov bl, #1 +no_4bpp_2: + mul bx + mov bx, ax + call dispi_get_virt_height + mov dx, ax + mov ax, #0x004f + ret +ASM_END + + +/** Function 07h - Set/Get Display Start + * + * Input(16-bit): + * AX = 4F07h + * BH = 00h Reserved and must be 00h + * BL = 00h Set Display Start + * = 01h Get Display Start + * = 02h Schedule Display Start (Alternate) + * = 03h Schedule Stereoscopic Display Start + * = 04h Get Scheduled Display Start Status + * = 05h Enable Stereoscopic Mode + * = 06h Disable Stereoscopic Mode + * = 80h Set Display Start during Vertical Retrace + * = 82h Set Display Start during Vertical Retrace (Alternate) + * = 83h Set Stereoscopic Display Start during Vertical Retrace + * ECX = If BL=02h/82h Display Start Address in bytes + * If BL=03h/83h Left Image Start Address in bytes + * EDX = If BL=03h/83h Right Image Start Address in bytes + * CX = If BL=00h/80h First Displayed Pixel In Scan Line + * DX = If BL=00h/80h First Displayed Scan Line + * + * Output: + * AX = VBE Return Status + * BH = If BL=01h Reserved and will be 0 + * CX = If BL=01h First Displayed Pixel In Scan Line + * If BL=04h 0 if flip has not occurred, not 0 if it has + * DX = If BL=01h First Displayed Scan Line + * + * Input(32-bit): + * BH = 00h Reserved and must be 00h + * BL = 00h Set Display Start + * = 80h Set Display Start during Vertical Retrace + * CX = Bits 0-15 of display start address + * DX = Bits 16-31 of display start address + * ES = Selector for memory mapped registers + */ +ASM_START +vbe_biosfn_set_get_display_start: + cmp bl, #0x80 + je set_display_start + cmp bl, #0x01 + je get_display_start + jb set_display_start + mov ax, #0x0100 + ret +set_display_start: + mov ax, cx + call dispi_set_x_offset + mov ax, dx + call dispi_set_y_offset + mov ax, #0x004f + ret +get_display_start: + call dispi_get_x_offset + mov cx, ax + call dispi_get_y_offset + mov dx, ax + xor bh, bh + mov ax, #0x004f + ret +ASM_END + + +/** Function 08h - Set/Get Dac Palette Format + * + * Input: + * AX = 4F08h + * BL = 00h set DAC palette width + * = 01h get DAC palette width + * BH = If BL=00h: desired number of bits per primary color + * Output: + * AX = VBE Return Status + * BH = current number of bits per primary color (06h = standard VGA) + */ +ASM_START +vbe_biosfn_set_get_dac_palette_format: + cmp bl, #0x01 + je get_dac_palette_format + jb set_dac_palette_format + mov ax, #0x0100 + ret +set_dac_palette_format: + call dispi_get_enable + cmp bh, #0x06 + je set_normal_dac + cmp bh, #0x08 + jne vbe_08_unsupported + or ax, # VBE_DISPI_8BIT_DAC + jnz set_dac_mode +set_normal_dac: + and ax, #~ VBE_DISPI_8BIT_DAC +set_dac_mode: + call _dispi_set_enable +get_dac_palette_format: + mov bh, #0x06 + call dispi_get_enable + and ax, # VBE_DISPI_8BIT_DAC + jz vbe_08_ok + mov bh, #0x08 +vbe_08_ok: + mov ax, #0x004f + ret +vbe_08_unsupported: + mov ax, #0x014f + ret +ASM_END + + +/** Function 09h - Set/Get Palette Data + * + * Input: + * AX = 4F09h + * Output: + * AX = VBE Return Status + * + * FIXME: incomplete API description, Input & Output + */ +void vbe_biosfn_set_get_palette_data(AX) +{ +} + +/** Function 0Ah - Return VBE Protected Mode Interface + * Input: AX = 4F0Ah VBE 2.0 Protected Mode Interface + * BL = 00h Return protected mode table + * + * + * Output: AX = Status + * ES = Real Mode Segment of Table + * DI = Offset of Table + * CX = Length of Table including protected mode code + * (for copying purposes) + */ +ASM_START +vbe_biosfn_return_protected_mode_interface: + test bl, bl + jnz _fail + mov di, #0xc000 + mov es, di + mov di, # vesa_pm_start + mov cx, # vesa_pm_end + sub cx, di + mov ax, #0x004f + ret +_fail: + mov ax, #0x014f + ret +ASM_END diff --git a/kvm/vgabios/vbe.h b/kvm/vgabios/vbe.h new file mode 100644 index 000000000..60434ac7d --- /dev/null +++ b/kvm/vgabios/vbe.h @@ -0,0 +1,313 @@ +#ifndef vbe_h_included +#define vbe_h_included + +#include "vgabios.h" + +// DISPI helper function +void dispi_set_enable(enable); + +/** VBE int10 API + * + * See the function descriptions in vbe.c for more information + */ +Boolean vbe_has_vbe_display(); +void vbe_biosfn_return_controller_information(AX, ES, DI); +void vbe_biosfn_return_mode_information(AX, CX, ES, DI); +void vbe_biosfn_set_mode(AX, BX, ES, DI); +void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX); +void vbe_biosfn_set_get_palette_data(AX); +void vbe_biosfn_return_protected_mode_interface(AX); + +// The official VBE Information Block +typedef struct VbeInfoBlock +{ + Bit8u VbeSignature[4]; + Bit16u VbeVersion; + Bit16u OemStringPtr_Off; + Bit16u OemStringPtr_Seg; + Bit8u Capabilities[4]; + Bit16u VideoModePtr_Off; + Bit16u VideoModePtr_Seg; + Bit16u TotalMemory; + Bit16u OemSoftwareRev; + Bit16u OemVendorNamePtr_Off; + Bit16u OemVendorNamePtr_Seg; + Bit16u OemProductNamePtr_Off; + Bit16u OemProductNamePtr_Seg; + Bit16u OemProductRevPtr_Off; + Bit16u OemProductRevPtr_Seg; + Bit16u Reserved[111]; // used for dynamicly generated mode list + Bit8u OemData[256]; +} VbeInfoBlock; + + +// This one is for compactly storing a static list of mode info blocks +// this saves us 189 bytes per block +typedef struct ModeInfoBlockCompact +{ +// Mandatory information for all VBE revisions + Bit16u ModeAttributes; + Bit8u WinAAttributes; + Bit8u WinBAttributes; + Bit16u WinGranularity; + Bit16u WinSize; + Bit16u WinASegment; + Bit16u WinBSegment; + Bit32u WinFuncPtr; + Bit16u BytesPerScanLine; +// Mandatory information for VBE 1.2 and above + Bit16u XResolution; + Bit16u YResolution; + Bit8u XCharSize; + Bit8u YCharSize; + Bit8u NumberOfPlanes; + Bit8u BitsPerPixel; + Bit8u NumberOfBanks; + Bit8u MemoryModel; + Bit8u BankSize; + Bit8u NumberOfImagePages; + Bit8u Reserved_page; +// Direct Color fields (required for direct/6 and YUV/7 memory models) + Bit8u RedMaskSize; + Bit8u RedFieldPosition; + Bit8u GreenMaskSize; + Bit8u GreenFieldPosition; + Bit8u BlueMaskSize; + Bit8u BlueFieldPosition; + Bit8u RsvdMaskSize; + Bit8u RsvdFieldPosition; + Bit8u DirectColorModeInfo; +// Mandatory information for VBE 2.0 and above + Bit32u PhysBasePtr; + Bit32u OffScreenMemOffset; + Bit16u OffScreenMemSize; +// Mandatory information for VBE 3.0 and above + Bit16u LinBytesPerScanLine; + Bit8u BnkNumberOfPages; + Bit8u LinNumberOfPages; + Bit8u LinRedMaskSize; + Bit8u LinRedFieldPosition; + Bit8u LinGreenMaskSize; + Bit8u LinGreenFieldPosition; + Bit8u LinBlueMaskSize; + Bit8u LinBlueFieldPosition; + Bit8u LinRsvdMaskSize; + Bit8u LinRsvdFieldPosition; + Bit32u MaxPixelClock; +// Bit8u Reserved[189]; // DO NOT PUT THIS IN HERE because of Compact Mode Info storage in bios +} ModeInfoBlockCompact; + +typedef struct ModeInfoBlock +{ +// Mandatory information for all VBE revisions + Bit16u ModeAttributes; + Bit8u WinAAttributes; + Bit8u WinBAttributes; + Bit16u WinGranularity; + Bit16u WinSize; + Bit16u WinASegment; + Bit16u WinBSegment; + Bit32u WinFuncPtr; + Bit16u BytesPerScanLine; +// Mandatory information for VBE 1.2 and above + Bit16u XResolution; + Bit16u YResolution; + Bit8u XCharSize; + Bit8u YCharSize; + Bit8u NumberOfPlanes; + Bit8u BitsPerPixel; + Bit8u NumberOfBanks; + Bit8u MemoryModel; + Bit8u BankSize; + Bit8u NumberOfImagePages; + Bit8u Reserved_page; +// Direct Color fields (required for direct/6 and YUV/7 memory models) + Bit8u RedMaskSize; + Bit8u RedFieldPosition; + Bit8u GreenMaskSize; + Bit8u GreenFieldPosition; + Bit8u BlueMaskSize; + Bit8u BlueFieldPosition; + Bit8u RsvdMaskSize; + Bit8u RsvdFieldPosition; + Bit8u DirectColorModeInfo; +// Mandatory information for VBE 2.0 and above + Bit32u PhysBasePtr; + Bit32u OffScreenMemOffset; + Bit16u OffScreenMemSize; +// Mandatory information for VBE 3.0 and above + Bit16u LinBytesPerScanLine; + Bit8u BnkNumberOfPages; + Bit8u LinNumberOfPages; + Bit8u LinRedMaskSize; + Bit8u LinRedFieldPosition; + Bit8u LinGreenMaskSize; + Bit8u LinGreenFieldPosition; + Bit8u LinBlueMaskSize; + Bit8u LinBlueFieldPosition; + Bit8u LinRsvdMaskSize; + Bit8u LinRsvdFieldPosition; + Bit32u MaxPixelClock; + Bit8u Reserved[189]; +} ModeInfoBlock; + +typedef struct ModeInfoListItem +{ + Bit16u mode; + ModeInfoBlockCompact info; +} ModeInfoListItem; + +// VBE Return Status Info +// AL +#define VBE_RETURN_STATUS_SUPPORTED 0x4F +#define VBE_RETURN_STATUS_UNSUPPORTED 0x00 +// AH +#define VBE_RETURN_STATUS_SUCCESSFULL 0x00 +#define VBE_RETURN_STATUS_FAILED 0x01 +#define VBE_RETURN_STATUS_NOT_SUPPORTED 0x02 +#define VBE_RETURN_STATUS_INVALID 0x03 + +// VBE Mode Numbers + +#define VBE_MODE_VESA_DEFINED 0x0100 +#define VBE_MODE_REFRESH_RATE_USE_CRTC 0x0800 +#define VBE_MODE_LINEAR_FRAME_BUFFER 0x4000 +#define VBE_MODE_PRESERVE_DISPLAY_MEMORY 0x8000 + +// VBE GFX Mode Number + +#define VBE_VESA_MODE_640X400X8 0x100 +#define VBE_VESA_MODE_640X480X8 0x101 +#define VBE_VESA_MODE_800X600X4 0x102 +#define VBE_VESA_MODE_800X600X8 0x103 +#define VBE_VESA_MODE_1024X768X4 0x104 +#define VBE_VESA_MODE_1024X768X8 0x105 +#define VBE_VESA_MODE_1280X1024X4 0x106 +#define VBE_VESA_MODE_1280X1024X8 0x107 +#define VBE_VESA_MODE_320X200X1555 0x10D +#define VBE_VESA_MODE_320X200X565 0x10E +#define VBE_VESA_MODE_320X200X888 0x10F +#define VBE_VESA_MODE_640X480X1555 0x110 +#define VBE_VESA_MODE_640X480X565 0x111 +#define VBE_VESA_MODE_640X480X888 0x112 +#define VBE_VESA_MODE_800X600X1555 0x113 +#define VBE_VESA_MODE_800X600X565 0x114 +#define VBE_VESA_MODE_800X600X888 0x115 +#define VBE_VESA_MODE_1024X768X1555 0x116 +#define VBE_VESA_MODE_1024X768X565 0x117 +#define VBE_VESA_MODE_1024X768X888 0x118 +#define VBE_VESA_MODE_1280X1024X1555 0x119 +#define VBE_VESA_MODE_1280X1024X565 0x11A +#define VBE_VESA_MODE_1280X1024X888 0x11B +#define VBE_VESA_MODE_1600X1200X8 0x11C +#define VBE_VESA_MODE_1600X1200X1555 0x11D +#define VBE_VESA_MODE_1600X1200X565 0x11E +#define VBE_VESA_MODE_1600X1200X888 0x11F + +// BOCHS/PLEX86 'own' mode numbers +#define VBE_OWN_MODE_320X200X8888 0x140 +#define VBE_OWN_MODE_640X400X8888 0x141 +#define VBE_OWN_MODE_640X480X8888 0x142 +#define VBE_OWN_MODE_800X600X8888 0x143 +#define VBE_OWN_MODE_1024X768X8888 0x144 +#define VBE_OWN_MODE_1280X1024X8888 0x145 +#define VBE_OWN_MODE_320X200X8 0x146 +#define VBE_OWN_MODE_1600X1200X8888 0x147 +#define VBE_OWN_MODE_1152X864X8 0x148 +#define VBE_OWN_MODE_1152X864X1555 0x149 +#define VBE_OWN_MODE_1152X864X565 0x14a +#define VBE_OWN_MODE_1152X864X888 0x14b +#define VBE_OWN_MODE_1152X864X8888 0x14c + +#define VBE_VESA_MODE_END_OF_LIST 0xFFFF + +// Capabilities + +#define VBE_CAPABILITY_8BIT_DAC 0x0001 +#define VBE_CAPABILITY_NOT_VGA_COMPATIBLE 0x0002 +#define VBE_CAPABILITY_RAMDAC_USE_BLANK_BIT 0x0004 +#define VBE_CAPABILITY_STEREOSCOPIC_SUPPORT 0x0008 +#define VBE_CAPABILITY_STEREO_VIA_VESA_EVC 0x0010 + +// Mode Attributes + +#define VBE_MODE_ATTRIBUTE_SUPPORTED 0x0001 +#define VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE 0x0002 +#define VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT 0x0004 +#define VBE_MODE_ATTRIBUTE_COLOR_MODE 0x0008 +#define VBE_MODE_ATTRIBUTE_GRAPHICS_MODE 0x0010 +#define VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE 0x0020 +#define VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW 0x0040 +#define VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE 0x0080 +#define VBE_MODE_ATTRIBUTE_DOUBLE_SCAN_MODE 0x0100 +#define VBE_MODE_ATTRIBUTE_INTERLACE_MODE 0x0200 +#define VBE_MODE_ATTRIBUTE_HARDWARE_TRIPLE_BUFFER 0x0400 +#define VBE_MODE_ATTRIBUTE_HARDWARE_STEREOSCOPIC_DISPLAY 0x0800 +#define VBE_MODE_ATTRIBUTE_DUAL_DISPLAY_START_ADDRESS 0x1000 + +#define VBE_MODE_ATTTRIBUTE_LFB_ONLY ( VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW | VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE ) + +// Window attributes + +#define VBE_WINDOW_ATTRIBUTE_RELOCATABLE 0x01 +#define VBE_WINDOW_ATTRIBUTE_READABLE 0x02 +#define VBE_WINDOW_ATTRIBUTE_WRITEABLE 0x04 + +// Memory model + +#define VBE_MEMORYMODEL_TEXT_MODE 0x00 +#define VBE_MEMORYMODEL_CGA_GRAPHICS 0x01 +#define VBE_MEMORYMODEL_HERCULES_GRAPHICS 0x02 +#define VBE_MEMORYMODEL_PLANAR 0x03 +#define VBE_MEMORYMODEL_PACKED_PIXEL 0x04 +#define VBE_MEMORYMODEL_NON_CHAIN_4_256 0x05 +#define VBE_MEMORYMODEL_DIRECT_COLOR 0x06 +#define VBE_MEMORYMODEL_YUV 0x07 + +// DirectColorModeInfo + +#define VBE_DIRECTCOLOR_COLOR_RAMP_PROGRAMMABLE 0x01 +#define VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE 0x02 + +// GUEST <-> HOST Communication API + +// FIXME: either dynamicly ask host for this or put somewhere high in physical memory +// like 0xE0000000 + + + #define VBE_DISPI_BANK_ADDRESS 0xA0000 + #define VBE_DISPI_BANK_SIZE_KB 64 + + #define VBE_DISPI_MAX_XRES 1024 + #define VBE_DISPI_MAX_YRES 768 + + #define VBE_DISPI_IOPORT_INDEX 0x01CE + #define VBE_DISPI_IOPORT_DATA 0x01CF + + #define VBE_DISPI_INDEX_ID 0x0 + #define VBE_DISPI_INDEX_XRES 0x1 + #define VBE_DISPI_INDEX_YRES 0x2 + #define VBE_DISPI_INDEX_BPP 0x3 + #define VBE_DISPI_INDEX_ENABLE 0x4 + #define VBE_DISPI_INDEX_BANK 0x5 + #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 + #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 + #define VBE_DISPI_INDEX_X_OFFSET 0x8 + #define VBE_DISPI_INDEX_Y_OFFSET 0x9 + + #define VBE_DISPI_ID0 0xB0C0 + #define VBE_DISPI_ID1 0xB0C1 + #define VBE_DISPI_ID2 0xB0C2 + #define VBE_DISPI_ID3 0xB0C3 + #define VBE_DISPI_ID4 0xB0C4 + + #define VBE_DISPI_DISABLED 0x00 + #define VBE_DISPI_ENABLED 0x01 + #define VBE_DISPI_GETCAPS 0x02 + #define VBE_DISPI_8BIT_DAC 0x20 + #define VBE_DISPI_LFB_ENABLED 0x40 + #define VBE_DISPI_NOCLEARMEM 0x80 + + #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + +#endif diff --git a/kvm/vgabios/vbe_display_api.txt b/kvm/vgabios/vbe_display_api.txt new file mode 100644 index 000000000..fddb78b4b --- /dev/null +++ b/kvm/vgabios/vbe_display_api.txt @@ -0,0 +1,237 @@ +VBE Display API +------------------------------------------------------------------------------------------------------------- + This document is part of the Bochs/VBEBios documentation, + it specifies the bochs host <-> vbebios client communication. + + That means, the display code implementation and the vbebios code depend + very heavily on each other. As such, this documents needs be synchronised + between bochs CVS and the vgabios CVS. + + This document does not describe how the VBEBios implements the VBE2/3 spec. + This document does not describe how the Bochs display code will display gfx based upon this spec. + + +API History +----------- +0xb0c0 supports the following VBE_DISPI_ interfaces (present in Bochs 1.4): + VBE_DISPI_INDEX_ID + VBE_DISPI_INDEX_XRES + VBE_DISPI_INDEX_YRES + VBE_DISPI_INDEX_BPP + VBE_DISPI_INDEX_ENABLE + VBE_DISPI_INDEX_BANK + + Bpp format supported is: + VBE_DISPI_BPP_8 + +0xb0c1 supports 0xb0c0 VBE_DISPI_ interfaces, additional interfaces (present in Bochs 2.0): + VBE_DISPI_INDEX_VIRT_WIDTH + VBE_DISPI_INDEX_VIRT_HEIGHT + VBE_DISPI_INDEX_X_OFFSET + VBE_DISPI_INDEX_Y_OFFSET + +0xb0c2 supports 0xb0c1 VBE_DISPI_ interfaces, interfaces updated for + additional features (present in Bochs 2.1): + VBE_DISPI_INDEX_BPP supports >8bpp color depth (value = bits) + VBE_DISPI_INDEX_ENABLE supports new flags VBE_DISPI_NOCLEARMEM and VBE_DISPI_LFB_ENABLED + VBE i/o registers changed from 0xFF80/81 to 0x01CE/CF + +0xb0c3 supports 0xb0c2 VBE_DISPI_ interfaces, interfaces updated for + additional features: + VBE_DISPI_INDEX_ENABLE supports new flags VBE_DISPI_GETCAPS and VBE_DISPI_8BIT_DAC + +0xb0c4 VBE video memory increased to 8 MB + + +History +------- + Version 0.6 2002 Nov 23 Jeroen Janssen + - Added LFB support + - Added Virt width, height and x,y offset + + Version 0.5 2002 March 08 Jeroen Janssen + - Added documentation about panic behaviour / current limits of the data values. + - Changed BPP API (in order to include future (A)RGB formats) + - Initial version (based upon extended display text of the vbe bochs display patch) + + +Todo +---- + Version 0.6+ [random order] + - Add lots of different (A)RGB formats + +References +---------- + [VBE3] VBE 3 Specification at + http://www.vesa.org/vbe3.pdf + + [BOCHS] Bochs Open Source IA-32 Emulator at + http://bochs.sourceforge.net + + [VBEBIOS] VBE Bios for Bochs at + http://savannah.gnu.org/projects/vgabios/ + + [Screenshots] Screenshots of programs using the VBE Bios at + http://japj.org/projects/bochs_plex86/screenshots.html + +Abbreviations +------------- + VBE Vesa Bios Extension + DISPI (Bochs) Display Interface + BPP Bits Per Pixel + LFB Linear Frame Buffer + + +#defines +-------- +vbetables-gen.c + #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 8 + +vbe.h + #define VBE_DISPI_BANK_ADDRESS 0xA0000 + #define VBE_DISPI_BANK_SIZE_KB 64 + + #define VBE_DISPI_MAX_XRES 1024 + #define VBE_DISPI_MAX_YRES 768 + + #define VBE_DISPI_IOPORT_INDEX 0x01CE + #define VBE_DISPI_IOPORT_DATA 0x01CF + + #define VBE_DISPI_INDEX_ID 0x0 + #define VBE_DISPI_INDEX_XRES 0x1 + #define VBE_DISPI_INDEX_YRES 0x2 + #define VBE_DISPI_INDEX_BPP 0x3 + #define VBE_DISPI_INDEX_ENABLE 0x4 + #define VBE_DISPI_INDEX_BANK 0x5 + #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 + #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 + #define VBE_DISPI_INDEX_X_OFFSET 0x8 + #define VBE_DISPI_INDEX_Y_OFFSET 0x9 + + #define VBE_DISPI_ID0 0xB0C0 + #define VBE_DISPI_ID1 0xB0C1 + #define VBE_DISPI_ID2 0xB0C2 + #define VBE_DISPI_ID3 0xB0C3 + #define VBE_DISPI_ID4 0xB0C4 + + #define VBE_DISPI_DISABLED 0x00 + #define VBE_DISPI_ENABLED 0x01 + #define VBE_DISPI_VBE_ENABLED 0x40 + #define VBE_DISPI_NOCLEARMEM 0x80 + + #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + +API +--- + The display api works by using a index (VBE_DISPI_IOPORT_INDEX) and + data (VBE_DISPI_IOPORT_DATA) ioport. One writes the index of the parameter to the index port. + Next, the parameter value can be read or written. + +[0xb0c0] + * VBE_DISPI_INDEX_ID : WORD {R,W} + This parameter can be used to detect the current display API (both bochs & vbebios). + The bios writes VBE_DISPI_ID0 to the dataport and reads it back again. + This way, the display code knows the vbebios 'ID' and the vbebios can check if the correct + display code is present. + As a result, a PANIC can be generated if an incompatible vbebios/display code combination is detected. + This panic can be generated from the bochs display code (NOT the bios, see Notes). + + Example values: VBE_DISPI_ID0 + + * VBE_DISPI_INDEX_XRES : WORD {R,W} + This parameter can be used to read/write the vbe display X resolution (in pixels). + It's illegal to set the XRES when the VBE is enabled (display code should generate PANIC). + + If the value written exceeds VBE_DISPI_MAX_XRES, the display code needs to generate a PANIC. + + Example values: 320,640,800,1024 + + * VBE_DISPI_INDEX_YRES : WORD {R,W} + This parameter can be used to read/write the vbe display Y resolution (in pixels). + It's illegal to set the YRES when the VBE is enabled (display code should generate PANIC). + + If the value written exceeds VBE_DISPI_MAX_YRES, the display code needs to generate a PANIC. + + Example values: 200,400,480,600,768 + + * VBE_DISPI_INDEX_BPP : WORD {R,W} + This parameter can be used to read/write the vbe display BPP. + It's illegal to set the BPP when the VBE is enabled (display code should generate PANIC). + + If the value written is an incompatible BPP, the display code needs to generate a PANIC. + + Example values: VBE_DISPI_BPP_8 + + * VBE_DISPI_INDEX_ENABLE : WORD {R,W} + This parameter can be used to read/write the vbe ENABLED state. + If the bios writes VBE_DISPI_ENABLED then the display code will setup a hostside display mode + with the current XRES, YRES and BPP settings. + If the bios write VBE_DISPI_DISABLED then the display code will switch back to normal vga mode behaviour. + + Example values: VBE_DISPI_ENABLED, VBE_DISPI_DISABLED + + * VBE_DISPI_INDEX_BANK : WORD {R,W} + This parameter can be used to read/write the current selected BANK (at 0xA0000). + This can be used for switching banks in banked mode. + +[0xb0c1] + * VBE_DISPI_INDEX_VIRT_WIDTH : WORD {R,W} + This parameter can be used to read/write the current virtual width. + Upon enabling a mode, this will be set to the current xres + Setting this field during enabled mode will result in the virtual width to be changed. + Value will be adjusted if current setting is not possible. + + * VBE_DISPI_INDEX_VIRT_HEIGHT : WORD {R} + This parameter can be read in order to obtain the current virtual height. + This setting will be adjusted after setting a virtual width in order to stay within limit of video memory. + + * VBE_DISPI_INDEX_X_OFFSET : WORD {R,W} + The current X offset (in pixels!) of the visible screen part. + Writing a new offset will also result in a complete screen refresh. + + * VBE_DISPI_INDEX_Y_OFFSET : WORD {R,W} + The current Y offset (in pixels!) of the visible screen part. + Writing a new offset will also result in a complete screen refresh. + + +[0xb0c2] + * VBE_DISPI_INDEX_BPP : WORD {R,W} + The value written is now the number of bits per pixel. A value of 0 is treated + the same as 8 for backward compatibilty. These values are supported: 8, 15, + 16, 24 and 32. The value of 4 is not yet handled in the VBE code. + * VBE_DISPI_INDEX_ENABLE : WORD {R,W} + The new flag VBE_DISPI_NOCLEARMEM allows to preserve the VBE video memory. + The new flag VBE_DISPI_LFB_ENABLED indicates the usage of the LFB. + +[0xb0c3] + * VBE_DISPI_INDEX_ENABLE : WORD {R,W} + If the new flag VBE_DISPI_GETCAPS is enabled, the xres, yres and bpp registers + return the gui capabilities. + The new flag VBE_DISPI_8BIT_DAC switches the DAC to 8 bit mode. + +[0xb0c4] + * VBE_DISPI_TOTAL_VIDEO_MEMORY_MB set to 8 (moved to auto-generated vbetables.h) + +Displaying GFX (banked mode) +-------------- + What happens is that the total screen is devided in banks of 'VBE_DISPI_BANK_SIZE_KB' KiloByte in size. + If you want to set a pixel you can calculate its bank by doing: + + offset = pixel_x + pixel_y * resolution_x; + bank = offset / 64 Kb (rounded 1.9999 -> 1) + + bank_pixel_pos = offset - bank * 64Kb + + Now you can set the current bank and put the pixel at VBE_DISPI_BANK_ADDRESS + bank_pixel_pos + +Displaying GFX (linear frame buffer mode) +-------------- + NOT WRITTEN YET + +Notes +----- + * Since the XRES/YRES/BPP may not be written when VBE is enabled, if you want to switch from one VBE mode + to another, you will need to disable VBE first. + + * Note when the bios doesn't find a valid DISPI_ID, it can disable the VBE functions. This allows people to + use the same bios for both vbe enabled and disabled bochs executables. diff --git a/kvm/vgabios/vbetables-gen.c b/kvm/vgabios/vbetables-gen.c new file mode 100644 index 000000000..3bf979d3d --- /dev/null +++ b/kvm/vgabios/vbetables-gen.c @@ -0,0 +1,264 @@ +/* Generate the VGABIOS VBE Tables */ +#include <stdlib.h> +#include <stdio.h> + +#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 16 + +typedef struct { + int width; + int height; + int depth; + int mode; +} ModeInfo; + +ModeInfo modes[] = { + /* standard VESA modes */ +{ 640, 400, 8 , 0x100}, +{ 640, 480, 8 , 0x101}, +{ 800, 600, 4 , 0x102}, +{ 800, 600, 8 , 0x103}, +{ 1024, 768, 4 , 0x104}, +{ 1024, 768, 8 , 0x105}, +{ 1280, 1024, 4 , 0x106}, +{ 1280, 1024, 8 , 0x107}, +{ 320, 200, 15 , 0x10D}, +{ 320, 200, 16 , 0x10E}, +{ 320, 200, 24 , 0x10F}, +{ 640, 480, 15 , 0x110}, +{ 640, 480, 16 , 0x111}, +{ 640, 480, 24 , 0x112}, +{ 800, 600, 15 , 0x113}, +{ 800, 600, 16 , 0x114}, +{ 800, 600, 24 , 0x115}, +{ 1024, 768, 15 , 0x116}, +{ 1024, 768, 16 , 0x117}, +{ 1024, 768, 24 , 0x118}, +{ 1280, 1024, 15 , 0x119}, +{ 1280, 1024, 16 , 0x11A}, +{ 1280, 1024, 24 , 0x11B}, +{ 1600, 1200, 8 , 0x11C}, +{ 1600, 1200, 15 , 0x11D}, +{ 1600, 1200, 16 , 0x11E}, +{ 1600, 1200, 24 , 0x11F}, + + /* BOCHS/PLE, 86 'own' mode numbers */ +{ 320, 200, 32 , 0x140}, +{ 640, 400, 32 , 0x141}, +{ 640, 480, 32 , 0x142}, +{ 800, 600, 32 , 0x143}, +{ 1024, 768, 32 , 0x144}, +{ 1280, 1024, 32 , 0x145}, +{ 320, 200, 8 , 0x146}, +{ 1600, 1200, 32 , 0x147}, +{ 1152, 864, 8 , 0x148}, +{ 1152, 864, 15 , 0x149}, +{ 1152, 864, 16 , 0x14a}, +{ 1152, 864, 24 , 0x14b}, +{ 1152, 864, 32 , 0x14c}, +{ 1280, 768, 16 , 0x175}, +{ 1280, 768, 24 , 0x176}, +{ 1280, 768, 32 , 0x177}, +{ 1280, 800, 16 , 0x178}, +{ 1280, 800, 24 , 0x179}, +{ 1280, 800, 32 , 0x17a}, +{ 1280, 960, 16 , 0x17b}, +{ 1280, 960, 24 , 0x17c}, +{ 1280, 960, 32 , 0x17d}, +{ 1440, 900, 16 , 0x17e}, +{ 1440, 900, 24 , 0x17f}, +{ 1440, 900, 32 , 0x180}, +{ 1400, 1050, 16 , 0x181}, +{ 1400, 1050, 24 , 0x182}, +{ 1400, 1050, 32 , 0x183}, +{ 1680, 1050, 16 , 0x184}, +{ 1680, 1050, 24 , 0x185}, +{ 1680, 1050, 32 , 0x186}, +{ 1920, 1200, 16 , 0x187}, +{ 1920, 1200, 24 , 0x188}, +{ 1920, 1200, 32 , 0x189}, +{ 2560, 1600, 16 , 0x18a}, +{ 2560, 1600, 24 , 0x18b}, +{ 2560, 1600, 32 , 0x18c}, +{ 0, }, +}; + +int main(int argc, char **argv) +{ + const ModeInfo *pm; + int pages, pitch; + int r_size, r_pos, g_size, g_pos, b_size, b_pos, a_size, a_pos; + const char *str; + long vram_size = VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024 * 1024; + + printf("/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); + printf("#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB %d\n\n", VBE_DISPI_TOTAL_VIDEO_MEMORY_MB); + printf("static ModeInfoListItem mode_info_list[]=\n"); + printf("{\n"); + for (pm = modes; pm->mode != 0; pm++) { + if (pm->depth == 4) + pitch = (pm->width + 7) / 8; + else + pitch = pm->width * ((pm->depth + 7) / 8); + pages = vram_size / (pm->height * pitch); + if (pages > 0) { + printf("{ 0x%04x, /* %dx%dx%d */\n", + pm->mode, pm->width, pm->height, pm->depth); + if (pm->depth == 4) + printf("{ /*Bit16u ModeAttributes*/ %s,\n", + "VBE_MODE_ATTRIBUTE_SUPPORTED | " + "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | " + "VBE_MODE_ATTRIBUTE_COLOR_MODE | " + "VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT | " + "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE"); + else + printf("{ /*Bit16u ModeAttributes*/ %s,\n", + "VBE_MODE_ATTRIBUTE_SUPPORTED | " + "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | " + "VBE_MODE_ATTRIBUTE_COLOR_MODE | " + "VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE | " + "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE"); + printf("/*Bit8u WinAAttributes*/ %s,\n", + "VBE_WINDOW_ATTRIBUTE_RELOCATABLE | " + "VBE_WINDOW_ATTRIBUTE_READABLE | " + "VBE_WINDOW_ATTRIBUTE_WRITEABLE"); + + printf("/*Bit8u WinBAttributes*/ %d,\n", 0); + + printf("/*Bit16u WinGranularity*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB"); + + printf("/*Bit16u WinSize*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB"); + + printf("/*Bit16u WinASegment*/ %s,\n", "VGAMEM_GRAPH"); + + printf("/*Bit16u WinBSegment*/ 0x%04x,\n", 0); + + printf("/*Bit32u WinFuncPtr*/ %d,\n", 0); + + printf("/*Bit16u BytesPerScanLine*/ %d,\n", pitch); + + // Mandatory information for VBE 1.2 and above + printf("/*Bit16u XResolution*/ %d,\n", pm->width); + printf("/*Bit16u YResolution*/ %d,\n", pm->height); + printf("/*Bit8u XCharSize*/ %d,\n", 8); + printf("/*Bit8u YCharSize*/ %d,\n", 16); + if (pm->depth == 4) { + printf("/*Bit8u NumberOfPlanes*/ %d,\n", 4); + } else { + printf("/*Bit8u NumberOfPlanes*/ %d,\n", 1); + } + printf("/*Bit8u BitsPerPixel*/ %d,\n", pm->depth); + printf("/*Bit8u NumberOfBanks*/ %d,\n", + (pm->height * pitch + 65535) / 65536); + + if (pm->depth == 4) + str = "VBE_MEMORYMODEL_PLANAR"; + else if (pm->depth == 8) + str = "VBE_MEMORYMODEL_PACKED_PIXEL"; + else + str = "VBE_MEMORYMODEL_DIRECT_COLOR"; + printf("/*Bit8u MemoryModel*/ %s,\n", str); + printf("/*Bit8u BankSize*/ %d,\n", 0); + if (pm->depth == 4) + printf("/*Bit8u NumberOfImagePages*/ %d,\n", (pages / 4) - 1); + else + printf("/*Bit8u NumberOfImagePages*/ %d,\n", pages - 1); + printf("/*Bit8u Reserved_page*/ %d,\n", 0); + + // Direct Color fields (required for direct/6 and YUV/7 memory models) + switch(pm->depth) { + case 15: + r_size = 5; + r_pos = 10; + g_size = 5; + g_pos = 5; + b_size = 5; + b_pos = 0; + a_size = 1; + a_pos = 15; + break; + case 16: + r_size = 5; + r_pos = 11; + g_size = 6; + g_pos = 5; + b_size = 5; + b_pos = 0; + a_size = 0; + a_pos = 0; + break; + case 24: + r_size = 8; + r_pos = 16; + g_size = 8; + g_pos = 8; + b_size = 8; + b_pos = 0; + a_size = 0; + a_pos = 0; + break; + case 32: + r_size = 8; + r_pos = 16; + g_size = 8; + g_pos = 8; + b_size = 8; + b_pos = 0; + a_size = 8; + a_pos = 24; + break; + default: + r_size = 0; + r_pos = 0; + g_size = 0; + g_pos = 0; + b_size = 0; + b_pos = 0; + a_size = 0; + a_pos = 0; + break; + } + + printf("/*Bit8u RedMaskSize*/ %d,\n", r_size); + printf("/*Bit8u RedFieldPosition*/ %d,\n", r_pos); + printf("/*Bit8u GreenMaskSize*/ %d,\n", g_size); + printf("/*Bit8u GreenFieldPosition*/ %d,\n", g_pos); + printf("/*Bit8u BlueMaskSize*/ %d,\n", b_size); + printf("/*Bit8u BlueFieldPosition*/ %d,\n", b_pos); + printf("/*Bit8u RsvdMaskSize*/ %d,\n", a_size); + printf("/*Bit8u RsvdFieldPosition*/ %d,\n", a_pos); + if (pm->depth == 32) + printf("/*Bit8u DirectColorModeInfo*/ %s,\n", + "VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE"); + else + printf("/*Bit8u DirectColorModeInfo*/ %s,\n", "0"); + +// Mandatory information for VBE 2.0 and above + if (pm->depth > 4) + printf("/*Bit32u PhysBasePtr*/ %s,\n", + "VBE_DISPI_LFB_PHYSICAL_ADDRESS"); + else + printf("/*Bit32u PhysBasePtr*/ %s,\n", "0"); + printf("/*Bit32u OffScreenMemOffset*/ %d,\n", 0); + printf("/*Bit16u OffScreenMemSize*/ %d,\n", 0); + // Mandatory information for VBE 3.0 and above + printf("/*Bit16u LinBytesPerScanLine*/ %d,\n", pitch); + printf("/*Bit8u BnkNumberOfPages*/ %d,\n", 0); + printf("/*Bit8u LinNumberOfPages*/ %d,\n", 0); + printf("/*Bit8u LinRedMaskSize*/ %d,\n", r_size); + printf("/*Bit8u LinRedFieldPosition*/ %d,\n", r_pos); + printf("/*Bit8u LinGreenMaskSize*/ %d,\n", g_size); + printf("/*Bit8u LinGreenFieldPosition*/ %d,\n", g_pos); + printf("/*Bit8u LinBlueMaskSize*/ %d,\n", b_size); + printf("/*Bit8u LinBlueFieldPosition*/ %d,\n", b_pos); + printf("/*Bit8u LinRsvdMaskSize*/ %d,\n", a_size); + printf("/*Bit8u LinRsvdFieldPosition*/ %d,\n", a_pos); + printf("/*Bit32u MaxPixelClock*/ %d,\n", 0); + printf("} },\n"); + } + } + printf("{ VBE_VESA_MODE_END_OF_LIST,\n"); + printf("{ 0,\n"); + printf("} },\n"); + printf("};\n"); + return 0; +} diff --git a/kvm/vgabios/vgabios.c b/kvm/vgabios/vgabios.c new file mode 100644 index 000000000..e6fe2a0d0 --- /dev/null +++ b/kvm/vgabios/vgabios.c @@ -0,0 +1,3853 @@ +// ============================================================================================ +/* + * vgabios.c + */ +// ============================================================================================ +// +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ============================================================================================ +// +// This VGA Bios is specific to the plex86/bochs Emulated VGA card. +// You can NOT drive any physical vga card with it. +// +// ============================================================================================ +// +// This file contains code ripped from : +// - rombios.c of plex86 +// +// This VGA Bios contains fonts from : +// - fntcol16.zip (c) by Joseph Gil avalable at : +// ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip +// These fonts are public domain +// +// This VGA Bios is based on information taken from : +// - Kevin Lawton's vga card emulation for bochs/plex86 +// - Ralf Brown's interrupts list available at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html +// - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/ +// - Michael Abrash's Graphics Programming Black Book +// - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex +// - DOSEMU 1.0.1 source code for several tables values and formulas +// +// Thanks for patches, comments and ideas to : +// - techt@pikeonline.net +// +// ============================================================================================ + +#include "vgabios.h" + +#ifdef VBE +#include "vbe.h" +#endif + +#define USE_BX_INFO + +/* Declares */ +static Bit8u read_byte(); +static Bit16u read_word(); +static void write_byte(); +static void write_word(); +static Bit8u inb(); +static Bit16u inw(); +static void outb(); +static void outw(); + +static Bit16u get_SS(); + +// Output +static void printf(); +static void unimplemented(); +static void unknown(); + +static Bit8u find_vga_entry(); + +static void memsetb(); +static void memsetw(); +static void memcpyb(); +static void memcpyw(); + +static void biosfn_set_video_mode(); +static void biosfn_set_cursor_shape(); +static void biosfn_set_cursor_pos(); +static void biosfn_get_cursor_pos(); +static void biosfn_set_active_page(); +static void biosfn_scroll(); +static void biosfn_read_char_attr(); +static void biosfn_write_char_attr(); +static void biosfn_write_char_only(); +static void biosfn_write_pixel(); +static void biosfn_read_pixel(); +static void biosfn_write_teletype(); +static void biosfn_perform_gray_scale_summing(); +static void biosfn_load_text_user_pat(); +static void biosfn_load_text_8_14_pat(); +static void biosfn_load_text_8_8_pat(); +static void biosfn_load_text_8_16_pat(); +static void biosfn_load_gfx_8_8_chars(); +static void biosfn_load_gfx_user_chars(); +static void biosfn_load_gfx_8_14_chars(); +static void biosfn_load_gfx_8_8_dd_chars(); +static void biosfn_load_gfx_8_16_chars(); +static void biosfn_get_font_info(); +static void biosfn_alternate_prtsc(); +static void biosfn_switch_video_interface(); +static void biosfn_enable_video_refresh_control(); +static void biosfn_write_string(); +static void biosfn_read_state_info(); +static void biosfn_read_video_state_size(); +static Bit16u biosfn_save_video_state(); +static Bit16u biosfn_restore_video_state(); +extern Bit8u video_save_pointer_table[]; + +// This is for compiling with gcc2 and gcc3 +#define ASM_START #asm +#define ASM_END #endasm + +ASM_START + +MACRO SET_INT_VECTOR + push ds + xor ax, ax + mov ds, ax + mov ax, ?3 + mov ?1*4, ax + mov ax, ?2 + mov ?1*4+2, ax + pop ds +MEND + +ASM_END + +ASM_START +.text +.rom +.org 0 + +use16 386 + +vgabios_start: +.byte 0x55, 0xaa /* BIOS signature, required for BIOS extensions */ + +.byte 0x40 /* BIOS extension length in units of 512 bytes */ + + +vgabios_entry_point: + + jmp vgabios_init_func + +#ifdef PCIBIOS +.org 0x18 +.word vgabios_pci_data +#endif + +// Info from Bart Oldeman +.org 0x1e +.ascii "IBM" +.byte 0x00 + +vgabios_name: +.ascii "Plex86/Bochs VGABios" +#ifdef PCIBIOS +.ascii " (PCI)" +#endif +.ascii " " +.byte 0x00 + +vgabios_version: +#ifndef VGABIOS_VERS +.ascii "current-cvs" +#else +.ascii VGABIOS_VERS +#endif +.ascii " " + +vgabios_date: +.ascii VGABIOS_DATE +.byte 0x0a,0x0d +.byte 0x00 + +vgabios_copyright: +.ascii "(C) 2008 the LGPL VGABios developers Team" +.byte 0x0a,0x0d +.byte 0x00 + +vgabios_license: +.ascii "This VGA/VBE Bios is released under the GNU LGPL" +.byte 0x0a,0x0d +.byte 0x0a,0x0d +.byte 0x00 + +vgabios_website: +.ascii "Please visit :" +.byte 0x0a,0x0d +;;.ascii " . http://www.plex86.org" +;;.byte 0x0a,0x0d +.ascii " . http://bochs.sourceforge.net" +.byte 0x0a,0x0d +.ascii " . http://www.nongnu.org/vgabios" +.byte 0x0a,0x0d +.byte 0x0a,0x0d +.byte 0x00 + +#ifdef PCIBIOS +vgabios_pci_data: +.ascii "PCIR" +#ifdef CIRRUS +.word 0x1013 +.word 0x00b8 // CLGD5446 +#else +#error "Unknown PCI vendor and device id" +#endif +.word 0 // reserved +.word 0x18 // dlen +.byte 0 // revision +.byte 0x0 // class,hi: vga display +.word 0x300 // class,lo: vga display +.word 0x40 // bios size +.word 1 // revision +.byte 0 // intel x86 data +.byte 0x80 // last image +.word 0 // reserved +#endif + + +;; ============================================================================================ +;; +;; Init Entry point +;; +;; ============================================================================================ +vgabios_init_func: + +;; init vga card + call init_vga_card + +;; init basic bios vars + call init_bios_area + +#ifdef VBE +;; init vbe functions + call vbe_init +#endif + +;; set int10 vect + SET_INT_VECTOR(0x10, #0xC000, #vgabios_int10_handler) + +#ifdef CIRRUS + call cirrus_init +#endif + +;; display splash screen + call _display_splash_screen + +;; init video mode and clear the screen + mov ax,#0x0003 + int #0x10 + +;; show info + call _display_info + +#ifdef VBE +;; show vbe info + call vbe_display_info +#endif + +#ifdef CIRRUS +;; show cirrus info + call cirrus_display_info +#endif + + retf +ASM_END + +/* + * int10 handled here + */ +ASM_START +vgabios_int10_handler: + pushf +#ifdef DEBUG + push es + push ds + pusha + mov bx, #0xc000 + mov ds, bx + call _int10_debugmsg + popa + pop ds + pop es +#endif + cmp ah, #0x0f + jne int10_test_1A + call biosfn_get_video_mode + jmp int10_end +int10_test_1A: + cmp ah, #0x1a + jne int10_test_0B + call biosfn_group_1A + jmp int10_end +int10_test_0B: + cmp ah, #0x0b + jne int10_test_1103 + call biosfn_group_0B + jmp int10_end +int10_test_1103: + cmp ax, #0x1103 + jne int10_test_12 + call biosfn_set_text_block_specifier + jmp int10_end +int10_test_12: + cmp ah, #0x12 + jne int10_test_101B + cmp bl, #0x10 + jne int10_test_BL30 + call biosfn_get_ega_info + jmp int10_end +int10_test_BL30: + cmp bl, #0x30 + jne int10_test_BL31 + call biosfn_select_vert_res + jmp int10_end +int10_test_BL31: + cmp bl, #0x31 + jne int10_test_BL32 + call biosfn_enable_default_palette_loading + jmp int10_end +int10_test_BL32: + cmp bl, #0x32 + jne int10_test_BL33 + call biosfn_enable_video_addressing + jmp int10_end +int10_test_BL33: + cmp bl, #0x33 + jne int10_test_BL34 + call biosfn_enable_grayscale_summing + jmp int10_end +int10_test_BL34: + cmp bl, #0x34 + jne int10_normal + call biosfn_enable_cursor_emulation + jmp int10_end +int10_test_101B: + cmp ax, #0x101b + je int10_normal + cmp ah, #0x10 +#ifndef VBE + jne int10_normal +#else + jne int10_test_4F +#endif + call biosfn_group_10 + jmp int10_end +#ifdef VBE +int10_test_4F: + cmp ah, #0x4f + jne int10_normal + cmp al, #0x03 + jne int10_test_vbe_05 + call vbe_biosfn_return_current_mode + jmp int10_end +int10_test_vbe_05: + cmp al, #0x05 + jne int10_test_vbe_06 + call vbe_biosfn_display_window_control + jmp int10_end +int10_test_vbe_06: + cmp al, #0x06 + jne int10_test_vbe_07 + call vbe_biosfn_set_get_logical_scan_line_length + jmp int10_end +int10_test_vbe_07: + cmp al, #0x07 + jne int10_test_vbe_08 + call vbe_biosfn_set_get_display_start + jmp int10_end +int10_test_vbe_08: + cmp al, #0x08 + jne int10_test_vbe_0A + call vbe_biosfn_set_get_dac_palette_format + jmp int10_end +int10_test_vbe_0A: + cmp al, #0x0A + jne int10_normal + call vbe_biosfn_return_protected_mode_interface + jmp int10_end +#endif + +int10_normal: + push es + push ds + pusha + +;; We have to set ds to access the right data segment + mov bx, #0xc000 + mov ds, bx + call _int10_func + + popa + pop ds + pop es +int10_end: + popf + iret +ASM_END + +#include "vgatables.h" +#include "vgafonts.h" + +/* + * Boot time harware inits + */ +ASM_START +init_vga_card: +;; switch to color mode and enable CPU access 480 lines + mov dx, #0x3C2 + mov al, #0xC3 + outb dx,al + +;; more than 64k 3C4/04 + mov dx, #0x3C4 + mov al, #0x04 + outb dx,al + mov dx, #0x3C5 + mov al, #0x02 + outb dx,al + +#if defined(USE_BX_INFO) || defined(DEBUG) + mov bx, #msg_vga_init + push bx + call _printf +#endif + inc sp + inc sp + ret + +#if defined(USE_BX_INFO) || defined(DEBUG) +msg_vga_init: +.ascii "VGABios $Id$" +.byte 0x0d,0x0a,0x00 +#endif +ASM_END + +// -------------------------------------------------------------------------------------------- +/* + * Boot time bios area inits + */ +ASM_START +init_bios_area: + push ds + mov ax, # BIOSMEM_SEG + mov ds, ax + +;; init detected hardware BIOS Area + mov bx, # BIOSMEM_INITIAL_MODE + mov ax, [bx] + and ax, #0xffcf +;; set 80x25 color (not clear from RBIL but usual) + or ax, #0x0020 + mov [bx], ax + +;; Just for the first int10 find its children + +;; the default char height + mov bx, # BIOSMEM_CHAR_HEIGHT + mov al, #0x10 + mov [bx], al + +;; Clear the screen + mov bx, # BIOSMEM_VIDEO_CTL + mov al, #0x60 + mov [bx], al + +;; Set the basic screen we have + mov bx, # BIOSMEM_SWITCHES + mov al, #0xf9 + mov [bx], al + +;; Set the basic modeset options + mov bx, # BIOSMEM_MODESET_CTL + mov al, #0x51 + mov [bx], al + +;; Set the default MSR + mov bx, # BIOSMEM_CURRENT_MSR + mov al, #0x09 + mov [bx], al + + pop ds + ret + +_video_save_pointer_table: + .word _video_param_table + .word 0xc000 + + .word 0 /* XXX: fill it */ + .word 0 + + .word 0 /* XXX: fill it */ + .word 0 + + .word 0 /* XXX: fill it */ + .word 0 + + .word 0 /* XXX: fill it */ + .word 0 + + .word 0 /* XXX: fill it */ + .word 0 + + .word 0 /* XXX: fill it */ + .word 0 + +ASM_END + +// -------------------------------------------------------------------------------------------- +/* + * Boot time Splash screen + */ +static void display_splash_screen() +{ +} + +// -------------------------------------------------------------------------------------------- +/* + * Tell who we are + */ + +static void display_info() +{ +ASM_START + mov ax,#0xc000 + mov ds,ax + mov si,#vgabios_name + call _display_string + mov si,#vgabios_version + call _display_string + + ;;mov si,#vgabios_copyright + ;;call _display_string + ;;mov si,#crlf + ;;call _display_string + + mov si,#vgabios_license + call _display_string + mov si,#vgabios_website + call _display_string +ASM_END +} + +static void display_string() +{ + // Get length of string +ASM_START + mov ax,ds + mov es,ax + mov di,si + xor cx,cx + not cx + xor al,al + cld + repne + scasb + not cx + dec cx + push cx + + mov ax,#0x0300 + mov bx,#0x0000 + int #0x10 + + pop cx + mov ax,#0x1301 + mov bx,#0x000b + mov bp,si + int #0x10 +ASM_END +} + +// -------------------------------------------------------------------------------------------- +#ifdef DEBUG +static void int10_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS) + Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS; +{ + // 0E is write char... + if(GET_AH()!=0x0E) + printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX); +} +#endif + +// -------------------------------------------------------------------------------------------- +/* + * int10 main dispatcher + */ +static void int10_func(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS) + Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS; +{ + + // BIOS functions + switch(GET_AH()) + { + case 0x00: + biosfn_set_video_mode(GET_AL()); + switch(GET_AL()&0x7F) + {case 6: + SET_AL(0x3F); + break; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 7: + SET_AL(0x30); + break; + default: + SET_AL(0x20); + } + break; + case 0x01: + biosfn_set_cursor_shape(GET_CH(),GET_CL()); + break; + case 0x02: + biosfn_set_cursor_pos(GET_BH(),DX); + break; + case 0x03: + biosfn_get_cursor_pos(GET_BH(),&CX,&DX); + break; + case 0x04: + // Read light pen pos (unimplemented) +#ifdef DEBUG + unimplemented(); +#endif + AX=0x00; + BX=0x00; + CX=0x00; + DX=0x00; + break; + case 0x05: + biosfn_set_active_page(GET_AL()); + break; + case 0x06: + biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_UP); + break; + case 0x07: + biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_DOWN); + break; + case 0x08: + biosfn_read_char_attr(GET_BH(),&AX); + break; + case 0x09: + biosfn_write_char_attr(GET_AL(),GET_BH(),GET_BL(),CX); + break; + case 0x0A: + biosfn_write_char_only(GET_AL(),GET_BH(),GET_BL(),CX); + break; + case 0x0C: + biosfn_write_pixel(GET_BH(),GET_AL(),CX,DX); + break; + case 0x0D: + biosfn_read_pixel(GET_BH(),CX,DX,&AX); + break; + case 0x0E: + // Ralf Brown Interrupt list is WRONG on bh(page) + // We do output only on the current page ! + biosfn_write_teletype(GET_AL(),0xff,GET_BL(),NO_ATTR); + break; + case 0x10: + // All other functions of group AH=0x10 rewritten in assembler + biosfn_perform_gray_scale_summing(BX,CX); + break; + case 0x11: + switch(GET_AL()) + { + case 0x00: + case 0x10: + biosfn_load_text_user_pat(GET_AL(),ES,BP,CX,DX,GET_BL(),GET_BH()); + break; + case 0x01: + case 0x11: + biosfn_load_text_8_14_pat(GET_AL(),GET_BL()); + break; + case 0x02: + case 0x12: + biosfn_load_text_8_8_pat(GET_AL(),GET_BL()); + break; + case 0x04: + case 0x14: + biosfn_load_text_8_16_pat(GET_AL(),GET_BL()); + break; + case 0x20: + biosfn_load_gfx_8_8_chars(ES,BP); + break; + case 0x21: + biosfn_load_gfx_user_chars(ES,BP,CX,GET_BL(),GET_DL()); + break; + case 0x22: + biosfn_load_gfx_8_14_chars(GET_BL()); + break; + case 0x23: + biosfn_load_gfx_8_8_dd_chars(GET_BL()); + break; + case 0x24: + biosfn_load_gfx_8_16_chars(GET_BL()); + break; + case 0x30: + biosfn_get_font_info(GET_BH(),&ES,&BP,&CX,&DX); + break; +#ifdef DEBUG + default: + unknown(); +#endif + } + + break; + case 0x12: + switch(GET_BL()) + { + case 0x20: + biosfn_alternate_prtsc(); + break; + case 0x35: + biosfn_switch_video_interface(GET_AL(),ES,DX); + SET_AL(0x12); + break; + case 0x36: + biosfn_enable_video_refresh_control(GET_AL()); + SET_AL(0x12); + break; +#ifdef DEBUG + default: + unknown(); +#endif + } + break; + case 0x13: + biosfn_write_string(GET_AL(),GET_BH(),GET_BL(),CX,GET_DH(),GET_DL(),ES,BP); + break; + case 0x1B: + biosfn_read_state_info(BX,ES,DI); + SET_AL(0x1B); + break; + case 0x1C: + switch(GET_AL()) + { + case 0x00: + biosfn_read_video_state_size(CX,&BX); + break; + case 0x01: + biosfn_save_video_state(CX,ES,BX); + break; + case 0x02: + biosfn_restore_video_state(CX,ES,BX); + break; +#ifdef DEBUG + default: + unknown(); +#endif + } + SET_AL(0x1C); + break; + +#ifdef VBE + case 0x4f: + if (vbe_has_vbe_display()) { + switch(GET_AL()) + { + case 0x00: + vbe_biosfn_return_controller_information(&AX,ES,DI); + break; + case 0x01: + vbe_biosfn_return_mode_information(&AX,CX,ES,DI); + break; + case 0x02: + vbe_biosfn_set_mode(&AX,BX,ES,DI); + break; + case 0x04: + vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX); + break; + case 0x09: + //FIXME +#ifdef DEBUG + unimplemented(); +#endif + // function failed + AX=0x100; + break; + case 0x0A: + //FIXME +#ifdef DEBUG + unimplemented(); +#endif + // function failed + AX=0x100; + break; + default: +#ifdef DEBUG + unknown(); +#endif + // function failed + AX=0x100; + } + } + else { + // No VBE display + AX=0x0100; + } + break; +#endif + +#ifdef DEBUG + default: + unknown(); +#endif + } +} + +// ============================================================================================ +// +// BIOS functions +// +// ============================================================================================ + +static void biosfn_set_video_mode(mode) Bit8u mode; +{// mode: Bit 7 is 1 if no clear screen + + // Should we clear the screen ? + Bit8u noclearmem=mode&0x80; + Bit8u line,mmask,*palette,vpti; + Bit16u i,twidth,theightm1,cheight; + Bit8u modeset_ctl,video_ctl,vga_switches; + Bit16u crtc_addr; + +#ifdef VBE + if (vbe_has_vbe_display()) { + dispi_set_enable(VBE_DISPI_DISABLED); + } +#endif // def VBE + + // The real mode + mode=mode&0x7f; + + // find the entry in the video modes + line=find_vga_entry(mode); + +#ifdef DEBUG + printf("mode search %02x found line %02x\n",mode,line); +#endif + + if(line==0xFF) + return; + + vpti=line_to_vpti[line]; + twidth=video_param_table[vpti].twidth; + theightm1=video_param_table[vpti].theightm1; + cheight=video_param_table[vpti].cheight; + + // Read the bios vga control + video_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL); + + // Read the bios vga switches + vga_switches=read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES); + + // Read the bios mode set control + modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL); + + // Then we know the number of lines +// FIXME + + // if palette loading (bit 3 of modeset ctl = 0) + if((modeset_ctl&0x08)==0) + {// Set the PEL mask + outb(VGAREG_PEL_MASK,vga_modes[line].pelmask); + + // Set the whole dac always, from 0 + outb(VGAREG_DAC_WRITE_ADDRESS,0x00); + + // From which palette + switch(vga_modes[line].dacmodel) + {case 0: + palette=&palette0; + break; + case 1: + palette=&palette1; + break; + case 2: + palette=&palette2; + break; + case 3: + palette=&palette3; + break; + } + // Always 256*3 values + for(i=0;i<0x0100;i++) + {if(i<=dac_regs[vga_modes[line].dacmodel]) + {outb(VGAREG_DAC_DATA,palette[(i*3)+0]); + outb(VGAREG_DAC_DATA,palette[(i*3)+1]); + outb(VGAREG_DAC_DATA,palette[(i*3)+2]); + } + else + {outb(VGAREG_DAC_DATA,0); + outb(VGAREG_DAC_DATA,0); + outb(VGAREG_DAC_DATA,0); + } + } + if((modeset_ctl&0x02)==0x02) + { + biosfn_perform_gray_scale_summing(0x00, 0x100); + } + } + + // Reset Attribute Ctl flip-flop + inb(VGAREG_ACTL_RESET); + + // Set Attribute Ctl + for(i=0;i<=0x13;i++) + {outb(VGAREG_ACTL_ADDRESS,i); + outb(VGAREG_ACTL_WRITE_DATA,video_param_table[vpti].actl_regs[i]); + } + outb(VGAREG_ACTL_ADDRESS,0x14); + outb(VGAREG_ACTL_WRITE_DATA,0x00); + + // Set Sequencer Ctl + outb(VGAREG_SEQU_ADDRESS,0); + outb(VGAREG_SEQU_DATA,0x03); + for(i=1;i<=4;i++) + {outb(VGAREG_SEQU_ADDRESS,i); + outb(VGAREG_SEQU_DATA,video_param_table[vpti].sequ_regs[i - 1]); + } + + // Set Grafx Ctl + for(i=0;i<=8;i++) + {outb(VGAREG_GRDC_ADDRESS,i); + outb(VGAREG_GRDC_DATA,video_param_table[vpti].grdc_regs[i]); + } + + // Set CRTC address VGA or MDA + crtc_addr=vga_modes[line].memmodel==MTEXT?VGAREG_MDA_CRTC_ADDRESS:VGAREG_VGA_CRTC_ADDRESS; + + // Disable CRTC write protection + outw(crtc_addr,0x0011); + // Set CRTC regs + for(i=0;i<=0x18;i++) + {outb(crtc_addr,i); + outb(crtc_addr+1,video_param_table[vpti].crtc_regs[i]); + } + + // Set the misc register + outb(VGAREG_WRITE_MISC_OUTPUT,video_param_table[vpti].miscreg); + + // Enable video + outb(VGAREG_ACTL_ADDRESS,0x20); + inb(VGAREG_ACTL_RESET); + + if(noclearmem==0x00) + { + if(vga_modes[line].class==TEXT) + { + memsetw(vga_modes[line].sstart,0,0x0720,0x4000); // 32k + } + else + { + if(mode<0x0d) + { + memsetw(vga_modes[line].sstart,0,0x0000,0x4000); // 32k + } + else + { + outb( VGAREG_SEQU_ADDRESS, 0x02 ); + mmask = inb( VGAREG_SEQU_DATA ); + outb( VGAREG_SEQU_DATA, 0x0f ); // all planes + memsetw(vga_modes[line].sstart,0,0x0000,0x8000); // 64k + outb( VGAREG_SEQU_DATA, mmask ); + } + } + } + + // Set the BIOS mem + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,mode); + write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS,twidth); + write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,*(Bit16u *)&video_param_table[vpti].slength_l); + write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,crtc_addr); + write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theightm1); + write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,cheight); + write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|noclearmem)); + write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9); + write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x7f); + + // FIXME We nearly have the good tables. to be reworked + write_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x08); // 8 is VGA should be ok for now + write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER, video_save_pointer_table); + write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2, 0xc000); + + // FIXME + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x00); // Unavailable on vanilla vga, but... + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x00); // Unavailable on vanilla vga, but... + + // Set cursor shape + if(vga_modes[line].class==TEXT) + { + biosfn_set_cursor_shape(0x06,0x07); + } + + // Set cursor pos for page 0..7 + for(i=0;i<8;i++) + biosfn_set_cursor_pos(i,0x0000); + + // Set active page 0 + biosfn_set_active_page(0x00); + + // Write the fonts in memory + if(vga_modes[line].class==TEXT) + { +ASM_START + ;; copy and activate 8x16 font + mov ax, #0x1104 + mov bl, #0x00 + int #0x10 + mov ax, #0x1103 + mov bl, #0x00 + int #0x10 +ASM_END + } + + // Set the ints 0x1F and 0x43 +ASM_START + SET_INT_VECTOR(0x1f, #0xC000, #_vgafont8+128*8) +ASM_END + + switch(cheight) + {case 8: +ASM_START + SET_INT_VECTOR(0x43, #0xC000, #_vgafont8) +ASM_END + break; + case 14: +ASM_START + SET_INT_VECTOR(0x43, #0xC000, #_vgafont14) +ASM_END + break; + case 16: +ASM_START + SET_INT_VECTOR(0x43, #0xC000, #_vgafont16) +ASM_END + break; + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_set_cursor_shape (CH,CL) +Bit8u CH;Bit8u CL; +{Bit16u cheight,curs,crtc_addr; + Bit8u modeset_ctl; + + CH&=0x3f; + CL&=0x1f; + + curs=(CH<<8)+CL; + write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,curs); + + modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL); + cheight = read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + if((modeset_ctl&0x01) && (cheight>8) && (CL<8) && (CH<0x20)) + { + if(CL!=(CH+1)) + { + CH = ((CH+1) * cheight / 8) -1; + } + else + { + CH = ((CL+1) * cheight / 8) - 2; + } + CL = ((CL+1) * cheight / 8) - 1; + } + + // CTRC regs 0x0a and 0x0b + crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + outb(crtc_addr,0x0a); + outb(crtc_addr+1,CH); + outb(crtc_addr,0x0b); + outb(crtc_addr+1,CL); +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_set_cursor_pos (page, cursor) +Bit8u page;Bit16u cursor; +{ + Bit8u xcurs,ycurs,current; + Bit16u nbcols,nbrows,address,crtc_addr; + + // Should not happen... + if(page>7)return; + + // Bios cursor pos + write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*page, cursor); + + // Set the hardware cursor + current=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + if(page==current) + { + // Get the dimensions + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Calculate the address knowing nbcols nbrows and page num + address=SCREEN_IO_START(nbcols,nbrows,page)+xcurs+ycurs*nbcols; + + // CRTC regs 0x0e and 0x0f + crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + outb(crtc_addr,0x0e); + outb(crtc_addr+1,(address&0xff00)>>8); + outb(crtc_addr,0x0f); + outb(crtc_addr+1,address&0x00ff); + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_get_cursor_pos (page,shape, pos) +Bit8u page;Bit16u *shape;Bit16u *pos; +{ + Bit16u ss=get_SS(); + + // Default + write_word(ss, shape, 0); + write_word(ss, pos, 0); + + if(page>7)return; + // FIXME should handle VGA 14/16 lines + write_word(ss,shape,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); + write_word(ss,pos,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2)); +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_set_active_page (page) +Bit8u page; +{ + Bit16u cursor,dummy,crtc_addr; + Bit16u nbcols,nbrows,address; + Bit8u mode,line; + + if(page>7)return; + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + + // Get pos curs pos for the right page + biosfn_get_cursor_pos(page,&dummy,&cursor); + + if(vga_modes[line].class==TEXT) + { + // Get the dimensions + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + + // Calculate the address knowing nbcols nbrows and page num + address=SCREEN_MEM_START(nbcols,nbrows,page); + write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START,address); + + // Start address + address=SCREEN_IO_START(nbcols,nbrows,page); + } + else + { + address = page * (*(Bit16u *)&video_param_table[line_to_vpti[line]].slength_l); + } + + // CRTC regs 0x0c and 0x0d + crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + outb(crtc_addr,0x0c); + outb(crtc_addr+1,(address&0xff00)>>8); + outb(crtc_addr,0x0d); + outb(crtc_addr+1,address&0x00ff); + + // And change the BIOS page + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page); + +#ifdef DEBUG + printf("Set active page %02x address %04x\n",page,address); +#endif + + // Display the cursor, now the page is active + biosfn_set_cursor_pos(page,cursor); +} + +// -------------------------------------------------------------------------------------------- +static void vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight) +Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight; +{ + Bit16u src,dest; + Bit8u i; + + src=ysrc*cheight*nbcols+xstart; + dest=ydest*cheight*nbcols+xstart; + outw(VGAREG_GRDC_ADDRESS, 0x0105); + for(i=0;i<cheight;i++) + { + memcpyb(0xa000,dest+i*nbcols,0xa000,src+i*nbcols,cols); + } + outw(VGAREG_GRDC_ADDRESS, 0x0005); +} + +// -------------------------------------------------------------------------------------------- +static void vgamem_fill_pl4(xstart,ystart,cols,nbcols,cheight,attr) +Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr; +{ + Bit16u dest; + Bit8u i; + + dest=ystart*cheight*nbcols+xstart; + outw(VGAREG_GRDC_ADDRESS, 0x0205); + for(i=0;i<cheight;i++) + { + memsetb(0xa000,dest+i*nbcols,attr,cols); + } + outw(VGAREG_GRDC_ADDRESS, 0x0005); +} + +// -------------------------------------------------------------------------------------------- +static void vgamem_copy_cga(xstart,ysrc,ydest,cols,nbcols,cheight) +Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight; +{ + Bit16u src,dest; + Bit8u i; + + src=((ysrc*cheight*nbcols)>>1)+xstart; + dest=((ydest*cheight*nbcols)>>1)+xstart; + for(i=0;i<cheight;i++) + { + if (i & 1) + memcpyb(0xb800,0x2000+dest+(i>>1)*nbcols,0xb800,0x2000+src+(i>>1)*nbcols,cols); + else + memcpyb(0xb800,dest+(i>>1)*nbcols,0xb800,src+(i>>1)*nbcols,cols); + } +} + +// -------------------------------------------------------------------------------------------- +static void vgamem_fill_cga(xstart,ystart,cols,nbcols,cheight,attr) +Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr; +{ + Bit16u dest; + Bit8u i; + + dest=((ystart*cheight*nbcols)>>1)+xstart; + for(i=0;i<cheight;i++) + { + if (i & 1) + memsetb(0xb800,0x2000+dest+(i>>1)*nbcols,attr,cols); + else + memsetb(0xb800,dest+(i>>1)*nbcols,attr,cols); + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_scroll (nblines,attr,rul,cul,rlr,clr,page,dir) +Bit8u nblines;Bit8u attr;Bit8u rul;Bit8u cul;Bit8u rlr;Bit8u clr;Bit8u page;Bit8u dir; +{ + // page == 0xFF if current + + Bit8u mode,line,cheight,bpp,cols; + Bit16u nbcols,nbrows,i; + Bit16u address; + + if(rul>rlr)return; + if(cul>clr)return; + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + // Get the current page + if(page==0xFF) + page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + + if(rlr>=nbrows)rlr=nbrows-1; + if(clr>=nbcols)clr=nbcols-1; + if(nblines>nbrows)nblines=0; + cols=clr-cul+1; + + if(vga_modes[line].class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page); +#ifdef DEBUG + printf("Scroll, address %04x (%04x %04x %02x)\n",address,nbrows,nbcols,page); +#endif + + if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) + { + memsetw(vga_modes[line].sstart,address,(Bit16u)attr*0x100+' ',nbrows*nbcols); + } + else + {// if Scroll up + if(dir==SCROLL_UP) + {for(i=rul;i<=rlr;i++) + { + if((i+nblines>rlr)||(nblines==0)) + memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols); + else + memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i+nblines)*nbcols+cul)*2,cols); + } + } + else + {for(i=rlr;i>=rul;i--) + { + if((i<rul+nblines)||(nblines==0)) + memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols); + else + memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i-nblines)*nbcols+cul)*2,cols); + if (i>rlr) break; + } + } + } + } + else + { + // FIXME gfx mode not complete + cheight=video_param_table[line_to_vpti[line]].cheight; + switch(vga_modes[line].memmodel) + { + case PLANAR4: + case PLANAR1: + if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) + { + outw(VGAREG_GRDC_ADDRESS, 0x0205); + memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight); + outw(VGAREG_GRDC_ADDRESS, 0x0005); + } + else + {// if Scroll up + if(dir==SCROLL_UP) + {for(i=rul;i<=rlr;i++) + { + if((i+nblines>rlr)||(nblines==0)) + vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr); + else + vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight); + } + } + else + {for(i=rlr;i>=rul;i--) + { + if((i<rul+nblines)||(nblines==0)) + vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr); + else + vgamem_copy_pl4(cul,i,i-nblines,cols,nbcols,cheight); + if (i>rlr) break; + } + } + } + break; + case CGA: + bpp=vga_modes[line].pixbits; + if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) + { + memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight*bpp); + } + else + { + if(bpp==2) + { + cul<<=1; + cols<<=1; + nbcols<<=1; + } + // if Scroll up + if(dir==SCROLL_UP) + {for(i=rul;i<=rlr;i++) + { + if((i+nblines>rlr)||(nblines==0)) + vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr); + else + vgamem_copy_cga(cul,i+nblines,i,cols,nbcols,cheight); + } + } + else + {for(i=rlr;i>=rul;i--) + { + if((i<rul+nblines)||(nblines==0)) + vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr); + else + vgamem_copy_cga(cul,i,i-nblines,cols,nbcols,cheight); + if (i>rlr) break; + } + } + } + break; +#ifdef DEBUG + default: + printf("Scroll in graphics mode "); + unimplemented(); +#endif + } + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_read_char_attr (page,car) +Bit8u page;Bit16u *car; +{Bit16u ss=get_SS(); + Bit8u xcurs,ycurs,mode,line; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + if(vga_modes[line].class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + write_word(ss,car,read_word(vga_modes[line].sstart,address)); + } + else + { + // FIXME gfx mode +#ifdef DEBUG + unimplemented(); +#endif + } +} + +// -------------------------------------------------------------------------------------------- +static void write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight) +Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u cheight; +{ + Bit8u i,j,mask; + Bit8u *fdata; + Bit16u addr,dest,src; + + switch(cheight) + {case 14: + fdata = &vgafont14; + break; + case 16: + fdata = &vgafont16; + break; + default: + fdata = &vgafont8; + } + addr=xcurs+ycurs*cheight*nbcols; + src = car * cheight; + outw(VGAREG_SEQU_ADDRESS, 0x0f02); + outw(VGAREG_GRDC_ADDRESS, 0x0205); + if(attr&0x80) + { + outw(VGAREG_GRDC_ADDRESS, 0x1803); + } + else + { + outw(VGAREG_GRDC_ADDRESS, 0x0003); + } + for(i=0;i<cheight;i++) + { + dest=addr+i*nbcols; + for(j=0;j<8;j++) + { + mask=0x80>>j; + outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08); + read_byte(0xa000,dest); + if(fdata[src+i]&mask) + { + write_byte(0xa000,dest,attr&0x0f); + } + else + { + write_byte(0xa000,dest,0x00); + } + } + } +ASM_START + mov dx, # VGAREG_GRDC_ADDRESS + mov ax, #0xff08 + out dx, ax + mov ax, #0x0005 + out dx, ax + mov ax, #0x0003 + out dx, ax +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static void write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp) +Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u bpp; +{ + Bit8u i,j,mask,data; + Bit8u *fdata; + Bit16u addr,dest,src; + + fdata = &vgafont8; + addr=(xcurs*bpp)+ycurs*320; + src = car * 8; + for(i=0;i<8;i++) + { + dest=addr+(i>>1)*80; + if (i & 1) dest += 0x2000; + mask = 0x80; + if (bpp == 1) + { + if (attr & 0x80) + { + data = read_byte(0xb800,dest); + } + else + { + data = 0x00; + } + for(j=0;j<8;j++) + { + if (fdata[src+i] & mask) + { + if (attr & 0x80) + { + data ^= (attr & 0x01) << (7-j); + } + else + { + data |= (attr & 0x01) << (7-j); + } + } + mask >>= 1; + } + write_byte(0xb800,dest,data); + } + else + { + while (mask > 0) + { + if (attr & 0x80) + { + data = read_byte(0xb800,dest); + } + else + { + data = 0x00; + } + for(j=0;j<4;j++) + { + if (fdata[src+i] & mask) + { + if (attr & 0x80) + { + data ^= (attr & 0x03) << ((3-j)*2); + } + else + { + data |= (attr & 0x03) << ((3-j)*2); + } + } + mask >>= 1; + } + write_byte(0xb800,dest,data); + dest += 1; + } + } + } +} + +// -------------------------------------------------------------------------------------------- +static void write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols) +Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols; +{ + Bit8u i,j,mask,data; + Bit8u *fdata; + Bit16u addr,dest,src; + + fdata = &vgafont8; + addr=xcurs*8+ycurs*nbcols*64; + src = car * 8; + for(i=0;i<8;i++) + { + dest=addr+i*nbcols*8; + mask = 0x80; + for(j=0;j<8;j++) + { + data = 0x00; + if (fdata[src+i] & mask) + { + data = attr; + } + write_byte(0xa000,dest+j,data); + mask >>= 1; + } + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_char_attr (car,page,attr,count) +Bit8u car;Bit8u page;Bit8u attr;Bit16u count; +{ + Bit8u cheight,xcurs,ycurs,mode,line,bpp; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + if(vga_modes[line].class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + dummy=((Bit16u)attr<<8)+car; + memsetw(vga_modes[line].sstart,address,dummy,count); + } + else + { + // FIXME gfx mode not complete + cheight=video_param_table[line_to_vpti[line]].cheight; + bpp=vga_modes[line].pixbits; + while((count-->0) && (xcurs<nbcols)) + { + switch(vga_modes[line].memmodel) + { + case PLANAR4: + case PLANAR1: + write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight); + break; + case CGA: + write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp); + break; + case LINEAR8: + write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } + xcurs++; + } + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_char_only (car,page,attr,count) +Bit8u car;Bit8u page;Bit8u attr;Bit16u count; +{ + Bit8u cheight,xcurs,ycurs,mode,line,bpp; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + if(vga_modes[line].class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + while(count-->0) + {write_byte(vga_modes[line].sstart,address,car); + address+=2; + } + } + else + { + // FIXME gfx mode not complete + cheight=video_param_table[line_to_vpti[line]].cheight; + bpp=vga_modes[line].pixbits; + while((count-->0) && (xcurs<nbcols)) + { + switch(vga_modes[line].memmodel) + { + case PLANAR4: + case PLANAR1: + write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight); + break; + case CGA: + write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp); + break; + case LINEAR8: + write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } + xcurs++; + } + } +} + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_group_0B: + cmp bh, #0x00 + je biosfn_set_border_color + cmp bh, #0x01 + je biosfn_set_palette +#ifdef DEBUG + call _unknown +#endif + ret +biosfn_set_border_color: + push ax + push bx + push cx + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x00 + out dx, al + mov al, bl + and al, #0x0f + test al, #0x08 + jz set_low_border + add al, #0x08 +set_low_border: + out dx, al + mov cl, #0x01 + and bl, #0x10 +set_intensity_loop: + mov dx, # VGAREG_ACTL_ADDRESS + mov al, cl + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + and al, #0xef + or al, bl + mov dx, # VGAREG_ACTL_ADDRESS + out dx, al + inc cl + cmp cl, #0x04 + jne set_intensity_loop + mov al, #0x20 + out dx, al + pop dx + pop cx + pop bx + pop ax + ret +biosfn_set_palette: + push ax + push bx + push cx + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov cl, #0x01 + and bl, #0x01 +set_cga_palette_loop: + mov dx, # VGAREG_ACTL_ADDRESS + mov al, cl + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + and al, #0xfe + or al, bl + mov dx, # VGAREG_ACTL_ADDRESS + out dx, al + inc cl + cmp cl, #0x04 + jne set_cga_palette_loop + mov al, #0x20 + out dx, al + pop dx + pop cx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_pixel (BH,AL,CX,DX) Bit8u BH;Bit8u AL;Bit16u CX;Bit16u DX; +{ + Bit8u mode,line,mask,attr,data; + Bit16u addr; + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + if(vga_modes[line].class==TEXT)return; + + switch(vga_modes[line].memmodel) + { + case PLANAR4: + case PLANAR1: + addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + mask = 0x80 >> (CX & 0x07); + outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08); + outw(VGAREG_GRDC_ADDRESS, 0x0205); + data = read_byte(0xa000,addr); + if (AL & 0x80) + { + outw(VGAREG_GRDC_ADDRESS, 0x1803); + } + write_byte(0xa000,addr,AL); +ASM_START + mov dx, # VGAREG_GRDC_ADDRESS + mov ax, #0xff08 + out dx, ax + mov ax, #0x0005 + out dx, ax + mov ax, #0x0003 + out dx, ax +ASM_END + break; + case CGA: + if(vga_modes[line].pixbits==2) + { + addr=(CX>>2)+(DX>>1)*80; + } + else + { + addr=(CX>>3)+(DX>>1)*80; + } + if (DX & 1) addr += 0x2000; + data = read_byte(0xb800,addr); + if(vga_modes[line].pixbits==2) + { + attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2); + mask = 0x03 << ((3 - (CX & 0x03)) * 2); + } + else + { + attr = (AL & 0x01) << (7 - (CX & 0x07)); + mask = 0x01 << (7 - (CX & 0x07)); + } + if (AL & 0x80) + { + data ^= attr; + } + else + { + data &= ~mask; + data |= attr; + } + write_byte(0xb800,addr,data); + break; + case LINEAR8: + addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8); + write_byte(0xa000,addr,AL); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_read_pixel (BH,CX,DX,AX) Bit8u BH;Bit16u CX;Bit16u DX;Bit16u *AX; +{ + Bit8u mode,line,mask,attr,data,i; + Bit16u addr; + Bit16u ss=get_SS(); + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + if(vga_modes[line].class==TEXT)return; + + switch(vga_modes[line].memmodel) + { + case PLANAR4: + case PLANAR1: + addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + mask = 0x80 >> (CX & 0x07); + attr = 0x00; + for(i=0;i<4;i++) + { + outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04); + data = read_byte(0xa000,addr) & mask; + if (data > 0) attr |= (0x01 << i); + } + break; + case CGA: + addr=(CX>>2)+(DX>>1)*80; + if (DX & 1) addr += 0x2000; + data = read_byte(0xb800,addr); + if(vga_modes[line].pixbits==2) + { + attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03; + } + else + { + attr = (data >> (7 - (CX & 0x07))) & 0x01; + } + break; + case LINEAR8: + addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8); + attr=read_byte(0xa000,addr); + break; + default: +#ifdef DEBUG + unimplemented(); +#endif + attr = 0; + } + write_word(ss,AX,(read_word(ss,AX) & 0xff00) | attr); +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_teletype (car, page, attr, flag) +Bit8u car;Bit8u page;Bit8u attr;Bit8u flag; +{// flag = WITH_ATTR / NO_ATTR + + Bit8u cheight,xcurs,ycurs,mode,line,bpp; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + + // special case if page is 0xff, use current page + if(page==0xff) + page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + + // Get the mode + mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + line=find_vga_entry(mode); + if(line==0xFF)return; + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + switch(car) + { + case 7: + //FIXME should beep + break; + + case 8: + if(xcurs>0)xcurs--; + break; + + case '\r': + xcurs=0; + break; + + case '\n': + ycurs++; + break; + + case '\t': + do + { + biosfn_write_teletype(' ',page,attr,flag); + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + }while(xcurs%8==0); + break; + + default: + + if(vga_modes[line].class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + // Write the char + write_byte(vga_modes[line].sstart,address,car); + + if(flag==WITH_ATTR) + write_byte(vga_modes[line].sstart,address+1,attr); + } + else + { + // FIXME gfx mode not complete + cheight=video_param_table[line_to_vpti[line]].cheight; + bpp=vga_modes[line].pixbits; + switch(vga_modes[line].memmodel) + { + case PLANAR4: + case PLANAR1: + write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight); + break; + case CGA: + write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp); + break; + case LINEAR8: + write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } + } + xcurs++; + } + + // Do we need to wrap ? + if(xcurs==nbcols) + {xcurs=0; + ycurs++; + } + + // Do we need to scroll ? + if(ycurs==nbrows) + { + if(vga_modes[line].class==TEXT) + { + biosfn_scroll(0x01,0x07,0,0,nbrows-1,nbcols-1,page,SCROLL_UP); + } + else + { + biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP); + } + ycurs-=1; + } + + // Set the cursor for the page + cursor=ycurs; cursor<<=8; cursor+=xcurs; + biosfn_set_cursor_pos(page,cursor); +} + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_get_video_mode: + push ds + mov ax, # BIOSMEM_SEG + mov ds, ax + push bx + mov bx, # BIOSMEM_CURRENT_PAGE + mov al, [bx] + pop bx + mov bh, al + push bx + mov bx, # BIOSMEM_VIDEO_CTL + mov ah, [bx] + and ah, #0x80 + mov bx, # BIOSMEM_CURRENT_MODE + mov al, [bx] + or al, ah + mov bx, # BIOSMEM_NB_COLS + mov ah, [bx] + pop bx + pop ds + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_group_10: + cmp al, #0x00 + jne int10_test_1001 + jmp biosfn_set_single_palette_reg +int10_test_1001: + cmp al, #0x01 + jne int10_test_1002 + jmp biosfn_set_overscan_border_color +int10_test_1002: + cmp al, #0x02 + jne int10_test_1003 + jmp biosfn_set_all_palette_reg +int10_test_1003: + cmp al, #0x03 + jne int10_test_1007 + jmp biosfn_toggle_intensity +int10_test_1007: + cmp al, #0x07 + jne int10_test_1008 + jmp biosfn_get_single_palette_reg +int10_test_1008: + cmp al, #0x08 + jne int10_test_1009 + jmp biosfn_read_overscan_border_color +int10_test_1009: + cmp al, #0x09 + jne int10_test_1010 + jmp biosfn_get_all_palette_reg +int10_test_1010: + cmp al, #0x10 + jne int10_test_1012 + jmp biosfn_set_single_dac_reg +int10_test_1012: + cmp al, #0x12 + jne int10_test_1013 + jmp biosfn_set_all_dac_reg +int10_test_1013: + cmp al, #0x13 + jne int10_test_1015 + jmp biosfn_select_video_dac_color_page +int10_test_1015: + cmp al, #0x15 + jne int10_test_1017 + jmp biosfn_read_single_dac_reg +int10_test_1017: + cmp al, #0x17 + jne int10_test_1018 + jmp biosfn_read_all_dac_reg +int10_test_1018: + cmp al, #0x18 + jne int10_test_1019 + jmp biosfn_set_pel_mask +int10_test_1019: + cmp al, #0x19 + jne int10_test_101A + jmp biosfn_read_pel_mask +int10_test_101A: + cmp al, #0x1a + jne int10_group_10_unknown + jmp biosfn_read_video_dac_state +int10_group_10_unknown: +#ifdef DEBUG + call _unknown +#endif + ret + +biosfn_set_single_palette_reg: + cmp bl, #0x14 + ja no_actl_reg1 + push ax + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, bl + out dx, al + mov al, bh + out dx, al + mov al, #0x20 + out dx, al + pop dx + pop ax +no_actl_reg1: + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_set_overscan_border_color: + push bx + mov bl, #0x11 + call biosfn_set_single_palette_reg + pop bx + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_set_all_palette_reg: + push ax + push bx + push cx + push dx + mov bx, dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov cl, #0x00 + mov dx, # VGAREG_ACTL_ADDRESS +set_palette_loop: + mov al, cl + out dx, al + seg es + mov al, [bx] + out dx, al + inc bx + inc cl + cmp cl, #0x10 + jne set_palette_loop + mov al, #0x11 + out dx, al + seg es + mov al, [bx] + out dx, al + mov al, #0x20 + out dx, al + pop dx + pop cx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_toggle_intensity: + push ax + push bx + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x10 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + and al, #0xf7 + and bl, #0x01 + shl bl, 3 + or al, bl + mov dx, # VGAREG_ACTL_ADDRESS + out dx, al + mov al, #0x20 + out dx, al + pop dx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_get_single_palette_reg: + cmp bl, #0x14 + ja no_actl_reg2 + push ax + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, bl + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + mov bh, al + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x20 + out dx, al + pop dx + pop ax +no_actl_reg2: + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_read_overscan_border_color: + push ax + push bx + mov bl, #0x11 + call biosfn_get_single_palette_reg + mov al, bh + pop bx + mov bh, al + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_get_all_palette_reg: + push ax + push bx + push cx + push dx + mov bx, dx + mov cl, #0x00 +get_palette_loop: + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, cl + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + seg es + mov [bx], al + inc bx + inc cl + cmp cl, #0x10 + jne get_palette_loop + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x11 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + seg es + mov [bx], al + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x20 + out dx, al + pop dx + pop cx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_set_single_dac_reg: + push ax + push dx + mov dx, # VGAREG_DAC_WRITE_ADDRESS + mov al, bl + out dx, al + mov dx, # VGAREG_DAC_DATA + pop ax + push ax + mov al, ah + out dx, al + mov al, ch + out dx, al + mov al, cl + out dx, al + pop dx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_set_all_dac_reg: + push ax + push bx + push cx + push dx + mov dx, # VGAREG_DAC_WRITE_ADDRESS + mov al, bl + out dx, al + pop dx + push dx + mov bx, dx + mov dx, # VGAREG_DAC_DATA +set_dac_loop: + seg es + mov al, [bx] + out dx, al + inc bx + seg es + mov al, [bx] + out dx, al + inc bx + seg es + mov al, [bx] + out dx, al + inc bx + dec cx + jnz set_dac_loop + pop dx + pop cx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_select_video_dac_color_page: + push ax + push bx + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x10 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + and bl, #0x01 + jnz set_dac_page + and al, #0x7f + shl bh, 7 + or al, bh + mov dx, # VGAREG_ACTL_ADDRESS + out dx, al + jmp set_actl_normal +set_dac_page: + push ax + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x14 + out dx, al + pop ax + and al, #0x80 + jnz set_dac_16_page + shl bh, 2 +set_dac_16_page: + and bh, #0x0f + mov al, bh + out dx, al +set_actl_normal: + mov al, #0x20 + out dx, al + pop dx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_read_single_dac_reg: + push ax + push dx + mov dx, # VGAREG_DAC_READ_ADDRESS + mov al, bl + out dx, al + pop ax + mov ah, al + mov dx, # VGAREG_DAC_DATA + in al, dx + xchg al, ah + push ax + in al, dx + mov ch, al + in al, dx + mov cl, al + pop dx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_read_all_dac_reg: + push ax + push bx + push cx + push dx + mov dx, # VGAREG_DAC_READ_ADDRESS + mov al, bl + out dx, al + pop dx + push dx + mov bx, dx + mov dx, # VGAREG_DAC_DATA +read_dac_loop: + in al, dx + seg es + mov [bx], al + inc bx + in al, dx + seg es + mov [bx], al + inc bx + in al, dx + seg es + mov [bx], al + inc bx + dec cx + jnz read_dac_loop + pop dx + pop cx + pop bx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_set_pel_mask: + push ax + push dx + mov dx, # VGAREG_PEL_MASK + mov al, bl + out dx, al + pop dx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_read_pel_mask: + push ax + push dx + mov dx, # VGAREG_PEL_MASK + in al, dx + mov bl, al + pop dx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_read_video_dac_state: + push ax + push dx + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x10 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + mov bl, al + shr bl, 7 + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x14 + out dx, al + mov dx, # VGAREG_ACTL_READ_DATA + in al, dx + mov bh, al + and bh, #0x0f + test bl, #0x01 + jnz get_dac_16_page + shr bh, 2 +get_dac_16_page: + mov dx, # VGAREG_ACTL_RESET + in al, dx + mov dx, # VGAREG_ACTL_ADDRESS + mov al, #0x20 + out dx, al + pop dx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +static void biosfn_perform_gray_scale_summing (start,count) +Bit16u start;Bit16u count; +{Bit8u r,g,b; + Bit16u i; + Bit16u index; + + inb(VGAREG_ACTL_RESET); + outb(VGAREG_ACTL_ADDRESS,0x00); + + for( index = 0; index < count; index++ ) + { + // set read address and switch to read mode + outb(VGAREG_DAC_READ_ADDRESS,start); + // get 6-bit wide RGB data values + r=inb( VGAREG_DAC_DATA ); + g=inb( VGAREG_DAC_DATA ); + b=inb( VGAREG_DAC_DATA ); + + // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue ) + i = ( ( 77*r + 151*g + 28*b ) + 0x80 ) >> 8; + + if(i>0x3f)i=0x3f; + + // set write address and switch to write mode + outb(VGAREG_DAC_WRITE_ADDRESS,start); + // write new intensity value + outb( VGAREG_DAC_DATA, i&0xff ); + outb( VGAREG_DAC_DATA, i&0xff ); + outb( VGAREG_DAC_DATA, i&0xff ); + start++; + } + inb(VGAREG_ACTL_RESET); + outb(VGAREG_ACTL_ADDRESS,0x20); +} + +// -------------------------------------------------------------------------------------------- +static void get_font_access() +{ +ASM_START + mov dx, # VGAREG_SEQU_ADDRESS + mov ax, #0x0100 + out dx, ax + mov ax, #0x0402 + out dx, ax + mov ax, #0x0704 + out dx, ax + mov ax, #0x0300 + out dx, ax + mov dx, # VGAREG_GRDC_ADDRESS + mov ax, #0x0204 + out dx, ax + mov ax, #0x0005 + out dx, ax + mov ax, #0x0406 + out dx, ax +ASM_END +} + +static void release_font_access() +{ +ASM_START + mov dx, # VGAREG_SEQU_ADDRESS + mov ax, #0x0100 + out dx, ax + mov ax, #0x0302 + out dx, ax + mov ax, #0x0304 + out dx, ax + mov ax, #0x0300 + out dx, ax + mov dx, # VGAREG_READ_MISC_OUTPUT + in al, dx + and al, #0x01 + shl al, 2 + or al, #0x0a + mov ah, al + mov al, #0x06 + mov dx, # VGAREG_GRDC_ADDRESS + out dx, ax + mov ax, #0x0004 + out dx, ax + mov ax, #0x1005 + out dx, ax +ASM_END +} + +ASM_START +idiv_u: + xor dx,dx + div bx + ret +ASM_END + +static void set_scan_lines(lines) Bit8u lines; +{ + Bit16u crtc_addr,cols,page,vde; + Bit8u crtc_r9,ovl,rows; + + crtc_addr = read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + outb(crtc_addr, 0x09); + crtc_r9 = inb(crtc_addr+1); + crtc_r9 = (crtc_r9 & 0xe0) | (lines - 1); + outb(crtc_addr+1, crtc_r9); + if(lines==8) + { + biosfn_set_cursor_shape(0x06,0x07); + } + else + { + biosfn_set_cursor_shape(lines-4,lines-3); + } + write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, lines); + outb(crtc_addr, 0x12); + vde = inb(crtc_addr+1); + outb(crtc_addr, 0x07); + ovl = inb(crtc_addr+1); + vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1); + rows = vde / lines; + write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, rows-1); + cols = read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, rows * cols * 2); +} + +static void biosfn_load_text_user_pat (AL,ES,BP,CX,DX,BL,BH) Bit8u AL;Bit16u ES;Bit16u BP;Bit16u CX;Bit16u DX;Bit8u BL;Bit8u BH; +{ + Bit16u blockaddr,dest,i,src; + + get_font_access(); + blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); + for(i=0;i<CX;i++) + { + src = BP + i * BH; + dest = blockaddr + (DX + i) * 32; + memcpyb(0xA000, dest, ES, src, BH); + } + release_font_access(); + if(AL>=0x10) + { + set_scan_lines(BH); + } +} + +static void biosfn_load_text_8_14_pat (AL,BL) Bit8u AL;Bit8u BL; +{ + Bit16u blockaddr,dest,i,src; + + get_font_access(); + blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); + for(i=0;i<0x100;i++) + { + src = i * 14; + dest = blockaddr + i * 32; + memcpyb(0xA000, dest, 0xC000, vgafont14+src, 14); + } + release_font_access(); + if(AL>=0x10) + { + set_scan_lines(14); + } +} + +static void biosfn_load_text_8_8_pat (AL,BL) Bit8u AL;Bit8u BL; +{ + Bit16u blockaddr,dest,i,src; + + get_font_access(); + blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); + for(i=0;i<0x100;i++) + { + src = i * 8; + dest = blockaddr + i * 32; + memcpyb(0xA000, dest, 0xC000, vgafont8+src, 8); + } + release_font_access(); + if(AL>=0x10) + { + set_scan_lines(8); + } +} + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_set_text_block_specifier: + push ax + push dx + mov dx, # VGAREG_SEQU_ADDRESS + mov ah, bl + mov al, #0x03 + out dx, ax + pop dx + pop ax + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +static void biosfn_load_text_8_16_pat (AL,BL) Bit8u AL;Bit8u BL; +{ + Bit16u blockaddr,dest,i,src; + + get_font_access(); + blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); + for(i=0;i<0x100;i++) + { + src = i * 16; + dest = blockaddr + i * 32; + memcpyb(0xA000, dest, 0xC000, vgafont16+src, 16); + } + release_font_access(); + if(AL>=0x10) + { + set_scan_lines(16); + } +} + +static void biosfn_load_gfx_8_8_chars (ES,BP) Bit16u ES;Bit16u BP; +{ +#ifdef DEBUG + unimplemented(); +#endif +} +static void biosfn_load_gfx_user_chars (ES,BP,CX,BL,DL) Bit16u ES;Bit16u BP;Bit16u CX;Bit8u BL;Bit8u DL; +{ +#ifdef DEBUG + unimplemented(); +#endif +} +static void biosfn_load_gfx_8_14_chars (BL) Bit8u BL; +{ +#ifdef DEBUG + unimplemented(); +#endif +} +static void biosfn_load_gfx_8_8_dd_chars (BL) Bit8u BL; +{ +#ifdef DEBUG + unimplemented(); +#endif +} +static void biosfn_load_gfx_8_16_chars (BL) Bit8u BL; +{ +#ifdef DEBUG + unimplemented(); +#endif +} +// -------------------------------------------------------------------------------------------- +static void biosfn_get_font_info (BH,ES,BP,CX,DX) +Bit8u BH;Bit16u *ES;Bit16u *BP;Bit16u *CX;Bit16u *DX; +{Bit16u ss=get_SS(); + + switch(BH) + {case 0x00: + write_word(ss,ES,read_word(0x00,0x1f*4)); + write_word(ss,BP,read_word(0x00,(0x1f*4)+2)); + break; + case 0x01: + write_word(ss,ES,read_word(0x00,0x43*4)); + write_word(ss,BP,read_word(0x00,(0x43*4)+2)); + break; + case 0x02: + write_word(ss,ES,0xC000); + write_word(ss,BP,vgafont14); + break; + case 0x03: + write_word(ss,ES,0xC000); + write_word(ss,BP,vgafont8); + break; + case 0x04: + write_word(ss,ES,0xC000); + write_word(ss,BP,vgafont8+128*8); + break; + case 0x05: + write_word(ss,ES,0xC000); + write_word(ss,BP,vgafont14alt); + break; + case 0x06: + write_word(ss,ES,0xC000); + write_word(ss,BP,vgafont16); + break; + case 0x07: + write_word(ss,ES,0xC000); + write_word(ss,BP,vgafont16alt); + break; + default: + #ifdef DEBUG + printf("Get font info BH(%02x) was discarded\n",BH); + #endif + return; + } + // Set byte/char of on screen font + write_word(ss,CX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); + + // Set Highest char row + write_word(ss,DX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); +} + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_get_ega_info: + push ds + push ax + mov ax, # BIOSMEM_SEG + mov ds, ax + xor ch, ch + mov bx, # BIOSMEM_SWITCHES + mov cl, [bx] + and cl, #0x0f + mov bx, # BIOSMEM_CRTC_ADDRESS + mov ax, [bx] + mov bx, #0x0003 + cmp ax, # VGAREG_MDA_CRTC_ADDRESS + jne mode_ega_color + mov bh, #0x01 +mode_ega_color: + pop ax + pop ds + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +static void biosfn_alternate_prtsc() +{ +#ifdef DEBUG + unimplemented(); +#endif +} + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_select_vert_res: + +; res : 00 200 lines, 01 350 lines, 02 400 lines + + push ds + push bx + push dx + mov dl, al + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_MODESET_CTL + mov al, [bx] + mov bx, # BIOSMEM_SWITCHES + mov ah, [bx] + cmp dl, #0x01 + je vert_res_350 + jb vert_res_200 + cmp dl, #0x02 + je vert_res_400 +#ifdef DEBUG + mov al, dl + xor ah, ah + push ax + mov bx, #msg_vert_res + push bx + call _printf + add sp, #4 +#endif + jmp set_retcode +vert_res_400: + + ; reset modeset ctl bit 7 and set bit 4 + ; set switches bit 3-0 to 0x09 + + and al, #0x7f + or al, #0x10 + and ah, #0xf0 + or ah, #0x09 + jnz set_vert_res +vert_res_350: + + ; reset modeset ctl bit 7 and bit 4 + ; set switches bit 3-0 to 0x09 + + and al, #0x6f + and ah, #0xf0 + or ah, #0x09 + jnz set_vert_res +vert_res_200: + + ; set modeset ctl bit 7 and reset bit 4 + ; set switches bit 3-0 to 0x08 + + and al, #0xef + or al, #0x80 + and ah, #0xf0 + or ah, #0x08 +set_vert_res: + mov bx, # BIOSMEM_MODESET_CTL + mov [bx], al + mov bx, # BIOSMEM_SWITCHES + mov [bx], ah +set_retcode: + mov ax, #0x1212 + pop dx + pop bx + pop ds + ret + +#ifdef DEBUG +msg_vert_res: +.ascii "Select vert res (%02x) was discarded" +.byte 0x0d,0x0a,0x00 +#endif + + +biosfn_enable_default_palette_loading: + push ds + push bx + push dx + mov dl, al + and dl, #0x01 + shl dl, 3 + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_MODESET_CTL + mov al, [bx] + and al, #0xf7 + or al, dl + mov [bx], al + mov ax, #0x1212 + pop dx + pop bx + pop ds + ret + + +biosfn_enable_video_addressing: + push bx + push dx + mov bl, al + and bl, #0x01 + xor bl, #0x01 + shl bl, 1 + mov dx, # VGAREG_READ_MISC_OUTPUT + in al, dx + and al, #0xfd + or al, bl + mov dx, # VGAREG_WRITE_MISC_OUTPUT + out dx, al + mov ax, #0x1212 + pop dx + pop bx + ret + + +biosfn_enable_grayscale_summing: + push ds + push bx + push dx + mov dl, al + and dl, #0x01 + xor dl, #0x01 + shl dl, 1 + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_MODESET_CTL + mov al, [bx] + and al, #0xfd + or al, dl + mov [bx], al + mov ax, #0x1212 + pop dx + pop bx + pop ds + ret + + +biosfn_enable_cursor_emulation: + push ds + push bx + push dx + mov dl, al + and dl, #0x01 + xor dl, #0x01 + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_MODESET_CTL + mov al, [bx] + and al, #0xfe + or al, dl + mov [bx], al + mov ax, #0x1212 + pop dx + pop bx + pop ds + ret +ASM_END + +// -------------------------------------------------------------------------------------------- +static void biosfn_switch_video_interface (AL,ES,DX) Bit8u AL;Bit16u ES;Bit16u DX; +{ +#ifdef DEBUG + unimplemented(); +#endif +} +static void biosfn_enable_video_refresh_control (AL) Bit8u AL; +{ +#ifdef DEBUG + unimplemented(); +#endif +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_string (flag,page,attr,count,row,col,seg,offset) +Bit8u flag;Bit8u page;Bit8u attr;Bit16u count;Bit8u row;Bit8u col;Bit16u seg;Bit16u offset; +{ + Bit16u newcurs,oldcurs,dummy; + Bit8u car,carattr; + + // Read curs info for the page + biosfn_get_cursor_pos(page,&dummy,&oldcurs); + + // if row=0xff special case : use current cursor position + if(row==0xff) + {col=oldcurs&0x00ff; + row=(oldcurs&0xff00)>>8; + } + + newcurs=row; newcurs<<=8; newcurs+=col; + biosfn_set_cursor_pos(page,newcurs); + + while(count--!=0) + { + car=read_byte(seg,offset++); + if((flag&0x02)!=0) + attr=read_byte(seg,offset++); + + biosfn_write_teletype(car,page,attr,WITH_ATTR); + } + + // Set back curs pos + if((flag&0x01)==0) + biosfn_set_cursor_pos(page,oldcurs); +} + +// -------------------------------------------------------------------------------------------- +ASM_START +biosfn_group_1A: + cmp al, #0x00 + je biosfn_read_display_code + cmp al, #0x01 + je biosfn_set_display_code +#ifdef DEBUG + call _unknown +#endif + ret +biosfn_read_display_code: + push ds + push ax + mov ax, # BIOSMEM_SEG + mov ds, ax + mov bx, # BIOSMEM_DCC_INDEX + mov al, [bx] + mov bl, al + xor bh, bh + pop ax + mov al, ah + pop ds + ret +biosfn_set_display_code: + push ds + push ax + push bx + mov ax, # BIOSMEM_SEG + mov ds, ax + mov ax, bx + mov bx, # BIOSMEM_DCC_INDEX + mov [bx], al +#ifdef DEBUG + mov al, ah + xor ah, ah + push ax + mov bx, #msg_alt_dcc + push bx + call _printf + add sp, #4 +#endif + pop bx + pop ax + mov al, ah + pop ds + ret + +#ifdef DEBUG +msg_alt_dcc: +.ascii "Alternate Display code (%02x) was discarded" +.byte 0x0d,0x0a,0x00 +#endif +ASM_END + +// -------------------------------------------------------------------------------------------- +static void biosfn_read_state_info (BX,ES,DI) +Bit16u BX;Bit16u ES;Bit16u DI; +{ + // Address of static functionality table + write_word(ES,DI+0x00,&static_functionality); + write_word(ES,DI+0x02,0xC000); + + // Hard coded copy from BIOS area. Should it be cleaner ? + memcpyb(ES,DI+0x04,BIOSMEM_SEG,0x49,30); + memcpyb(ES,DI+0x22,BIOSMEM_SEG,0x84,3); + + write_byte(ES,DI+0x25,read_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX)); + write_byte(ES,DI+0x26,0); + write_byte(ES,DI+0x27,16); + write_byte(ES,DI+0x28,0); + write_byte(ES,DI+0x29,8); + write_byte(ES,DI+0x2a,2); + write_byte(ES,DI+0x2b,0); + write_byte(ES,DI+0x2c,0); + write_byte(ES,DI+0x31,3); + write_byte(ES,DI+0x32,0); + + memsetb(ES,DI+0x33,0,13); +} + +// -------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------- +static Bit16u biosfn_read_video_state_size2 (CX) + Bit16u CX; +{ + Bit16u size; + size = 0; + if (CX & 1) { + size += 0x46; + } + if (CX & 2) { + size += (5 + 8 + 5) * 2 + 6; + } + if (CX & 4) { + size += 3 + 256 * 3 + 1; +} + return size; +} + +static void biosfn_read_video_state_size (CX, BX) + Bit16u CX; Bit16u *BX; +{ + Bit16u ss=get_SS(); + write_word(ss, BX, biosfn_read_video_state_size2(CX)); +} + +static Bit16u biosfn_save_video_state (CX,ES,BX) + Bit16u CX;Bit16u ES;Bit16u BX; +{ + Bit16u i, v, crtc_addr, ar_index; + + crtc_addr = read_word(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS); + if (CX & 1) { + write_byte(ES, BX, inb(VGAREG_SEQU_ADDRESS)); BX++; + write_byte(ES, BX, inb(crtc_addr)); BX++; + write_byte(ES, BX, inb(VGAREG_GRDC_ADDRESS)); BX++; + inb(VGAREG_ACTL_RESET); + ar_index = inb(VGAREG_ACTL_ADDRESS); + write_byte(ES, BX, ar_index); BX++; + write_byte(ES, BX, inb(VGAREG_READ_FEATURE_CTL)); BX++; + + for(i=1;i<=4;i++){ + outb(VGAREG_SEQU_ADDRESS, i); + write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++; + } + outb(VGAREG_SEQU_ADDRESS, 0); + write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++; + + for(i=0;i<=0x18;i++) { + outb(crtc_addr,i); + write_byte(ES, BX, inb(crtc_addr+1)); BX++; + } + + for(i=0;i<=0x13;i++) { + inb(VGAREG_ACTL_RESET); + outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20)); + write_byte(ES, BX, inb(VGAREG_ACTL_READ_DATA)); BX++; + } + inb(VGAREG_ACTL_RESET); + + for(i=0;i<=8;i++) { + outb(VGAREG_GRDC_ADDRESS,i); + write_byte(ES, BX, inb(VGAREG_GRDC_DATA)); BX++; + } + + write_word(ES, BX, crtc_addr); BX+= 2; + + /* XXX: read plane latches */ + write_byte(ES, BX, 0); BX++; + write_byte(ES, BX, 0); BX++; + write_byte(ES, BX, 0); BX++; + write_byte(ES, BX, 0); BX++; + } + if (CX & 2) { + write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)); BX++; + write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)); BX += 2; + write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)); BX += 2; + write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)); BX += 2; + write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); BX++; + write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); BX += 2; + write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)); BX++; + write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES)); BX++; + write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)); BX++; + write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); BX += 2; + for(i=0;i<8;i++) { + write_word(ES, BX, read_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i)); + BX += 2; + } + write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START)); BX += 2; + write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)); BX++; + /* current font */ + write_word(ES, BX, read_word(0, 0x1f * 4)); BX += 2; + write_word(ES, BX, read_word(0, 0x1f * 4 + 2)); BX += 2; + write_word(ES, BX, read_word(0, 0x43 * 4)); BX += 2; + write_word(ES, BX, read_word(0, 0x43 * 4 + 2)); BX += 2; + } + if (CX & 4) { + /* XXX: check this */ + write_byte(ES, BX, inb(VGAREG_DAC_STATE)); BX++; /* read/write mode dac */ + write_byte(ES, BX, inb(VGAREG_DAC_WRITE_ADDRESS)); BX++; /* pix address */ + write_byte(ES, BX, inb(VGAREG_PEL_MASK)); BX++; + // Set the whole dac always, from 0 + outb(VGAREG_DAC_WRITE_ADDRESS,0x00); + for(i=0;i<256*3;i++) { + write_byte(ES, BX, inb(VGAREG_DAC_DATA)); BX++; + } + write_byte(ES, BX, 0); BX++; /* color select register */ + } + return BX; +} + +static Bit16u biosfn_restore_video_state (CX,ES,BX) + Bit16u CX;Bit16u ES;Bit16u BX; +{ + Bit16u i, crtc_addr, v, addr1, ar_index; + + if (CX & 1) { + // Reset Attribute Ctl flip-flop + inb(VGAREG_ACTL_RESET); + + crtc_addr = read_word(ES, BX + 0x40); + addr1 = BX; + BX += 5; + + for(i=1;i<=4;i++){ + outb(VGAREG_SEQU_ADDRESS, i); + outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++; + } + outb(VGAREG_SEQU_ADDRESS, 0); + outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++; + + // Disable CRTC write protection + outw(crtc_addr,0x0011); + // Set CRTC regs + for(i=0;i<=0x18;i++) { + if (i != 0x11) { + outb(crtc_addr,i); + outb(crtc_addr+1, read_byte(ES, BX)); + } + BX++; + } + // select crtc base address + v = inb(VGAREG_READ_MISC_OUTPUT) & ~0x01; + if (crtc_addr = 0x3d4) + v |= 0x01; + outb(VGAREG_WRITE_MISC_OUTPUT, v); + + // enable write protection if needed + outb(crtc_addr, 0x11); + outb(crtc_addr+1, read_byte(ES, BX - 0x18 + 0x11)); + + // Set Attribute Ctl + ar_index = read_byte(ES, addr1 + 0x03); + inb(VGAREG_ACTL_RESET); + for(i=0;i<=0x13;i++) { + outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20)); + outb(VGAREG_ACTL_WRITE_DATA, read_byte(ES, BX)); BX++; + } + outb(VGAREG_ACTL_ADDRESS, ar_index); + inb(VGAREG_ACTL_RESET); + + for(i=0;i<=8;i++) { + outb(VGAREG_GRDC_ADDRESS,i); + outb(VGAREG_GRDC_DATA, read_byte(ES, BX)); BX++; + } + BX += 2; /* crtc_addr */ + BX += 4; /* plane latches */ + + outb(VGAREG_SEQU_ADDRESS, read_byte(ES, addr1)); addr1++; + outb(crtc_addr, read_byte(ES, addr1)); addr1++; + outb(VGAREG_GRDC_ADDRESS, read_byte(ES, addr1)); addr1++; + addr1++; + outb(crtc_addr - 0x4 + 0xa, read_byte(ES, addr1)); addr1++; + } + if (CX & 2) { + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE, read_byte(ES, BX)); BX++; + write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS, read_word(ES, BX)); BX += 2; + write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, read_word(ES, BX)); BX += 2; + write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS, read_word(ES, BX)); BX += 2; + write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, read_byte(ES, BX)); BX++; + write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, read_word(ES, BX)); BX += 2; + write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL, read_byte(ES, BX)); BX++; + write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES, read_byte(ES, BX)); BX++; + write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL, read_byte(ES, BX)); BX++; + write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE, read_word(ES, BX)); BX += 2; + for(i=0;i<8;i++) { + write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i, read_word(ES, BX)); + BX += 2; + } + write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START, read_word(ES, BX)); BX += 2; + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE, read_byte(ES, BX)); BX++; + /* current font */ + write_word(0, 0x1f * 4, read_word(ES, BX)); BX += 2; + write_word(0, 0x1f * 4 + 2, read_word(ES, BX)); BX += 2; + write_word(0, 0x43 * 4, read_word(ES, BX)); BX += 2; + write_word(0, 0x43 * 4 + 2, read_word(ES, BX)); BX += 2; + } + if (CX & 4) { + BX++; + v = read_byte(ES, BX); BX++; + outb(VGAREG_PEL_MASK, read_byte(ES, BX)); BX++; + // Set the whole dac always, from 0 + outb(VGAREG_DAC_WRITE_ADDRESS,0x00); + for(i=0;i<256*3;i++) { + outb(VGAREG_DAC_DATA, read_byte(ES, BX)); BX++; + } + BX++; + outb(VGAREG_DAC_WRITE_ADDRESS, v); + } + return BX; +} + +// ============================================================================================ +// +// Video Utils +// +// ============================================================================================ + +// -------------------------------------------------------------------------------------------- +static Bit8u find_vga_entry(mode) +Bit8u mode; +{ + Bit8u i,line=0xFF; + for(i=0;i<=MODE_MAX;i++) + if(vga_modes[i].svgamode==mode) + {line=i; + break; + } + return line; +} + +/* =========================================================== */ +/* + * Misc Utils +*/ +/* =========================================================== */ + +// -------------------------------------------------------------------------------------------- +static void memsetb(seg,offset,value,count) + Bit16u seg; + Bit16u offset; + Bit16u value; + Bit16u count; +{ +ASM_START + push bp + mov bp, sp + + push ax + push cx + push es + push di + + mov cx, 10[bp] ; count + cmp cx, #0x00 + je memsetb_end + mov ax, 4[bp] ; segment + mov es, ax + mov ax, 6[bp] ; offset + mov di, ax + mov al, 8[bp] ; value + cld + rep + stosb + +memsetb_end: + pop di + pop es + pop cx + pop ax + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static void memsetw(seg,offset,value,count) + Bit16u seg; + Bit16u offset; + Bit16u value; + Bit16u count; +{ +ASM_START + push bp + mov bp, sp + + push ax + push cx + push es + push di + + mov cx, 10[bp] ; count + cmp cx, #0x00 + je memsetw_end + mov ax, 4[bp] ; segment + mov es, ax + mov ax, 6[bp] ; offset + mov di, ax + mov ax, 8[bp] ; value + cld + rep + stosw + +memsetw_end: + pop di + pop es + pop cx + pop ax + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static void memcpyb(dseg,doffset,sseg,soffset,count) + Bit16u dseg; + Bit16u doffset; + Bit16u sseg; + Bit16u soffset; + Bit16u count; +{ +ASM_START + push bp + mov bp, sp + + push ax + push cx + push es + push di + push ds + push si + + mov cx, 12[bp] ; count + cmp cx, #0x0000 + je memcpyb_end + mov ax, 4[bp] ; dsegment + mov es, ax + mov ax, 6[bp] ; doffset + mov di, ax + mov ax, 8[bp] ; ssegment + mov ds, ax + mov ax, 10[bp] ; soffset + mov si, ax + cld + rep + movsb + +memcpyb_end: + pop si + pop ds + pop di + pop es + pop cx + pop ax + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static void memcpyw(dseg,doffset,sseg,soffset,count) + Bit16u dseg; + Bit16u doffset; + Bit16u sseg; + Bit16u soffset; + Bit16u count; +{ +ASM_START + push bp + mov bp, sp + + push ax + push cx + push es + push di + push ds + push si + + mov cx, 12[bp] ; count + cmp cx, #0x0000 + je memcpyw_end + mov ax, 4[bp] ; dsegment + mov es, ax + mov ax, 6[bp] ; doffset + mov di, ax + mov ax, 8[bp] ; ssegment + mov ds, ax + mov ax, 10[bp] ; soffset + mov si, ax + cld + rep + movsw + +memcpyw_end: + pop si + pop ds + pop di + pop es + pop cx + pop ax + + pop bp +ASM_END +} + +/* =========================================================== */ +/* + * These functions where ripped from Kevin's rombios.c +*/ +/* =========================================================== */ + +// -------------------------------------------------------------------------------------------- +static Bit8u +read_byte(seg, offset) + Bit16u seg; + Bit16u offset; +{ +ASM_START + push bp + mov bp, sp + + push bx + push ds + mov ax, 4[bp] ; segment + mov ds, ax + mov bx, 6[bp] ; offset + mov al, [bx] + ;; al = return value (byte) + pop ds + pop bx + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static Bit16u +read_word(seg, offset) + Bit16u seg; + Bit16u offset; +{ +ASM_START + push bp + mov bp, sp + + push bx + push ds + mov ax, 4[bp] ; segment + mov ds, ax + mov bx, 6[bp] ; offset + mov ax, [bx] + ;; ax = return value (word) + pop ds + pop bx + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static void +write_byte(seg, offset, data) + Bit16u seg; + Bit16u offset; + Bit8u data; +{ +ASM_START + push bp + mov bp, sp + + push ax + push bx + push ds + mov ax, 4[bp] ; segment + mov ds, ax + mov bx, 6[bp] ; offset + mov al, 8[bp] ; data byte + mov [bx], al ; write data byte + pop ds + pop bx + pop ax + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- +static void +write_word(seg, offset, data) + Bit16u seg; + Bit16u offset; + Bit16u data; +{ +ASM_START + push bp + mov bp, sp + + push ax + push bx + push ds + mov ax, 4[bp] ; segment + mov ds, ax + mov bx, 6[bp] ; offset + mov ax, 8[bp] ; data word + mov [bx], ax ; write data word + pop ds + pop bx + pop ax + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- + Bit8u +inb(port) + Bit16u port; +{ +ASM_START + push bp + mov bp, sp + + push dx + mov dx, 4[bp] + in al, dx + pop dx + + pop bp +ASM_END +} + + Bit16u +inw(port) + Bit16u port; +{ +ASM_START + push bp + mov bp, sp + + push dx + mov dx, 4[bp] + in ax, dx + pop dx + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- + void +outb(port, val) + Bit16u port; + Bit8u val; +{ +ASM_START + push bp + mov bp, sp + + push ax + push dx + mov dx, 4[bp] + mov al, 6[bp] + out dx, al + pop dx + pop ax + + pop bp +ASM_END +} + +// -------------------------------------------------------------------------------------------- + void +outw(port, val) + Bit16u port; + Bit16u val; +{ +ASM_START + push bp + mov bp, sp + + push ax + push dx + mov dx, 4[bp] + mov ax, 6[bp] + out dx, ax + pop dx + pop ax + + pop bp +ASM_END +} + +Bit16u get_SS() +{ +ASM_START + mov ax, ss +ASM_END +} + +#ifdef DEBUG +void unimplemented() +{ + printf("--> Unimplemented\n"); +} + +void unknown() +{ + printf("--> Unknown int10\n"); +} +#endif + +// -------------------------------------------------------------------------------------------- +#if defined(USE_BX_INFO) || defined(DEBUG) || defined(CIRRUS_DEBUG) +void printf(s) + Bit8u *s; +{ + Bit8u c, format_char; + Boolean in_format; + unsigned format_width, i; + Bit16u *arg_ptr; + Bit16u arg_seg, arg, digit, nibble, shift_count; + + arg_ptr = &s; + arg_seg = get_SS(); + + in_format = 0; + format_width = 0; + + while (c = read_byte(0xc000, s)) { + if ( c == '%' ) { + in_format = 1; + format_width = 0; + } + else if (in_format) { + if ( (c>='0') && (c<='9') ) { + format_width = (format_width * 10) + (c - '0'); + } + else if (c == 'x') { + arg_ptr++; // increment to next arg + arg = read_word(arg_seg, arg_ptr); + if (format_width == 0) + format_width = 4; + i = 0; + digit = format_width - 1; + for (i=0; i<format_width; i++) { + nibble = (arg >> (4 * digit)) & 0x000f; + if (nibble <= 9) + outb(0x0500, nibble + '0'); + else + outb(0x0500, (nibble - 10) + 'A'); + digit--; + } + in_format = 0; + } + //else if (c == 'd') { + // in_format = 0; + // } + } + else { + outb(0x0500, c); + } + s ++; + } +} +#endif + +#ifdef VBE +#include "vbe.c" +#endif + +#ifdef CIRRUS +#include "clext.c" +#endif + +// -------------------------------------------------------------------------------------------- + +ASM_START +;; DATA_SEG_DEFS_HERE +ASM_END + +ASM_START +.ascii "vgabios ends here" +.byte 0x00 +vgabios_end: +.byte 0xCB +;; BLOCK_STRINGS_BEGIN +ASM_END diff --git a/kvm/vgabios/vgabios.h b/kvm/vgabios/vgabios.h new file mode 100644 index 000000000..3ad4bae94 --- /dev/null +++ b/kvm/vgabios/vgabios.h @@ -0,0 +1,47 @@ +#ifndef vgabios_h_included +#define vgabios_h_included + +/* Types */ +typedef unsigned char Bit8u; +typedef unsigned short Bit16u; +typedef unsigned long Bit32u; +typedef unsigned short Boolean; + +/* Defines */ + +#define SET_AL(val8) AX = ((AX & 0xff00) | (val8)) +#define SET_BL(val8) BX = ((BX & 0xff00) | (val8)) +#define SET_CL(val8) CX = ((CX & 0xff00) | (val8)) +#define SET_DL(val8) DX = ((DX & 0xff00) | (val8)) +#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8)) +#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8)) +#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8)) +#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8)) + +#define GET_AL() ( AX & 0x00ff ) +#define GET_BL() ( BX & 0x00ff ) +#define GET_CL() ( CX & 0x00ff ) +#define GET_DL() ( DX & 0x00ff ) +#define GET_AH() ( AX >> 8 ) +#define GET_BH() ( BX >> 8 ) +#define GET_CH() ( CX >> 8 ) +#define GET_DH() ( DX >> 8 ) + +#define SET_CF() FLAGS |= 0x0001 +#define CLEAR_CF() FLAGS &= 0xfffe +#define GET_CF() (FLAGS & 0x0001) + +#define SET_ZF() FLAGS |= 0x0040 +#define CLEAR_ZF() FLAGS &= 0xffbf +#define GET_ZF() (FLAGS & 0x0040) + +#define SCROLL_DOWN 0 +#define SCROLL_UP 1 +#define NO_ATTR 2 +#define WITH_ATTR 3 + +#define SCREEN_SIZE(x,y) (((x*y*2)|0x00ff)+1) +#define SCREEN_MEM_START(x,y,p) ((((x*y*2)|0x00ff)+1)*p) +#define SCREEN_IO_START(x,y,p) ((((x*y)|0x00ff)+1)*p) + +#endif diff --git a/kvm/vgabios/vgafonts.h b/kvm/vgabios/vgafonts.h new file mode 100644 index 000000000..0c213e66b --- /dev/null +++ b/kvm/vgabios/vgafonts.h @@ -0,0 +1,784 @@ +/* + * These fonts come from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip + * The package is (c) by Joseph Gil + * The individual fonts are public domain + */ +static Bit8u vgafont8[256*8]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, + 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, + 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, + 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, + 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, + 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, + 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, + 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, + 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, + 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, + 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, + 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, + 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, + 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, + 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, + 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, + 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, + 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, + 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, + 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, + 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, + 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static Bit8u vgafont14[256*14]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, + 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0x40, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static Bit8u vgafont16[256*16]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static Bit8u vgafont14alt[1]={0x00}; +static Bit8u vgafont16alt[1]={0x00}; diff --git a/kvm/vgabios/vgatables.h b/kvm/vgabios/vgatables.h new file mode 100644 index 000000000..3ac96bbbb --- /dev/null +++ b/kvm/vgabios/vgatables.h @@ -0,0 +1,622 @@ +/* + * + * BIOS Memory + * + */ +#define BIOSMEM_SEG 0x40 + +#define BIOSMEM_INITIAL_MODE 0x10 +#define BIOSMEM_CURRENT_MODE 0x49 +#define BIOSMEM_NB_COLS 0x4A +#define BIOSMEM_PAGE_SIZE 0x4C +#define BIOSMEM_CURRENT_START 0x4E +#define BIOSMEM_CURSOR_POS 0x50 +#define BIOSMEM_CURSOR_TYPE 0x60 +#define BIOSMEM_CURRENT_PAGE 0x62 +#define BIOSMEM_CRTC_ADDRESS 0x63 +#define BIOSMEM_CURRENT_MSR 0x65 +#define BIOSMEM_CURRENT_PAL 0x66 +#define BIOSMEM_NB_ROWS 0x84 +#define BIOSMEM_CHAR_HEIGHT 0x85 +#define BIOSMEM_VIDEO_CTL 0x87 +#define BIOSMEM_SWITCHES 0x88 +#define BIOSMEM_MODESET_CTL 0x89 +#define BIOSMEM_DCC_INDEX 0x8A +#define BIOSMEM_VS_POINTER 0xA8 +#define BIOSMEM_VBE_FLAG 0xB9 +#define BIOSMEM_VBE_MODE 0xBA + + +/* + * + * VGA registers + * + */ +#define VGAREG_ACTL_ADDRESS 0x3c0 +#define VGAREG_ACTL_WRITE_DATA 0x3c0 +#define VGAREG_ACTL_READ_DATA 0x3c1 + +#define VGAREG_INPUT_STATUS 0x3c2 +#define VGAREG_WRITE_MISC_OUTPUT 0x3c2 +#define VGAREG_VIDEO_ENABLE 0x3c3 +#define VGAREG_SEQU_ADDRESS 0x3c4 +#define VGAREG_SEQU_DATA 0x3c5 + +#define VGAREG_PEL_MASK 0x3c6 +#define VGAREG_DAC_STATE 0x3c7 +#define VGAREG_DAC_READ_ADDRESS 0x3c7 +#define VGAREG_DAC_WRITE_ADDRESS 0x3c8 +#define VGAREG_DAC_DATA 0x3c9 + +#define VGAREG_READ_FEATURE_CTL 0x3ca +#define VGAREG_READ_MISC_OUTPUT 0x3cc + +#define VGAREG_GRDC_ADDRESS 0x3ce +#define VGAREG_GRDC_DATA 0x3cf + +#define VGAREG_MDA_CRTC_ADDRESS 0x3b4 +#define VGAREG_MDA_CRTC_DATA 0x3b5 +#define VGAREG_VGA_CRTC_ADDRESS 0x3d4 +#define VGAREG_VGA_CRTC_DATA 0x3d5 + +#define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba +#define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da +#define VGAREG_ACTL_RESET 0x3da + +#define VGAREG_MDA_MODECTL 0x3b8 +#define VGAREG_CGA_MODECTL 0x3d8 +#define VGAREG_CGA_PALETTE 0x3d9 + +/* Video memory */ +#define VGAMEM_GRAPH 0xA000 +#define VGAMEM_CTEXT 0xB800 +#define VGAMEM_MTEXT 0xB000 + +/* + * + * Tables of default values for each mode + * + */ +#define MODE_MAX 15 +#define TEXT 0x00 +#define GRAPH 0x01 + +#define CTEXT 0x00 +#define MTEXT 0x01 +#define CGA 0x02 +#define PLANAR1 0x03 +#define PLANAR4 0x04 +#define LINEAR8 0x05 + +// for SVGA +#define LINEAR15 0x10 +#define LINEAR16 0x11 +#define LINEAR24 0x12 +#define LINEAR32 0x13 + +typedef struct +{Bit8u svgamode; + Bit8u class; /* TEXT, GRAPH */ + Bit8u memmodel; /* CTEXT,MTEXT,CGA,PL1,PL2,PL4,P8,P15,P16,P24,P32 */ + Bit8u pixbits; + Bit16u sstart; + Bit8u pelmask; + Bit8u dacmodel; /* 0 1 2 3 */ +} VGAMODES; + +static VGAMODES vga_modes[MODE_MAX+1]= +{//mode class model bits sstart pelm dac + {0x00, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x01, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x02, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x03, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x04, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01}, + {0x05, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01}, + {0x06, GRAPH, CGA, 1, 0xB800, 0xFF, 0x01}, + {0x07, TEXT, MTEXT, 4, 0xB000, 0xFF, 0x00}, + {0x0D, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01}, + {0x0E, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01}, + {0x0F, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x00}, + {0x10, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}, + {0x11, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x02}, + {0x12, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}, + {0x13, GRAPH, LINEAR8, 8, 0xA000, 0xFF, 0x03}, + {0x6A, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02} +}; + +/* convert index in vga_modes[] to index in video_param_table[] */ +static Bit8u line_to_vpti[MODE_MAX+1]={ + 0x17, 0x17, 0x18, 0x18, 0x04, 0x05, 0x06, 0x07, + 0x0d, 0x0e, 0x11, 0x12, 0x1a, 0x1b, 0x1c, 0x1d, +}; + +/* Default Palette */ +#define DAC_MAX_MODEL 3 + +static Bit8u dac_regs[DAC_MAX_MODEL+1]= +{0x3f,0x3f,0x3f,0xff}; + +/* standard BIOS Video Parameter Table */ +typedef struct { + Bit8u twidth; + Bit8u theightm1; + Bit8u cheight; + Bit8u slength_l; + Bit8u slength_h; + Bit8u sequ_regs[4]; + Bit8u miscreg; + Bit8u crtc_regs[25]; + Bit8u actl_regs[20]; + Bit8u grdc_regs[9]; +} VideoParamTableEntry; + +static VideoParamTableEntry video_param_table[30] = { +{ + /* index=0x00 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x01 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x02 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x03 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x04 vga mode 0x04 */ + 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */ + 0x09, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x63, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, + 0xff, /* crtc_regs */ + 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x03, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x05 vga mode 0x05 */ + 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */ + 0x09, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x63, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, + 0xff, /* crtc_regs */ + 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x03, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x06 vga mode 0x06 */ + 80, 24, 8, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x01, 0x01, 0x00, 0x06, /* sequ_regs */ + 0x63, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2, + 0xff, /* crtc_regs */ + 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x01, 0x00, 0x01, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x07 vga mode 0x07 */ + 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x66, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x08 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x09 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0a no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0b no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0c no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0d vga mode 0x0d */ + 40, 24, 8, 0x00, 0x20, /* tw, th-1, ch, slength */ + 0x09, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0x63, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x0e vga mode 0x0e */ + 80, 24, 8, 0x00, 0x40, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0x63, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x0f no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x10 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x11 vga mode 0x0f */ + 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xa3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x12 vga mode 0x10 */ + 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xa3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x13 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x14 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x15 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x16 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x17 vga mode 0x01 */ + 40, 24, 16, 0x00, 0x08, /* tw, th-1, ch, slength */ + 0x08, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x67, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x18 vga mode 0x03 */ + 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x67, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x19 vga mode 0x07 */ + 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x66, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1a vga mode 0x11 */ + 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xe3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1b vga mode 0x12 */ + 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xe3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1c vga mode 0x13 */ + 40, 24, 8, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x0e, /* sequ_regs */ + 0x63, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x41, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1d vga mode 0x6a */ + 100, 36, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xe3, /* miscreg */ + 0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x8d, 0x57, 0x32, 0x00, 0x57, 0x73, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +}; + +/* Mono */ +static Bit8u palette0[63+1][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f +}; + +static Bit8u palette1[63+1][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f +}; + +static Bit8u palette2[63+1][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f, 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f, + 0x00,0x15,0x00, 0x00,0x15,0x2a, 0x00,0x3f,0x00, 0x00,0x3f,0x2a, 0x2a,0x15,0x00, 0x2a,0x15,0x2a, 0x2a,0x3f,0x00, 0x2a,0x3f,0x2a, + 0x00,0x15,0x15, 0x00,0x15,0x3f, 0x00,0x3f,0x15, 0x00,0x3f,0x3f, 0x2a,0x15,0x15, 0x2a,0x15,0x3f, 0x2a,0x3f,0x15, 0x2a,0x3f,0x3f, + 0x15,0x00,0x00, 0x15,0x00,0x2a, 0x15,0x2a,0x00, 0x15,0x2a,0x2a, 0x3f,0x00,0x00, 0x3f,0x00,0x2a, 0x3f,0x2a,0x00, 0x3f,0x2a,0x2a, + 0x15,0x00,0x15, 0x15,0x00,0x3f, 0x15,0x2a,0x15, 0x15,0x2a,0x3f, 0x3f,0x00,0x15, 0x3f,0x00,0x3f, 0x3f,0x2a,0x15, 0x3f,0x2a,0x3f, + 0x15,0x15,0x00, 0x15,0x15,0x2a, 0x15,0x3f,0x00, 0x15,0x3f,0x2a, 0x3f,0x15,0x00, 0x3f,0x15,0x2a, 0x3f,0x3f,0x00, 0x3f,0x3f,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f +}; + +static Bit8u palette3[256][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x05,0x05,0x05, 0x08,0x08,0x08, 0x0b,0x0b,0x0b, 0x0e,0x0e,0x0e, 0x11,0x11,0x11, 0x14,0x14,0x14, 0x18,0x18,0x18, + 0x1c,0x1c,0x1c, 0x20,0x20,0x20, 0x24,0x24,0x24, 0x28,0x28,0x28, 0x2d,0x2d,0x2d, 0x32,0x32,0x32, 0x38,0x38,0x38, 0x3f,0x3f,0x3f, + 0x00,0x00,0x3f, 0x10,0x00,0x3f, 0x1f,0x00,0x3f, 0x2f,0x00,0x3f, 0x3f,0x00,0x3f, 0x3f,0x00,0x2f, 0x3f,0x00,0x1f, 0x3f,0x00,0x10, + 0x3f,0x00,0x00, 0x3f,0x10,0x00, 0x3f,0x1f,0x00, 0x3f,0x2f,0x00, 0x3f,0x3f,0x00, 0x2f,0x3f,0x00, 0x1f,0x3f,0x00, 0x10,0x3f,0x00, + 0x00,0x3f,0x00, 0x00,0x3f,0x10, 0x00,0x3f,0x1f, 0x00,0x3f,0x2f, 0x00,0x3f,0x3f, 0x00,0x2f,0x3f, 0x00,0x1f,0x3f, 0x00,0x10,0x3f, + 0x1f,0x1f,0x3f, 0x27,0x1f,0x3f, 0x2f,0x1f,0x3f, 0x37,0x1f,0x3f, 0x3f,0x1f,0x3f, 0x3f,0x1f,0x37, 0x3f,0x1f,0x2f, 0x3f,0x1f,0x27, + + 0x3f,0x1f,0x1f, 0x3f,0x27,0x1f, 0x3f,0x2f,0x1f, 0x3f,0x37,0x1f, 0x3f,0x3f,0x1f, 0x37,0x3f,0x1f, 0x2f,0x3f,0x1f, 0x27,0x3f,0x1f, + 0x1f,0x3f,0x1f, 0x1f,0x3f,0x27, 0x1f,0x3f,0x2f, 0x1f,0x3f,0x37, 0x1f,0x3f,0x3f, 0x1f,0x37,0x3f, 0x1f,0x2f,0x3f, 0x1f,0x27,0x3f, + 0x2d,0x2d,0x3f, 0x31,0x2d,0x3f, 0x36,0x2d,0x3f, 0x3a,0x2d,0x3f, 0x3f,0x2d,0x3f, 0x3f,0x2d,0x3a, 0x3f,0x2d,0x36, 0x3f,0x2d,0x31, + 0x3f,0x2d,0x2d, 0x3f,0x31,0x2d, 0x3f,0x36,0x2d, 0x3f,0x3a,0x2d, 0x3f,0x3f,0x2d, 0x3a,0x3f,0x2d, 0x36,0x3f,0x2d, 0x31,0x3f,0x2d, + 0x2d,0x3f,0x2d, 0x2d,0x3f,0x31, 0x2d,0x3f,0x36, 0x2d,0x3f,0x3a, 0x2d,0x3f,0x3f, 0x2d,0x3a,0x3f, 0x2d,0x36,0x3f, 0x2d,0x31,0x3f, + 0x00,0x00,0x1c, 0x07,0x00,0x1c, 0x0e,0x00,0x1c, 0x15,0x00,0x1c, 0x1c,0x00,0x1c, 0x1c,0x00,0x15, 0x1c,0x00,0x0e, 0x1c,0x00,0x07, + 0x1c,0x00,0x00, 0x1c,0x07,0x00, 0x1c,0x0e,0x00, 0x1c,0x15,0x00, 0x1c,0x1c,0x00, 0x15,0x1c,0x00, 0x0e,0x1c,0x00, 0x07,0x1c,0x00, + 0x00,0x1c,0x00, 0x00,0x1c,0x07, 0x00,0x1c,0x0e, 0x00,0x1c,0x15, 0x00,0x1c,0x1c, 0x00,0x15,0x1c, 0x00,0x0e,0x1c, 0x00,0x07,0x1c, + + 0x0e,0x0e,0x1c, 0x11,0x0e,0x1c, 0x15,0x0e,0x1c, 0x18,0x0e,0x1c, 0x1c,0x0e,0x1c, 0x1c,0x0e,0x18, 0x1c,0x0e,0x15, 0x1c,0x0e,0x11, + 0x1c,0x0e,0x0e, 0x1c,0x11,0x0e, 0x1c,0x15,0x0e, 0x1c,0x18,0x0e, 0x1c,0x1c,0x0e, 0x18,0x1c,0x0e, 0x15,0x1c,0x0e, 0x11,0x1c,0x0e, + 0x0e,0x1c,0x0e, 0x0e,0x1c,0x11, 0x0e,0x1c,0x15, 0x0e,0x1c,0x18, 0x0e,0x1c,0x1c, 0x0e,0x18,0x1c, 0x0e,0x15,0x1c, 0x0e,0x11,0x1c, + 0x14,0x14,0x1c, 0x16,0x14,0x1c, 0x18,0x14,0x1c, 0x1a,0x14,0x1c, 0x1c,0x14,0x1c, 0x1c,0x14,0x1a, 0x1c,0x14,0x18, 0x1c,0x14,0x16, + 0x1c,0x14,0x14, 0x1c,0x16,0x14, 0x1c,0x18,0x14, 0x1c,0x1a,0x14, 0x1c,0x1c,0x14, 0x1a,0x1c,0x14, 0x18,0x1c,0x14, 0x16,0x1c,0x14, + 0x14,0x1c,0x14, 0x14,0x1c,0x16, 0x14,0x1c,0x18, 0x14,0x1c,0x1a, 0x14,0x1c,0x1c, 0x14,0x1a,0x1c, 0x14,0x18,0x1c, 0x14,0x16,0x1c, + 0x00,0x00,0x10, 0x04,0x00,0x10, 0x08,0x00,0x10, 0x0c,0x00,0x10, 0x10,0x00,0x10, 0x10,0x00,0x0c, 0x10,0x00,0x08, 0x10,0x00,0x04, + 0x10,0x00,0x00, 0x10,0x04,0x00, 0x10,0x08,0x00, 0x10,0x0c,0x00, 0x10,0x10,0x00, 0x0c,0x10,0x00, 0x08,0x10,0x00, 0x04,0x10,0x00, + + 0x00,0x10,0x00, 0x00,0x10,0x04, 0x00,0x10,0x08, 0x00,0x10,0x0c, 0x00,0x10,0x10, 0x00,0x0c,0x10, 0x00,0x08,0x10, 0x00,0x04,0x10, + 0x08,0x08,0x10, 0x0a,0x08,0x10, 0x0c,0x08,0x10, 0x0e,0x08,0x10, 0x10,0x08,0x10, 0x10,0x08,0x0e, 0x10,0x08,0x0c, 0x10,0x08,0x0a, + 0x10,0x08,0x08, 0x10,0x0a,0x08, 0x10,0x0c,0x08, 0x10,0x0e,0x08, 0x10,0x10,0x08, 0x0e,0x10,0x08, 0x0c,0x10,0x08, 0x0a,0x10,0x08, + 0x08,0x10,0x08, 0x08,0x10,0x0a, 0x08,0x10,0x0c, 0x08,0x10,0x0e, 0x08,0x10,0x10, 0x08,0x0e,0x10, 0x08,0x0c,0x10, 0x08,0x0a,0x10, + 0x0b,0x0b,0x10, 0x0c,0x0b,0x10, 0x0d,0x0b,0x10, 0x0f,0x0b,0x10, 0x10,0x0b,0x10, 0x10,0x0b,0x0f, 0x10,0x0b,0x0d, 0x10,0x0b,0x0c, + 0x10,0x0b,0x0b, 0x10,0x0c,0x0b, 0x10,0x0d,0x0b, 0x10,0x0f,0x0b, 0x10,0x10,0x0b, 0x0f,0x10,0x0b, 0x0d,0x10,0x0b, 0x0c,0x10,0x0b, + 0x0b,0x10,0x0b, 0x0b,0x10,0x0c, 0x0b,0x10,0x0d, 0x0b,0x10,0x0f, 0x0b,0x10,0x10, 0x0b,0x0f,0x10, 0x0b,0x0d,0x10, 0x0b,0x0c,0x10, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00 +}; + +static Bit8u static_functionality[0x10]= +{ + /* 0 */ 0xff, // All modes supported #1 + /* 1 */ 0xe0, // All modes supported #2 + /* 2 */ 0x0f, // All modes supported #3 + /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved + /* 7 */ 0x07, // 200, 350, 400 scan lines + /* 8 */ 0x02, // mamimum number of visible charsets in text mode + /* 9 */ 0x08, // total number of charset blocks in text mode + /* a */ 0xe7, // Change to add new functions + /* b */ 0x0c, // Change to add new functions + /* c */ 0x00, // reserved + /* d */ 0x00, // reserved + /* e */ 0x00, // Change to add new functions + /* f */ 0x00 // reserved +}; @@ -56,6 +56,9 @@ #include "json-streamer.h" #include "json-parser.h" #include "osdep.h" +#include "exec-all.h" + +#include "qemu-kvm.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -729,6 +732,9 @@ static void print_cpu_iter(QObject *obj, void *opaque) monitor_printf(mon, " (halted)"); } + monitor_printf(mon, " thread_id=%" PRId64 " ", + qdict_get_int(cpu, "thread_id")); + monitor_printf(mon, "\n"); } @@ -793,6 +799,7 @@ static void do_info_cpus(Monitor *mon, QObject **ret_data) #elif defined(TARGET_MIPS) qdict_put(cpu, "PC", qint_from_int(env->active_tc.PC)); #endif + qdict_put(cpu, "thread_id", qint_from_int(env->thread_id)); qlist_append(cpu_list, cpu); } @@ -807,6 +814,27 @@ static void do_cpu_set(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Invalid CPU index\n"); } +static void do_cpu_set_nr(Monitor *mon, const QDict *qdict) +{ + int state, value; + const char *status; + + status = qdict_get_str(qdict, "state"); + value = qdict_get_int(qdict, "cpu"); + + if (!strcmp(status, "online")) + state = 1; + else if (!strcmp(status, "offline")) + state = 0; + else { + monitor_printf(mon, "invalid status: %s\n", status); + return; + } +#if defined(TARGET_I386) || defined(TARGET_X86_64) + qemu_system_cpu_hot_add(value, state); +#endif +} + static void do_info_jit(Monitor *mon) { dump_exec_info((FILE *)mon, monitor_fprintf); @@ -2017,7 +2045,10 @@ static void do_inject_nmi(Monitor *mon, const QDict *qdict) for (env = first_cpu; env != NULL; env = env->next_cpu) if (env->cpu_index == cpu_index) { - cpu_interrupt(env, CPU_INTERRUPT_NMI); + if (kvm_enabled()) + kvm_inject_interrupt(env, CPU_INTERRUPT_NMI); + else + cpu_interrupt(env, CPU_INTERRUPT_NMI); break; } } @@ -104,7 +104,11 @@ void *qemu_memalign(size_t alignment, size_t size) /* alloc shared memory pages */ void *qemu_vmalloc(size_t size) { +#ifndef __ia64__ return qemu_memalign(getpagesize(), size); +#else + return qemu_memalign(65536, size); +#endif } void qemu_vfree(void *ptr) diff --git a/pc-bios/bios-vista.diff b/pc-bios/bios-vista.diff new file mode 100644 index 000000000..684a3105e --- /dev/null +++ b/pc-bios/bios-vista.diff @@ -0,0 +1,17 @@ +Index: rombios32.c +=================================================================== +RCS file: /cvsroot/bochs/bochs/bios/rombios32.c,v +retrieving revision 1.9 +diff -u -w -r1.9 rombios32.c +--- rombios32.c 20 Feb 2007 09:36:55 -0000 1.9 ++++ rombios32.c 2 May 2007 06:07:31 -0000 +@@ -1191,7 +1191,7 @@ + { + memcpy(h->signature, sig, 4); + h->length = cpu_to_le32(len); +- h->revision = 0; ++ h->revision = 1; + #ifdef BX_QEMU + memcpy(h->oem_id, "QEMU ", 6); + memcpy(h->oem_table_id, "QEMU", 4); + diff --git a/pc-bios/bochs-manifest b/pc-bios/bochs-manifest new file mode 100644 index 000000000..1b25aa412 --- /dev/null +++ b/pc-bios/bochs-manifest @@ -0,0 +1,24 @@ +.cvsignore 1.2 +BIOS-bochs-latest 1.145 +BIOS-bochs-legacy 1.9 +Makefile.in 1.26 +VGABIOS-elpin-2.40 1.4 +VGABIOS-elpin-LICENSE 1.3 +VGABIOS-lgpl-README 1.9 +VGABIOS-lgpl-latest 1.13 +VGABIOS-lgpl-latest-cirrus 1.5 +VGABIOS-lgpl-latest-cirrus-debug 1.5 +VGABIOS-lgpl-latest-debug 1.9 +acpi-dsdt.dsl 1.1 +acpi-dsdt.hex 1.1 +apmbios.S 1.5 +bios_usage 1.1 +biossums.c 1.3 +makesym.perl 1.1 +notes 1.1 +rombios.c 1.178 +rombios.h 1.4 +rombios32.c 1.9 +rombios32.ld 1.1 +rombios32start.S 1.3 +usage.cc 1.4 diff --git a/pc-bios/openbios-sparc b/pc-bios/openbios-sparc Binary files differnew file mode 100644 index 000000000..7a729aa81 --- /dev/null +++ b/pc-bios/openbios-sparc diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index 54db88249..84c77018d 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -15,6 +15,8 @@ QEMU_CFLAGS = $(CFLAGS) build-all: multiboot.bin linuxboot.bin +build-all: extboot.bin vapic.bin + %.img: %.o $(call quiet-command,$(LD) -Ttext 0 -e _start -s -o $@ $<," Building $(TARGET_DIR)$@") diff --git a/pc-bios/optionrom/extboot.S b/pc-bios/optionrom/extboot.S new file mode 100644 index 000000000..1e60f6878 --- /dev/null +++ b/pc-bios/optionrom/extboot.S @@ -0,0 +1,695 @@ +/* + * Extended Boot Option ROM + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corporation, 2007 + * Authors: Anthony Liguori <aliguori@us.ibm.com> + */ + +.code16 +.text + .global _start +_start: + .short 0xaa55 + .byte (_end - _start) / 512 + push %eax + push %ds + + /* setup ds so we can access the IVT */ + xor %ax, %ax + mov %ax, %ds + + /* there is one more bootable HD */ + incb 0x0475 + + /* save old int 19 */ + mov (0x19*4), %eax + mov %eax, %cs:old_int19 + + /* install out int 19 handler */ + movw $int19_handler, (0x19*4) + mov %cs, (0x19*4+2) + + pop %ds + pop %eax + lret + +int19_handler: + push %eax + push %bx + push %cx + push %dx + push %ds + + /* setup ds to access IVT */ + xor %ax, %ax + mov %ax, %ds + + movw $0x404, %dx + inb %dx, %al + cmp $1, %al + je 1f + cmp $2, %al + je 2f + jmp 3f + +1: /* hook int13: intb(0x404) == 1 */ + /* save old int 13 to int 2c */ + mov (0x13*4), %eax + mov %eax, %cs:old_int13 + + /* install our int 13 handler */ + movw $int13_handler, (0x13*4) + mov %cs, (0x13*4+2) + jmp 3f + +2: /* linux boot: intb(0x404) == 2 */ + cli + cld + mov $0x9000, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov $0x8ffe, %sp + ljmp $0x9000 + 0x20, $0 + +3: /* fall through: inb(0x404) == 0 */ + /* restore previous int $0x19 handler */ + mov %cs:old_int19,%eax + mov %eax,(0x19*4) + + pop %ds + pop %dx + pop %cx + pop %bx + pop %eax + ljmpw *%cs:old_int19 + +#define FLAGS_CF 0x01 + +/* The two macro below clear/set the carry flag to indicate the status + * of the interrupt execution. It is not enough to issue a clc/stc instruction, + * since the value of the flags register will be overwritten by whatever is + * in the stack frame + */ +.macro clc_stack + push %bp + mov %sp, %bp + /* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */ + and $(~FLAGS_CF), 8(%bp) + pop %bp +.endm + +.macro stc_stack + push %bp + /* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */ + or $(FLAGS_CF), 8(%bp) + pop %bp +.endm + +/* we clobber %bx */ +.macro alloca size + push %ds + push %bp + mov %sp, %bp /* remember the current stack position */ + + mov %ss, %bx + mov %bx, %ds + + sub \size, %sp + and $(~0x0F), %sp + mov %sp, %bx + + push %bp + mov 0(%bp), %bp +.endm + +/* we clobber %bp */ +.macro allocbpa size + mov %sp, %bp /* remember the current stack position */ + sub \size, %sp + and $(~0x0F), %sp + push %bp + mov %sp, %bp + add $2, %bp +.endm + +.macro freea + pop %sp + add $2, %sp + pop %ds +.endm + +.macro freebpa + pop %sp +.endm + +.macro dump reg + push %ax + push %dx + + mov \reg, %ax + mov $0x406, %dx + outw %ax, %dx + + pop %dx + pop %ax +.endm + +.macro callout value + push %bp + push %bx + mov %sp, %bp + alloca $16 + push %ax + push %dx + + mov %ax, 0(%bx) /* ax */ + mov 0(%bp), %ax /* bx */ + mov %ax, 2(%bx) + mov %cx, 4(%bx) /* cx */ + mov %dx, 6(%bx) /* dx */ + mov %si, 8(%bx) /* si */ + mov %ds, 10(%bx) /* ds */ + mov %es, 12(%bx) /* ds */ + movw \value, 14(%bx) /* value */ + + mov %bx, %ax + shr $4, %ax + mov %ds, %dx + add %dx, %ax + + mov $0x407, %dx + outw %ax, %dx + + pop %dx + pop %ax + freea + pop %bx + pop %bp +.endm + +send_command: + push %bp + mov %sp, %bp + push %ax + push %bx + push %dx + + mov 4(%bp), %ax + shr $4, %ax + and $0x0FFF, %ax + mov %ss, %bx + add %bx, %ax + + mov $0x405, %dx + outw %ax, %dx + + pop %dx + pop %bx + pop %ax + pop %bp + + push %ax + mov 2(%bx), %ax + pop %ax + + ret + +add32: /* lo, hi, lo, hi */ + push %bp + mov %sp, %bp + + movw 4(%bp), %cx /* hi */ + movw 6(%bp), %dx /* lo */ + + add 10(%bp), %dx + jnc 1f + add $1, %cx +1: add 8(%bp), %cx + + pop %bp + ret + +mul32: /* lo, hi, lo, hi */ + /* 10(%bp), 8(%bp), 6(%bp), 4(%bp) */ + push %bp + mov %sp, %bp + push %ax + push %bx + + xor %cx, %cx + xor %dx, %dx + + /* for (i = 0; i < 16;) */ + xor %bx, %bx +0: + cmp $16, %bx + jge 2f + + mov 6(%bp), %ax + and $1, %ax + cmp $1, %ax + jne 1f + push 10(%bp) + push 8(%bp) + push %dx + push %cx + call add32 + add $8, %sp +1: + shlw $1, 8(%bp) + movw 10(%bp), %ax + and $0x8000, %ax + cmp $0x8000, %ax + jne 1f + orw $1, 8(%bp) +1: + shlw $1, 10(%bp) + shrw $1, 6(%bp) + + /* i++) { */ + add $1, %bx + jmp 0b + +2: + pop %bx + pop %ax + pop %bp + ret + +disk_reset: + movb $0, %ah + clc_stack + ret + +/* this really should be a function, not a macro but i'm lazy */ +.macro read_write_disk_sectors cmd + push %ax + push %bx + push %cx + push %dx + push %si + + push %bp + sub $10, %sp + mov %sp, %bp + + /* save nb_sectors */ + mov %al, 6(%bp) + movb $0, 7(%bp) + + /* save buffer */ + mov %bx, 8(%bp) + + /* cylinders */ + xor %ax, %ax + mov %cl, %al + shl $2, %ax + and $0x300, %ax + mov %ch, %al + mov %ax, 0(%bp) + + /* heads */ + xor %ax, %ax + mov %dh, %al + mov %ax, 2(%bp) + + /* sectors - 1 */ + xor %ax, %ax + mov %cl, %al + and $0x3F, %al + sub $1, %ax + mov %ax, 4(%bp) + + alloca $16 + + movw $0, 0(%bx) /* read c,h,s */ + push %bx + call send_command + add $2, %sp + + mov 6(%bx), %ax /* total_sectors */ + mov 2(%bp), %si /* *= heads */ + mul %si + add 4(%bp), %ax /* += sectors - 1 */ + + push 4(%bx) /* total_heads */ + push $0 + push 6(%bx) /* total_sectors */ + push $0 + call mul32 + add $8, %sp + + push 0(%bp) /* cylinders */ + push $0 + push %dx + push %cx + call mul32 + add $8, %sp + + add %ax, %dx + jnc 1f + add $1, %cx +1: + freea + + alloca $16 + + movw \cmd, 0(%bx) /* read */ + movw 6(%bp), %ax /* nb_sectors */ + movw %ax, 2(%bx) + movw %es, 4(%bx) /* segment */ + movw 8(%bp), %ax /* offset */ + mov %ax, 6(%bx) + movw %dx, 8(%bx) /* sector */ + movw %cx, 10(%bx) + movw $0, 12(%bx) + movw $0, 14(%bx) + + push %bx + call send_command + add $2, %sp + + freea + + add $10, %sp + pop %bp + + pop %si + pop %dx + pop %cx + pop %bx + pop %ax + + mov $0, %ah + clc_stack + ret +.endm + +read_disk_sectors: + read_write_disk_sectors $0x01 + +write_disk_sectors: + read_write_disk_sectors $0x02 + +read_disk_drive_parameters: + push %bx + + /* allocate memory for packet, pointer gets returned in bx */ + alloca $16 + + /* issue command */ + movw $0, 0(%bx) /* cmd = 0, read c,h,s */ + push %bx + call send_command + add $2, %sp + + /* normalize sector value */ + movb 6(%bx), %cl + andb $0x3F, %cl + movb %cl, 6(%bx) + + /* normalize cylinders */ + subw $2, 2(%bx) + + /* normalize heads */ + subw $1, 4(%bx) + + /* return code */ + mov $0, %ah + + /* cylinders */ + movb 2(%bx), %ch + movb 3(%bx), %cl + shlb $6, %cl + andb $0xC0, %cl + + /* sectors */ + orb 6(%bx), %cl + + /* heads */ + movb 4(%bx), %dh + + /* drives */ + movb $1, %dl + + /* status */ + mov $0, %ah + + freea + + pop %bx + + /* do this last since it's the most sensitive */ + clc_stack + ret + +alternate_disk_reset: + movb $0, %ah + clc_stack + ret + +read_disk_drive_size: + push %bx + alloca $16 + + movw $0, 0(%bx) /* cmd = 0, read c,h,s */ + push %bx + call send_command + add $2, %sp + + /* cylinders - 1 to cx:dx */ + mov 2(%bx), %dx + xor %cx, %cx + sub $1, %dx + + /* heads */ + push 4(%bx) + push $0 + push %dx + push %cx + call mul32 + add $8, %sp + + /* sectors */ + push 6(%bx) + push $0 + push %dx + push %cx + call mul32 + add $8, %sp + + /* status */ + mov $3, %ah + + freea + pop %bx + + clc_stack + ret + +check_if_extensions_present: + mov $0x30, %ah + mov $0xAA55, %bx + mov $0x07, %cx + clc_stack + ret + +.macro extended_read_write_sectors cmd + cmpb $10, 0(%si) + jg 1f + mov $1, %ah + stc_stack + ret +1: + push %ax + push %bp + allocbpa $16 + + movw \cmd, 0(%bp) /* read */ + movw 2(%si), %ax /* nb_sectors */ + movw %ax, 2(%bp) + movw 4(%si), %ax /* offset */ + movw %ax, 6(%bp) + movw 6(%si), %ax /* segment */ + movw %ax, 4(%bp) + movw 8(%si), %ax /* block */ + movw %ax, 8(%bp) + movw 10(%si), %ax + movw %ax, 10(%bp) + movw 12(%si), %ax + movw %ax, 12(%bp) + movw 14(%si), %ax + movw %ax, 14(%bp) + + push %bp + call send_command + add $2, %sp + + freebpa + pop %bp + pop %ax + + mov $0, %ah + clc_stack + ret +.endm + +extended_read_sectors: + extended_read_write_sectors $0x01 + +extended_write_sectors: + extended_read_write_sectors $0x02 + +get_extended_drive_parameters: + push %ax + push %bp + push %cx + push %dx + + allocbpa $16 + + movw $0, 0(%bp) /* read c,h,s */ + push %bp + call send_command + add $2, %sp + + /* write size */ + movw $26, 0(%si) + + /* set flags to 2 */ + movw $2, 2(%si) + + /* cylinders */ + mov 2(%bp), %ax + mov %ax, 4(%si) + xor %ax, %ax + mov %ax, 6(%si) + + /* heads */ + mov 4(%bp), %ax + mov %ax, 8(%si) + xor %ax, %ax + mov %ax, 10(%si) + + /* sectors */ + mov 6(%bp), %ax + mov %ax, 12(%si) + xor %ax, %ax + mov %ax, 14(%si) + + /* set total number of sectors */ + mov 8(%bp), %ax + mov %ax, 16(%si) + mov 10(%bp), %ax + mov %ax, 18(%si) + mov 12(%bp), %ax + mov %ax, 20(%si) + mov 14(%bp), %ax + mov %ax, 22(%si) + + /* number of bytes per sector */ + movw $512, 24(%si) + + freebpa + + pop %dx + pop %cx + pop %bp + pop %ax + + mov $0, %ah + clc_stack + ret + +terminate_disk_emulation: + mov $1, %ah + stc_stack + ret + +int13_handler: + cmp $0x80, %dl + je 1f + ljmpw *%cs:old_int13 +1: + cmp $0x0, %ah + jne 1f + call disk_reset + iret +1: + cmp $0x2, %ah + jne 1f + call read_disk_sectors + iret +1: + cmp $0x8, %ah + jne 1f + call read_disk_drive_parameters + iret +1: + cmp $0x15, %ah + jne 1f + call read_disk_drive_size + iret +1: + cmp $0x41, %ah + jne 1f + call check_if_extensions_present + iret +1: + cmp $0x42, %ah + jne 1f + call extended_read_sectors + iret +1: + cmp $0x48, %ah + jne 1f + call get_extended_drive_parameters + iret +1: + cmp $0x4b, %ah + jne 1f + call terminate_disk_emulation + iret +1: + cmp $0x0d, %ah + jne 1f + call alternate_disk_reset + iret +1: + cmp $0x03, %ah + jne 1f + call write_disk_sectors + iret +1: + cmp $0x43, %ah + jne 1f + call extended_write_sectors + iret +1: + int $0x18 /* boot failed */ + iret + +/* Variables */ +.align 4, 0 +old_int13: .long 0 +old_int19: .long 0 + +.align 512, 0 +_end: diff --git a/pc-bios/optionrom/vapic.S b/pc-bios/optionrom/vapic.S new file mode 100644 index 000000000..afe98a9ca --- /dev/null +++ b/pc-bios/optionrom/vapic.S @@ -0,0 +1,315 @@ + .text 0 + .code16 +.global _start +_start: + .short 0xaa55 + .byte (_end - _start) / 512 + mov $vapic_base, %ax + out %ax, $0x7e + lret + + .code32 +vapic_size = 2*4096 + +.macro fixup delta=-4 +777: + .text 1 + .long 777b + \delta - vapic_base + .text 0 +.endm + +.macro reenable_vtpr + out %al, $0x7e +.endm + +.text 1 + fixup_start = . +.text 0 + +.align 16 + +vapic_base: + .ascii "kvm aPiC" + + /* relocation data */ + .long vapic_base ; fixup + .long fixup_start ; fixup + .long fixup_end ; fixup + + .long vapic ; fixup + .long vapic_size +vcpu_shift: + .long 0 +real_tpr: + .long 0 + .long up_set_tpr ; fixup + .long up_set_tpr_eax ; fixup + .long up_get_tpr_eax ; fixup + .long up_get_tpr_ecx ; fixup + .long up_get_tpr_edx ; fixup + .long up_get_tpr_ebx ; fixup + .long 0 /* esp. won't work. */ + .long up_get_tpr_ebp ; fixup + .long up_get_tpr_esi ; fixup + .long up_get_tpr_edi ; fixup + .long up_get_tpr_stack ; fixup + .long mp_set_tpr ; fixup + .long mp_set_tpr_eax ; fixup + .long mp_get_tpr_eax ; fixup + .long mp_get_tpr_ecx ; fixup + .long mp_get_tpr_edx ; fixup + .long mp_get_tpr_ebx ; fixup + .long 0 /* esp. won't work. */ + .long mp_get_tpr_ebp ; fixup + .long mp_get_tpr_esi ; fixup + .long mp_get_tpr_edi ; fixup + .long mp_get_tpr_stack ; fixup + +.macro kvm_hypercall + .byte 0x0f, 0x01, 0xc1 +.endm + +kvm_hypercall_vapic_poll_irq = 1 + +pcr_cpu = 0x51 + +.align 64 + +mp_get_tpr_eax: + pushf + cli + reenable_vtpr + push %ecx + + fs/movzbl pcr_cpu, %eax + + mov vcpu_shift, %ecx ; fixup + shl %cl, %eax + testb $1, vapic+4(%eax) ; fixup delta=-5 + jz mp_get_tpr_bad + movzbl vapic(%eax), %eax ; fixup + +mp_get_tpr_out: + pop %ecx + popf + ret + +mp_get_tpr_bad: + mov real_tpr, %eax ; fixup + mov (%eax), %eax + jmp mp_get_tpr_out + +mp_get_tpr_ebx: + mov %eax, %ebx + call mp_get_tpr_eax + xchg %eax, %ebx + ret + +mp_get_tpr_ecx: + mov %eax, %ecx + call mp_get_tpr_eax + xchg %eax, %ecx + ret + +mp_get_tpr_edx: + mov %eax, %edx + call mp_get_tpr_eax + xchg %eax, %edx + ret + +mp_get_tpr_esi: + mov %eax, %esi + call mp_get_tpr_eax + xchg %eax, %esi + ret + +mp_get_tpr_edi: + mov %eax, %edi + call mp_get_tpr_edi + xchg %eax, %edi + ret + +mp_get_tpr_ebp: + mov %eax, %ebp + call mp_get_tpr_eax + xchg %eax, %ebp + ret + +mp_get_tpr_stack: + call mp_get_tpr_eax + xchg %eax, 4(%esp) + ret + +mp_set_tpr_eax: + push %eax + call mp_set_tpr + ret + +mp_set_tpr: + pushf + push %eax + push %ecx + push %edx + push %ebx + cli + reenable_vtpr + +mp_set_tpr_failed: + fs/movzbl pcr_cpu, %edx + + mov vcpu_shift, %ecx ; fixup + shl %cl, %edx + + testb $1, vapic+4(%edx) ; fixup delta=-5 + jz mp_set_tpr_bad + + mov vapic(%edx), %eax ; fixup + + mov %eax, %ebx + mov 24(%esp), %bl + + /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */ + + lock cmpxchg %ebx, vapic(%edx) ; fixup + jnz mp_set_tpr_failed + + /* compute ppr */ + cmp %bh, %bl + jae mp_tpr_is_bigger +mp_isr_is_bigger: + mov %bh, %bl +mp_tpr_is_bigger: + /* %bl = ppr */ + mov %bl, %ch /* ch = ppr */ + rol $8, %ebx + /* now: %bl = irr, %bh = ppr */ + cmp %bh, %bl + ja mp_set_tpr_poll_irq + +mp_set_tpr_out: + pop %ebx + pop %edx + pop %ecx + pop %eax + popf + ret $4 + +mp_set_tpr_poll_irq: + mov $kvm_hypercall_vapic_poll_irq, %eax + kvm_hypercall + jmp mp_set_tpr_out + +mp_set_tpr_bad: + mov 24(%esp), %ecx + mov real_tpr, %eax ; fixup + mov %ecx, (%eax) + jmp mp_set_tpr_out + +up_get_tpr_eax: + reenable_vtpr + movzbl vapic, %eax ; fixup + ret + +up_get_tpr_ebx: + reenable_vtpr + movzbl vapic, %ebx ; fixup + ret + +up_get_tpr_ecx: + reenable_vtpr + movzbl vapic, %ecx ; fixup + ret + +up_get_tpr_edx: + reenable_vtpr + movzbl vapic, %edx ; fixup + ret + +up_get_tpr_esi: + reenable_vtpr + movzbl vapic, %esi ; fixup + ret + +up_get_tpr_edi: + reenable_vtpr + movzbl vapic, %edi ; fixup + ret + +up_get_tpr_ebp: + reenable_vtpr + movzbl vapic, %ebp ; fixup + ret + +up_get_tpr_stack: + reenable_vtpr + movzbl vapic, %eax ; fixup + xchg %eax, 4(%esp) + ret + +up_set_tpr_eax: + push %eax + call up_set_tpr + ret + +up_set_tpr: + pushf + push %eax + push %ecx + push %ebx + reenable_vtpr + +up_set_tpr_failed: + mov vapic, %eax ; fixup + + mov %eax, %ebx + mov 20(%esp), %bl + + /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */ + + lock cmpxchg %ebx, vapic ; fixup + jnz up_set_tpr_failed + + /* compute ppr */ + cmp %bh, %bl + jae up_tpr_is_bigger +up_isr_is_bigger: + mov %bh, %bl +up_tpr_is_bigger: + /* %bl = ppr */ + mov %bl, %ch /* ch = ppr */ + rol $8, %ebx + /* now: %bl = irr, %bh = ppr */ + cmp %bh, %bl + ja up_set_tpr_poll_irq + +up_set_tpr_out: + pop %ebx + pop %ecx + pop %eax + popf + ret $4 + +up_set_tpr_poll_irq: + mov $kvm_hypercall_vapic_poll_irq, %eax + kvm_hypercall + jmp up_set_tpr_out + +.text 1 + fixup_end = . +.text 0 + +/* + * vapic format: + * per-vcpu records of size 2^vcpu shift. + * byte 0: tpr (r/w) + * byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero + * byte 2: zero (r/o) + * byte 3: highest pending interrupt (irr) (r/o) + */ +.text 2 + +.align 128 + +vapic: +. = . + vapic_size +_end: diff --git a/pc-bios/vapic.bin b/pc-bios/vapic.bin Binary files differnew file mode 100755 index 000000000..35f0cf2a3 --- /dev/null +++ b/pc-bios/vapic.bin diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin Binary files differindex 4fa8f99f7..7007bf093 100644 --- a/pc-bios/vgabios-cirrus.bin +++ b/pc-bios/vgabios-cirrus.bin diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin Binary files differindex fa6f815fc..c38c62ae3 100644 --- a/pc-bios/vgabios.bin +++ b/pc-bios/vgabios.bin diff --git a/posix-aio-compat.c b/posix-aio-compat.c index dc14f5355..272e99832 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -26,6 +26,7 @@ #include "osdep.h" #include "qemu-common.h" #include "block_int.h" +#include "compatfd.h" #include "block/raw-posix-aio.h" @@ -53,7 +54,7 @@ struct qemu_paiocb { }; typedef struct PosixAioState { - int rfd, wfd; + int fd; struct qemu_paiocb *first_aio; } PosixAioState; @@ -472,18 +473,29 @@ static int posix_aio_process_queue(void *opaque) static void posix_aio_read(void *opaque) { PosixAioState *s = opaque; - ssize_t len; + union { + struct qemu_signalfd_siginfo siginfo; + char buf[128]; + } sig; + size_t offset; - /* read all bytes from signal pipe */ - for (;;) { - char bytes[16]; + /* try to read from signalfd, don't freak out if we can't read anything */ + offset = 0; + while (offset < 128) { + ssize_t len; - len = read(s->rfd, bytes, sizeof(bytes)); + len = read(s->fd, sig.buf + offset, 128 - offset); if (len == -1 && errno == EINTR) - continue; /* try again */ - if (len == sizeof(bytes)) - continue; /* more to read */ - break; + continue; + if (len == -1 && errno == EAGAIN) { + /* there is no natural reason for this to happen, + * so we'll spin hard until we get everything just + * to be on the safe side. */ + if (offset > 0) + continue; + } + + offset += len; } posix_aio_process_queue(s); @@ -497,17 +509,6 @@ static int posix_aio_flush(void *opaque) static PosixAioState *posix_aio_state; -static void aio_signal_handler(int signum) -{ - if (posix_aio_state) { - char byte = 0; - - write(posix_aio_state->wfd, &byte, sizeof(byte)); - } - - qemu_service_io(); -} - static void paio_remove(struct qemu_paiocb *acb) { struct qemu_paiocb **pacb; @@ -609,9 +610,8 @@ BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd, int paio_init(void) { - struct sigaction act; + sigset_t mask; PosixAioState *s; - int fds[2]; int ret; if (posix_aio_state) @@ -619,24 +619,21 @@ int paio_init(void) s = qemu_malloc(sizeof(PosixAioState)); - sigfillset(&act.sa_mask); - act.sa_flags = 0; /* do not restart syscalls to interrupt select() */ - act.sa_handler = aio_signal_handler; - sigaction(SIGUSR2, &act, NULL); + /* Make sure to block AIO signal */ + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, NULL); s->first_aio = NULL; - if (qemu_pipe(fds) == -1) { - fprintf(stderr, "failed to create pipe\n"); + s->fd = qemu_signalfd(&mask); + if (s->fd == -1) { + fprintf(stderr, "failed to create signalfd\n"); return -1; } - s->rfd = fds[0]; - s->wfd = fds[1]; - - fcntl(s->rfd, F_SETFL, O_NONBLOCK); - fcntl(s->wfd, F_SETFL, O_NONBLOCK); + fcntl(s->fd, F_SETFL, O_NONBLOCK); - qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, + qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush, posix_aio_process_queue, s); ret = pthread_attr_init(&attr); diff --git a/qemu-common.h b/qemu-common.h index d96060adb..1c5c0b222 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -226,6 +226,14 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id); /* Force QEMU to stop what it's doing and service IO */ void qemu_service_io(void); +/* work queue */ +struct qemu_work_item { + struct qemu_work_item *next; + void (*func)(void *data); + void *data; + int done; +}; + /* Force QEMU to process pending events */ void qemu_notify_event(void); diff --git a/qemu-config.c b/qemu-config.c index c3203c87f..2caf76c93 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -78,6 +78,10 @@ QemuOptsList qemu_drive_opts = { },{ .name = "readonly", .type = QEMU_OPT_BOOL, + },{ + .name = "boot", + .type = QEMU_OPT_BOOL, + .help = "make this a boot drive", }, { /* end if list */ } }, diff --git a/qemu-kvm-helper.c b/qemu-kvm-helper.c new file mode 100644 index 000000000..9420eb176 --- /dev/null +++ b/qemu-kvm-helper.c @@ -0,0 +1,40 @@ + +#include "config.h" +#include "config-host.h" + +#include "exec.h" + +#include "qemu-kvm.h" + +void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *newenv) +{ + CPUState *oldenv; +#define DECLARE_HOST_REGS +#include "hostregs_helper.h" + + oldenv = newenv; + +#define SAVE_HOST_REGS +#include "hostregs_helper.h" + + env = newenv; + + env_to_regs(); + func(data); + regs_to_env(); + + env = oldenv; + +#include "hostregs_helper.h" +} + +static void call_helper_cpuid(void *junk) +{ + helper_cpuid(); +} + +void qemu_kvm_cpuid_on_env(CPUState *env) +{ + qemu_kvm_call_with_env(call_helper_cpuid, NULL, env); +} + diff --git a/qemu-kvm-ia64.c b/qemu-kvm-ia64.c new file mode 100644 index 000000000..a11fde86d --- /dev/null +++ b/qemu-kvm-ia64.c @@ -0,0 +1,143 @@ +#include "config.h" +#include "config-host.h" + +#include <string.h> + +#include "hw/hw.h" +#include "qemu-kvm.h" +#include <pthread.h> +#include <sys/utsname.h> +#include <sys/io.h> + + + +int kvm_arch_qemu_create_context(void) +{ + return 0; +} + +void kvm_arch_load_regs(CPUState *env) +{ +} + + +void kvm_arch_save_regs(CPUState *env) +{ +} + +int kvm_arch_init_vcpu(CPUState *cenv) +{ + return 0; +} + +int kvm_arch_halt(kvm_vcpu_context_t vcpu) +{ + CPUState *env = cpu_single_env; + env->hflags |= HF_HALTED_MASK; + return 1; +} + +void kvm_arch_pre_kvm_run(void *opaque, CPUState *env) +{ +} + +void kvm_arch_post_kvm_run(void *opaque, CPUState *env) +{ +} + +int kvm_arch_has_work(CPUState *env) +{ + return 1; +} + +int kvm_arch_try_push_interrupts(void *opaque) +{ + return 1; +} + +int kvm_arch_insert_sw_breakpoint(CPUState *current_env, + struct kvm_sw_breakpoint *bp) +{ + return -EINVAL; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *current_env, + struct kvm_sw_breakpoint *bp) +{ + return -EINVAL; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + return -ENOSYS; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + return -ENOSYS; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ +} + +int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) +{ + return 0; +} + +void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) +{ +} + +void kvm_arch_save_mpstate(CPUState *env) +{ +#ifdef KVM_CAP_MP_STATE + int r; + struct kvm_mp_state mp_state; + + r = kvm_get_mpstate(env->kvm_cpu_state.vcpu_ctx, &mp_state); + if (r < 0) + env->mp_state = -1; + else + env->mp_state = mp_state.mp_state; +#endif +} + +void kvm_arch_load_mpstate(CPUState *env) +{ +#ifdef KVM_CAP_MP_STATE + struct kvm_mp_state mp_state = { .mp_state = env->mp_state }; + + /* + * -1 indicates that the host did not support GET_MP_STATE ioctl, + * so don't touch it. + */ + if (env->mp_state != -1) + kvm_set_mpstate(env->kvm_cpu_state.vcpu_ctx, &mp_state); +#endif +} + +void kvm_arch_cpu_reset(CPUState *env) +{ + if (kvm_irqchip_in_kernel(kvm_context)) { +#ifdef KVM_CAP_MP_STATE + kvm_reset_mpstate(env->kvm_cpu_state.vcpu_ctx); +#endif + } else { + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + env->halted = 1; + } +} + +void kvm_arch_do_ioperm(void *_data) +{ + struct ioperm_data *data = _data; + ioperm(data->start_port, data->num, data->turn_on); +} + +void kvm_arch_process_irqchip_events(CPUState *env) +{ +} diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c new file mode 100644 index 000000000..7f820a4f0 --- /dev/null +++ b/qemu-kvm-x86.c @@ -0,0 +1,1705 @@ +/* + * qemu/kvm integration, x86 specific code + * + * Copyright (C) 2006-2008 Qumranet Technologies + * + * Licensed under the terms of the GNU GPL version 2 or higher. + */ + +#include "config.h" +#include "config-host.h" + +#include <string.h> +#include "hw/hw.h" +#include "gdbstub.h" +#include <sys/io.h> + +#include "qemu-kvm.h" +#include "libkvm.h" +#include <pthread.h> +#include <sys/utsname.h> +#include <linux/kvm_para.h> +#include <sys/ioctl.h> + +#include "kvm.h" +#include "hw/pc.h" + +#define MSR_IA32_TSC 0x10 + +static struct kvm_msr_list *kvm_msr_list; +extern unsigned int kvm_shadow_memory; +static int kvm_has_msr_star; +static int kvm_has_vm_hsave_pa; + +static int lm_capable_kernel; + +int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr) +{ +#ifdef KVM_CAP_SET_TSS_ADDR + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_SET_TSS_ADDR, addr); + if (r < 0) { + fprintf(stderr, "kvm_set_tss_addr: %m\n"); + return r; + } + return 0; + } +#endif + return -ENOSYS; +} + +static int kvm_init_tss(kvm_context_t kvm) +{ +#ifdef KVM_CAP_SET_TSS_ADDR + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); + if (r > 0) { + /* + * this address is 3 pages before the bios, and the bios should present + * as unavaible memory + */ + r = kvm_set_tss_addr(kvm, 0xfeffd000); + if (r < 0) { + fprintf(stderr, "kvm_init_tss: unable to set tss addr\n"); + return r; + } + + } +#endif + return 0; +} + +static int kvm_set_identity_map_addr(kvm_context_t kvm, uint64_t addr) +{ +#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_SET_IDENTITY_MAP_ADDR, &addr); + if (r == -1) { + fprintf(stderr, "kvm_set_identity_map_addr: %m\n"); + return -errno; + } + return 0; + } +#endif + return -ENOSYS; +} + +static int kvm_init_identity_map_page(kvm_context_t kvm) +{ +#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); + if (r > 0) { + /* + * this address is 4 pages before the bios, and the bios should present + * as unavaible memory + */ + r = kvm_set_identity_map_addr(kvm, 0xfeffc000); + if (r < 0) { + fprintf(stderr, "kvm_init_identity_map_page: " + "unable to set identity mapping addr\n"); + return r; + } + + } +#endif + return 0; +} + +static int kvm_create_pit(kvm_context_t kvm) +{ +#ifdef KVM_CAP_PIT + int r; + + kvm->pit_in_kernel = 0; + if (!kvm->no_pit_creation) { + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_PIT); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); + if (r >= 0) + kvm->pit_in_kernel = 1; + else { + fprintf(stderr, "Create kernel PIC irqchip failed\n"); + return r; + } + } + } +#endif + return 0; +} + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + int r = 0; + + r = kvm_init_tss(kvm); + if (r < 0) + return r; + + r = kvm_init_identity_map_page(kvm); + if (r < 0) + return r; + + r = kvm_create_pit(kvm); + if (r < 0) + return r; + + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) + return r; + +#ifdef KVM_EXIT_TPR_ACCESS + kvm_tpr_opt_setup(); +#endif + + return 0; +} + +#ifdef KVM_EXIT_TPR_ACCESS + +static int kvm_handle_tpr_access(CPUState *env) +{ + struct kvm_run *run = env->kvm_run; + kvm_tpr_access_report(env, + run->tpr_access.rip, + run->tpr_access.is_write); + return 0; +} + + +int kvm_enable_vapic(CPUState *env, uint64_t vapic) +{ + struct kvm_vapic_addr va = { + .vapic_addr = vapic, + }; + + return kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va); +} + +#endif + +int kvm_arch_run(CPUState *env) +{ + int r = 0; + struct kvm_run *run = env->kvm_run; + + + switch (run->exit_reason) { +#ifdef KVM_EXIT_SET_TPR + case KVM_EXIT_SET_TPR: + break; +#endif +#ifdef KVM_EXIT_TPR_ACCESS + case KVM_EXIT_TPR_ACCESS: + r = kvm_handle_tpr_access(env); + break; +#endif + default: + r = 1; + break; + } + + return r; +} + +#define MAX_ALIAS_SLOTS 4 +static struct { + uint64_t start; + uint64_t len; +} kvm_aliases[MAX_ALIAS_SLOTS]; + +static int get_alias_slot(uint64_t start) +{ + int i; + + for (i=0; i<MAX_ALIAS_SLOTS; i++) + if (kvm_aliases[i].start == start) + return i; + return -1; +} +static int get_free_alias_slot(void) +{ + int i; + + for (i=0; i<MAX_ALIAS_SLOTS; i++) + if (kvm_aliases[i].len == 0) + return i; + return -1; +} + +static void register_alias(int slot, uint64_t start, uint64_t len) +{ + kvm_aliases[slot].start = start; + kvm_aliases[slot].len = len; +} + +int kvm_create_memory_alias(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len, + uint64_t target_phys) +{ + struct kvm_memory_alias alias = { + .flags = 0, + .guest_phys_addr = phys_start, + .memory_size = len, + .target_phys_addr = target_phys, + }; + int r; + int slot; + + slot = get_alias_slot(phys_start); + if (slot < 0) + slot = get_free_alias_slot(); + if (slot < 0) + return -EBUSY; + alias.slot = slot; + + r = kvm_vm_ioctl(kvm_state, KVM_SET_MEMORY_ALIAS, &alias); + if (r == -1) + return -errno; + + register_alias(slot, phys_start, len); + return 0; +} + +int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start) +{ + return kvm_create_memory_alias(kvm, phys_start, 0, 0); +} + +#ifdef KVM_CAP_IRQCHIP + +int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s) +{ + int r = 0; + + if (!kvm_irqchip_in_kernel()) + return r; + + r = kvm_vcpu_ioctl(env, KVM_GET_LAPIC, s); + if (r < 0) + fprintf(stderr, "KVM_GET_LAPIC failed\n"); + return r; +} + +int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s) +{ + int r = 0; + + if (!kvm_irqchip_in_kernel()) + return 0; + + r = kvm_vcpu_ioctl(env, KVM_SET_LAPIC, s); + + if (r < 0) + fprintf(stderr, "KVM_SET_LAPIC failed\n"); + return r; +} + +#endif + +#ifdef KVM_CAP_PIT + +int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s) +{ + if (!kvm->pit_in_kernel) + return 0; + return kvm_vm_ioctl(kvm_state, KVM_GET_PIT, s); +} + +int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s) +{ + if (!kvm->pit_in_kernel) + return 0; + return kvm_vm_ioctl(kvm_state, KVM_SET_PIT, s); +} + +#ifdef KVM_CAP_PIT_STATE2 +int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2) +{ + if (!kvm->pit_in_kernel) + return 0; + return kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, ps2); +} + +int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2) +{ + if (!kvm->pit_in_kernel) + return 0; + return kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, ps2); +} + +#endif +#endif + +int kvm_has_pit_state2(kvm_context_t kvm) +{ + int r = 0; + +#ifdef KVM_CAP_PIT_STATE2 + r = kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2); +#endif + return r; +} + +void kvm_show_code(CPUState *env) +{ +#define SHOW_CODE_LEN 50 + struct kvm_regs regs; + struct kvm_sregs sregs; + int r, n; + int back_offset; + unsigned char code; + char code_str[SHOW_CODE_LEN * 3 + 1]; + unsigned long rip; + + r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (r < 0 ) { + perror("KVM_GET_SREGS"); + return; + } + r = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + if (r < 0) { + perror("KVM_GET_REGS"); + return; + } + rip = sregs.cs.base + regs.rip; + back_offset = regs.rip; + if (back_offset > 20) + back_offset = 20; + *code_str = 0; + for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) { + if (n == 0) + strcat(code_str, " -->"); + cpu_physical_memory_rw(rip + n, &code, 1, 1); + sprintf(code_str + strlen(code_str), " %02x", code); + } + fprintf(stderr, "code:%s\n", code_str); +} + + +/* + * Returns available msr list. User must free. + */ +struct kvm_msr_list *kvm_get_msr_list(kvm_context_t kvm) +{ + struct kvm_msr_list sizer, *msrs; + int r; + + sizer.nmsrs = 0; + r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, &sizer); + if (r < 0 && r != -E2BIG) + return NULL; + /* Old kernel modules had a bug and could write beyond the provided + memory. Allocate at least a safe amount of 1K. */ + msrs = qemu_malloc(MAX(1024, sizeof(*msrs) + + sizer.nmsrs * sizeof(*msrs->indices))); + + msrs->nmsrs = sizer.nmsrs; + r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, msrs); + if (r < 0) { + free(msrs); + errno = r; + return NULL; + } + return msrs; +} + +int kvm_get_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n) +{ + struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); + int r; + + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs); + memcpy(msrs, kmsrs->entries, n * sizeof *msrs); + free(kmsrs); + return r; +} + +int kvm_set_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n) +{ + struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); + int r; + + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = kvm_vcpu_ioctl(env, KVM_SET_MSRS, kmsrs); + free(kmsrs); + return r; +} + +int kvm_get_mce_cap_supported(kvm_context_t kvm, uint64_t *mce_cap, + int *max_banks) +{ +#ifdef KVM_CAP_MCE + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MCE); + if (r > 0) { + *max_banks = r; + return kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); + } +#endif + return -ENOSYS; +} + +int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap) +{ +#ifdef KVM_CAP_MCE + return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap); +#else + return -ENOSYS; +#endif +} + +int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m) +{ +#ifdef KVM_CAP_MCE + return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m); +#else + return -ENOSYS; +#endif +} + +static void print_seg(FILE *file, const char *name, struct kvm_segment *seg) +{ + fprintf(stderr, + "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d" + " g %d avl %d)\n", + name, seg->selector, seg->base, seg->limit, seg->present, + seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g, + seg->avl); +} + +static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt) +{ + fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit); +} + +void kvm_show_regs(CPUState *env) +{ + struct kvm_regs regs; + struct kvm_sregs sregs; + int r; + + r = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + if (r < 0) { + perror("KVM_GET_REGS"); + return; + } + fprintf(stderr, + "rax %016llx rbx %016llx rcx %016llx rdx %016llx\n" + "rsi %016llx rdi %016llx rsp %016llx rbp %016llx\n" + "r8 %016llx r9 %016llx r10 %016llx r11 %016llx\n" + "r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n" + "rip %016llx rflags %08llx\n", + regs.rax, regs.rbx, regs.rcx, regs.rdx, + regs.rsi, regs.rdi, regs.rsp, regs.rbp, + regs.r8, regs.r9, regs.r10, regs.r11, + regs.r12, regs.r13, regs.r14, regs.r15, + regs.rip, regs.rflags); + r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (r < 0) { + perror("KVM_GET_SREGS"); + return; + } + print_seg(stderr, "cs", &sregs.cs); + print_seg(stderr, "ds", &sregs.ds); + print_seg(stderr, "es", &sregs.es); + print_seg(stderr, "ss", &sregs.ss); + print_seg(stderr, "fs", &sregs.fs); + print_seg(stderr, "gs", &sregs.gs); + print_seg(stderr, "tr", &sregs.tr); + print_seg(stderr, "ldt", &sregs.ldt); + print_dt(stderr, "gdt", &sregs.gdt); + print_dt(stderr, "idt", &sregs.idt); + fprintf(stderr, "cr0 %llx cr2 %llx cr3 %llx cr4 %llx cr8 %llx" + " efer %llx\n", + sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4, sregs.cr8, + sregs.efer); +} + +static void kvm_set_cr8(CPUState *env, uint64_t cr8) +{ + env->kvm_run->cr8 = cr8; +} + +int kvm_setup_cpuid(CPUState *env, int nent, + struct kvm_cpuid_entry *entries) +{ + struct kvm_cpuid *cpuid; + int r; + + cpuid = qemu_malloc(sizeof(*cpuid) + nent * sizeof(*entries)); + + cpuid->nent = nent; + memcpy(cpuid->entries, entries, nent * sizeof(*entries)); + r = kvm_vcpu_ioctl(env, KVM_SET_CPUID, cpuid); + + free(cpuid); + return r; +} + +int kvm_setup_cpuid2(CPUState *env, int nent, + struct kvm_cpuid_entry2 *entries) +{ + struct kvm_cpuid2 *cpuid; + int r; + + cpuid = qemu_malloc(sizeof(*cpuid) + nent * sizeof(*entries)); + + cpuid->nent = nent; + memcpy(cpuid->entries, entries, nent * sizeof(*entries)); + r = kvm_vcpu_ioctl(env, KVM_SET_CPUID2, cpuid); + free(cpuid); + return r; +} + +int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages) +{ +#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, + KVM_CAP_MMU_SHADOW_CACHE_CONTROL); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_SET_NR_MMU_PAGES, nrshadow_pages); + if (r < 0) { + fprintf(stderr, "kvm_set_shadow_pages: %m\n"); + return r; + } + return 0; + } +#endif + return -1; +} + +int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages) +{ +#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, + KVM_CAP_MMU_SHADOW_CACHE_CONTROL); + if (r > 0) { + *nrshadow_pages = kvm_vm_ioctl(kvm_state, KVM_GET_NR_MMU_PAGES); + return 0; + } +#endif + return -1; +} + +#ifdef KVM_CAP_VAPIC + +static int tpr_access_reporting(CPUState *env, int enabled) +{ + int r; + struct kvm_tpr_access_ctl tac = { + .enabled = enabled, + }; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC); + if (r <= 0) + return -ENOSYS; + return kvm_vcpu_ioctl(env, KVM_TPR_ACCESS_REPORTING, &tac); +} + +int kvm_enable_tpr_access_reporting(CPUState *env) +{ + return tpr_access_reporting(env, 1); +} + +int kvm_disable_tpr_access_reporting(CPUState *env) +{ + return tpr_access_reporting(env, 0); +} + +#endif + +#ifdef KVM_CAP_EXT_CPUID + +static struct kvm_cpuid2 *try_get_cpuid(kvm_context_t kvm, int max) +{ + struct kvm_cpuid2 *cpuid; + int r, size; + + size = sizeof(*cpuid) + max * sizeof(*cpuid->entries); + cpuid = qemu_malloc(size); + cpuid->nent = max; + r = kvm_ioctl(kvm_state, KVM_GET_SUPPORTED_CPUID, cpuid); + if (r == 0 && cpuid->nent >= max) + r = -E2BIG; + if (r < 0) { + if (r == -E2BIG) { + free(cpuid); + return NULL; + } else { + fprintf(stderr, "KVM_GET_SUPPORTED_CPUID failed: %s\n", + strerror(-r)); + exit(1); + } + } + return cpuid; +} + +#define R_EAX 0 +#define R_ECX 1 +#define R_EDX 2 +#define R_EBX 3 +#define R_ESP 4 +#define R_EBP 5 +#define R_ESI 6 +#define R_EDI 7 + +uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg) +{ + struct kvm_cpuid2 *cpuid; + int i, max; + uint32_t ret = 0; + uint32_t cpuid_1_edx; + + if (!kvm_check_extension(kvm_state, KVM_CAP_EXT_CPUID)) { + return -1U; + } + + max = 1; + while ((cpuid = try_get_cpuid(kvm, max)) == NULL) { + max *= 2; + } + + for (i = 0; i < cpuid->nent; ++i) { + if (cpuid->entries[i].function == function) { + switch (reg) { + case R_EAX: + ret = cpuid->entries[i].eax; + break; + case R_EBX: + ret = cpuid->entries[i].ebx; + break; + case R_ECX: + ret = cpuid->entries[i].ecx; + break; + case R_EDX: + ret = cpuid->entries[i].edx; + if (function == 1) { + /* kvm misreports the following features + */ + ret |= 1 << 12; /* MTRR */ + ret |= 1 << 16; /* PAT */ + ret |= 1 << 7; /* MCE */ + ret |= 1 << 14; /* MCA */ + } + + /* On Intel, kvm returns cpuid according to + * the Intel spec, so add missing bits + * according to the AMD spec: + */ + if (function == 0x80000001) { + cpuid_1_edx = kvm_get_supported_cpuid(kvm, 1, R_EDX); + ret |= cpuid_1_edx & 0xdfeff7ff; + } + break; + } + } + } + + free(cpuid); + + return ret; +} + +#else + +uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg) +{ + return -1U; +} + +#endif +int kvm_qemu_create_memory_alias(uint64_t phys_start, + uint64_t len, + uint64_t target_phys) +{ + return kvm_create_memory_alias(kvm_context, phys_start, len, target_phys); +} + +int kvm_qemu_destroy_memory_alias(uint64_t phys_start) +{ + return kvm_destroy_memory_alias(kvm_context, phys_start); +} + +#ifdef KVM_CAP_ADJUST_CLOCK +static struct kvm_clock_data kvmclock_data; + +static void kvmclock_pre_save(void *opaque) +{ + struct kvm_clock_data *cl = opaque; + + kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, cl); +} + +static int kvmclock_post_load(void *opaque, int version_id) +{ + struct kvm_clock_data *cl = opaque; + + return kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, cl); +} + +static const VMStateDescription vmstate_kvmclock= { + .name = "kvmclock", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = kvmclock_pre_save, + .post_load = kvmclock_post_load, + .fields = (VMStateField []) { + VMSTATE_U64(clock, struct kvm_clock_data), + VMSTATE_END_OF_LIST() + } +}; +#endif + +int kvm_arch_qemu_create_context(void) +{ + int i; + struct utsname utsname; + + uname(&utsname); + lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; + + if (kvm_shadow_memory) + kvm_set_shadow_pages(kvm_context, kvm_shadow_memory); + + kvm_msr_list = kvm_get_msr_list(kvm_context); + if (!kvm_msr_list) + return -1; + for (i = 0; i < kvm_msr_list->nmsrs; ++i) { + if (kvm_msr_list->indices[i] == MSR_STAR) + kvm_has_msr_star = 1; + if (kvm_msr_list->indices[i] == MSR_VM_HSAVE_PA) + kvm_has_vm_hsave_pa = 1; + } + +#ifdef KVM_CAP_ADJUST_CLOCK + if (kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK)) + vmstate_register(0, &vmstate_kvmclock, &kvmclock_data); +#endif + return 0; +} + +static void set_msr_entry(struct kvm_msr_entry *entry, uint32_t index, + uint64_t data) +{ + entry->index = index; + entry->data = data; +} + +/* returns 0 on success, non-0 on failure */ +static int get_msr_entry(struct kvm_msr_entry *entry, CPUState *env) +{ + switch (entry->index) { + case MSR_IA32_SYSENTER_CS: + env->sysenter_cs = entry->data; + break; + case MSR_IA32_SYSENTER_ESP: + env->sysenter_esp = entry->data; + break; + case MSR_IA32_SYSENTER_EIP: + env->sysenter_eip = entry->data; + break; + case MSR_STAR: + env->star = entry->data; + break; +#ifdef TARGET_X86_64 + case MSR_CSTAR: + env->cstar = entry->data; + break; + case MSR_KERNELGSBASE: + env->kernelgsbase = entry->data; + break; + case MSR_FMASK: + env->fmask = entry->data; + break; + case MSR_LSTAR: + env->lstar = entry->data; + break; +#endif + case MSR_IA32_TSC: + env->tsc = entry->data; + break; + case MSR_VM_HSAVE_PA: + env->vm_hsave = entry->data; + break; + case MSR_KVM_SYSTEM_TIME: + env->system_time_msr = entry->data; + break; + case MSR_KVM_WALL_CLOCK: + env->wall_clock_msr = entry->data; + break; + default: + printf("Warning unknown msr index 0x%x\n", entry->index); + return 1; + } + return 0; +} + +static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs) +{ + lhs->selector = rhs->selector; + lhs->base = rhs->base; + lhs->limit = rhs->limit; + lhs->type = 3; + lhs->present = 1; + lhs->dpl = 3; + lhs->db = 0; + lhs->s = 1; + lhs->l = 0; + lhs->g = 0; + lhs->avl = 0; + lhs->unusable = 0; +} + +static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs) +{ + unsigned flags = rhs->flags; + lhs->selector = rhs->selector; + lhs->base = rhs->base; + lhs->limit = rhs->limit; + lhs->type = (flags >> DESC_TYPE_SHIFT) & 15; + lhs->present = (flags & DESC_P_MASK) != 0; + lhs->dpl = rhs->selector & 3; + lhs->db = (flags >> DESC_B_SHIFT) & 1; + lhs->s = (flags & DESC_S_MASK) != 0; + lhs->l = (flags >> DESC_L_SHIFT) & 1; + lhs->g = (flags & DESC_G_MASK) != 0; + lhs->avl = (flags & DESC_AVL_MASK) != 0; + lhs->unusable = 0; +} + +static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) +{ + lhs->selector = rhs->selector; + lhs->base = rhs->base; + lhs->limit = rhs->limit; + lhs->flags = + (rhs->type << DESC_TYPE_SHIFT) + | (rhs->present * DESC_P_MASK) + | (rhs->dpl << DESC_DPL_SHIFT) + | (rhs->db << DESC_B_SHIFT) + | (rhs->s * DESC_S_MASK) + | (rhs->l << DESC_L_SHIFT) + | (rhs->g * DESC_G_MASK) + | (rhs->avl * DESC_AVL_MASK); +} + +void kvm_arch_load_regs(CPUState *env) +{ + struct kvm_regs regs; + struct kvm_fpu fpu; + struct kvm_sregs sregs; + struct kvm_msr_entry msrs[100]; + int rc, n, i; + + regs.rax = env->regs[R_EAX]; + regs.rbx = env->regs[R_EBX]; + regs.rcx = env->regs[R_ECX]; + regs.rdx = env->regs[R_EDX]; + regs.rsi = env->regs[R_ESI]; + regs.rdi = env->regs[R_EDI]; + regs.rsp = env->regs[R_ESP]; + regs.rbp = env->regs[R_EBP]; +#ifdef TARGET_X86_64 + regs.r8 = env->regs[8]; + regs.r9 = env->regs[9]; + regs.r10 = env->regs[10]; + regs.r11 = env->regs[11]; + regs.r12 = env->regs[12]; + regs.r13 = env->regs[13]; + regs.r14 = env->regs[14]; + regs.r15 = env->regs[15]; +#endif + + regs.rflags = env->eflags; + regs.rip = env->eip; + + kvm_set_regs(env, ®s); + + memset(&fpu, 0, sizeof fpu); + fpu.fsw = env->fpus & ~(7 << 11); + fpu.fsw |= (env->fpstt & 7) << 11; + fpu.fcw = env->fpuc; + for (i = 0; i < 8; ++i) + fpu.ftwx |= (!env->fptags[i]) << i; + memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); + memcpy(fpu.xmm, env->xmm_regs, sizeof env->xmm_regs); + fpu.mxcsr = env->mxcsr; + kvm_set_fpu(env, &fpu); + + memset(sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap)); + if (env->interrupt_injected >= 0) { + sregs.interrupt_bitmap[env->interrupt_injected / 64] |= + (uint64_t)1 << (env->interrupt_injected % 64); + } + + if ((env->eflags & VM_MASK)) { + set_v8086_seg(&sregs.cs, &env->segs[R_CS]); + set_v8086_seg(&sregs.ds, &env->segs[R_DS]); + set_v8086_seg(&sregs.es, &env->segs[R_ES]); + set_v8086_seg(&sregs.fs, &env->segs[R_FS]); + set_v8086_seg(&sregs.gs, &env->segs[R_GS]); + set_v8086_seg(&sregs.ss, &env->segs[R_SS]); + } else { + set_seg(&sregs.cs, &env->segs[R_CS]); + set_seg(&sregs.ds, &env->segs[R_DS]); + set_seg(&sregs.es, &env->segs[R_ES]); + set_seg(&sregs.fs, &env->segs[R_FS]); + set_seg(&sregs.gs, &env->segs[R_GS]); + set_seg(&sregs.ss, &env->segs[R_SS]); + + if (env->cr[0] & CR0_PE_MASK) { + /* force ss cpl to cs cpl */ + sregs.ss.selector = (sregs.ss.selector & ~3) | + (sregs.cs.selector & 3); + sregs.ss.dpl = sregs.ss.selector & 3; + } + } + + set_seg(&sregs.tr, &env->tr); + set_seg(&sregs.ldt, &env->ldt); + + sregs.idt.limit = env->idt.limit; + sregs.idt.base = env->idt.base; + sregs.gdt.limit = env->gdt.limit; + sregs.gdt.base = env->gdt.base; + + sregs.cr0 = env->cr[0]; + sregs.cr2 = env->cr[2]; + sregs.cr3 = env->cr[3]; + sregs.cr4 = env->cr[4]; + + sregs.cr8 = cpu_get_apic_tpr(env); + sregs.apic_base = cpu_get_apic_base(env); + + sregs.efer = env->efer; + + kvm_set_sregs(env, &sregs); + + /* msrs */ + n = 0; + /* Remember to increase msrs size if you add new registers below */ + set_msr_entry(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); + set_msr_entry(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); + set_msr_entry(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); + if (kvm_has_msr_star) + set_msr_entry(&msrs[n++], MSR_STAR, env->star); + if (kvm_has_vm_hsave_pa) + set_msr_entry(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); +#ifdef TARGET_X86_64 + if (lm_capable_kernel) { + set_msr_entry(&msrs[n++], MSR_CSTAR, env->cstar); + set_msr_entry(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); + set_msr_entry(&msrs[n++], MSR_FMASK, env->fmask); + set_msr_entry(&msrs[n++], MSR_LSTAR , env->lstar); + } +#endif + set_msr_entry(&msrs[n++], MSR_KVM_SYSTEM_TIME, env->system_time_msr); + set_msr_entry(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr); + + rc = kvm_set_msrs(env, msrs, n); + if (rc == -1) + perror("kvm_set_msrs FAILED"); +} + +void kvm_load_tsc(CPUState *env) +{ + int rc; + struct kvm_msr_entry msr; + + set_msr_entry(&msr, MSR_IA32_TSC, env->tsc); + + rc = kvm_set_msrs(env, &msr, 1); + if (rc == -1) + perror("kvm_set_tsc FAILED.\n"); +} + +void kvm_arch_save_mpstate(CPUState *env) +{ +#ifdef KVM_CAP_MP_STATE + int r; + struct kvm_mp_state mp_state; + + r = kvm_get_mpstate(env, &mp_state); + if (r < 0) + env->mp_state = -1; + else + env->mp_state = mp_state.mp_state; +#else + env->mp_state = -1; +#endif +} + +void kvm_arch_load_mpstate(CPUState *env) +{ +#ifdef KVM_CAP_MP_STATE + struct kvm_mp_state mp_state = { .mp_state = env->mp_state }; + + /* + * -1 indicates that the host did not support GET_MP_STATE ioctl, + * so don't touch it. + */ + if (env->mp_state != -1) + kvm_set_mpstate(env, &mp_state); +#endif +} + +void kvm_arch_save_regs(CPUState *env) +{ + struct kvm_regs regs; + struct kvm_fpu fpu; + struct kvm_sregs sregs; + struct kvm_msr_entry msrs[100]; + uint32_t hflags; + uint32_t i, n, rc, bit; + + kvm_get_regs(env, ®s); + + env->regs[R_EAX] = regs.rax; + env->regs[R_EBX] = regs.rbx; + env->regs[R_ECX] = regs.rcx; + env->regs[R_EDX] = regs.rdx; + env->regs[R_ESI] = regs.rsi; + env->regs[R_EDI] = regs.rdi; + env->regs[R_ESP] = regs.rsp; + env->regs[R_EBP] = regs.rbp; +#ifdef TARGET_X86_64 + env->regs[8] = regs.r8; + env->regs[9] = regs.r9; + env->regs[10] = regs.r10; + env->regs[11] = regs.r11; + env->regs[12] = regs.r12; + env->regs[13] = regs.r13; + env->regs[14] = regs.r14; + env->regs[15] = regs.r15; +#endif + + env->eflags = regs.rflags; + env->eip = regs.rip; + + kvm_get_fpu(env, &fpu); + env->fpstt = (fpu.fsw >> 11) & 7; + env->fpus = fpu.fsw; + env->fpuc = fpu.fcw; + for (i = 0; i < 8; ++i) + env->fptags[i] = !((fpu.ftwx >> i) & 1); + memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); + memcpy(env->xmm_regs, fpu.xmm, sizeof env->xmm_regs); + env->mxcsr = fpu.mxcsr; + + kvm_get_sregs(env, &sregs); + + /* There can only be one pending IRQ set in the bitmap at a time, so try + to find it and save its number instead (-1 for none). */ + env->interrupt_injected = -1; + for (i = 0; i < ARRAY_SIZE(sregs.interrupt_bitmap); i++) { + if (sregs.interrupt_bitmap[i]) { + bit = ctz64(sregs.interrupt_bitmap[i]); + env->interrupt_injected = i * 64 + bit; + break; + } + } + + get_seg(&env->segs[R_CS], &sregs.cs); + get_seg(&env->segs[R_DS], &sregs.ds); + get_seg(&env->segs[R_ES], &sregs.es); + get_seg(&env->segs[R_FS], &sregs.fs); + get_seg(&env->segs[R_GS], &sregs.gs); + get_seg(&env->segs[R_SS], &sregs.ss); + + get_seg(&env->tr, &sregs.tr); + get_seg(&env->ldt, &sregs.ldt); + + env->idt.limit = sregs.idt.limit; + env->idt.base = sregs.idt.base; + env->gdt.limit = sregs.gdt.limit; + env->gdt.base = sregs.gdt.base; + + env->cr[0] = sregs.cr0; + env->cr[2] = sregs.cr2; + env->cr[3] = sregs.cr3; + env->cr[4] = sregs.cr4; + + cpu_set_apic_base(env, sregs.apic_base); + + env->efer = sregs.efer; + //cpu_set_apic_tpr(env, sregs.cr8); + +#define HFLAG_COPY_MASK ~( \ + HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \ + HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \ + HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ + HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) + + + + hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; + hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT); + hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) & + (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); + hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK)); + hflags |= (env->cr[4] & CR4_OSFXSR_MASK) << + (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); + + if (env->efer & MSR_EFER_LMA) { + hflags |= HF_LMA_MASK; + } + + if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) { + hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; + } else { + hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >> + (DESC_B_SHIFT - HF_CS32_SHIFT); + hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >> + (DESC_B_SHIFT - HF_SS32_SHIFT); + if (!(env->cr[0] & CR0_PE_MASK) || + (env->eflags & VM_MASK) || + !(hflags & HF_CS32_MASK)) { + hflags |= HF_ADDSEG_MASK; + } else { + hflags |= ((env->segs[R_DS].base | + env->segs[R_ES].base | + env->segs[R_SS].base) != 0) << + HF_ADDSEG_SHIFT; + } + } + env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags; + + /* msrs */ + n = 0; + /* Remember to increase msrs size if you add new registers below */ + msrs[n++].index = MSR_IA32_SYSENTER_CS; + msrs[n++].index = MSR_IA32_SYSENTER_ESP; + msrs[n++].index = MSR_IA32_SYSENTER_EIP; + if (kvm_has_msr_star) + msrs[n++].index = MSR_STAR; + msrs[n++].index = MSR_IA32_TSC; + if (kvm_has_vm_hsave_pa) + msrs[n++].index = MSR_VM_HSAVE_PA; +#ifdef TARGET_X86_64 + if (lm_capable_kernel) { + msrs[n++].index = MSR_CSTAR; + msrs[n++].index = MSR_KERNELGSBASE; + msrs[n++].index = MSR_FMASK; + msrs[n++].index = MSR_LSTAR; + } +#endif + msrs[n++].index = MSR_KVM_SYSTEM_TIME; + msrs[n++].index = MSR_KVM_WALL_CLOCK; + + rc = kvm_get_msrs(env, msrs, n); + if (rc == -1) { + perror("kvm_get_msrs FAILED"); + } + else { + n = rc; /* actual number of MSRs */ + for (i=0 ; i<n; i++) { + if (get_msr_entry(&msrs[i], env)) + return; + } + } + kvm_arch_save_mpstate(env); +} + +static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function, + uint32_t count, CPUState *env) +{ + env->regs[R_EAX] = function; + env->regs[R_ECX] = count; + qemu_kvm_cpuid_on_env(env); + e->function = function; + e->flags = 0; + e->index = 0; + e->eax = env->regs[R_EAX]; + e->ebx = env->regs[R_EBX]; + e->ecx = env->regs[R_ECX]; + e->edx = env->regs[R_EDX]; +} + +struct kvm_para_features { + int cap; + int feature; +} para_features[] = { +#ifdef KVM_CAP_CLOCKSOURCE + { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE }, +#endif +#ifdef KVM_CAP_NOP_IO_DELAY + { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY }, +#endif +#ifdef KVM_CAP_PV_MMU + { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP }, +#endif +#ifdef KVM_CAP_CR3_CACHE + { KVM_CAP_CR3_CACHE, KVM_FEATURE_CR3_CACHE }, +#endif + { -1, -1 } +}; + +static int get_para_features(kvm_context_t kvm_context) +{ + int i, features = 0; + + for (i = 0; i < ARRAY_SIZE(para_features)-1; i++) { + if (kvm_check_extension(kvm_state, para_features[i].cap)) + features |= (1 << para_features[i].feature); + } + + return features; +} + +static void kvm_trim_features(uint32_t *features, uint32_t supported) +{ + int i; + uint32_t mask; + + for (i = 0; i < 32; ++i) { + mask = 1U << i; + if ((*features & mask) && !(supported & mask)) { + *features &= ~mask; + } + } +} + +int kvm_arch_init_vcpu(CPUState *cenv) +{ + struct kvm_cpuid_entry2 cpuid_ent[100]; +#ifdef KVM_CPUID_SIGNATURE + struct kvm_cpuid_entry2 *pv_ent; + uint32_t signature[3]; +#endif + int cpuid_nent = 0; + CPUState copy; + uint32_t i, j, limit; + + qemu_kvm_load_lapic(cenv); + + cenv->interrupt_injected = -1; + +#ifdef KVM_CPUID_SIGNATURE + /* Paravirtualization CPUIDs */ + memcpy(signature, "KVMKVMKVM\0\0\0", 12); + pv_ent = &cpuid_ent[cpuid_nent++]; + memset(pv_ent, 0, sizeof(*pv_ent)); + pv_ent->function = KVM_CPUID_SIGNATURE; + pv_ent->eax = 0; + pv_ent->ebx = signature[0]; + pv_ent->ecx = signature[1]; + pv_ent->edx = signature[2]; + + pv_ent = &cpuid_ent[cpuid_nent++]; + memset(pv_ent, 0, sizeof(*pv_ent)); + pv_ent->function = KVM_CPUID_FEATURES; + pv_ent->eax = get_para_features(kvm_context); +#endif + + kvm_trim_features(&cenv->cpuid_features, + kvm_arch_get_supported_cpuid(cenv, 1, R_EDX)); + + /* prevent the hypervisor bit from being cleared by the kernel */ + i = cenv->cpuid_ext_features & CPUID_EXT_HYPERVISOR; + kvm_trim_features(&cenv->cpuid_ext_features, + kvm_arch_get_supported_cpuid(cenv, 1, R_ECX)); + cenv->cpuid_ext_features |= i; + + kvm_trim_features(&cenv->cpuid_ext2_features, + kvm_arch_get_supported_cpuid(cenv, 0x80000001, R_EDX)); + kvm_trim_features(&cenv->cpuid_ext3_features, + kvm_arch_get_supported_cpuid(cenv, 0x80000001, R_ECX)); + + copy = *cenv; + + copy.regs[R_EAX] = 0; + qemu_kvm_cpuid_on_env(©); + limit = copy.regs[R_EAX]; + + for (i = 0; i <= limit; ++i) { + if (i == 4 || i == 0xb || i == 0xd) { + for (j = 0; ; ++j) { + do_cpuid_ent(&cpuid_ent[cpuid_nent], i, j, ©); + + cpuid_ent[cpuid_nent].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpuid_ent[cpuid_nent].index = j; + + cpuid_nent++; + + if (i == 4 && copy.regs[R_EAX] == 0) + break; + if (i == 0xb && !(copy.regs[R_ECX] & 0xff00)) + break; + if (i == 0xd && copy.regs[R_EAX] == 0) + break; + } + } else + do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); + } + + copy.regs[R_EAX] = 0x80000000; + qemu_kvm_cpuid_on_env(©); + limit = copy.regs[R_EAX]; + + for (i = 0x80000000; i <= limit; ++i) + do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); + + kvm_setup_cpuid2(cenv, cpuid_nent, cpuid_ent); + +#ifdef KVM_CAP_MCE + if (((cenv->cpuid_version >> 8)&0xF) >= 6 + && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA) + && kvm_check_extension(kvm_state, KVM_CAP_MCE) > 0) { + uint64_t mcg_cap; + int banks; + + if (kvm_get_mce_cap_supported(kvm_context, &mcg_cap, &banks)) + perror("kvm_get_mce_cap_supported FAILED"); + else { + if (banks > MCE_BANKS_DEF) + banks = MCE_BANKS_DEF; + mcg_cap &= MCE_CAP_DEF; + mcg_cap |= banks; + if (kvm_setup_mce(cenv, &mcg_cap)) + perror("kvm_setup_mce FAILED"); + else + cenv->mcg_cap = mcg_cap; + } + } +#endif + +#ifdef KVM_EXIT_TPR_ACCESS + kvm_tpr_vcpu_start(cenv); +#endif + return 0; +} + +int kvm_arch_halt(CPUState *env) +{ + + if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) && + !(env->interrupt_request & CPU_INTERRUPT_NMI)) { + env->halted = 1; + } + return 1; +} + +int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) +{ + if (env->update_vapic) { + kvm_tpr_enable_vapic(env); + } + if (!kvm_irqchip_in_kernel()) + kvm_set_cr8(env, cpu_get_apic_tpr(env)); + return 0; +} + +int kvm_arch_has_work(CPUState *env) +{ + if (((env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) || + (env->interrupt_request & CPU_INTERRUPT_NMI)) + return 1; + return 0; +} + +int kvm_arch_try_push_interrupts(void *opaque) +{ + CPUState *env = cpu_single_env; + int r, irq; + + if (kvm_is_ready_for_interrupt_injection(env) && + (env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) { + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + irq = cpu_get_pic_interrupt(env); + if (irq >= 0) { + r = kvm_inject_irq(env, irq); + if (r < 0) + printf("cpu %d fail inject %x\n", env->cpu_index, irq); + } + } + + return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0; +} + +#ifdef KVM_CAP_USER_NMI +void kvm_arch_push_nmi(void *opaque) +{ + CPUState *env = cpu_single_env; + int r; + + if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI))) + return; + + env->interrupt_request &= ~CPU_INTERRUPT_NMI; + r = kvm_inject_nmi(env); + if (r < 0) + printf("cpu %d fail inject NMI\n", env->cpu_index); +} +#endif /* KVM_CAP_USER_NMI */ + +void kvm_arch_cpu_reset(CPUState *env) +{ + kvm_arch_reset_vcpu(env); + kvm_arch_load_regs(env); + kvm_put_vcpu_events(env); + if (!cpu_is_bsp(env)) { + if (kvm_irqchip_in_kernel()) { +#ifdef KVM_CAP_MP_STATE + kvm_reset_mpstate(env); +#endif + } else { + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + env->halted = 1; + } + } +} + +int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) +{ + uint8_t int3 = 0xcc; + + if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) || + cpu_memory_rw_debug(env, bp->pc, &int3, 1, 1)) + return -EINVAL; + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) +{ + uint8_t int3; + + if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || + cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) + return -EINVAL; + return 0; +} + +#ifdef KVM_CAP_SET_GUEST_DEBUG +static struct { + target_ulong addr; + int len; + int type; +} hw_breakpoint[4]; + +static int nb_hw_breakpoint; + +static int find_hw_breakpoint(target_ulong addr, int len, int type) +{ + int n; + + for (n = 0; n < nb_hw_breakpoint; n++) + if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && + (hw_breakpoint[n].len == len || len == -1)) + return n; + return -1; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + len = 1; + break; + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + switch (len) { + case 1: + break; + case 2: + case 4: + case 8: + if (addr & (len - 1)) + return -EINVAL; + break; + default: + return -EINVAL; + } + break; + default: + return -ENOSYS; + } + + if (nb_hw_breakpoint == 4) + return -ENOBUFS; + + if (find_hw_breakpoint(addr, len, type) >= 0) + return -EEXIST; + + hw_breakpoint[nb_hw_breakpoint].addr = addr; + hw_breakpoint[nb_hw_breakpoint].len = len; + hw_breakpoint[nb_hw_breakpoint].type = type; + nb_hw_breakpoint++; + + return 0; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + int n; + + n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); + if (n < 0) + return -ENOENT; + + nb_hw_breakpoint--; + hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; + + return 0; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + nb_hw_breakpoint = 0; +} + +static CPUWatchpoint hw_watchpoint; + +int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) +{ + int handle = 0; + int n; + + if (arch_info->exception == 1) { + if (arch_info->dr6 & (1 << 14)) { + if (cpu_single_env->singlestep_enabled) + handle = 1; + } else { + for (n = 0; n < 4; n++) + if (arch_info->dr6 & (1 << n)) + switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { + case 0x0: + handle = 1; + break; + case 0x1: + handle = 1; + cpu_single_env->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_breakpoint[n].addr; + hw_watchpoint.flags = BP_MEM_WRITE; + break; + case 0x3: + handle = 1; + cpu_single_env->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_breakpoint[n].addr; + hw_watchpoint.flags = BP_MEM_ACCESS; + break; + } + } + } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) + handle = 1; + + if (!handle) + kvm_update_guest_debug(cpu_single_env, + (arch_info->exception == 1) ? + KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP); + + return handle; +} + +void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) +{ + const uint8_t type_code[] = { + [GDB_BREAKPOINT_HW] = 0x0, + [GDB_WATCHPOINT_WRITE] = 0x1, + [GDB_WATCHPOINT_ACCESS] = 0x3 + }; + const uint8_t len_code[] = { + [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2 + }; + int n; + + if (kvm_sw_breakpoints_active(env)) + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + + if (nb_hw_breakpoint > 0) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; + dbg->arch.debugreg[7] = 0x0600; + for (n = 0; n < nb_hw_breakpoint; n++) { + dbg->arch.debugreg[n] = hw_breakpoint[n].addr; + dbg->arch.debugreg[7] |= (2 << (n * 2)) | + (type_code[hw_breakpoint[n].type] << (16 + n*4)) | + (len_code[hw_breakpoint[n].len] << (18 + n*4)); + } + } +} +#endif + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +void kvm_arch_do_ioperm(void *_data) +{ + struct ioperm_data *data = _data; + ioperm(data->start_port, data->num, data->turn_on); +} +#endif + +/* + * Setup x86 specific IRQ routing + */ +int kvm_arch_init_irq_routing(void) +{ + int i, r; + + if (kvm_irqchip && kvm_has_gsi_routing(kvm_context)) { + kvm_clear_gsi_routes(kvm_context); + for (i = 0; i < 8; ++i) { + if (i == 2) + continue; + r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_PIC_MASTER, i); + if (r < 0) + return r; + } + for (i = 8; i < 16; ++i) { + r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_PIC_SLAVE, i - 8); + if (r < 0) + return r; + } + for (i = 0; i < 24; ++i) { + if (i == 0) { + r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_IOAPIC, 2); + } else if (i != 2) { + r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_IOAPIC, i); + } + if (r < 0) + return r; + } + kvm_commit_irq_routes(kvm_context); + } + return 0; +} + +uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, + int reg) +{ + return kvm_get_supported_cpuid(kvm_context, function, reg); +} + +void kvm_arch_process_irqchip_events(CPUState *env) +{ + if (env->interrupt_request & CPU_INTERRUPT_INIT) { + kvm_cpu_synchronize_state(env); + do_cpu_init(env); + } + if (env->interrupt_request & CPU_INTERRUPT_SIPI) { + kvm_cpu_synchronize_state(env); + do_cpu_sipi(env); + } +} diff --git a/qemu-kvm.c b/qemu-kvm.c new file mode 100644 index 000000000..25addaf3b --- /dev/null +++ b/qemu-kvm.c @@ -0,0 +1,2665 @@ +/* + * qemu/kvm integration + * + * Copyright (C) 2006-2008 Qumranet Technologies + * + * Licensed under the terms of the GNU GPL version 2 or higher. + */ +#include "config.h" +#include "config-host.h" + +#include <assert.h> +#include <string.h> +#include "hw/hw.h" +#include "sysemu.h" +#include "qemu-common.h" +#include "console.h" +#include "block.h" +#include "compatfd.h" +#include "gdbstub.h" + +#include "qemu-kvm.h" +#include "libkvm.h" + +#include <pthread.h> +#include <sys/utsname.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include "compatfd.h" +#include <sys/prctl.h> + +#define false 0 +#define true 1 + +#ifndef PR_MCE_KILL +#define PR_MCE_KILL 33 +#endif + +#ifndef BUS_MCEERR_AR +#define BUS_MCEERR_AR 4 +#endif +#ifndef BUS_MCEERR_AO +#define BUS_MCEERR_AO 5 +#endif + +#define EXPECTED_KVM_API_VERSION 12 + +#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION +#error libkvm: userspace and kernel version mismatch +#endif + +int kvm_allowed = 1; +int kvm_irqchip = 1; +int kvm_pit = 1; +int kvm_pit_reinject = 1; +int kvm_nested = 0; + + +KVMState *kvm_state; +kvm_context_t kvm_context; + +pthread_mutex_t qemu_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t qemu_vcpu_cond = PTHREAD_COND_INITIALIZER; +pthread_cond_t qemu_system_cond = PTHREAD_COND_INITIALIZER; +pthread_cond_t qemu_pause_cond = PTHREAD_COND_INITIALIZER; +pthread_cond_t qemu_work_cond = PTHREAD_COND_INITIALIZER; +__thread CPUState *current_env; + +static int qemu_system_ready; + +#define SIG_IPI (SIGRTMIN+4) + +pthread_t io_thread; +static int io_thread_fd = -1; +static int io_thread_sigfd = -1; + +static CPUState *kvm_debug_cpu_requested; + +static uint64_t phys_ram_size; + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +/* The list of ioperm_data */ +static QLIST_HEAD(, ioperm_data) ioperm_head; +#endif + +//#define DEBUG_MEMREG +#ifdef DEBUG_MEMREG +#define DPRINTF(fmt, args...) \ + do { fprintf(stderr, "%s:%d " fmt , __func__, __LINE__, ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while (0) +#endif + +#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) + +int kvm_abi = EXPECTED_KVM_API_VERSION; +int kvm_page_size; + +#ifdef KVM_CAP_SET_GUEST_DEBUG +static int kvm_debug(CPUState *env, + struct kvm_debug_exit_arch *arch_info) +{ + int handle = kvm_arch_debug(arch_info); + + if (handle) { + kvm_debug_cpu_requested = env; + env->stopped = 1; + } + return handle; +} +#endif + +static int handle_unhandled(uint64_t reason) +{ + fprintf(stderr, "kvm: unhandled exit %" PRIx64 "\n", reason); + return -EINVAL; +} + + +static inline void set_gsi(kvm_context_t kvm, unsigned int gsi) +{ + uint32_t *bitmap = kvm->used_gsi_bitmap; + + if (gsi < kvm->max_gsi) + bitmap[gsi / 32] |= 1U << (gsi % 32); + else + DPRINTF("Invalid GSI %u\n", gsi); +} + +static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi) +{ + uint32_t *bitmap = kvm->used_gsi_bitmap; + + if (gsi < kvm->max_gsi) + bitmap[gsi / 32] &= ~(1U << (gsi % 32)); + else + DPRINTF("Invalid GSI %u\n", gsi); +} + +struct slot_info { + unsigned long phys_addr; + unsigned long len; + unsigned long userspace_addr; + unsigned flags; + int logging_count; +}; + +struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS]; + +static void init_slots(void) +{ + int i; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) + slots[i].len = 0; +} + +static int get_free_slot(kvm_context_t kvm) +{ + int i; + int tss_ext; + +#if defined(KVM_CAP_SET_TSS_ADDR) && !defined(__s390__) + tss_ext = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); +#else + tss_ext = 0; +#endif + + /* + * on older kernels where the set tss ioctl is not supprted we must save + * slot 0 to hold the extended memory, as the vmx will use the last 3 + * pages of this slot. + */ + if (tss_ext > 0) + i = 0; + else + i = 1; + + for (; i < KVM_MAX_NUM_MEM_REGIONS; ++i) + if (!slots[i].len) + return i; + return -1; +} + +static void register_slot(int slot, unsigned long phys_addr, + unsigned long len, unsigned long userspace_addr, + unsigned flags) +{ + slots[slot].phys_addr = phys_addr; + slots[slot].len = len; + slots[slot].userspace_addr = userspace_addr; + slots[slot].flags = flags; +} + +static void free_slot(int slot) +{ + slots[slot].len = 0; + slots[slot].logging_count = 0; +} + +static int get_slot(unsigned long phys_addr) +{ + int i; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) { + if (slots[i].len && slots[i].phys_addr <= phys_addr && + (slots[i].phys_addr + slots[i].len - 1) >= phys_addr) + return i; + } + return -1; +} + +/* Returns -1 if this slot is not totally contained on any other, + * and the number of the slot otherwise */ +static int get_container_slot(uint64_t phys_addr, unsigned long size) +{ + int i; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) + if (slots[i].len && slots[i].phys_addr <= phys_addr && + (slots[i].phys_addr + slots[i].len) >= phys_addr + size) + return i; + return -1; +} + +int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_addr, + unsigned long size) +{ + int slot = get_container_slot(phys_addr, size); + if (slot == -1) + return 0; + return 1; +} + +/* + * dirty pages logging control + */ +static int kvm_dirty_pages_log_change(kvm_context_t kvm, + unsigned long phys_addr, unsigned flags, + unsigned mask) +{ + int r = -1; + int slot = get_slot(phys_addr); + + if (slot == -1) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__); + return 1; + } + + flags = (slots[slot].flags & ~mask) | flags; + if (flags == slots[slot].flags) + return 0; + slots[slot].flags = flags; + + { + struct kvm_userspace_memory_region mem = { + .slot = slot, + .memory_size = slots[slot].len, + .guest_phys_addr = slots[slot].phys_addr, + .userspace_addr = slots[slot].userspace_addr, + .flags = slots[slot].flags, + }; + + + DPRINTF("slot %d start %llx len %llx flags %x\n", + mem.slot, mem.guest_phys_addr, mem.memory_size, mem.flags); + r = kvm_vm_ioctl(kvm_state, KVM_SET_USER_MEMORY_REGION, &mem); + if (r < 0) + fprintf(stderr, "%s: %m\n", __FUNCTION__); + } + return r; +} + +static int kvm_dirty_pages_log_change_all(kvm_context_t kvm, + int (*change)(kvm_context_t kvm, + uint64_t start, + uint64_t len)) +{ + int i, r; + + for (i = r = 0; i < KVM_MAX_NUM_MEM_REGIONS && r == 0; i++) { + if (slots[i].len) + r = change(kvm, slots[i].phys_addr, slots[i].len); + } + return r; +} + +int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, uint64_t phys_addr, + uint64_t len) +{ + int slot = get_slot(phys_addr); + + DPRINTF("start %" PRIx64 " len %" PRIx64 "\n", phys_addr, len); + if (slot == -1) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); + return -EINVAL; + } + + if (slots[slot].logging_count++) + return 0; + + return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr, + KVM_MEM_LOG_DIRTY_PAGES, + KVM_MEM_LOG_DIRTY_PAGES); +} + +int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, uint64_t phys_addr, + uint64_t len) +{ + int slot = get_slot(phys_addr); + + if (slot == -1) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); + return -EINVAL; + } + + if (--slots[slot].logging_count) + return 0; + + return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr, 0, + KVM_MEM_LOG_DIRTY_PAGES); +} + +/** + * Enable dirty page logging for all memory regions + */ +int kvm_dirty_pages_log_enable_all(kvm_context_t kvm) +{ + if (kvm->dirty_pages_log_all) + return 0; + kvm->dirty_pages_log_all = 1; + return kvm_dirty_pages_log_change_all(kvm, kvm_dirty_pages_log_enable_slot); +} + +/** + * Enable dirty page logging only for memory regions that were created with + * dirty logging enabled (disable for all other memory regions). + */ +int kvm_dirty_pages_log_reset(kvm_context_t kvm) +{ + if (!kvm->dirty_pages_log_all) + return 0; + kvm->dirty_pages_log_all = 0; + return kvm_dirty_pages_log_change_all(kvm, + kvm_dirty_pages_log_disable_slot); +} + + +static int kvm_create_context(void); + +int kvm_init(int smp_cpus) +{ + int fd; + int r, gsi_count; + + + fd = open("/dev/kvm", O_RDWR); + if (fd == -1) { + perror("open /dev/kvm"); + return -1; + } + r = ioctl(fd, KVM_GET_API_VERSION, 0); + if (r == -1) { + fprintf(stderr, + "kvm kernel version too old: " + "KVM_GET_API_VERSION ioctl not supported\n"); + goto out_close; + } + if (r < EXPECTED_KVM_API_VERSION) { + fprintf(stderr, "kvm kernel version too old: " + "We expect API version %d or newer, but got " + "version %d\n", EXPECTED_KVM_API_VERSION, r); + goto out_close; + } + if (r > EXPECTED_KVM_API_VERSION) { + fprintf(stderr, "kvm userspace version too old\n"); + goto out_close; + } + kvm_abi = r; + kvm_page_size = getpagesize(); + kvm_state = qemu_mallocz(sizeof(*kvm_state)); + kvm_context = &kvm_state->kvm_context; + + kvm_state->fd = fd; + kvm_state->vmfd = -1; + kvm_context->opaque = cpu_single_env; + kvm_context->dirty_pages_log_all = 0; + kvm_context->no_irqchip_creation = 0; + kvm_context->no_pit_creation = 0; + +#ifdef KVM_CAP_SET_GUEST_DEBUG + QTAILQ_INIT(&kvm_state->kvm_sw_breakpoints); +#endif + + gsi_count = kvm_get_gsi_count(kvm_context); + if (gsi_count > 0) { + int gsi_bits, i; + + /* Round up so we can search ints using ffs */ + gsi_bits = ALIGN(gsi_count, 32); + kvm_context->used_gsi_bitmap = qemu_mallocz(gsi_bits / 8); + kvm_context->max_gsi = gsi_bits; + + /* Mark any over-allocated bits as already in use */ + for (i = gsi_count; i < gsi_bits; i++) + set_gsi(kvm_context, i); + } + + pthread_mutex_lock(&qemu_mutex); + return kvm_create_context(); + + out_close: + close(fd); + return -1; +} + +static void kvm_finalize(KVMState *s) +{ + /* FIXME + if (kvm->vcpu_fd[0] != -1) + close(kvm->vcpu_fd[0]); + if (kvm->vm_fd != -1) + close(kvm->vm_fd); + */ + close(s->fd); + free(s); +} + +void kvm_disable_irqchip_creation(kvm_context_t kvm) +{ + kvm->no_irqchip_creation = 1; +} + +void kvm_disable_pit_creation(kvm_context_t kvm) +{ + kvm->no_pit_creation = 1; +} + +static void kvm_create_vcpu(CPUState *env, int id) +{ + long mmap_size; + int r; + + r = kvm_vm_ioctl(kvm_state, KVM_CREATE_VCPU, id); + if (r < 0) { + fprintf(stderr, "kvm_create_vcpu: %m\n"); + return; + } + + env->kvm_fd = r; + env->kvm_state = kvm_state; + + mmap_size = kvm_ioctl(kvm_state, KVM_GET_VCPU_MMAP_SIZE, 0); + if (mmap_size < 0) { + fprintf(stderr, "get vcpu mmap size: %m\n"); + goto err_fd; + } + env->kvm_run = + mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, env->kvm_fd, + 0); + if (env->kvm_run == MAP_FAILED) { + fprintf(stderr, "mmap vcpu area: %m\n"); + goto err_fd; + } + + return; + err_fd: + close(env->kvm_fd); +} + +static int kvm_set_boot_vcpu_id(kvm_context_t kvm, uint32_t id) +{ +#ifdef KVM_CAP_SET_BOOT_CPU_ID + int r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_BOOT_CPU_ID); + if (r > 0) + return kvm_vm_ioctl(kvm_state, KVM_SET_BOOT_CPU_ID, id); + return -ENOSYS; +#else + return -ENOSYS; +#endif +} + +int kvm_create_vm(kvm_context_t kvm) +{ + int fd; +#ifdef KVM_CAP_IRQ_ROUTING + kvm->irq_routes = qemu_mallocz(sizeof(*kvm->irq_routes)); + kvm->nr_allocated_irq_routes = 0; +#endif + + fd = kvm_ioctl(kvm_state, KVM_CREATE_VM, 0); + if (fd < 0) { + fprintf(stderr, "kvm_create_vm: %m\n"); + return -1; + } + kvm_state->vmfd = fd; + return 0; +} + +static int kvm_create_default_phys_mem(kvm_context_t kvm, + unsigned long phys_mem_bytes, + void **vm_mem) +{ +#ifdef KVM_CAP_USER_MEMORY + int r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY); + if (r > 0) + return 0; + fprintf(stderr, + "Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported\n"); +#else +#error Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported +#endif + return -1; +} + +void kvm_create_irqchip(kvm_context_t kvm) +{ + int r; + + kvm->irqchip_in_kernel = 0; +#ifdef KVM_CAP_IRQCHIP + if (!kvm->no_irqchip_creation) { + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_IRQCHIP); + if (r > 0) { /* kernel irqchip supported */ + r = kvm_vm_ioctl(kvm_state, KVM_CREATE_IRQCHIP); + if (r >= 0) { + kvm->irqchip_inject_ioctl = KVM_IRQ_LINE; +#if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS) + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, + KVM_CAP_IRQ_INJECT_STATUS); + if (r > 0) + kvm->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; +#endif + kvm->irqchip_in_kernel = 1; + } else + fprintf(stderr, "Create kernel PIC irqchip failed\n"); + } + } +#endif + kvm_state->irqchip_in_kernel = kvm->irqchip_in_kernel; +} + +int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem) +{ + int r; + + r = kvm_create_vm(kvm); + if (r < 0) + return r; + r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem); + if (r < 0) + return r; + init_slots(); + r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem); + if (r < 0) + return r; + kvm_create_irqchip(kvm); + + return 0; +} + + +int kvm_register_phys_mem(kvm_context_t kvm, + unsigned long phys_start, void *userspace_addr, + unsigned long len, int log) +{ + + struct kvm_userspace_memory_region memory = { + .memory_size = len, + .guest_phys_addr = phys_start, + .userspace_addr = (unsigned long) (uintptr_t) userspace_addr, + .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0, + }; + int r; + + memory.slot = get_free_slot(kvm); + DPRINTF + ("memory: gpa: %llx, size: %llx, uaddr: %llx, slot: %x, flags: %x\n", + memory.guest_phys_addr, memory.memory_size, memory.userspace_addr, + memory.slot, memory.flags); + r = kvm_vm_ioctl(kvm_state, KVM_SET_USER_MEMORY_REGION, &memory); + if (r < 0) { + fprintf(stderr, "create_userspace_phys_mem: %s\n", strerror(-r)); + return -1; + } + register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size, + memory.userspace_addr, memory.flags); + return 0; +} + + +/* destroy/free a whole slot. + * phys_start, len and slot are the params passed to kvm_create_phys_mem() + */ +void kvm_destroy_phys_mem(kvm_context_t kvm, unsigned long phys_start, + unsigned long len) +{ + int slot; + int r; + struct kvm_userspace_memory_region memory = { + .memory_size = 0, + .guest_phys_addr = phys_start, + .userspace_addr = 0, + .flags = 0, + }; + + slot = get_slot(phys_start); + + if ((slot >= KVM_MAX_NUM_MEM_REGIONS) || (slot == -1)) { + fprintf(stderr, "BUG: %s: invalid parameters (slot=%d)\n", __FUNCTION__, + slot); + return; + } + if (phys_start != slots[slot].phys_addr) { + fprintf(stderr, + "WARNING: %s: phys_start is 0x%lx expecting 0x%lx\n", + __FUNCTION__, phys_start, slots[slot].phys_addr); + phys_start = slots[slot].phys_addr; + } + + memory.slot = slot; + DPRINTF("slot %d start %llx len %llx flags %x\n", + memory.slot, memory.guest_phys_addr, memory.memory_size, + memory.flags); + r = kvm_vm_ioctl(kvm_state, KVM_SET_USER_MEMORY_REGION, &memory); + if (r < 0) { + fprintf(stderr, "destroy_userspace_phys_mem: %s", strerror(-r)); + return; + } + + free_slot(memory.slot); +} + +void kvm_unregister_memory_area(kvm_context_t kvm, uint64_t phys_addr, + unsigned long size) +{ + + int slot = get_container_slot(phys_addr, size); + + if (slot != -1) { + DPRINTF("Unregistering memory region %" PRIx64 " (%lx)\n", phys_addr, size); + kvm_destroy_phys_mem(kvm, phys_addr, size); + return; + } +} + +static int kvm_get_map(kvm_context_t kvm, int ioctl_num, int slot, void *buf) +{ + int r; + struct kvm_dirty_log log = { + .slot = slot, + }; + + log.dirty_bitmap = buf; + + r = kvm_vm_ioctl(kvm_state, ioctl_num, &log); + if (r < 0) + return r; + return 0; +} + +int kvm_get_dirty_pages(kvm_context_t kvm, unsigned long phys_addr, void *buf) +{ + int slot; + + slot = get_slot(phys_addr); + return kvm_get_map(kvm, KVM_GET_DIRTY_LOG, slot, buf); +} + +int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr, + unsigned long len, void *opaque, + int (*cb)(unsigned long start, + unsigned long len, void *bitmap, + void *opaque)) +{ + int i; + int r; + unsigned long end_addr = phys_addr + len; + void *buf; + + for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) { + if ((slots[i].len && (uint64_t) slots[i].phys_addr >= phys_addr) + && ((uint64_t) slots[i].phys_addr + slots[i].len <= end_addr)) { + buf = qemu_malloc(BITMAP_SIZE(slots[i].len)); + r = kvm_get_map(kvm, KVM_GET_DIRTY_LOG, i, buf); + if (r) { + qemu_free(buf); + return r; + } + r = cb(slots[i].phys_addr, slots[i].len, buf, opaque); + qemu_free(buf); + if (r) + return r; + } + } + return 0; +} + +#ifdef KVM_CAP_IRQCHIP + +int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status) +{ + struct kvm_irq_level event; + int r; + + if (!kvm->irqchip_in_kernel) + return 0; + event.level = level; + event.irq = irq; + r = kvm_vm_ioctl(kvm_state, kvm->irqchip_inject_ioctl, &event); + if (r < 0) + perror("kvm_set_irq_level"); + + if (status) { +#ifdef KVM_CAP_IRQ_INJECT_STATUS + *status = + (kvm->irqchip_inject_ioctl == KVM_IRQ_LINE) ? 1 : event.status; +#else + *status = 1; +#endif + } + + return 1; +} + +int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip) +{ + int r; + + if (!kvm->irqchip_in_kernel) + return 0; + r = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip); + if (r < 0) { + perror("kvm_get_irqchip\n"); + } + return r; +} + +int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip) +{ + int r; + + if (!kvm->irqchip_in_kernel) + return 0; + r = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip); + if (r < 0) { + perror("kvm_set_irqchip\n"); + } + return r; +} + +#endif + +static int handle_debug(CPUState *env) +{ +#ifdef KVM_CAP_SET_GUEST_DEBUG + struct kvm_run *run = env->kvm_run; + + return kvm_debug(env, &run->debug.arch); +#else + return 0; +#endif +} + +int kvm_get_regs(CPUState *env, struct kvm_regs *regs) +{ + return kvm_vcpu_ioctl(env, KVM_GET_REGS, regs); +} + +int kvm_set_regs(CPUState *env, struct kvm_regs *regs) +{ + return kvm_vcpu_ioctl(env, KVM_SET_REGS, regs); +} + +int kvm_get_fpu(CPUState *env, struct kvm_fpu *fpu) +{ + return kvm_vcpu_ioctl(env, KVM_GET_FPU, fpu); +} + +int kvm_set_fpu(CPUState *env, struct kvm_fpu *fpu) +{ + return kvm_vcpu_ioctl(env, KVM_SET_FPU, fpu); +} + +int kvm_get_sregs(CPUState *env, struct kvm_sregs *sregs) +{ + return kvm_vcpu_ioctl(env, KVM_GET_SREGS, sregs); +} + +int kvm_set_sregs(CPUState *env, struct kvm_sregs *sregs) +{ + return kvm_vcpu_ioctl(env, KVM_SET_SREGS, sregs); +} + +#ifdef KVM_CAP_MP_STATE +int kvm_get_mpstate(CPUState *env, struct kvm_mp_state *mp_state) +{ + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); + if (r > 0) + return kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, mp_state); + return -ENOSYS; +} + +int kvm_set_mpstate(CPUState *env, struct kvm_mp_state *mp_state) +{ + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); + if (r > 0) + return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, mp_state); + return -ENOSYS; +} +#endif + +static int handle_mmio(CPUState *env) +{ + unsigned long addr = env->kvm_run->mmio.phys_addr; + struct kvm_run *kvm_run = env->kvm_run; + void *data = kvm_run->mmio.data; + + /* hack: Red Hat 7.1 generates these weird accesses. */ + if ((addr > 0xa0000 - 4 && addr <= 0xa0000) && kvm_run->mmio.len == 3) + return 0; + + cpu_physical_memory_rw(addr, data, kvm_run->mmio.len, kvm_run->mmio.is_write); + return 0; +} + +int handle_io_window(kvm_context_t kvm) +{ + return 1; +} + +int handle_shutdown(kvm_context_t kvm, CPUState *env) +{ + /* stop the current vcpu from going back to guest mode */ + env->stopped = 1; + + qemu_system_reset_request(); + return 1; +} + +static inline void push_nmi(kvm_context_t kvm) +{ +#ifdef KVM_CAP_USER_NMI + kvm_arch_push_nmi(kvm->opaque); +#endif /* KVM_CAP_USER_NMI */ +} + +void post_kvm_run(kvm_context_t kvm, CPUState *env) +{ + pthread_mutex_lock(&qemu_mutex); + kvm_arch_post_run(env, env->kvm_run); + cpu_single_env = env; +} + +int pre_kvm_run(kvm_context_t kvm, CPUState *env) +{ + kvm_arch_pre_run(env, env->kvm_run); + + if (env->kvm_cpu_state.regs_modified) { + kvm_arch_put_registers(env); + env->kvm_cpu_state.regs_modified = 0; + } + + pthread_mutex_unlock(&qemu_mutex); + return 0; +} + +int kvm_is_ready_for_interrupt_injection(CPUState *env) +{ + return env->kvm_run->ready_for_interrupt_injection; +} + +static int kvm_handle_internal_error(kvm_context_t kvm, + CPUState *env, + struct kvm_run *run) +{ + fprintf(stderr, "KVM internal error. Suberror: %d\n", + run->internal.suberror); +#ifdef KVM_CAP_INTERNAL_ERROR_DATA + if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { + int i; + + for (i = 0; i < run->internal.ndata; ++i) { + fprintf(stderr, "extra data[%d]: %"PRIx64"\n", + i, (uint64_t)run->internal.data[i]); + } + } +#endif + kvm_show_regs(env); + if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) + fprintf(stderr, "emulation failure, check dmesg for details\n"); + vm_stop(0); + return 1; +} + +int kvm_run(CPUState *env) +{ + int r; + kvm_context_t kvm = &env->kvm_state->kvm_context; + struct kvm_run *run = env->kvm_run; + int fd = env->kvm_fd; + + again: + push_nmi(kvm); +#if !defined(__s390__) + if (!kvm->irqchip_in_kernel) + run->request_interrupt_window = kvm_arch_try_push_interrupts(env); +#endif + + r = pre_kvm_run(kvm, env); + if (r) + return r; + r = ioctl(fd, KVM_RUN, 0); + + if (r == -1 && errno != EINTR && errno != EAGAIN) { + r = -errno; + post_kvm_run(kvm, env); + fprintf(stderr, "kvm_run: %s\n", strerror(-r)); + return r; + } + + post_kvm_run(kvm, env); + +#if defined(KVM_CAP_COALESCED_MMIO) + if (kvm_state->coalesced_mmio) { + struct kvm_coalesced_mmio_ring *ring = + (void *) run + kvm_state->coalesced_mmio * PAGE_SIZE; + while (ring->first != ring->last) { + cpu_physical_memory_rw(ring->coalesced_mmio[ring->first].phys_addr, + &ring->coalesced_mmio[ring->first].data[0], + ring->coalesced_mmio[ring->first].len, 1); + smp_wmb(); + ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; + } + } +#endif + +#if !defined(__s390__) + if (r == -1) { + r = handle_io_window(kvm); + goto more; + } +#endif + if (1) { + switch (run->exit_reason) { + case KVM_EXIT_UNKNOWN: + r = handle_unhandled(run->hw.hardware_exit_reason); + break; + case KVM_EXIT_FAIL_ENTRY: + r = handle_unhandled(run->fail_entry.hardware_entry_failure_reason); + break; + case KVM_EXIT_EXCEPTION: + fprintf(stderr, "exception %d (%x)\n", run->ex.exception, + run->ex.error_code); + kvm_show_regs(env); + kvm_show_code(env); + abort(); + break; + case KVM_EXIT_IO: + r = kvm_handle_io(run->io.port, + (uint8_t *)run + run->io.data_offset, + run->io.direction, + run->io.size, + run->io.count); + r = 0; + break; + case KVM_EXIT_DEBUG: + r = handle_debug(env); + break; + case KVM_EXIT_MMIO: + r = handle_mmio(env); + break; + case KVM_EXIT_HLT: + r = kvm_arch_halt(env); + break; + case KVM_EXIT_IRQ_WINDOW_OPEN: + break; + case KVM_EXIT_SHUTDOWN: + r = handle_shutdown(kvm, env); + break; +#if defined(__s390__) + case KVM_EXIT_S390_SIEIC: + r = kvm_s390_handle_intercept(kvm, env, run); + break; + case KVM_EXIT_S390_RESET: + r = kvm_s390_handle_reset(kvm, env, run); + break; +#endif + case KVM_EXIT_INTERNAL_ERROR: + r = kvm_handle_internal_error(kvm, env, run); + break; + default: + if (kvm_arch_run(env)) { + fprintf(stderr, "unhandled vm exit: 0x%x\n", run->exit_reason); + kvm_show_regs(env); + abort(); + } + break; + } + } + more: + if (!r) + goto again; + return r; +} + +int kvm_inject_irq(CPUState *env, unsigned irq) +{ + struct kvm_interrupt intr; + + intr.irq = irq; + return kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); +} + +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_set_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) +{ + return kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, dbg); +} +#endif + +int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset) +{ + struct kvm_signal_mask *sigmask; + int r; + + if (!sigset) { + return kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, NULL); + } + sigmask = qemu_malloc(sizeof(*sigmask) + sizeof(*sigset)); + + sigmask->len = 8; + memcpy(sigmask->sigset, sigset, sizeof(*sigset)); + r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask); + free(sigmask); + return r; +} + +int kvm_pit_in_kernel(kvm_context_t kvm) +{ + return kvm->pit_in_kernel; +} + +int kvm_inject_nmi(CPUState *env) +{ +#ifdef KVM_CAP_USER_NMI + return kvm_vcpu_ioctl(env, KVM_NMI); +#else + return -ENOSYS; +#endif +} + +int kvm_init_coalesced_mmio(kvm_context_t kvm) +{ + int r = 0; + kvm_state->coalesced_mmio = 0; +#ifdef KVM_CAP_COALESCED_MMIO + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO); + if (r > 0) { + kvm_state->coalesced_mmio = r; + return 0; + } +#endif + return r; +} + +#ifdef KVM_CAP_DEVICE_ASSIGNMENT +int kvm_assign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev) +{ + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_PCI_DEVICE, assigned_dev); +} + +static int kvm_old_assign_irq(kvm_context_t kvm, + struct kvm_assigned_irq *assigned_irq) +{ + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_IRQ, assigned_irq); +} + +#ifdef KVM_CAP_ASSIGN_DEV_IRQ +int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq) +{ + int ret; + + ret = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ); + if (ret > 0) { + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_DEV_IRQ, assigned_irq); + } + + return kvm_old_assign_irq(kvm, assigned_irq); +} + +int kvm_deassign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq) +{ + return kvm_vm_ioctl(kvm_state, KVM_DEASSIGN_DEV_IRQ, assigned_irq); +} +#else +int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq) +{ + return kvm_old_assign_irq(kvm, assigned_irq); +} +#endif +#endif + +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT +int kvm_deassign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev) +{ + return kvm_vm_ioctl(kvm_state, KVM_DEASSIGN_PCI_DEVICE, assigned_dev); +} +#endif + +int kvm_destroy_memory_region_works(kvm_context_t kvm) +{ + int ret = 0; + +#ifdef KVM_CAP_DESTROY_MEMORY_REGION_WORKS + ret = + kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, + KVM_CAP_DESTROY_MEMORY_REGION_WORKS); + if (ret <= 0) + ret = 0; +#endif + return ret; +} + +int kvm_reinject_control(kvm_context_t kvm, int pit_reinject) +{ +#ifdef KVM_CAP_REINJECT_CONTROL + int r; + struct kvm_reinject_control control; + + control.pit_reinject = pit_reinject; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL); + if (r > 0) { + return kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); + } +#endif + return -ENOSYS; +} + +int kvm_has_gsi_routing(kvm_context_t kvm) +{ + int r = 0; + +#ifdef KVM_CAP_IRQ_ROUTING + r = kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING); +#endif + return r; +} + +int kvm_get_gsi_count(kvm_context_t kvm) +{ +#ifdef KVM_CAP_IRQ_ROUTING + return kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING); +#else + return -EINVAL; +#endif +} + +int kvm_clear_gsi_routes(kvm_context_t kvm) +{ +#ifdef KVM_CAP_IRQ_ROUTING + kvm->irq_routes->nr = 0; + return 0; +#else + return -EINVAL; +#endif +} + +int kvm_add_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry *entry) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing *z; + struct kvm_irq_routing_entry *new; + int n, size; + + if (kvm->irq_routes->nr == kvm->nr_allocated_irq_routes) { + n = kvm->nr_allocated_irq_routes * 2; + if (n < 64) + n = 64; + size = sizeof(struct kvm_irq_routing); + size += n * sizeof(*new); + z = realloc(kvm->irq_routes, size); + if (!z) + return -ENOMEM; + kvm->nr_allocated_irq_routes = n; + kvm->irq_routes = z; + } + n = kvm->irq_routes->nr++; + new = &kvm->irq_routes->entries[n]; + memset(new, 0, sizeof(*new)); + new->gsi = entry->gsi; + new->type = entry->type; + new->flags = entry->flags; + new->u = entry->u; + + set_gsi(kvm, entry->gsi); + + return 0; +#else + return -ENOSYS; +#endif +} + +int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry e; + + e.gsi = gsi; + e.type = KVM_IRQ_ROUTING_IRQCHIP; + e.flags = 0; + e.u.irqchip.irqchip = irqchip; + e.u.irqchip.pin = pin; + return kvm_add_routing_entry(kvm, &e); +#else + return -ENOSYS; +#endif +} + +int kvm_del_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry *entry) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry *e, *p; + int i, gsi, found = 0; + + gsi = entry->gsi; + + for (i = 0; i < kvm->irq_routes->nr; ++i) { + e = &kvm->irq_routes->entries[i]; + if (e->type == entry->type && e->gsi == gsi) { + switch (e->type) { + case KVM_IRQ_ROUTING_IRQCHIP:{ + if (e->u.irqchip.irqchip == + entry->u.irqchip.irqchip + && e->u.irqchip.pin == entry->u.irqchip.pin) { + p = &kvm->irq_routes->entries[--kvm->irq_routes->nr]; + *e = *p; + found = 1; + } + break; + } + case KVM_IRQ_ROUTING_MSI:{ + if (e->u.msi.address_lo == + entry->u.msi.address_lo + && e->u.msi.address_hi == + entry->u.msi.address_hi + && e->u.msi.data == entry->u.msi.data) { + p = &kvm->irq_routes->entries[--kvm->irq_routes->nr]; + *e = *p; + found = 1; + } + break; + } + default: + break; + } + if (found) { + /* If there are no other users of this GSI + * mark it available in the bitmap */ + for (i = 0; i < kvm->irq_routes->nr; i++) { + e = &kvm->irq_routes->entries[i]; + if (e->gsi == gsi) + break; + } + if (i == kvm->irq_routes->nr) + clear_gsi(kvm, gsi); + + return 0; + } + } + } + return -ESRCH; +#else + return -ENOSYS; +#endif +} + +int kvm_update_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry *entry, + struct kvm_irq_routing_entry *newentry) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry *e; + int i; + + if (entry->gsi != newentry->gsi || entry->type != newentry->type) { + return -EINVAL; + } + + for (i = 0; i < kvm->irq_routes->nr; ++i) { + e = &kvm->irq_routes->entries[i]; + if (e->type != entry->type || e->gsi != entry->gsi) { + continue; + } + switch (e->type) { + case KVM_IRQ_ROUTING_IRQCHIP: + if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip && + e->u.irqchip.pin == entry->u.irqchip.pin) { + memcpy(&e->u.irqchip, &newentry->u.irqchip, + sizeof e->u.irqchip); + return 0; + } + break; + case KVM_IRQ_ROUTING_MSI: + if (e->u.msi.address_lo == entry->u.msi.address_lo && + e->u.msi.address_hi == entry->u.msi.address_hi && + e->u.msi.data == entry->u.msi.data) { + memcpy(&e->u.msi, &newentry->u.msi, sizeof e->u.msi); + return 0; + } + break; + default: + break; + } + } + return -ESRCH; +#else + return -ENOSYS; +#endif +} + +int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin) +{ +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing_entry e; + + e.gsi = gsi; + e.type = KVM_IRQ_ROUTING_IRQCHIP; + e.flags = 0; + e.u.irqchip.irqchip = irqchip; + e.u.irqchip.pin = pin; + return kvm_del_routing_entry(kvm, &e); +#else + return -ENOSYS; +#endif +} + +int kvm_commit_irq_routes(kvm_context_t kvm) +{ +#ifdef KVM_CAP_IRQ_ROUTING + kvm->irq_routes->flags = 0; + return kvm_vm_ioctl(kvm_state, KVM_SET_GSI_ROUTING, kvm->irq_routes); +#else + return -ENOSYS; +#endif +} + +int kvm_get_irq_route_gsi(kvm_context_t kvm) +{ + int i, bit; + uint32_t *buf = kvm->used_gsi_bitmap; + + /* Return the lowest unused GSI in the bitmap */ + for (i = 0; i < kvm->max_gsi / 32; i++) { + bit = ffs(~buf[i]); + if (!bit) + continue; + + return bit - 1 + i * 32; + } + + return -ENOSPC; +} + +#ifdef KVM_CAP_DEVICE_MSIX +int kvm_assign_set_msix_nr(kvm_context_t kvm, + struct kvm_assigned_msix_nr *msix_nr) +{ + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_NR, msix_nr); +} + +int kvm_assign_set_msix_entry(kvm_context_t kvm, + struct kvm_assigned_msix_entry *entry) +{ + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_ENTRY, entry); +} +#endif + +#if defined(KVM_CAP_IRQFD) && defined(CONFIG_EVENTFD) + +#include <sys/eventfd.h> + +static int _kvm_irqfd(kvm_context_t kvm, int fd, int gsi, int flags) +{ + struct kvm_irqfd data = { + .fd = fd, + .gsi = gsi, + .flags = flags, + }; + + return kvm_vm_ioctl(kvm_state, KVM_IRQFD, &data); +} + +int kvm_irqfd(kvm_context_t kvm, int gsi, int flags) +{ + int r; + int fd; + + if (!kvm_check_extension(kvm_state, KVM_CAP_IRQFD)) + return -ENOENT; + + fd = eventfd(0, 0); + if (fd < 0) + return -errno; + + r = _kvm_irqfd(kvm, fd, gsi, 0); + if (r < 0) { + close(fd); + return -errno; + } + + return fd; +} + +#else /* KVM_CAP_IRQFD */ + +int kvm_irqfd(kvm_context_t kvm, int gsi, int flags) +{ + return -ENOSYS; +} + +#endif /* KVM_CAP_IRQFD */ +static inline unsigned long kvm_get_thread_id(void) +{ + return syscall(SYS_gettid); +} + +static void qemu_cond_wait(pthread_cond_t *cond) +{ + CPUState *env = cpu_single_env; + + pthread_cond_wait(cond, &qemu_mutex); + cpu_single_env = env; +} + +static void sig_ipi_handler(int n) +{ +} + +static void hardware_memory_error(void) +{ + fprintf(stderr, "Hardware memory error!\n"); + exit(1); +} + +static void sigbus_reraise(void) +{ + sigset_t set; + struct sigaction action; + + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_DFL; + if (!sigaction(SIGBUS, &action, NULL)) { + raise(SIGBUS); + sigemptyset(&set); + sigaddset(&set, SIGBUS); + sigprocmask(SIG_UNBLOCK, &set, NULL); + } + perror("Failed to re-raise SIGBUS!\n"); + abort(); +} + +static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo, + void *ctx) +{ +#if defined(KVM_CAP_MCE) && defined(TARGET_I386) + if (first_cpu->mcg_cap && siginfo->ssi_addr + && siginfo->ssi_code == BUS_MCEERR_AO) { + uint64_t status; + unsigned long paddr; + CPUState *cenv; + + /* Hope we are lucky for AO MCE */ + if (do_qemu_ram_addr_from_host((void *)(intptr_t)siginfo->ssi_addr, + &paddr)) { + fprintf(stderr, "Hardware memory error for memory used by " + "QEMU itself instead of guest system!: %llx\n", + (unsigned long long)siginfo->ssi_addr); + return; + } + status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN + | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S + | 0xc0; + kvm_inject_x86_mce(first_cpu, 9, status, + MCG_STATUS_MCIP | MCG_STATUS_RIPV, paddr, + (MCM_ADDR_PHYS << 6) | 0xc, 1); + for (cenv = first_cpu->next_cpu; cenv != NULL; cenv = cenv->next_cpu) + kvm_inject_x86_mce(cenv, 1, MCI_STATUS_VAL | MCI_STATUS_UC, + MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0, 1); + } else +#endif + { + if (siginfo->ssi_code == BUS_MCEERR_AO) + return; + else if (siginfo->ssi_code == BUS_MCEERR_AR) + hardware_memory_error(); + else + sigbus_reraise(); + } +} + +static void on_vcpu(CPUState *env, void (*func)(void *data), void *data) +{ + struct qemu_work_item wi; + + if (env == current_env) { + func(data); + return; + } + + wi.func = func; + wi.data = data; + if (!env->kvm_cpu_state.queued_work_first) + env->kvm_cpu_state.queued_work_first = &wi; + else + env->kvm_cpu_state.queued_work_last->next = &wi; + env->kvm_cpu_state.queued_work_last = &wi; + wi.next = NULL; + wi.done = false; + + pthread_kill(env->kvm_cpu_state.thread, SIG_IPI); + while (!wi.done) + qemu_cond_wait(&qemu_work_cond); +} + +void kvm_arch_get_registers(CPUState *env) +{ + kvm_arch_save_regs(env); +} + +static void do_kvm_cpu_synchronize_state(void *_env) +{ + CPUState *env = _env; + if (!env->kvm_cpu_state.regs_modified) { + kvm_arch_get_registers(env); + env->kvm_cpu_state.regs_modified = 1; + } +} + +void kvm_cpu_synchronize_state(CPUState *env) +{ + if (!env->kvm_cpu_state.regs_modified) + on_vcpu(env, do_kvm_cpu_synchronize_state, env); +} + +static void inject_interrupt(void *data) +{ + cpu_interrupt(current_env, (long) data); +} + +void kvm_inject_interrupt(CPUState *env, int mask) +{ + on_vcpu(env, inject_interrupt, (void *) (long) mask); +} + +void kvm_update_interrupt_request(CPUState *env) +{ + int signal = 0; + + if (env) { + if (!current_env || !current_env->created) + signal = 1; + /* + * Testing for created here is really redundant + */ + if (current_env && current_env->created && + env != current_env && !env->kvm_cpu_state.signalled) + signal = 1; + + if (signal) { + env->kvm_cpu_state.signalled = 1; + if (env->kvm_cpu_state.thread) + pthread_kill(env->kvm_cpu_state.thread, SIG_IPI); + } + } +} + +static void kvm_do_load_registers(void *_env) +{ + CPUState *env = _env; + + kvm_arch_load_regs(env); +} + +void kvm_load_registers(CPUState *env) +{ + if (kvm_enabled() && qemu_system_ready) + on_vcpu(env, kvm_do_load_registers, env); +} + +static void kvm_do_save_registers(void *_env) +{ + CPUState *env = _env; + + kvm_arch_save_regs(env); +} + +void kvm_save_registers(CPUState *env) +{ + if (kvm_enabled()) + on_vcpu(env, kvm_do_save_registers, env); +} + +static void kvm_do_load_mpstate(void *_env) +{ + CPUState *env = _env; + + kvm_arch_load_mpstate(env); +} + +void kvm_load_mpstate(CPUState *env) +{ + if (kvm_enabled() && qemu_system_ready && kvm_vcpu_inited(env)) + on_vcpu(env, kvm_do_load_mpstate, env); +} + +static void kvm_do_save_mpstate(void *_env) +{ + CPUState *env = _env; + + kvm_arch_save_mpstate(env); +#ifdef KVM_CAP_MP_STATE + if (kvm_irqchip_in_kernel()) + env->halted = (env->mp_state == KVM_MP_STATE_HALTED); +#endif +} + +void kvm_save_mpstate(CPUState *env) +{ + if (kvm_enabled()) + on_vcpu(env, kvm_do_save_mpstate, env); +} + +int kvm_cpu_exec(CPUState *env) +{ + int r; + + r = kvm_run(env); + if (r < 0) { + printf("kvm_run returned %d\n", r); + vm_stop(0); + } + + return 0; +} + +static int is_cpu_stopped(CPUState *env) +{ + return !vm_running || env->stopped; +} + +static void flush_queued_work(CPUState *env) +{ + struct qemu_work_item *wi; + + if (!env->kvm_cpu_state.queued_work_first) + return; + + while ((wi = env->kvm_cpu_state.queued_work_first)) { + env->kvm_cpu_state.queued_work_first = wi->next; + wi->func(wi->data); + wi->done = true; + } + env->kvm_cpu_state.queued_work_last = NULL; + pthread_cond_broadcast(&qemu_work_cond); +} + +static void kvm_on_sigbus(CPUState *env, siginfo_t *siginfo) +{ +#if defined(KVM_CAP_MCE) && defined(TARGET_I386) + struct kvm_x86_mce mce = { + .bank = 9, + }; + unsigned long paddr; + int r; + + if (env->mcg_cap && siginfo->si_addr + && (siginfo->si_code == BUS_MCEERR_AR + || siginfo->si_code == BUS_MCEERR_AO)) { + if (siginfo->si_code == BUS_MCEERR_AR) { + /* Fake an Intel architectural Data Load SRAR UCR */ + mce.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN + | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S + | MCI_STATUS_AR | 0x134; + mce.misc = (MCM_ADDR_PHYS << 6) | 0xc; + mce.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV; + } else { + /* Fake an Intel architectural Memory scrubbing UCR */ + mce.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN + | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S + | 0xc0; + mce.misc = (MCM_ADDR_PHYS << 6) | 0xc; + mce.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV; + } + if (do_qemu_ram_addr_from_host((void *)siginfo->si_addr, &paddr)) { + fprintf(stderr, "Hardware memory error for memory used by " + "QEMU itself instaed of guest system!\n"); + /* Hope we are lucky for AO MCE */ + if (siginfo->si_code == BUS_MCEERR_AO) + return; + else + hardware_memory_error(); + } + mce.addr = paddr; + r = kvm_set_mce(env, &mce); + if (r < 0) { + fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno)); + abort(); + } + } else +#endif + { + if (siginfo->si_code == BUS_MCEERR_AO) + return; + else if (siginfo->si_code == BUS_MCEERR_AR) + hardware_memory_error(); + else + sigbus_reraise(); + } +} + +static void kvm_main_loop_wait(CPUState *env, int timeout) +{ + struct timespec ts; + int r, e; + siginfo_t siginfo; + sigset_t waitset; + sigset_t chkset; + + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + sigaddset(&waitset, SIGBUS); + + do { + pthread_mutex_unlock(&qemu_mutex); + + r = sigtimedwait(&waitset, &siginfo, &ts); + e = errno; + + pthread_mutex_lock(&qemu_mutex); + + if (r == -1 && !(e == EAGAIN || e == EINTR)) { + printf("sigtimedwait: %s\n", strerror(e)); + exit(1); + } + + switch (r) { + case SIGBUS: + kvm_on_sigbus(env, &siginfo); + break; + default: + break; + } + + r = sigpending(&chkset); + if (r == -1) { + printf("sigpending: %s\n", strerror(e)); + exit(1); + } + } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS)); + + cpu_single_env = env; + flush_queued_work(env); + + if (env->stop) { + env->stop = 0; + env->stopped = 1; + pthread_cond_signal(&qemu_pause_cond); + } + + env->kvm_cpu_state.signalled = 0; +} + +static int all_threads_paused(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + if (penv->stop) + return 0; + penv = (CPUState *) penv->next_cpu; + } + + return 1; +} + +static void pause_all_threads(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + if (penv != cpu_single_env) { + penv->stop = 1; + pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI); + } else { + penv->stop = 0; + penv->stopped = 1; + cpu_exit(penv); + } + penv = (CPUState *) penv->next_cpu; + } + + while (!all_threads_paused()) + qemu_cond_wait(&qemu_pause_cond); +} + +static void resume_all_threads(void) +{ + CPUState *penv = first_cpu; + + assert(!cpu_single_env); + + while (penv) { + penv->stop = 0; + penv->stopped = 0; + pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI); + penv = (CPUState *) penv->next_cpu; + } +} + +static void kvm_vm_state_change_handler(void *context, int running, int reason) +{ + if (running) + resume_all_threads(); + else + pause_all_threads(); +} + +static void setup_kernel_sigmask(CPUState *env) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGUSR2); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + sigprocmask(SIG_BLOCK, &set, NULL); + + sigprocmask(SIG_BLOCK, NULL, &set); + sigdelset(&set, SIG_IPI); + sigdelset(&set, SIGBUS); + + kvm_set_signal_mask(env, &set); +} + +static void qemu_kvm_system_reset(void) +{ + CPUState *penv = first_cpu; + + pause_all_threads(); + + qemu_system_reset(); + + while (penv) { + kvm_arch_cpu_reset(penv); + penv = (CPUState *) penv->next_cpu; + } + + resume_all_threads(); +} + +static void process_irqchip_events(CPUState *env) +{ + kvm_arch_process_irqchip_events(env); + if (kvm_arch_has_work(env)) + env->halted = 0; +} + +static int kvm_main_loop_cpu(CPUState *env) +{ + while (1) { + int run_cpu = !is_cpu_stopped(env); + if (run_cpu && !kvm_irqchip_in_kernel()) { + process_irqchip_events(env); + run_cpu = !env->halted; + } + if (run_cpu) { + kvm_cpu_exec(env); + kvm_main_loop_wait(env, 0); + } else { + kvm_main_loop_wait(env, 1000); + } + } + pthread_mutex_unlock(&qemu_mutex); + return 0; +} + +static void *ap_main_loop(void *_env) +{ + CPUState *env = _env; + sigset_t signals; +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + struct ioperm_data *data = NULL; +#endif + + current_env = env; + env->thread_id = kvm_get_thread_id(); + sigfillset(&signals); + sigprocmask(SIG_BLOCK, &signals, NULL); + kvm_create_vcpu(env, env->cpu_index); + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + /* do ioperm for io ports of assigned devices */ + QLIST_FOREACH(data, &ioperm_head, entries) + on_vcpu(env, kvm_arch_do_ioperm, data); +#endif + + setup_kernel_sigmask(env); + + pthread_mutex_lock(&qemu_mutex); + cpu_single_env = env; + + kvm_arch_init_vcpu(env); + + kvm_arch_load_regs(env); + + /* signal VCPU creation */ + current_env->created = 1; + pthread_cond_signal(&qemu_vcpu_cond); + + /* and wait for machine initialization */ + while (!qemu_system_ready) + qemu_cond_wait(&qemu_system_cond); + + /* re-initialize cpu_single_env after re-acquiring qemu_mutex */ + cpu_single_env = env; + + kvm_main_loop_cpu(env); + return NULL; +} + +void kvm_init_vcpu(CPUState *env) +{ + pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env); + + while (env->created == 0) + qemu_cond_wait(&qemu_vcpu_cond); +} + +int kvm_vcpu_inited(CPUState *env) +{ + return env->created; +} + +#ifdef TARGET_I386 +void kvm_hpet_disable_kpit(void) +{ + struct kvm_pit_state2 ps2; + + kvm_get_pit2(kvm_context, &ps2); + ps2.flags |= KVM_PIT_FLAGS_HPET_LEGACY; + kvm_set_pit2(kvm_context, &ps2); +} + +void kvm_hpet_enable_kpit(void) +{ + struct kvm_pit_state2 ps2; + + kvm_get_pit2(kvm_context, &ps2); + ps2.flags &= ~KVM_PIT_FLAGS_HPET_LEGACY; + kvm_set_pit2(kvm_context, &ps2); +} +#endif + +int kvm_init_ap(void) +{ + struct sigaction action; + + qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL); + + signal(SIG_IPI, sig_ipi_handler); + + memset(&action, 0, sizeof(action)); + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler; + sigaction(SIGBUS, &action, NULL); + prctl(PR_MCE_KILL, 1, 1); + return 0; +} + +void qemu_kvm_notify_work(void) +{ + uint64_t value = 1; + char buffer[8]; + size_t offset = 0; + + if (io_thread_fd == -1) + return; + + memcpy(buffer, &value, sizeof(value)); + + while (offset < 8) { + ssize_t len; + + len = write(io_thread_fd, buffer + offset, 8 - offset); + if (len == -1 && errno == EINTR) + continue; + + /* In case we have a pipe, there is not reason to insist writing + * 8 bytes + */ + if (len == -1 && errno == EAGAIN) + break; + + if (len <= 0) + break; + + offset += len; + } +} + +/* If we have signalfd, we mask out the signals we want to handle and then + * use signalfd to listen for them. We rely on whatever the current signal + * handler is to dispatch the signals when we receive them. + */ + +static void sigfd_handler(void *opaque) +{ + int fd = (unsigned long) opaque; + struct qemu_signalfd_siginfo info; + struct sigaction action; + ssize_t len; + + while (1) { + do { + len = read(fd, &info, sizeof(info)); + } while (len == -1 && errno == EINTR); + + if (len == -1 && errno == EAGAIN) + break; + + if (len != sizeof(info)) { + printf("read from sigfd returned %zd: %m\n", len); + return; + } + + sigaction(info.ssi_signo, NULL, &action); + if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) + action.sa_sigaction(info.ssi_signo, + (siginfo_t *)&info, NULL); + else if (action.sa_handler) + action.sa_handler(info.ssi_signo); + + } +} + +/* Used to break IO thread out of select */ +static void io_thread_wakeup(void *opaque) +{ + int fd = (unsigned long) opaque; + char buffer[4096]; + + /* Drain the pipe/(eventfd) */ + while (1) { + ssize_t len; + + len = read(fd, buffer, sizeof(buffer)); + if (len == -1 && errno == EINTR) + continue; + + if (len <= 0) + break; + } +} + +int kvm_main_loop(void) +{ + int fds[2]; + sigset_t mask; + int sigfd; + + io_thread = pthread_self(); + qemu_system_ready = 1; + + if (qemu_eventfd(fds) == -1) { + fprintf(stderr, "failed to create eventfd\n"); + return -errno; + } + + fcntl(fds[0], F_SETFL, O_NONBLOCK); + fcntl(fds[1], F_SETFL, O_NONBLOCK); + + qemu_set_fd_handler2(fds[0], NULL, io_thread_wakeup, NULL, + (void *)(unsigned long) fds[0]); + + io_thread_fd = fds[1]; + + sigemptyset(&mask); + sigaddset(&mask, SIGIO); + sigaddset(&mask, SIGALRM); + sigaddset(&mask, SIGBUS); + sigprocmask(SIG_BLOCK, &mask, NULL); + + sigfd = qemu_signalfd(&mask); + if (sigfd == -1) { + fprintf(stderr, "failed to create signalfd\n"); + return -errno; + } + + fcntl(sigfd, F_SETFL, O_NONBLOCK); + + qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, + (void *)(unsigned long) sigfd); + + pthread_cond_broadcast(&qemu_system_cond); + + io_thread_sigfd = sigfd; + cpu_single_env = NULL; + + while (1) { + main_loop_wait(1000); + if (qemu_shutdown_requested()) { + if (qemu_no_shutdown()) { + vm_stop(0); + } else + break; + } else if (qemu_powerdown_requested()) + qemu_irq_raise(qemu_system_powerdown); + else if (qemu_reset_requested()) + qemu_kvm_system_reset(); + else if (kvm_debug_cpu_requested) { + gdb_set_stop_cpu(kvm_debug_cpu_requested); + vm_stop(EXCP_DEBUG); + kvm_debug_cpu_requested = NULL; + } + } + + pause_all_threads(); + pthread_mutex_unlock(&qemu_mutex); + + return 0; +} + +#ifdef TARGET_I386 +static int destroy_region_works = 0; +#endif + + +#if !defined(TARGET_I386) +int kvm_arch_init_irq_routing(void) +{ + return 0; +} +#endif + +extern int no_hpet; + +static int kvm_create_context(void) +{ + int r; + + if (!kvm_irqchip) { + kvm_disable_irqchip_creation(kvm_context); + } + if (!kvm_pit) { + kvm_disable_pit_creation(kvm_context); + } + if (kvm_create(kvm_context, 0, NULL) < 0) { + kvm_finalize(kvm_state); + return -1; + } + r = kvm_arch_qemu_create_context(); + if (r < 0) + kvm_finalize(kvm_state); + if (kvm_pit && !kvm_pit_reinject) { + if (kvm_reinject_control(kvm_context, 0)) { + fprintf(stderr, "failure to disable in-kernel PIT reinjection\n"); + return -1; + } + } +#ifdef TARGET_I386 + destroy_region_works = kvm_destroy_memory_region_works(kvm_context); +#endif + + r = kvm_arch_init_irq_routing(); + if (r < 0) { + return r; + } + + kvm_state->vcpu_events = 0; +#ifdef KVM_CAP_VCPU_EVENTS + kvm_state->vcpu_events = kvm_check_extension(kvm_state, KVM_CAP_VCPU_EVENTS); +#endif + + kvm_init_ap(); + if (kvm_irqchip) { + if (!qemu_kvm_has_gsi_routing()) { + irq0override = 0; +#ifdef TARGET_I386 + /* if kernel can't do irq routing, interrupt source + * override 0->2 can not be set up as required by hpet, + * so disable hpet. + */ + no_hpet = 1; + } else if (!qemu_kvm_has_pit_state2()) { + no_hpet = 1; + } +#else + } +#endif + } + + return 0; +} + +#ifdef TARGET_I386 +static int must_use_aliases_source(target_phys_addr_t addr) +{ + if (destroy_region_works) + return false; + if (addr == 0xa0000 || addr == 0xa8000) + return true; + return false; +} + +static int must_use_aliases_target(target_phys_addr_t addr) +{ + if (destroy_region_works) + return false; + if (addr >= 0xe0000000 && addr < 0x100000000ull) + return true; + return false; +} + +static struct mapping { + target_phys_addr_t phys; + ram_addr_t ram; + ram_addr_t len; +} mappings[50]; +static int nr_mappings; + +static struct mapping *find_ram_mapping(ram_addr_t ram_addr) +{ + struct mapping *p; + + for (p = mappings; p < mappings + nr_mappings; ++p) { + if (p->ram <= ram_addr && ram_addr < p->ram + p->len) { + return p; + } + } + return NULL; +} + +static struct mapping *find_mapping(target_phys_addr_t start_addr) +{ + struct mapping *p; + + for (p = mappings; p < mappings + nr_mappings; ++p) { + if (p->phys <= start_addr && start_addr < p->phys + p->len) { + return p; + } + } + return NULL; +} + +static void drop_mapping(target_phys_addr_t start_addr) +{ + struct mapping *p = find_mapping(start_addr); + + if (p) + *p = mappings[--nr_mappings]; +} +#endif + +void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, + ram_addr_t phys_offset) +{ + int r = 0; + unsigned long area_flags; +#ifdef TARGET_I386 + struct mapping *p; +#endif + + if (start_addr + size > phys_ram_size) { + phys_ram_size = start_addr + size; + } + + phys_offset &= ~IO_MEM_ROM; + area_flags = phys_offset & ~TARGET_PAGE_MASK; + + if (area_flags != IO_MEM_RAM) { +#ifdef TARGET_I386 + if (must_use_aliases_source(start_addr)) { + kvm_destroy_memory_alias(kvm_context, start_addr); + return; + } + if (must_use_aliases_target(start_addr)) + return; +#endif + while (size > 0) { + p = find_mapping(start_addr); + if (p) { + kvm_unregister_memory_area(kvm_context, p->phys, p->len); + drop_mapping(p->phys); + } + start_addr += TARGET_PAGE_SIZE; + if (size > TARGET_PAGE_SIZE) { + size -= TARGET_PAGE_SIZE; + } else { + size = 0; + } + } + return; + } + + r = kvm_is_containing_region(kvm_context, start_addr, size); + if (r) + return; + + if (area_flags >= TLB_MMIO) + return; + +#ifdef TARGET_I386 + if (must_use_aliases_source(start_addr)) { + p = find_ram_mapping(phys_offset); + if (p) { + kvm_create_memory_alias(kvm_context, start_addr, size, + p->phys + (phys_offset - p->ram)); + } + return; + } +#endif + + r = kvm_register_phys_mem(kvm_context, start_addr, + qemu_get_ram_ptr(phys_offset), size, 0); + if (r < 0) { + printf("kvm_cpu_register_physical_memory: failed\n"); + exit(1); + } +#ifdef TARGET_I386 + drop_mapping(start_addr); + p = &mappings[nr_mappings++]; + p->phys = start_addr; + p->ram = phys_offset; + p->len = size; +#endif + + return; +} + +int kvm_setup_guest_memory(void *area, unsigned long size) +{ + int ret = 0; + +#ifdef MADV_DONTFORK + if (kvm_enabled() && !kvm_has_sync_mmu()) + ret = madvise(area, size, MADV_DONTFORK); +#endif + + if (ret) + perror("madvise"); + + return ret; +} + +#ifdef KVM_CAP_SET_GUEST_DEBUG + +struct kvm_set_guest_debug_data { + struct kvm_guest_debug dbg; + int err; +}; + +static void kvm_invoke_set_guest_debug(void *data) +{ + struct kvm_set_guest_debug_data *dbg_data = data; + + if (cpu_single_env->kvm_cpu_state.regs_modified) { + kvm_arch_put_registers(cpu_single_env); + cpu_single_env->kvm_cpu_state.regs_modified = 0; + } + dbg_data->err = + kvm_set_guest_debug(cpu_single_env, + &dbg_data->dbg); +} + +int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) +{ + struct kvm_set_guest_debug_data data; + + data.dbg.control = 0; + if (env->singlestep_enabled) + data.dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; + + kvm_arch_update_guest_debug(env, &data.dbg); + data.dbg.control |= reinject_trap; + + on_vcpu(env, kvm_invoke_set_guest_debug, &data); + return data.err; +} + +#endif + +/* + * dirty pages logging + */ +/* FIXME: use unsigned long pointer instead of unsigned char */ +unsigned char *kvm_dirty_bitmap = NULL; +int kvm_physical_memory_set_dirty_tracking(int enable) +{ + int r = 0; + + if (!kvm_enabled()) + return 0; + + if (enable) { + if (!kvm_dirty_bitmap) { + unsigned bitmap_size = BITMAP_SIZE(phys_ram_size); + kvm_dirty_bitmap = qemu_malloc(bitmap_size); + r = kvm_dirty_pages_log_enable_all(kvm_context); + } + } else { + if (kvm_dirty_bitmap) { + r = kvm_dirty_pages_log_reset(kvm_context); + qemu_free(kvm_dirty_bitmap); + kvm_dirty_bitmap = NULL; + } + } + return r; +} + +/* get kvm's dirty pages bitmap and update qemu's */ +static int kvm_get_dirty_pages_log_range(unsigned long start_addr, + unsigned char *bitmap, + unsigned long offset, + unsigned long mem_size) +{ + unsigned int i, j, n = 0; + unsigned char c; + unsigned long page_number, addr, addr1; + ram_addr_t ram_addr; + unsigned int len = ((mem_size / TARGET_PAGE_SIZE) + 7) / 8; + + /* + * bitmap-traveling is faster than memory-traveling (for addr...) + * especially when most of the memory is not dirty. + */ + for (i = 0; i < len; i++) { + c = bitmap[i]; + while (c > 0) { + j = ffsl(c) - 1; + c &= ~(1u << j); + page_number = i * 8 + j; + addr1 = page_number * TARGET_PAGE_SIZE; + addr = offset + addr1; + ram_addr = cpu_get_physical_page_desc(addr); + cpu_physical_memory_set_dirty(ram_addr); + n++; + } + } + return 0; +} + +static int kvm_get_dirty_bitmap_cb(unsigned long start, unsigned long len, + void *bitmap, void *opaque) +{ + return kvm_get_dirty_pages_log_range(start, bitmap, start, len); +} + +/* + * get kvm's dirty pages bitmap and update qemu's + * we only care about physical ram, which resides in slots 0 and 3 + */ +int kvm_update_dirty_pages_log(void) +{ + int r = 0; + + + r = kvm_get_dirty_pages_range(kvm_context, 0, -1UL, NULL, + kvm_get_dirty_bitmap_cb); + return r; +} + +void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, + int log) +{ + if (log) + kvm_dirty_pages_log_enable_slot(kvm_context, start, size); + else { +#ifdef TARGET_I386 + if (must_use_aliases_target(start)) + return; +#endif + kvm_dirty_pages_log_disable_slot(kvm_context, start, size); + } +} + +#ifdef KVM_CAP_IRQCHIP + +int kvm_set_irq(int irq, int level, int *status) +{ + return kvm_set_irq_level(kvm_context, irq, level, status); +} + +#endif + +int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf) +{ + return kvm_get_dirty_pages(kvm_context, phys_addr, buf); +} + +void kvm_mutex_unlock(void) +{ + assert(!cpu_single_env); + pthread_mutex_unlock(&qemu_mutex); +} + +void kvm_mutex_lock(void) +{ + pthread_mutex_lock(&qemu_mutex); + cpu_single_env = NULL; +} + +void qemu_mutex_unlock_iothread(void) +{ + if (kvm_enabled()) + kvm_mutex_unlock(); +} + +void qemu_mutex_lock_iothread(void) +{ + if (kvm_enabled()) + kvm_mutex_lock(); +} + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +void kvm_add_ioperm_data(struct ioperm_data *data) +{ + QLIST_INSERT_HEAD(&ioperm_head, data, entries); +} + +void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num) +{ + struct ioperm_data *data; + + data = QLIST_FIRST(&ioperm_head); + while (data) { + struct ioperm_data *next = QLIST_NEXT(data, entries); + + if (data->start_port == start_port && data->num == num) { + QLIST_REMOVE(data, entries); + qemu_free(data); + } + + data = next; + } +} + +void kvm_ioperm(CPUState *env, void *data) +{ + if (kvm_enabled() && qemu_system_ready) + on_vcpu(env, kvm_arch_do_ioperm, data); +} + +#endif + +int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, + target_phys_addr_t end_addr) +{ +#ifndef TARGET_IA64 + +#ifdef TARGET_I386 + if (must_use_aliases_source(start_addr)) + return 0; +#endif + + kvm_get_dirty_pages_range(kvm_context, start_addr, + end_addr - start_addr, NULL, + kvm_get_dirty_bitmap_cb); +#endif + return 0; +} + +int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len) +{ +#ifdef TARGET_I386 + if (must_use_aliases_source(phys_addr)) + return 0; +#endif + +#ifndef TARGET_IA64 + kvm_qemu_log_memory(phys_addr, len, 1); +#endif + return 0; +} + +int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len) +{ +#ifdef TARGET_I386 + if (must_use_aliases_source(phys_addr)) + return 0; +#endif + +#ifndef TARGET_IA64 + kvm_qemu_log_memory(phys_addr, len, 0); +#endif + return 0; +} + +int kvm_set_boot_cpu_id(uint32_t id) +{ + return kvm_set_boot_vcpu_id(kvm_context, id); +} + +#ifdef TARGET_I386 +#ifdef KVM_CAP_MCE +struct kvm_x86_mce_data { + CPUState *env; + struct kvm_x86_mce *mce; + int abort_on_error; +}; + +static void kvm_do_inject_x86_mce(void *_data) +{ + struct kvm_x86_mce_data *data = _data; + int r; + + r = kvm_set_mce(data->env, data->mce); + if (r < 0) { + perror("kvm_set_mce FAILED"); + if (data->abort_on_error) + abort(); + } +} +#endif + +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc, + int abort_on_error) +{ +#ifdef KVM_CAP_MCE + struct kvm_x86_mce mce = { + .bank = bank, + .status = status, + .mcg_status = mcg_status, + .addr = addr, + .misc = misc, + }; + struct kvm_x86_mce_data data = { + .env = cenv, + .mce = &mce, + .abort_on_error = abort_on_error, + }; + + if (!cenv->mcg_cap) { + fprintf(stderr, "MCE support is not enabled!\n"); + return; + } + on_vcpu(cenv, kvm_do_inject_x86_mce, &data); +#else + if (abort_on_error) + abort(); +#endif +} +#endif diff --git a/qemu-kvm.h b/qemu-kvm.h new file mode 100644 index 000000000..6b3e5a1c4 --- /dev/null +++ b/qemu-kvm.h @@ -0,0 +1,1169 @@ +/* + * qemu/kvm integration + * + * Copyright (C) 2006-2008 Qumranet Technologies + * + * Licensed under the terms of the GNU GPL version 2 or higher. + */ +#ifndef THE_ORIGINAL_AND_TRUE_QEMU_KVM_H +#define THE_ORIGINAL_AND_TRUE_QEMU_KVM_H + +#ifndef QEMU_KVM_NO_CPU +#include "cpu.h" +#endif + +#include <signal.h> +#include <stdlib.h> + +#ifdef CONFIG_KVM + +#if defined(__s390__) +#include <asm/ptrace.h> +#endif + +#include <stdint.h> + +#ifndef __user +#define __user /* temporary, until installed via make headers_install */ +#endif + +#include <linux/kvm.h> + +#include <signal.h> + +/* FIXME: share this number with kvm */ +/* FIXME: or dynamically alloc/realloc regions */ +#ifdef __s390__ +#define KVM_MAX_NUM_MEM_REGIONS 1u +#define MAX_VCPUS 64 +#define LIBKVM_S390_ORIGIN (0UL) +#elif defined(__ia64__) +#define KVM_MAX_NUM_MEM_REGIONS 32u +#define MAX_VCPUS 256 +#else +#define KVM_MAX_NUM_MEM_REGIONS 32u +#define MAX_VCPUS 16 +#endif + +/* kvm abi verison variable */ +extern int kvm_abi; + +/** + * \brief The KVM context + * + * The verbose KVM context + */ + +struct kvm_context { + void *opaque; + /// is dirty pages logging enabled for all regions or not + int dirty_pages_log_all; + /// do not create in-kernel irqchip if set + int no_irqchip_creation; + /// in-kernel irqchip status + int irqchip_in_kernel; + /// ioctl to use to inject interrupts + int irqchip_inject_ioctl; + /// do not create in-kernel pit if set + int no_pit_creation; + /// in-kernel pit status + int pit_in_kernel; +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing *irq_routes; + int nr_allocated_irq_routes; +#endif + void *used_gsi_bitmap; + int max_gsi; +}; + +typedef struct kvm_context *kvm_context_t; + +#include "kvm.h" +int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory, + void **vm_mem); +int kvm_alloc_userspace_memory(kvm_context_t kvm, unsigned long memory, + void **vm_mem); + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem); + +int kvm_arch_run(CPUState *env); + + +void kvm_show_code(CPUState *env); + +int handle_halt(CPUState *env); + +#ifndef QEMU_KVM_NO_CPU + +int handle_shutdown(kvm_context_t kvm, CPUState *env); +void post_kvm_run(kvm_context_t kvm, CPUState *env); +int pre_kvm_run(kvm_context_t kvm, CPUState *env); +int handle_io_window(kvm_context_t kvm); +int try_push_interrupts(kvm_context_t kvm); + +#if defined(__x86_64__) || defined(__i386__) +struct kvm_msr_list *kvm_get_msr_list(kvm_context_t); +int kvm_get_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n); +int kvm_set_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n); +int kvm_get_mce_cap_supported(kvm_context_t, uint64_t *mce_cap, + int *max_banks); +int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap); +struct kvm_x86_mce; +int kvm_set_mce(CPUState *env, struct kvm_x86_mce *mce); +#endif + +#endif + +/*! + * \brief Create new KVM context + * + * This creates a new kvm_context. A KVM context is a small area of data that + * holds information about the KVM instance that gets created by this call.\n + * This should always be your first call to KVM. + * + * \param opaque Not used + * \return NULL on failure + */ +int kvm_init(int smp_cpus); + +/*! + * \brief Disable the in-kernel IRQCHIP creation + * + * In-kernel irqchip is enabled by default. If userspace irqchip is to be used, + * this should be called prior to kvm_create(). + * + * \param kvm Pointer to the kvm_context + */ +void kvm_disable_irqchip_creation(kvm_context_t kvm); + +/*! + * \brief Disable the in-kernel PIT creation + * + * In-kernel pit is enabled by default. If userspace pit is to be used, + * this should be called prior to kvm_create(). + * + * \param kvm Pointer to the kvm_context + */ +void kvm_disable_pit_creation(kvm_context_t kvm); + +/*! + * \brief Create new virtual machine + * + * This creates a new virtual machine, maps physical RAM to it, and creates a + * virtual CPU for it.\n + * \n + * Memory gets mapped for addresses 0->0xA0000, 0xC0000->phys_mem_bytes + * + * \param kvm Pointer to the current kvm_context + * \param phys_mem_bytes The amount of physical ram you want the VM to have + * \param phys_mem This pointer will be set to point to the memory that + * kvm_create allocates for physical RAM + * \return 0 on success + */ +int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **phys_mem); +int kvm_create_vm(kvm_context_t kvm); +void kvm_create_irqchip(kvm_context_t kvm); + +/*! + * \brief Start the VCPU + * + * This starts the VCPU and virtualization is started.\n + * \n + * This function will not return until any of these conditions are met: + * - An IO/MMIO handler does not return "0" + * - An exception that neither the guest OS, nor KVM can handle occurs + * + * \note This function will call the callbacks registered in kvm_init() + * to emulate those functions + * \note If you at any point want to interrupt the VCPU, kvm_run() will + * listen to the EINTR signal. This allows you to simulate external interrupts + * and asyncronous IO. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be started + * \return 0 on success, but you really shouldn't expect this function to + * return except for when an error has occured, or when you have sent it + * an EINTR signal. + */ +int kvm_run(CPUState *env); + +/*! + * \brief Check if a vcpu is ready for interrupt injection + * + * This checks if vcpu interrupts are not masked by mov ss or sti. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return boolean indicating interrupt injection readiness + */ +int kvm_is_ready_for_interrupt_injection(CPUState *env); + +/*! + * \brief Read VCPU registers + * + * This gets the GP registers from the VCPU and outputs them + * into a kvm_regs structure + * + * \note This function returns a \b copy of the VCPUs registers.\n + * If you wish to modify the VCPUs GP registers, you should call kvm_set_regs() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_regs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_get_regs(CPUState *env, struct kvm_regs *regs); + +/*! + * \brief Write VCPU registers + * + * This sets the GP registers on the VCPU from a kvm_regs structure + * + * \note When this function returns, the regs pointer and the data it points to + * can be discarded + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_regs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_set_regs(CPUState *env, struct kvm_regs *regs); +/*! + * \brief Read VCPU fpu registers + * + * This gets the FPU registers from the VCPU and outputs them + * into a kvm_fpu structure + * + * \note This function returns a \b copy of the VCPUs registers.\n + * If you wish to modify the VCPU FPU registers, you should call kvm_set_fpu() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param fpu Pointer to a kvm_fpu which will be populated with the VCPUs + * fpu registers values + * \return 0 on success + */ +int kvm_get_fpu(CPUState *env, struct kvm_fpu *fpu); + +/*! + * \brief Write VCPU fpu registers + * + * This sets the FPU registers on the VCPU from a kvm_fpu structure + * + * \note When this function returns, the fpu pointer and the data it points to + * can be discarded + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param fpu Pointer to a kvm_fpu which holds the new vcpu fpu state + * \return 0 on success + */ +int kvm_set_fpu(CPUState *env, struct kvm_fpu *fpu); + +/*! + * \brief Read VCPU system registers + * + * This gets the non-GP registers from the VCPU and outputs them + * into a kvm_sregs structure + * + * \note This function returns a \b copy of the VCPUs registers.\n + * If you wish to modify the VCPUs non-GP registers, you should call + * kvm_set_sregs() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_get_sregs(CPUState *env, struct kvm_sregs *regs); + +/*! + * \brief Write VCPU system registers + * + * This sets the non-GP registers on the VCPU from a kvm_sregs structure + * + * \note When this function returns, the regs pointer and the data it points to + * can be discarded + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs + * registers values + * \return 0 on success + */ +int kvm_set_sregs(CPUState *env, struct kvm_sregs *regs); + +#ifdef KVM_CAP_MP_STATE +/*! + * * \brief Read VCPU MP state + * + */ +int kvm_get_mpstate(CPUState *env, struct kvm_mp_state *mp_state); + +/*! + * * \brief Write VCPU MP state + * + */ +int kvm_set_mpstate(CPUState *env, struct kvm_mp_state *mp_state); +/*! + * * \brief Reset VCPU MP state + * + */ +static inline int kvm_reset_mpstate(CPUState *env) +{ + struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED + }; + return kvm_set_mpstate(env, &mp_state); +} +#endif + +/*! + * \brief Simulate an external vectored interrupt + * + * This allows you to simulate an external vectored interrupt. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \param irq Vector number + * \return 0 on success + */ +int kvm_inject_irq(CPUState *env, unsigned irq); + +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_set_guest_debug(CPUState *env, struct kvm_guest_debug *dbg); +#endif + +#if defined(__i386__) || defined(__x86_64__) +/*! + * \brief Setup a vcpu's cpuid instruction emulation + * + * Set up a table of cpuid function to cpuid outputs.\n + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be initialized + * \param nent number of entries to be installed + * \param entries cpuid function entries table + * \return 0 on success, or -errno on error + */ +int kvm_setup_cpuid(CPUState *env, int nent, + struct kvm_cpuid_entry *entries); + +/*! + * \brief Setup a vcpu's cpuid instruction emulation + * + * Set up a table of cpuid function to cpuid outputs. + * This call replaces the older kvm_setup_cpuid interface by adding a few + * parameters to support cpuid functions that have sub-leaf values. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be initialized + * \param nent number of entries to be installed + * \param entries cpuid function entries table + * \return 0 on success, or -errno on error + */ +int kvm_setup_cpuid2(CPUState *env, int nent, + struct kvm_cpuid_entry2 *entries); + +/*! + * \brief Setting the number of shadow pages to be allocated to the vm + * + * \param kvm pointer to kvm_context + * \param nrshadow_pages number of pages to be allocated + */ +int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages); + +/*! + * \brief Getting the number of shadow pages that are allocated to the vm + * + * \param kvm pointer to kvm_context + * \param nrshadow_pages number of pages to be allocated + */ +int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages); + +#endif + +/*! + * \brief Set a vcpu's signal mask for guest mode + * + * A vcpu can have different signals blocked in guest mode and user mode. + * This allows guest execution to be interrupted on a signal, without requiring + * that the signal be delivered to a signal handler (the signal can be + * dequeued using sigwait(2). + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be initialized + * \param sigset signal mask for guest mode + * \return 0 on success, or -errno on error + */ +int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset); + +/*! + * \brief Dump VCPU registers + * + * This dumps some of the information that KVM has about a virtual CPU, namely: + * - GP Registers + * + * A much more verbose version of this is available as kvm_dump_vcpu() + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return 0 on success + */ +void kvm_show_regs(CPUState *env); + + +void *kvm_create_phys_mem(kvm_context_t, unsigned long phys_start, + unsigned long len, int log, int writable); +void kvm_destroy_phys_mem(kvm_context_t, unsigned long phys_start, + unsigned long len); +void kvm_unregister_memory_area(kvm_context_t, uint64_t phys_start, + unsigned long len); + +int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_start, + unsigned long size); +int kvm_register_phys_mem(kvm_context_t kvm, unsigned long phys_start, + void *userspace_addr, unsigned long len, int log); +int kvm_get_dirty_pages(kvm_context_t, unsigned long phys_addr, void *buf); +int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr, + unsigned long end_addr, void *opaque, + int (*cb)(unsigned long start, + unsigned long len, void *bitmap, + void *opaque)); +int kvm_register_coalesced_mmio(kvm_context_t kvm, uint64_t addr, + uint32_t size); +int kvm_unregister_coalesced_mmio(kvm_context_t kvm, uint64_t addr, + uint32_t size); + +/*! + * \brief Create a memory alias + * + * Aliases a portion of physical memory to another portion. If the guest + * accesses the alias region, it will behave exactly as if it accessed + * the target memory. + */ +int kvm_create_memory_alias(kvm_context_t, uint64_t phys_start, uint64_t len, + uint64_t target_phys); + +/*! + * \brief Destroy a memory alias + * + * Removes an alias created with kvm_create_memory_alias(). + */ +int kvm_destroy_memory_alias(kvm_context_t, uint64_t phys_start); + +/*! + * \brief Get a bitmap of guest ram pages which are allocated to the guest. + * + * \param kvm Pointer to the current kvm_context + * \param phys_addr Memory slot phys addr + * \param bitmap Long aligned address of a big enough bitmap (one bit per page) + */ +int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *bitmap); +int kvm_get_mem_map_range(kvm_context_t kvm, unsigned long phys_addr, + unsigned long len, void *buf, void *opaque, + int (*cb)(unsigned long start, + unsigned long len, void *bitmap, + void *opaque)); +int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status); + +int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, uint64_t phys_start, + uint64_t len); +int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, uint64_t phys_start, + uint64_t len); +/*! + * \brief Enable dirty-pages-logging for all memory regions + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_dirty_pages_log_enable_all(kvm_context_t kvm); + +/*! + * \brief Disable dirty-page-logging for some memory regions + * + * Disable dirty-pages-logging for those memory regions that were + * created with dirty-page-logging disabled. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_dirty_pages_log_reset(kvm_context_t kvm); + +#ifdef KVM_CAP_IRQCHIP +/*! + * \brief Dump in kernel IRQCHIP contents + * + * Dump one of the in kernel irq chip devices, including PIC (master/slave) + * and IOAPIC into a kvm_irqchip structure + * + * \param kvm Pointer to the current kvm_context + * \param chip The irq chip device to be dumped + */ +int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip); + +/*! + * \brief Set in kernel IRQCHIP contents + * + * Write one of the in kernel irq chip devices, including PIC (master/slave) + * and IOAPIC + * + * + * \param kvm Pointer to the current kvm_context + * \param chip THe irq chip device to be written + */ +int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip); + +#if defined(__i386__) || defined(__x86_64__) +/*! + * \brief Get in kernel local APIC for vcpu + * + * Save the local apic state including the timer of a virtual CPU + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be accessed + * \param s Local apic state of the specific virtual CPU + */ +int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s); + +/*! + * \brief Set in kernel local APIC for vcpu + * + * Restore the local apic state including the timer of a virtual CPU + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should be accessed + * \param s Local apic state of the specific virtual CPU + */ +int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s); + +#endif + +/*! + * \brief Simulate an NMI + * + * This allows you to simulate a non-maskable interrupt. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return 0 on success + */ +int kvm_inject_nmi(CPUState *env); + +#endif + +/*! + * \brief Simulate an x86 MCE + * + * This allows you to simulate a x86 MCE. + * + * \param cenv Which virtual CPU should get MCE injected + * \param bank Bank number + * \param status MSR_MCI_STATUS + * \param mcg_status MSR_MCG_STATUS + * \param addr MSR_MCI_ADDR + * \param misc MSR_MCI_MISC + * \param abort_on_error abort on error + */ +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc, + int abort_on_error); + +/*! + * \brief Query wheather in kernel pit is used + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_pit_in_kernel(kvm_context_t kvm); + +/*! + * \brief Initialize coalesced MMIO + * + * Check for coalesced MMIO capability and store in context + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_init_coalesced_mmio(kvm_context_t kvm); + +#ifdef KVM_CAP_PIT + +#if defined(__i386__) || defined(__x86_64__) +/*! + * \brief Get in kernel PIT of the virtual domain + * + * Save the PIT state. + * + * \param kvm Pointer to the current kvm_context + * \param s PIT state of the virtual domain + */ +int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s); + +/*! + * \brief Set in kernel PIT of the virtual domain + * + * Restore the PIT state. + * Timer would be retriggerred after restored. + * + * \param kvm Pointer to the current kvm_context + * \param s PIT state of the virtual domain + */ +int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s); + +int kvm_reinject_control(kvm_context_t kvm, int pit_reinject); + +#ifdef KVM_CAP_PIT_STATE2 +/*! + * \brief Check for kvm support of kvm_pit_state2 + * + * \param kvm Pointer to the current kvm_context + * \return 0 on success + */ +int kvm_has_pit_state2(kvm_context_t kvm); + +/*! + * \brief Set in kernel PIT state2 of the virtual domain + * + * + * \param kvm Pointer to the current kvm_context + * \param ps2 PIT state2 of the virtual domain + * \return 0 on success + */ +int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2); + +/*! + * \brief Get in kernel PIT state2 of the virtual domain + * + * + * \param kvm Pointer to the current kvm_context + * \param ps2 PIT state2 of the virtual domain + * \return 0 on success + */ +int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2); + +#endif +#endif +#endif + +#ifdef KVM_CAP_VAPIC + +/*! + * \brief Enable kernel tpr access reporting + * + * When tpr access reporting is enabled, the kernel will call the + * ->tpr_access() callback every time the guest vcpu accesses the tpr. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu vcpu to enable tpr access reporting on + */ +int kvm_enable_tpr_access_reporting(CPUState *env); + +/*! + * \brief Disable kernel tpr access reporting + * + * Undoes the effect of kvm_enable_tpr_access_reporting(). + * + * \param kvm Pointer to the current kvm_context + * \param vcpu vcpu to disable tpr access reporting on + */ +int kvm_disable_tpr_access_reporting(CPUState *env); + +int kvm_enable_vapic(CPUState *env, uint64_t vapic); + +#endif + +#if defined(__s390__) +int kvm_s390_initial_reset(kvm_context_t kvm, int slot); +int kvm_s390_interrupt(kvm_context_t kvm, int slot, + struct kvm_s390_interrupt *kvmint); +int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw); +int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr); +#endif + +#ifdef KVM_CAP_DEVICE_ASSIGNMENT +/*! + * \brief Notifies host kernel about a PCI device to be assigned to a guest + * + * Used for PCI device assignment, this function notifies the host + * kernel about the assigning of the physical PCI device to a guest. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_dev Parameters, like bus, devfn number, etc + */ +int kvm_assign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev); + +/*! + * \brief Assign IRQ for an assigned device + * + * Used for PCI device assignment, this function assigns IRQ numbers for + * an physical device and guest IRQ handling. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc + */ +int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq); + +#ifdef KVM_CAP_ASSIGN_DEV_IRQ +/*! + * \brief Deassign IRQ for an assigned device + * + * Used for PCI device assignment, this function deassigns IRQ numbers + * for an assigned device. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc + */ +int kvm_deassign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq); +#endif +#endif + +/*! + * \brief Determines whether destroying memory regions is allowed + * + * KVM before 2.6.29 had a bug when destroying memory regions. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_destroy_memory_region_works(kvm_context_t kvm); + +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT +/*! + * \brief Notifies host kernel about a PCI device to be deassigned from a guest + * + * Used for hot remove PCI device, this function notifies the host + * kernel about the deassigning of the physical PCI device from a guest. + * + * \param kvm Pointer to the current kvm_context + * \param assigned_dev Parameters, like bus, devfn number, etc + */ +int kvm_deassign_pci_device(kvm_context_t kvm, + struct kvm_assigned_pci_dev *assigned_dev); +#endif + +/*! + * \brief Checks whether the generic irq routing capability is present + * + * Checks whether kvm can reroute interrupts among the various interrupt + * controllers. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_has_gsi_routing(kvm_context_t kvm); + +/*! + * \brief Determines the number of gsis that can be routed + * + * Returns the number of distinct gsis that can be routed by kvm. This is + * also the number of distinct routes (if a gsi has two routes, than another + * gsi cannot be used...) + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_get_gsi_count(kvm_context_t kvm); + +/*! + * \brief Clears the temporary irq routing table + * + * Clears the temporary irq routing table. Nothing is committed to the + * running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_clear_gsi_routes(kvm_context_t kvm); + +/*! + * \brief Adds an irq route to the temporary irq routing table + * + * Adds an irq route to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin); + +/*! + * \brief Removes an irq route from the temporary irq routing table + * + * Adds an irq route to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin); + +struct kvm_irq_routing_entry; +/*! + * \brief Adds a routing entry to the temporary irq routing table + * + * Adds a filled routing entry to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_add_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry *entry); + +/*! + * \brief Removes a routing from the temporary irq routing table + * + * Remove a routing to the temporary irq routing table. Nothing is + * committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_del_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry *entry); + +/*! + * \brief Updates a routing in the temporary irq routing table + * + * Update a routing in the temporary irq routing table + * with a new value. entry type and GSI can not be changed. + * Nothing is committed to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_update_routing_entry(kvm_context_t kvm, + struct kvm_irq_routing_entry *entry, + struct kvm_irq_routing_entry *newentry); + +/*! + * \brief Commit the temporary irq routing table + * + * Commit the temporary irq routing table to the running VM. + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_commit_irq_routes(kvm_context_t kvm); + +/*! + * \brief Get unused GSI number for irq routing table + * + * Get unused GSI number for irq routing table + * + * \param kvm Pointer to the current kvm_context + */ +int kvm_get_irq_route_gsi(kvm_context_t kvm); + +/*! + * \brief Create a file descriptor for injecting interrupts + * + * Creates an eventfd based file-descriptor that maps to a specific GSI + * in the guest. eventfd compliant signaling (write() from userspace, or + * eventfd_signal() from kernelspace) will cause the GSI to inject + * itself into the guest at the next available window. + * + * \param kvm Pointer to the current kvm_context + * \param gsi GSI to assign to this fd + * \param flags reserved, must be zero + */ +int kvm_irqfd(kvm_context_t kvm, int gsi, int flags); + +#ifdef KVM_CAP_DEVICE_MSIX +int kvm_assign_set_msix_nr(kvm_context_t kvm, + struct kvm_assigned_msix_nr *msix_nr); +int kvm_assign_set_msix_entry(kvm_context_t kvm, + struct kvm_assigned_msix_entry *entry); +#endif + +uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg); + +#else /* !CONFIG_KVM */ + +typedef struct kvm_context *kvm_context_t; +typedef struct kvm_vcpu_context *kvm_vcpu_context_t; + +struct kvm_pit_state { +}; + +static inline int kvm_init(int smp_cpus) +{ + return 0; +} + +#ifndef QEMU_KVM_NO_CPU + +static inline void kvm_inject_x86_mce(CPUState *cenv, int bank, + uint64_t status, uint64_t mcg_status, + uint64_t addr, uint64_t misc, + int abort_on_error) +{ + if (abort_on_error) + abort(); +} + +#endif + +extern int kvm_allowed; + +#endif /* !CONFIG_KVM */ + + +int kvm_main_loop(void); +int kvm_init_ap(void); +#ifndef QEMU_KVM_NO_CPU +int kvm_vcpu_inited(CPUState *env); +void kvm_load_registers(CPUState *env); +void kvm_save_registers(CPUState *env); +void kvm_load_mpstate(CPUState *env); +void kvm_save_mpstate(CPUState *env); +int kvm_cpu_exec(CPUState *env); +int kvm_insert_breakpoint(CPUState * current_env, target_ulong addr, + target_ulong len, int type); +int kvm_remove_breakpoint(CPUState * current_env, target_ulong addr, + target_ulong len, int type); +void kvm_remove_all_breakpoints(CPUState * current_env); +int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap); +void kvm_apic_init(CPUState *env); +/* called from vcpu initialization */ +void qemu_kvm_load_lapic(CPUState *env); +#endif + +void kvm_hpet_enable_kpit(void); +void kvm_hpet_disable_kpit(void); +int kvm_set_irq(int irq, int level, int *status); + +int kvm_physical_memory_set_dirty_tracking(int enable); +int kvm_update_dirty_pages_log(void); + +#ifndef QEMU_KVM_NO_CPU +void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *env); +void qemu_kvm_cpuid_on_env(CPUState *env); +void kvm_inject_interrupt(CPUState *env, int mask); +void kvm_update_after_sipi(CPUState *env); +void kvm_update_interrupt_request(CPUState *env); +#endif +void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, + ram_addr_t phys_offset); +void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr, unsigned long size, + int log, int writable); + +void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr, + unsigned long size); +void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, + int log); +int kvm_setup_guest_memory(void *area, unsigned long size); +int kvm_qemu_create_memory_alias(uint64_t phys_start, uint64_t len, + uint64_t target_phys); +int kvm_qemu_destroy_memory_alias(uint64_t phys_start); + +int kvm_arch_qemu_create_context(void); + +#ifndef QEMU_KVM_NO_CPU +void kvm_arch_save_regs(CPUState *env); +void kvm_arch_load_regs(CPUState *env); +void kvm_arch_load_mpstate(CPUState *env); +void kvm_arch_save_mpstate(CPUState *env); +int kvm_arch_init_vcpu(CPUState *cenv); +int kvm_arch_pre_run(CPUState *env, struct kvm_run *run); +int kvm_arch_post_run(CPUState *env, struct kvm_run *run); +int kvm_arch_has_work(CPUState *env); +void kvm_arch_process_irqchip_events(CPUState *env); +int kvm_arch_try_push_interrupts(void *opaque); +void kvm_arch_push_nmi(void *opaque); +void kvm_arch_cpu_reset(CPUState *env); +int kvm_set_boot_cpu_id(uint32_t id); + +struct kvm_guest_debug; +struct kvm_debug_exit_arch; + +struct kvm_sw_breakpoint { + target_ulong pc; + target_ulong saved_insn; + int use_count; + QTAILQ_ENTRY(kvm_sw_breakpoint) entry; +}; + +QTAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint); + +int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info); +int kvm_sw_breakpoints_active(CPUState *env); +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, + target_ulong pc); +int kvm_arch_insert_sw_breakpoint(CPUState * current_env, + struct kvm_sw_breakpoint *bp); +int kvm_arch_remove_sw_breakpoint(CPUState * current_env, + struct kvm_sw_breakpoint *bp); +int kvm_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, + int type); +int kvm_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, + int type); +void kvm_arch_remove_all_hw_breakpoints(void); +void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg); + +#endif + +void qemu_kvm_aio_wait_start(void); +void qemu_kvm_aio_wait(void); +void qemu_kvm_aio_wait_end(void); + +void qemu_kvm_notify_work(void); + +#ifndef QEMU_KVM_NO_CPU +void kvm_tpr_opt_setup(void); +void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write); +void kvm_tpr_vcpu_start(CPUState *env); +#endif + +int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf); +int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); +int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); + +int kvm_arch_init_irq_routing(void); + +int kvm_mmio_read(void *opaque, uint64_t addr, uint8_t * data, int len); +int kvm_mmio_write(void *opaque, uint64_t addr, uint8_t * data, int len); + +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +struct ioperm_data; + +void kvm_ioperm(CPUState *env, void *data); +void kvm_add_ioperm_data(struct ioperm_data *data); +void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num); +void kvm_arch_do_ioperm(void *_data); +#endif + +#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) +#ifndef QEMU_KVM_NO_CPU +#define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8) +#endif + +#ifdef CONFIG_KVM +#include "qemu-queue.h" + +extern int kvm_allowed; +extern int kvm_irqchip; +extern int kvm_pit; +extern int kvm_pit_reinject; +extern int kvm_nested; +extern kvm_context_t kvm_context; + +struct ioperm_data { + unsigned long start_port; + unsigned long num; + int turn_on; + QLIST_ENTRY(ioperm_data) entries; +}; + +void qemu_kvm_cpu_stop(CPUState *env); +int kvm_arch_halt(CPUState *env); +int handle_tpr_access(void *opaque, CPUState *env, uint64_t rip, + int is_write); +int kvm_has_sync_mmu(void); + +#define kvm_enabled() (kvm_allowed) +#define qemu_kvm_pit_in_kernel() kvm_pit_in_kernel(kvm_context) +#define qemu_kvm_has_gsi_routing() kvm_has_gsi_routing(kvm_context) +#ifdef TARGET_I386 +#define qemu_kvm_has_pit_state2() kvm_has_pit_state2(kvm_context) +#endif +void kvm_init_vcpu(CPUState *env); +void kvm_load_tsc(CPUState *env); +#else +#define kvm_has_sync_mmu() (0) +#define kvm_enabled() (0) +#define kvm_nested 0 +#define qemu_kvm_pit_in_kernel() (0) +#define qemu_kvm_has_gsi_routing() (0) +#ifndef QEMU_KVM_NO_CPU +#ifdef TARGET_I386 +#define qemu_kvm_has_pit_state2() (0) +#endif +#define kvm_load_registers(env) do {} while(0) +#define kvm_save_registers(env) do {} while(0) +#define kvm_save_mpstate(env) do {} while(0) +#define qemu_kvm_cpu_stop(env) do {} while(0) +static inline void kvm_init_vcpu(CPUState *env) +{ +} + +static inline void kvm_load_tsc(CPUState *env) +{ +} +#endif +#endif + +void kvm_mutex_unlock(void); +void kvm_mutex_lock(void); + +int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, + target_phys_addr_t end_addr); + +int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len); +int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len); + + +static inline int kvm_sync_vcpus(void) +{ + return 0; +} + +#ifndef QEMU_KVM_NO_CPU +void kvm_arch_get_registers(CPUState *env); + +static inline void kvm_arch_put_registers(CPUState *env) +{ + kvm_load_registers(env); +} + +void kvm_cpu_synchronize_state(CPUState *env); + +static inline void cpu_synchronize_state(CPUState *env) +{ + if (kvm_enabled()) { + kvm_cpu_synchronize_state(env); + } +} + +uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, + int reg); + + +#endif + +static inline int kvm_set_migration_log(int enable) +{ + return kvm_physical_memory_set_dirty_tracking(enable); +} + + +int kvm_irqchip_in_kernel(void); +#ifdef CONFIG_KVM + +typedef struct KVMSlot { + target_phys_addr_t start_addr; + ram_addr_t memory_size; + ram_addr_t phys_offset; + int slot; + int flags; +} KVMSlot; + +typedef struct kvm_dirty_log KVMDirtyLog; + +typedef struct KVMState { + KVMSlot slots[32]; + int fd; + int vmfd; + int coalesced_mmio; + int broken_set_mem_region; + int migration_log; + int vcpu_events; +#ifdef KVM_CAP_SET_GUEST_DEBUG + QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; +#endif + int irqchip_in_kernel; + + struct kvm_context kvm_context; +} KVMState; + +extern KVMState *kvm_state; + +int kvm_ioctl(KVMState *s, int type, ...); +int kvm_vm_ioctl(KVMState *s, int type, ...); +int kvm_vcpu_ioctl(CPUState *env, int type, ...); +int kvm_check_extension(KVMState *s, unsigned int ext); + +int kvm_tpr_enable_vapic(CPUState *env); + +#endif + +#endif diff --git a/qemu-lock.h b/qemu-lock.h index 9a3e6acce..bc8f91a18 100644 --- a/qemu-lock.h +++ b/qemu-lock.h @@ -184,11 +184,11 @@ static inline int testandset (spinlock_t *p) #elif defined(__ia64) -#include <ia64intrin.h> +#include "ia64intrin.h" static inline int testandset (int *p) { - return __sync_lock_test_and_set (p, 1); + return (int)cmpxchg_acq(p,0,1); } #elif defined(__mips__) static inline int testandset (int *p) diff --git a/qemu-monitor.hx b/qemu-monitor.hx index 1aa78186b..9e3ea3cfb 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -807,7 +807,7 @@ ETEXI { .name = "pci_add", .args_type = "pci_addr:s,type:s,opts:s?", - .params = "auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", + .params = "auto|[[<domain>:]<bus>:]<slot> nic|storage|host [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]... [host=02:00.0[,name=string][,dma=none]", .help = "hot-add PCI device", .user_print = pci_device_hot_add_print, .mhandler.cmd_new = pci_device_hot_add, @@ -1062,6 +1062,19 @@ STEXI Set the encrypted device @var{device} password to @var{password} ETEXI + { + .name = "cpu_set", + .args_type = "cpu:i,state:s", + .params = "cpu [online|offline]", + .help = "change cpu state", + .mhandler.cmd = do_cpu_set_nr, + }, + +STEXI +@item cpu_set @var{cpu} [online|offline] +Set CPU @var{cpu} online or offline. +ETEXI + STEXI @end table ETEXI diff --git a/qemu-options.hx b/qemu-options.hx index c22b99996..e2fbebd9b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -104,6 +104,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" " [,cache=writethrough|writeback|none][,format=f][,serial=s]\n" " [,addr=A][,id=name][,aio=threads|native]\n" + " [,boot=on|off]\n" " use 'file' as a drive image\n") DEF("set", HAS_ARG, QEMU_OPTION_set, "-set group.id.arg=value\n" @@ -1950,6 +1951,37 @@ STEXI Write device configuration to @var{file}. ETEXI +DEF("no-kvm", 0, QEMU_OPTION_no_kvm, + "-no-kvm disable KVM hardware virtualization\n") +DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, + "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n") +DEF("no-kvm-pit", 0, QEMU_OPTION_no_kvm_pit, + "-no-kvm-pit disable KVM kernel mode PIT\n") +DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection, + "-no-kvm-pit-reinjection disable KVM kernel mode PIT interrupt reinjection\n") +#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__) +DEF("pcidevice", HAS_ARG, QEMU_OPTION_pcidevice, + "-pcidevice host=bus:dev.func[,dma=none][,name=string]\n" + " expose a PCI device to the guest OS.\n" + " dma=none: don't perform any dma translations (default is to use an iommu)\n" + " 'string' is used in log output.\n") +#endif +DEF("enable-nesting", 0, QEMU_OPTION_enable_nesting, + "-enable-nesting enable support for running a VM inside the VM (AMD only)\n") +DEF("nvram", HAS_ARG, QEMU_OPTION_nvram, + "-nvram FILE provide ia64 nvram contents\n") +DEF("tdf", 0, QEMU_OPTION_tdf, + "-tdf enable guest time drift compensation\n") +DEF("kvm-shadow-memory", HAS_ARG, QEMU_OPTION_kvm_shadow_memory, + "-kvm-shadow-memory MEGABYTES\n" + " allocate MEGABYTES for kvm mmu shadowing\n") +DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, + "-mem-path FILE provide backing storage for guest RAM\n") +#ifdef MAP_POPULATE +DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, + "-mem-prealloc preallocate guest memory (use with -mempath)\n") +#endif + HXCOMM This is the last statement. Insert new options before this line! STEXI @end table diff --git a/qemu/pc-bios/extboot.bin b/qemu/pc-bios/extboot.bin Binary files differnew file mode 100644 index 000000000..e1ffe965d --- /dev/null +++ b/qemu/pc-bios/extboot.bin @@ -872,6 +872,29 @@ const VMStateInfo vmstate_info_uint64 = { .put = put_uint64, }; +/* 64 bit linux kernel unsigned int */ + +#ifdef __linux__ +static int get_u64(QEMUFile *f, void *pv, size_t size) +{ + uint64_t *v = pv; + qemu_get_be64s(f, v); + return 0; +} + +static void put_u64(QEMUFile *f, void *pv, size_t size) +{ + uint64_t *v = pv; + qemu_put_be64s(f, v); +} + +const VMStateInfo vmstate_info_u64 = { + .name = "__u64", + .get = get_u64, + .put = put_u64, +}; +#endif /* __linux__ */ + /* 8 bit int. See that the received value is the same than the one in the field */ @@ -47,6 +47,7 @@ void cpu_disable_ticks(void); void qemu_system_reset_request(void); void qemu_system_shutdown_request(void); void qemu_system_powerdown_request(void); +int qemu_no_shutdown(void); int qemu_shutdown_requested(void); int qemu_reset_requested(void); int qemu_powerdown_requested(void); @@ -141,6 +142,7 @@ extern int semihosting_enabled; extern int old_param; extern int boot_menu; extern QEMUClock *rtc_clock; +extern long hpagesize; #define MAX_NODES 64 extern int nb_numa_nodes; @@ -192,6 +194,7 @@ typedef struct DriveInfo { extern QTAILQ_HEAD(drivelist, DriveInfo) drives; extern QTAILQ_HEAD(driveoptlist, DriveOpt) driveopts; +extern DriveInfo *extboot_drive; extern DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); extern DriveInfo *drive_get_by_id(const char *id); @@ -207,6 +210,9 @@ BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type); extern QemuOpts *drive_add(const char *file, const char *fmt, ...); extern DriveInfo *drive_init(QemuOpts *arg, void *machine, int *fatal_error); +/* acpi */ +void qemu_system_cpu_hot_add(int cpu, int state); + /* device-hotplug */ DriveInfo *add_init_drive(const char *opts); diff --git a/target-i386/cpu.h b/target-i386/cpu.h index f3834b307..0df6f1d6c 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -250,16 +250,32 @@ #define PG_ERROR_RSVD_MASK 0x08 #define PG_ERROR_I_D_MASK 0x10 -#define MCG_CTL_P (1UL<<8) /* MCG_CAP register available */ +#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */ +#define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */ -#define MCE_CAP_DEF MCG_CTL_P +#define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P) #define MCE_BANKS_DEF 10 +#define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ +#define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */ #define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */ #define MCI_STATUS_VAL (1ULL<<63) /* valid error */ #define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */ #define MCI_STATUS_UC (1ULL<<61) /* uncorrected error */ +#define MCI_STATUS_EN (1ULL<<60) /* error enabled */ +#define MCI_STATUS_MISCV (1ULL<<59) /* misc error reg. valid */ +#define MCI_STATUS_ADDRV (1ULL<<58) /* addr reg. valid */ +#define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */ +#define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */ +#define MCI_STATUS_AR (1ULL<<55) /* Action required */ + +/* MISC register defines */ +#define MCM_ADDR_SEGOFF 0 /* segment offset */ +#define MCM_ADDR_LINEAR 1 /* linear address */ +#define MCM_ADDR_PHYS 2 /* physical address */ +#define MCM_ADDR_MEM 3 /* memory address */ +#define MCM_ADDR_GENERIC 7 /* generic */ #define MSR_IA32_TSC 0x10 #define MSR_IA32_APICBASE 0x1b @@ -717,6 +733,8 @@ typedef struct CPUX86State { uint16_t fpus_vmstate; uint16_t fptag_vmstate; uint16_t fpregs_format_vmstate; + + int update_vapic; } CPUX86State; CPUX86State *cpu_x86_init(const char *cpu_model); @@ -877,7 +895,7 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define cpu_signal_handler cpu_x86_signal_handler #define cpu_list x86_cpu_list -#define CPU_SAVE_VERSION 11 +#define CPU_SAVE_VERSION 12 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff --git a/target-i386/fake-exec.c b/target-i386/fake-exec.c new file mode 100644 index 000000000..dfa202d6a --- /dev/null +++ b/target-i386/fake-exec.c @@ -0,0 +1,50 @@ +/* + * fake-exec.c + * + * This is a file for stub functions so that compilation is possible + * when TCG CPU emulation is disabled during compilation. + * + * Copyright 2007 IBM Corporation. + * Added by & Authors: + * Jerone Young <jyoung5@us.ibm.com> + * This work is licensed under the GNU GPL licence version 2 or later. + * + */ +#include "exec.h" +#include "cpu.h" + +int code_copy_enabled = 0; + +CCTable cc_table[CC_OP_NB]; + +void cpu_dump_statistics (CPUState *env, FILE*f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ +} + +unsigned long code_gen_max_block_size(void) +{ + return 32; +} + +void cpu_gen_init(void) +{ +} + +int cpu_restore_state(TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc) + +{ + return 0; +} + +int cpu_x86_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) +{ + return 0; +} + +void optimize_flags_init(void) +{ +} diff --git a/target-i386/helper.c b/target-i386/helper.c index 9d7fec3c7..fb22f88d8 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -28,6 +28,8 @@ #include "qemu-common.h" #include "kvm.h" +#include "qemu-kvm.h" + //#define DEBUG_MMU /* feature flags taken from "Intel Processor Identification and the CPUID @@ -42,7 +44,7 @@ static const char *feature_name[] = { static const char *ext_feature_name[] = { "pni" /* Intel,AMD sse3 */, NULL, NULL, "monitor", "ds_cpl", "vmx", NULL /* Linux smx */, "est", "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL, - NULL, NULL, "dca", NULL, NULL, NULL, NULL, "popcnt", + NULL, NULL, "dca", NULL, NULL, "x2apic", NULL, "popcnt", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "hypervisor", }; static const char *ext2_feature_name[] = { @@ -516,7 +518,7 @@ static int cpu_x86_register (CPUX86State *env, const char *cpu_model) env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2; env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3; } - env->cpuid_vendor_override = def->vendor_override; + env->cpuid_vendor_override = def->vendor_override || kvm_enabled(); env->cpuid_level = def->level; if (def->family > 0x0f) env->cpuid_version = 0xf00 | ((def->family - 0x0f) << 20); @@ -1548,6 +1550,11 @@ void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, unsigned bank_num = mcg_cap & 0xff; uint64_t *banks = cenv->mce_banks; + if (kvm_enabled()) { + kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, 0); + return; + } + if (bank >= bank_num || !(status & MCI_STATUS_VAL)) return; @@ -1611,7 +1618,7 @@ static void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { -#if defined(CONFIG_KVM) +#if defined(CONFIG_KVM) || defined(USE_KVM) uint32_t vec[4]; #ifdef __x86_64__ @@ -1787,8 +1794,32 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } if (kvm_enabled()) { - /* Nested SVM not yet supported in upstream QEMU */ - *ecx &= ~CPUID_EXT3_SVM; + uint32_t h_eax, h_edx; + + host_cpuid(index, 0, &h_eax, NULL, NULL, &h_edx); + + /* disable CPU features that the host does not support */ + + /* long mode */ + if ((h_edx & 0x20000000) == 0 /* || !lm_capable_kernel */) + *edx &= ~0x20000000; + /* syscall */ + if ((h_edx & 0x00000800) == 0) + *edx &= ~0x00000800; + /* nx */ + if ((h_edx & 0x00100000) == 0) + *edx &= ~0x00100000; + + /* disable CPU features that KVM cannot support */ + + /* svm */ + if (!kvm_nested) + *ecx &= ~CPUID_EXT3_SVM; + /* 3dnow */ + *edx &= ~0xc0000000; + } else { + /* AMD 3DNow! is not supported in QEMU */ + *edx &= ~(CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT); } break; case 0x80000002: @@ -1903,8 +1934,6 @@ CPUX86State *cpu_x86_init(const char *cpu_model) } mce_init(env); - qemu_init_vcpu(env); - return env; } diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 408450361..0376cc58c 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -25,6 +25,7 @@ #include "gdbstub.h" #include "host-utils.h" +#ifdef KVM_UPSTREAM //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -225,6 +226,7 @@ int kvm_arch_init_vcpu(CPUState *env) return kvm_vcpu_ioctl(env, KVM_SET_CPUID2, &cpuid_data); } +#endif void kvm_arch_reset_vcpu(CPUState *env) { env->exception_injected = -1; @@ -232,6 +234,7 @@ void kvm_arch_reset_vcpu(CPUState *env) env->nmi_injected = 0; env->nmi_pending = 0; } +#ifdef KVM_UPSTREAM static int kvm_has_msr_star(CPUState *env) { @@ -491,6 +494,7 @@ static int kvm_put_msrs(CPUState *env) if (kvm_has_msr_star(env)) kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star); kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); + kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); #ifdef TARGET_X86_64 /* FIXME if lm capable */ kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); @@ -634,6 +638,7 @@ static int kvm_get_msrs(CPUState *env) if (kvm_has_msr_star(env)) msrs[n++].index = MSR_STAR; msrs[n++].index = MSR_IA32_TSC; + msrs[n++].index = MSR_VM_HSAVE_PA; #ifdef TARGET_X86_64 /* FIXME lm_capable_kernel */ msrs[n++].index = MSR_CSTAR; @@ -686,6 +691,9 @@ static int kvm_get_msrs(CPUState *env) case MSR_KVM_WALL_CLOCK: env->wall_clock_msr = msrs[i].data; break; + case MSR_VM_HSAVE_PA: + env->vm_hsave = msrs[i].data; + break; } } @@ -711,8 +719,9 @@ static int kvm_get_mp_state(CPUState *env) env->mp_state = mp_state.mp_state; return 0; } +#endif -static int kvm_put_vcpu_events(CPUState *env) +int kvm_put_vcpu_events(CPUState *env) { #ifdef KVM_CAP_VCPU_EVENTS struct kvm_vcpu_events events; @@ -736,13 +745,16 @@ static int kvm_put_vcpu_events(CPUState *env) events.sipi_vector = env->sipi_vector; + events.flags = + KVM_VCPUEVENT_VALID_NMI_PENDING | KVM_VCPUEVENT_VALID_SIPI_VECTOR; + return kvm_vcpu_ioctl(env, KVM_SET_VCPU_EVENTS, &events); #else return 0; #endif } -static int kvm_get_vcpu_events(CPUState *env) +int kvm_get_vcpu_events(CPUState *env) { #ifdef KVM_CAP_VCPU_EVENTS struct kvm_vcpu_events events; @@ -779,6 +791,7 @@ static int kvm_get_vcpu_events(CPUState *env) return 0; } +#ifdef KVM_UPSTREAM int kvm_arch_put_registers(CPUState *env) { int ret; @@ -874,6 +887,7 @@ int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) return 0; } +#endif int kvm_arch_post_run(CPUState *env, struct kvm_run *run) { @@ -888,6 +902,7 @@ int kvm_arch_post_run(CPUState *env, struct kvm_run *run) return 0; } +#ifdef KVM_UPSTREAM static int kvm_handle_halt(CPUState *env) { if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && @@ -1085,3 +1100,6 @@ void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) } } #endif /* KVM_CAP_SET_GUEST_DEBUG */ +#endif + +#include "qemu-kvm-x86.c" diff --git a/target-i386/libkvm.h b/target-i386/libkvm.h new file mode 100644 index 000000000..d85b6a1e0 --- /dev/null +++ b/target-i386/libkvm.h @@ -0,0 +1,28 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for x86. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * derived from libkvm.c + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_X86_H +#define KVM_X86_H + +#define PAGE_SIZE 4096ul +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr); + +#define smp_wmb() asm volatile("" ::: "memory") + +#endif diff --git a/target-i386/machine.c b/target-i386/machine.c index 87704918b..0b8a33afd 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -5,6 +5,7 @@ #include "exec-all.h" #include "kvm.h" +#include "qemu-kvm.h" static const VMStateDescription vmstate_segment = { .name = "segment", @@ -322,6 +323,10 @@ static void cpu_pre_save(void *opaque) int i; cpu_synchronize_state(env); + if (kvm_enabled()) { + kvm_save_mpstate(env); + kvm_get_vcpu_events(env); + } /* FPU */ env->fpus_vmstate = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; @@ -364,6 +369,17 @@ static int cpu_post_load(void *opaque, int version_id) hw_breakpoint_insert(env, i); tlb_flush(env, 1); + + if (kvm_enabled()) { + /* when in-kernel irqchip is used, env->halted causes deadlock + because no userspace IRQs will ever clear this flag */ + env->halted = 0; + + kvm_load_tsc(env); + kvm_load_mpstate(env); + kvm_put_vcpu_events(env); + } + return 0; } diff --git a/target-ia64/cpu.h b/target-ia64/cpu.h new file mode 100644 index 000000000..fb51463d1 --- /dev/null +++ b/target-ia64/cpu.h @@ -0,0 +1,85 @@ +/* + * IA64 virtual CPU header + * + * Copyright (c) 2003 Fabrice Bellard + * + * Copyright (c) 2007 Intel Corporation + * Zhang xiantao <xiantao.zhang@intel.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CPU_IA64_H +#define CPU_IA64_H +#include "config.h" +#include "ia64intrin.h" + +#include<string.h> + +#define TARGET_LONG_BITS 64 + +#define TARGET_PAGE_BITS 16 + +#define ELF_MACHINE EM_IA_64 + +#define NB_MMU_MODES 2 +#define CPU_PAL_HALT 1 +#define HF_HALTED_MASK (1 << CPU_PAL_HALT) + +#include "cpu-defs.h" + +#include "softfloat.h" + +#define CPUState struct CPUIA64State + +typedef struct CPUIA64State { + CPU_COMMON; + uint32_t hflags; + int mp_state; +} CPUIA64State; + +#define cpu_gen_code cpu_ia64_gen_code +#define cpu_init cpu_ia64_init +#define cpu_signal_handler cpu_ia64_signal_handler + +extern struct CPUIA64State *env; +int cpu_get_pic_interrupt(CPUIA64State *s); +int cpu_exec(CPUState *env1); +CPUState *cpu_ia64_init(const char * cpu_model); + +static inline int cpu_mmu_index (CPUState *env) +{ + return 0; +} + +#define CPU_PC_FROM_TB(env, tb) do{}while(0) + +#include "cpu-all.h" + +/* + * These ones really should go to the appropriate tcg header file, if/when + * tcg support is added for ia64. + */ +void tcg_dump_info(FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); + +static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = 0; + *cs_base = 0; + *flags = 0; +} + +#endif diff --git a/target-ia64/exec.h b/target-ia64/exec.h new file mode 100644 index 000000000..060d9c35b --- /dev/null +++ b/target-ia64/exec.h @@ -0,0 +1,61 @@ +/* + * IA64 execution defines + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2007 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __IA64_H__ +#define __IA64_H__ + +//#include "dyngen-exec.h" +#include "config.h" + +#include "dyngen-exec.h" + +#include "cpu.h" +#include "exec-all.h" + +#define tcg_qemu_tb_exec(tb_ptr) 0 + +register struct CPUIA64State *env asm(AREG0); + +static inline void env_to_regs(void) +{ +} + +static inline void regs_to_env(void) +{ +} + +void do_interrupt (CPUState *env); + +void cpu_lock(void); +void cpu_unlock(void); + +static inline int cpu_halted(CPUState *env) { + /* handle exit of HALTED state */ + if (!(env->hflags & HF_HALTED_MASK)) + return 0; + return EXCP_HALTED; +} + +static inline int cpu_has_work(CPUState *env) +{ + return (env->interrupt_request & (CPU_INTERRUPT_HARD)); +} + +#endif diff --git a/target-ia64/fake-exec.c b/target-ia64/fake-exec.c new file mode 100644 index 000000000..8d6ded0a7 --- /dev/null +++ b/target-ia64/fake-exec.c @@ -0,0 +1,50 @@ +/* + * fake-exec.c for ia64. + * + * This is a file for stub functions so that compilation is possible + * when TCG CPU emulation is disabled during compilation. + * + * Copyright 2007 IBM Corporation. + * Added by & Authors: + * Jerone Young <jyoung5@us.ibm.com> + * + * Copyright 2008 Intel Corporation. + * Added by Xiantao Zhang <xiantao.zhang@intel.com> + * + * This work is licensed under the GNU GPL licence version 2 or later. + * + */ +#include <stdio.h> + +#include "cpu.h" +#include "exec-all.h" + +int code_copy_enabled = 0; + +void cpu_gen_init(void) +{ +} + +unsigned long code_gen_max_block_size(void) +{ + return 32; +} + +int cpu_ia64_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) +{ + return 0; +} + +void tcg_dump_info(FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +{ + return; +} + +int cpu_restore_state(TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc) + +{ + return 0; +} diff --git a/target-ia64/firmware.c b/target-ia64/firmware.c new file mode 100644 index 000000000..79f846421 --- /dev/null +++ b/target-ia64/firmware.c @@ -0,0 +1,715 @@ +/* + * firmware.c : Firmware build logic for ia64 platform. + * + * Ported from Xen 3.0 Source. + * Copyright (c) 2007, Intel Corporation. + * Zhang Xiantao <xiantao.zhang@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <zlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "cpu.h" + +#include "firmware.h" + +#include "qemu-common.h" + +typedef struct { + unsigned long signature; + unsigned int type; + unsigned int length; +} HOB_GENERIC_HEADER; + +/* + * INFO HOB is the first data data in one HOB list + * it contains the control information of the HOB list + */ +typedef struct { + HOB_GENERIC_HEADER header; + unsigned long length; // current length of hob + unsigned long cur_pos; // current poisiton of hob + unsigned long buf_size; // size of hob buffer +} HOB_INFO; + +typedef struct{ + unsigned long start; + unsigned long size; +} hob_mem_t; + +typedef enum { + HOB_TYPE_INFO=0, + HOB_TYPE_TERMINAL, + HOB_TYPE_MEM, + HOB_TYPE_PAL_BUS_GET_FEATURES_DATA, + HOB_TYPE_PAL_CACHE_SUMMARY, + HOB_TYPE_PAL_MEM_ATTRIB, + HOB_TYPE_PAL_CACHE_INFO, + HOB_TYPE_PAL_CACHE_PROT_INFO, + HOB_TYPE_PAL_DEBUG_INFO, + HOB_TYPE_PAL_FIXED_ADDR, + HOB_TYPE_PAL_FREQ_BASE, + HOB_TYPE_PAL_FREQ_RATIOS, + HOB_TYPE_PAL_HALT_INFO, + HOB_TYPE_PAL_PERF_MON_INFO, + HOB_TYPE_PAL_PROC_GET_FEATURES, + HOB_TYPE_PAL_PTCE_INFO, + HOB_TYPE_PAL_REGISTER_INFO, + HOB_TYPE_PAL_RSE_INFO, + HOB_TYPE_PAL_TEST_INFO, + HOB_TYPE_PAL_VM_SUMMARY, + HOB_TYPE_PAL_VM_INFO, + HOB_TYPE_PAL_VM_PAGE_SIZE, + HOB_TYPE_NR_VCPU, + HOB_TYPE_NR_NVRAM, + HOB_TYPE_MAX +} hob_type_t; + +static int hob_init(void *buffer ,unsigned long buf_size); +static int add_pal_hob(void* hob_buf); +static int add_mem_hob(void* hob_buf, unsigned long dom_mem_size); +static int add_vcpus_hob(void* hob_buf, unsigned long nr_vcpu); +static int add_nvram_hob(void *hob_buf, unsigned long nvram_addr); +static int build_hob(void *hob_buf, unsigned long hob_buf_size, + unsigned long dom_mem_size, unsigned long vcpus, + unsigned long nvram_addr); +static int load_hob(void *hob_buf, unsigned long dom_mem_size); + +int +kvm_ia64_build_hob(unsigned long memsize, unsigned long vcpus, + unsigned long nvram_addr) +{ + char *hob_buf; + + hob_buf = malloc(GFW_HOB_SIZE); + if (hob_buf == NULL) { + Hob_Output("Hob: Could not allocate hob"); + return -1; + } + + if (build_hob(hob_buf, GFW_HOB_SIZE, memsize, vcpus, nvram_addr) < 0) { + free(hob_buf); + Hob_Output("Could not build hob"); + return -1; + } + + if (load_hob(hob_buf, memsize) < 0) { + free(hob_buf); + Hob_Output("Could not load hob"); + return -1; + } + free(hob_buf); + + return 0; +} + +static int +hob_init(void *buffer, unsigned long buf_size) +{ + HOB_INFO *phit; + HOB_GENERIC_HEADER *terminal; + + if (sizeof(HOB_INFO) + sizeof(HOB_GENERIC_HEADER) > buf_size) { + // buffer too small + return -1; + } + + phit = (HOB_INFO*)buffer; + phit->header.signature = HOB_SIGNATURE; + phit->header.type = HOB_TYPE_INFO; + phit->header.length = sizeof(HOB_INFO); + phit->length = sizeof(HOB_INFO) + sizeof(HOB_GENERIC_HEADER); + phit->cur_pos = 0; + phit->buf_size = buf_size; + + terminal = (HOB_GENERIC_HEADER*)(buffer + sizeof(HOB_INFO)); + terminal->signature = HOB_SIGNATURE; + terminal->type = HOB_TYPE_TERMINAL; + terminal->length = sizeof(HOB_GENERIC_HEADER); + + return 0; +} + +/* + * Add a new HOB to the HOB List. + * + * hob_start - start address of hob buffer + * type - type of the hob to be added + * data - data of the hob to be added + * data_size - size of the data + */ +static int +hob_add(void* hob_start, int type, void* data, int data_size) +{ + HOB_INFO *phit; + HOB_GENERIC_HEADER *newhob, *tail; + + phit = (HOB_INFO*)hob_start; + + if (phit->length + data_size > phit->buf_size) { + // no space for new hob + return -1; + } + + //append new HOB + newhob = (HOB_GENERIC_HEADER*)(hob_start + phit->length - + sizeof(HOB_GENERIC_HEADER)); + newhob->signature = HOB_SIGNATURE; + newhob->type = type; + newhob->length = data_size + sizeof(HOB_GENERIC_HEADER); + memcpy((void*)newhob + sizeof(HOB_GENERIC_HEADER), data, data_size); + + // append terminal HOB + tail = (HOB_GENERIC_HEADER*)(hob_start + phit->length + data_size); + tail->signature = HOB_SIGNATURE; + tail->type = HOB_TYPE_TERMINAL; + tail->length = sizeof(HOB_GENERIC_HEADER); + + // adjust HOB list length + phit->length += sizeof(HOB_GENERIC_HEADER) + data_size; + + return 0; +} + +static int +get_hob_size(void* hob_buf) +{ + HOB_INFO *phit = (HOB_INFO*)hob_buf; + + if (phit->header.signature != HOB_SIGNATURE) { + Hob_Output("xc_get_hob_size:Incorrect signature"); + return -1; + } + return phit->length; +} + +static int +add_max_hob_entry(void* hob_buf) +{ + long max_hob = 0; + return hob_add(hob_buf, HOB_TYPE_MAX, &max_hob, sizeof(long)); +} + +static int +build_hob(void* hob_buf, unsigned long hob_buf_size, + unsigned long dom_mem_size, unsigned long vcpus, + unsigned long nvram_addr) +{ + //Init HOB List + if (hob_init(hob_buf, hob_buf_size) < 0) { + Hob_Output("buffer too small"); + goto err_out; + } + + if (add_mem_hob(hob_buf,dom_mem_size) < 0) { + Hob_Output("Add memory hob failed, buffer too small"); + goto err_out; + } + + if (add_vcpus_hob(hob_buf, vcpus) < 0) { + Hob_Output("Add NR_VCPU hob failed, buffer too small"); + goto err_out; + } + + if (add_pal_hob(hob_buf) < 0) { + Hob_Output("Add PAL hob failed, buffer too small"); + goto err_out; + } + + if (add_nvram_hob(hob_buf, nvram_addr) < 0) { + Hob_Output("Add nvram hob failed, buffer too small"); + goto err_out; + } + + if (add_max_hob_entry(hob_buf) < 0) { + Hob_Output("Add max hob entry failed, buffer too small"); + goto err_out; + } + return 0; + +err_out: + return -1; +} +static int +load_hob(void *hob_buf, unsigned long dom_mem_size) +{ + int hob_size; + + hob_size = get_hob_size(hob_buf); + if (hob_size < 0) { + Hob_Output("Invalid hob data"); + return -1; + } + + if (hob_size > GFW_HOB_SIZE) { + Hob_Output("No enough memory for hob data"); + return -1; + } + + cpu_physical_memory_write(GFW_HOB_START, hob_buf, hob_size); + + return 0; +} + +static int +add_mem_hob(void* hob_buf, unsigned long dom_mem_size) +{ + hob_mem_t memhob; + + // less than 3G + memhob.start = 0; + memhob.size = MIN(dom_mem_size, 0xC0000000); + + if (hob_add(hob_buf, HOB_TYPE_MEM, &memhob, sizeof(memhob)) < 0) + return -1; + + if (dom_mem_size > 0xC0000000) { + // 4G ~ 4G+remain + memhob.start = 0x100000000; //4G + memhob.size = dom_mem_size - 0xC0000000; + if (hob_add(hob_buf, HOB_TYPE_MEM, &memhob, sizeof(memhob)) < 0) + return -1; + } + return 0; +} + +static int +add_vcpus_hob(void* hob_buf, unsigned long vcpus) +{ + return hob_add(hob_buf, HOB_TYPE_NR_VCPU, &vcpus, sizeof(vcpus)); +} + +static int +add_nvram_hob(void *hob_buf, unsigned long nvram_addr) +{ + return hob_add(hob_buf, HOB_TYPE_NR_NVRAM, + &nvram_addr, sizeof(nvram_addr)); +} + +static const unsigned char config_pal_bus_get_features_data[24] = { + 0, 0, 0, 32, 0, 0, 240, 189, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_cache_summary[16] = { + 3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_mem_attrib[8] = { + 241, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_cache_info[152] = { + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 4, 6, 7, 255, 1, 0, 1, 0, 64, 0, 0, 12, 12, + 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 7, 0, 1, + 0, 1, 0, 64, 0, 0, 12, 12, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 7, 7, 255, 7, 0, 11, 0, 0, 16, 0, + 12, 17, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 7, + 7, 7, 5, 9, 11, 0, 0, 4, 0, 12, 15, 49, 0, 254, 255, + 255, 255, 255, 255, 255, 255, 2, 8, 7, 7, 7, 5, 9, + 11, 0, 0, 4, 0, 12, 15, 49, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 12, 7, 7, 7, 14, 1, 3, 0, 0, 192, 0, 12, 20, 49, 0 +}; + +static const unsigned char config_pal_cache_prot_info[200] = { + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 45, 0, 16, 8, 0, 76, 12, 64, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 16, 4, 0, 76, 44, 68, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 0, 16, 8, 0, 81, 44, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, + 112, 12, 0, 79, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255, + 32, 0, 112, 12, 0, 79, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 160, + 12, 0, 84, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const unsigned char config_pal_debug_info[16] = { + 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_fixed_addr[8] = { + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_freq_base[8] = { + 109, 219, 182, 13, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_freq_ratios[24] = { + 11, 1, 0, 0, 77, 7, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 4, + 0, 0, 0, 7, 0, 0, 0 +}; + +static const unsigned char config_pal_halt_info[64] = { + 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_perf_mon_info[136] = { + 12, 47, 18, 8, 0, 0, 0, 0, 241, 255, 0, 0, 255, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 0, 0, 223, 0, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_proc_get_features[104] = { + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 64, 6, 64, 49, 0, 0, 0, 0, 64, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 231, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, + 63, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_ptce_info[24] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_register_info[64] = { + 255, 0, 47, 127, 17, 17, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 208, 128, 238, 238, 0, 0, 248, 255, 255, 255, 255, 255, 0, 0, 7, 3, + 251, 3, 0, 0, 0, 0, 255, 7, 3, 0, 0, 0, 0, 0, 248, 252, 4, + 252, 255, 255, 255, 255, 2, 248, 252, 255, 255, 255, 255, 255 +}; + +static const unsigned char config_pal_rse_info[16] = { + 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_test_info[48] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_vm_summary[16] = { + 101, 18, 15, 2, 7, 7, 4, 2, 59, 18, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_vm_info[104] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 32, 32, 0, 0, 0, 0, 0, 0, 112, 85, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 32, 32, 0, 0, 0, 0, 0, 0, 112, 85, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 128, 128, 0, + 4, 0, 0, 0, 0, 112, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 128, 128, 0, 4, 0, 0, 0, 0, 112, 85, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_vm_page_size[16] = { + 0, 112, 85, 21, 0, 0, 0, 0, 0, 112, 85, 21, 0, 0, 0, 0 +}; + +typedef struct{ + hob_type_t type; + void* data; + unsigned long size; +} hob_batch_t; + +static const hob_batch_t hob_batch[]={ + { HOB_TYPE_PAL_BUS_GET_FEATURES_DATA, + &config_pal_bus_get_features_data, + sizeof(config_pal_bus_get_features_data) + }, + { HOB_TYPE_PAL_CACHE_SUMMARY, + &config_pal_cache_summary, + sizeof(config_pal_cache_summary) + }, + { HOB_TYPE_PAL_MEM_ATTRIB, + &config_pal_mem_attrib, + sizeof(config_pal_mem_attrib) + }, + { HOB_TYPE_PAL_CACHE_INFO, + &config_pal_cache_info, + sizeof(config_pal_cache_info) + }, + { HOB_TYPE_PAL_CACHE_PROT_INFO, + &config_pal_cache_prot_info, + sizeof(config_pal_cache_prot_info) + }, + { HOB_TYPE_PAL_DEBUG_INFO, + &config_pal_debug_info, + sizeof(config_pal_debug_info) + }, + { HOB_TYPE_PAL_FIXED_ADDR, + &config_pal_fixed_addr, + sizeof(config_pal_fixed_addr) + }, + { HOB_TYPE_PAL_FREQ_BASE, + &config_pal_freq_base, + sizeof(config_pal_freq_base) + }, + { HOB_TYPE_PAL_FREQ_RATIOS, + &config_pal_freq_ratios, + sizeof(config_pal_freq_ratios) + }, + { HOB_TYPE_PAL_HALT_INFO, + &config_pal_halt_info, + sizeof(config_pal_halt_info) + }, + { HOB_TYPE_PAL_PERF_MON_INFO, + &config_pal_perf_mon_info, + sizeof(config_pal_perf_mon_info) + }, + { HOB_TYPE_PAL_PROC_GET_FEATURES, + &config_pal_proc_get_features, + sizeof(config_pal_proc_get_features) + }, + { HOB_TYPE_PAL_PTCE_INFO, + &config_pal_ptce_info, + sizeof(config_pal_ptce_info) + }, + { HOB_TYPE_PAL_REGISTER_INFO, + &config_pal_register_info, + sizeof(config_pal_register_info) + }, + { HOB_TYPE_PAL_RSE_INFO, + &config_pal_rse_info, + sizeof(config_pal_rse_info) + }, + { HOB_TYPE_PAL_TEST_INFO, + &config_pal_test_info, + sizeof(config_pal_test_info) + }, + { HOB_TYPE_PAL_VM_SUMMARY, + &config_pal_vm_summary, + sizeof(config_pal_vm_summary) + }, + { HOB_TYPE_PAL_VM_INFO, + &config_pal_vm_info, + sizeof(config_pal_vm_info) + }, + { HOB_TYPE_PAL_VM_PAGE_SIZE, + &config_pal_vm_page_size, + sizeof(config_pal_vm_page_size) + }, +}; + +static int +add_pal_hob(void* hob_buf) +{ + int i; + for (i = 0; i < sizeof(hob_batch)/sizeof(hob_batch_t); i++) { + if (hob_add(hob_buf, hob_batch[i].type, hob_batch[i].data, + hob_batch[i].size) < 0) + return -1; + } + return 0; +} + +uint8_t *read_image(const char *filename, unsigned long *size) +{ + int kernel_fd = -1; + gzFile kernel_gfd = NULL; + uint8_t *image = NULL, *tmp; + unsigned int bytes; + + if ((filename == NULL) || (size == NULL)) + return NULL; + + kernel_fd = open(filename, O_RDONLY); + if (kernel_fd < 0) { + Hob_Output("Could not open kernel image\n"); + goto out_1; + } + + if ((kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL) { + Hob_Output("Could not allocate decompression state for state file\n"); + goto out_1; + } + + *size = 0; + +#define CHUNK 1*1024*1024 + while(1) + { + if ((tmp = realloc(image, *size + CHUNK)) == NULL) { + Hob_Output("Could not allocate memory for kernel image"); + free(image); + image = NULL; + goto out; + } + image = tmp; + + bytes = gzread(kernel_gfd, image + *size, CHUNK); + switch (bytes) { + case -1: + Hob_Output("Error reading kernel image"); + free(image); + image = NULL; + goto out; + case 0: /* EOF */ + goto out; + default: + *size += bytes; + break; + } + } +#undef CHUNK + +out: + if (*size == 0) { + Hob_Output("Could not read kernel image"); + free(image); + image = NULL; + } else if (image) { + /* Shrink allocation to fit image. */ + tmp = realloc(image, *size); + if (tmp) + image = tmp; + } + + if (kernel_gfd != NULL) + gzclose(kernel_gfd); + else if (kernel_fd >= 0) + close(kernel_fd); + return image; + +out_1: + return NULL; +} + +int kvm_ia64_nvram_init(unsigned long type) +{ + unsigned long nvram_fd; + char nvram_path[PATH_MAX]; + unsigned long i; + + if (nvram) { + if (strlen(nvram) > PATH_MAX) { + goto out; + } + if (type == READ_FROM_NVRAM) { + if (access(nvram, R_OK | W_OK | X_OK) == -1) + goto out; + nvram_fd = open(nvram, O_RDONLY); + return nvram_fd; + } + else { /* write from gfw to nvram file */ + i = access(nvram, R_OK | W_OK | X_OK); + if ((i == -1) && (errno != ENOENT)) + goto out; + nvram_fd = open(nvram, O_CREAT|O_RDWR, 0777); + return nvram_fd; + } + } + else { + strcpy(nvram_path, "nvram.dat"); + if (type == READ_FROM_NVRAM) { + if (access(nvram_path, R_OK | W_OK | X_OK) == -1) + goto out; + nvram_fd = open(nvram_path, O_RDONLY); + return nvram_fd; + } + else { /* write from gfw to nvram file */ + i = access(nvram_path, R_OK | W_OK | X_OK); + if ((i == -1) && (errno != ENOENT)) + goto out; + nvram_fd = open(nvram_path, O_CREAT|O_RDWR, 0777); + return nvram_fd; + } + } +out: + return -1; +} + +int +kvm_ia64_copy_from_nvram_to_GFW(unsigned long nvram_fd) +{ + struct stat file_stat; + uint8_t *nvram_buf; + int r = 0; + + nvram_buf = malloc(NVRAM_SIZE); + + if ((fstat(nvram_fd, &file_stat) < 0) || + (NVRAM_SIZE != file_stat.st_size) || + (read(nvram_fd, nvram_buf, NVRAM_SIZE) != NVRAM_SIZE)) { + r = -1; + goto out; + } + + cpu_physical_memory_write(NVRAM_START, nvram_buf, NVRAM_SIZE); + + out: + free(nvram_buf); + return r; +} + +int +kvm_ia64_copy_from_GFW_to_nvram() +{ + struct nvram_save_addr nvram_addr_buf; + uint8_t *nvram_buf; + unsigned long nvram_fd; + unsigned long type = WRITE_TO_NVRAM; + int ret = -1; + + nvram_buf = malloc(NVRAM_SIZE); + if (!nvram_buf) + goto out_free; + + cpu_physical_memory_read(NVRAM_START, (uint8_t *)&nvram_addr_buf, + sizeof(struct nvram_save_addr)); + if (nvram_addr_buf.signature != NVRAM_VALID_SIG) { + goto out_free; + } + + cpu_physical_memory_read(nvram_addr_buf.addr, nvram_buf, NVRAM_SIZE); + + nvram_fd = kvm_ia64_nvram_init(type); + if (nvram_fd == -1) + goto out; + + lseek(nvram_fd, 0, SEEK_SET); + if (write(nvram_fd, nvram_buf, NVRAM_SIZE) != NVRAM_SIZE) + goto out; + + ret = 0; + out: + close(nvram_fd); + out_free: + free(nvram_buf); + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/target-ia64/firmware.h b/target-ia64/firmware.h new file mode 100644 index 000000000..a47db671d --- /dev/null +++ b/target-ia64/firmware.h @@ -0,0 +1,62 @@ +/* + * firmwar.h: Firmware build logic head file + * + * Copyright (c) 2007, Intel Corporation. + * Zhang Xiantao <xiantao.zhang@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __FIRM_WARE_H +#define __FIRM_WARE_ +#include "cpu.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <zlib.h> + +#define GFW_SIZE (16UL<<20) +#define GFW_START ((4UL<<30) - GFW_SIZE) + +#define HOB_SIGNATURE 0x3436474953424f48 // "HOBSIG64" +#define GFW_HOB_START ((4UL<<30) - (14UL<<20)) // 4G - 14M +#define GFW_HOB_SIZE (1UL<<20) // 1M +#define HOB_OFFSET (GFW_HOB_START-GFW_START) + +#define Hob_Output(s) fprintf(stderr, s) + +#define NVRAM_START (GFW_START + NVRAM_OFFSET) +#define NVRAM_OFFSET (10 * (1UL << 20)) +#define NVRAM_SIZE (64 * (1UL << 10)) +#define NVRAM_VALID_SIG 0x4650494e45584948 /* "HIXENIPF" */ +#define VALIDATE_NVRAM_FD(x) ((1UL<<(sizeof(x)*8 - 1)) | x) +#define IS_VALID_NVRAM_FD(x) ((uint64_t)x >> (sizeof(x)*8 - 1)) +#define READ_FROM_NVRAM 0 +#define WRITE_TO_NVRAM 1 + +struct nvram_save_addr { + unsigned long addr; + unsigned long signature; +}; + +extern const char *nvram; +extern int kvm_ia64_build_hob(unsigned long memsize, unsigned long vcpus, + unsigned long nvram_addr); +extern uint8_t *read_image(const char *filename, unsigned long *size); + +extern int kvm_ia64_copy_from_GFW_to_nvram(void); +extern int kvm_ia64_nvram_init(unsigned long type); +extern int kvm_ia64_copy_from_nvram_to_GFW(unsigned long nvram_fd); +#endif //__FIRM_WARE_ diff --git a/target-ia64/helper.c b/target-ia64/helper.c new file mode 100644 index 000000000..4a94dcafb --- /dev/null +++ b/target-ia64/helper.c @@ -0,0 +1,5 @@ + +/* + * IA64 emulation helpers for qemu. (Leave it as blank now.) + * + */ diff --git a/target-ia64/libkvm.c b/target-ia64/libkvm.c new file mode 100644 index 000000000..bf9dded8b --- /dev/null +++ b/target-ia64/libkvm.c @@ -0,0 +1,82 @@ +/* + * libkvm-ia64.c :Kernel-based Virtual Machine control library for ia64. + * + * This library provides an API to control the kvm hardware virtualization + * module. + * + * Copyright (C) 2006 Qumranet + * + * Authors: + * + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright (C) 2007 Intel + * Added by : Zhang Xiantao <xiantao.zhang@intel.com> + * + * This work is licensed under the GNU LGPL license, version 2. + * + */ + +#include "libkvm-all.h" +#include "libkvm.h" +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> +#include <unistd.h> +#include <stropts.h> +#include <sys/mman.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + int r; + + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) + return r; + + return 0; +} + +int kvm_arch_run(kvm_vcpu_context_t vcpu) +{ + int r = 0; + + switch (vcpu->run->exit_reason) { + default: + r = 1; + break; + } + + return r; +} + +void kvm_show_code(kvm_vcpu_context_t vcpu) +{ + fprintf(stderr, "kvm_show_code not supported yet!\n"); +} + +void kvm_show_regs(kvm_vcpu_context_t vcpu) +{ + fprintf(stderr,"kvm_show_regs not supportted today!\n"); +} + +int kvm_create_memory_alias(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len, + uint64_t target_phys) +{ + return 0; +} + +int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start) +{ + return 0; +} diff --git a/target-ia64/libkvm.h b/target-ia64/libkvm.h new file mode 100644 index 000000000..417f7f10c --- /dev/null +++ b/target-ia64/libkvm.h @@ -0,0 +1,31 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for x86. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * derived from libkvm.c + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_IA64_H +#define KVM_IA64_H + +#include "libkvm-all.h" + +extern int kvm_page_size; + +#define PAGE_SIZE kvm_page_size +#define PAGE_MASK (~(kvm_page_size - 1)) + +#define ia64_mf() asm volatile ("mf" ::: "memory") +#define smp_wmb() ia64_mf() + +#endif diff --git a/target-ia64/machine.c b/target-ia64/machine.c new file mode 100644 index 000000000..70ef379dd --- /dev/null +++ b/target-ia64/machine.c @@ -0,0 +1,35 @@ +#include "hw/hw.h" +#include "hw/boards.h" + +#include "exec-all.h" +#include "qemu-kvm.h" + +void cpu_save(QEMUFile *f, void *opaque) +{ + CPUState *env = opaque; + + if (kvm_enabled()) { + kvm_save_registers(env); + kvm_arch_save_mpstate(env); + } +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + CPUState *env = opaque; + + if (kvm_enabled()) { + kvm_load_registers(env); + kvm_arch_load_mpstate(env); + } + return 0; +} + +extern QEMUMachine ipf_machine; + +static void ipf_machine_init(void) +{ + qemu_register_machine(&ipf_machine); +} + +machine_init(ipf_machine_init); diff --git a/target-ia64/op.c b/target-ia64/op.c new file mode 100644 index 000000000..f7301c641 --- /dev/null +++ b/target-ia64/op.c @@ -0,0 +1,22 @@ +/* + * IA64 micro operations + * + * Leave it blank for future implementation + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + diff --git a/target-ia64/op_helper.c b/target-ia64/op_helper.c new file mode 100644 index 000000000..d51525ac4 --- /dev/null +++ b/target-ia64/op_helper.c @@ -0,0 +1,104 @@ +/* + * op_helper.c: IA64 emulation cpu micro-operations helpers for qemu. + * + * Copyright (c) 2007 Intel Corporation + * Zhang Xiantao <xiantao.zhang@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cpu.h" +#include "exec-all.h" + +#include "qemu-kvm.h" +#include "qemu-common.h" + +void cpu_ia64_set_model(CPUIA64State *env, uint32_t id); +void cpu_ia64_close(CPUIA64State *env); +void switch_mode(CPUState *env, int mode); +void do_interrupt(CPUIA64State *env); +int cpu_ia64_handle_mmu_fault (CPUState *env, target_ulong address, + int access_type, int is_user, int is_softmmu); +CPUState *cpu_ia64_init(const char *cpu_model) +{ + CPUState *env; + env = qemu_mallocz(sizeof(CPUState)); + if (!env) + return NULL; + cpu_exec_init(env); + cpu_reset(env); + if (kvm_enabled()) { + kvm_qemu_init_env(env); + kvm_init_vcpu(env); + } + return env; +} + +void cpu_reset(CPUIA64State *env) +{ +} + +static inline void set_feature(CPUIA64State *env, int feature) +{ +} + +void cpu_ia64_set_model(CPUIA64State *env, uint32_t id) +{ +} + +void cpu_ia64_close(CPUIA64State *env) +{ + free(env); +} + +extern int semihosting_enabled; + +void switch_mode(CPUState *env, int mode) +{ +} + +/* Handle a CPU exception. */ +void do_interrupt(CPUIA64State *env) +{ + if (kvm_enabled()) { + printf("%s: unexpect\n", __FUNCTION__); + exit(-1); + } +} + +int cpu_ia64_handle_mmu_fault (CPUState *env, target_ulong address, + int access_type, int is_user, int is_softmmu) +{ + return 1; +} + +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return -1; +} + +void cpu_dump_state(CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ + return; +} + +void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) +{ + return; +} diff --git a/target-ia64/translate.c b/target-ia64/translate.c new file mode 100644 index 000000000..86f48f50c --- /dev/null +++ b/target-ia64/translate.c @@ -0,0 +1,39 @@ +/* + * translation.c : IA64 translation code. + * Just put it as blank now, and implement it later. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +static uint16_t *gen_opc_ptr; + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" +#include "gen-op.h" + +int gen_intermediate_code(CPUState *env, TranslationBlock *tb) +{ + return 0; +} +int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) +{ + return 0; +} diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 2535cbc0c..339468e95 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -36,9 +36,10 @@ #if defined(TARGET_PPCEMB) /* Specific definitions for PowerPC embedded */ /* BookE have 36 bits physical address space */ -#if defined(CONFIG_USER_ONLY) +#if defined(CONFIG_USER_ONLY) || defined(USE_KVM) /* It looks like a lot of Linux programs assume page size * is 4kB long. This is evil, but we have to deal with it... + * Also kvm for embedded powerpc needs (atm) 4kB aligned pages */ #define TARGET_PAGE_BITS 12 #else /* defined(CONFIG_USER_ONLY) */ @@ -1602,4 +1603,11 @@ static inline void cpu_set_tls(CPUState *env, target_ulong newtls) #endif } +/* hidden flags (hflags) - used internally by qemu to represent additional + * cpu states. + */ +#define HF_HALTED_SHIFT 1 + +#define HF_HALTED_MASK 1<<HF_HALTED_SHIFT + #endif /* !defined (__CPU_PPC_H__) */ diff --git a/target-ppc/fake-exec.c b/target-ppc/fake-exec.c new file mode 100644 index 000000000..259e06d5a --- /dev/null +++ b/target-ppc/fake-exec.c @@ -0,0 +1,104 @@ +/* + * fake-exec.c + * + * This is a file for stub functions so that compilation is possible + * when TCG CPU emulation is disabled during compilation. + * + * Copyright 2007 IBM Corporation. + * Added by & Authors: + * Jerone Young <jyoung5@us.ibm.com> + * This work is licensed under the GNU GPL licence version 2 or later. + * + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "cpu.h" +#include "exec-all.h" + + +struct ppc_def_t { + const unsigned char *name; + uint32_t pvr; + uint32_t svr; + uint64_t insns_flags; + uint64_t msr_mask; + powerpc_mmu_t mmu_model; + powerpc_excp_t excp_model; + powerpc_input_t bus_model; + uint32_t flags; + int bfd_mach; + void (*init_proc)(CPUPPCState *env); + int (*check_pow)(CPUPPCState *env); +}; + +int code_copy_enabled = 0; + +void cpu_dump_state (CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ +} + +void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +{ +} + +void cpu_dump_statistics (CPUState *env, FILE*f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ +} + +unsigned long code_gen_max_block_size(void) +{ + return 32; +} + +void cpu_gen_init(void) +{ +} + +int cpu_restore_state(TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc) + +{ + return 0; +} + +int cpu_ppc_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) +{ + return 0; +} + +void init_proc_ppc440ep_kvm(CPUPPCState *env) +{ + ppc40x_irq_init(env); +} + +static ppc_def_t ppc440ep_kvm = { + .name = "440EP KVM", + .mmu_model = POWERPC_MMU_SOFT_4xx, /*XXX needed for GDB stub */ + .init_proc = init_proc_ppc440ep_kvm, +}; + +const ppc_def_t *cpu_ppc_find_by_name (const unsigned char *name) +{ + return &ppc440ep_kvm; +} + +int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def) +{ + env->mmu_model = def->mmu_model; + (*def->init_proc)(env); + return 0; +} + +void flush_icache_range(unsigned long start, unsigned long stop) +{ +} diff --git a/target-ppc/helper.c b/target-ppc/helper.c index b233d4f53..d6197377b 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -28,6 +28,7 @@ #include "helper_regs.h" #include "qemu-common.h" #include "kvm.h" +#include "qemu-kvm.h" //#define DEBUG_MMU //#define DEBUG_BATS diff --git a/target-ppc/libkvm.c b/target-ppc/libkvm.c new file mode 100644 index 000000000..da93026f1 --- /dev/null +++ b/target-ppc/libkvm.c @@ -0,0 +1,102 @@ +/* + * This file contains the powerpc specific implementation for the + * architecture dependent functions defined in kvm-common.h and + * libkvm.h + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright IBM Corp. 2007,2008 + * Authors: + * Jerone Young <jyoung5@us.ibm.com> + * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#include "libkvm-all.h" +#include "libkvm.h" +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> + +int handle_dcr(kvm_vcpu_context_t vcpu) +{ + int ret = 0; + struct kvm_run *run = vcpu->run; + kvm_context_t kvm = vcpu->kvm; + + if (run->dcr.is_write) + ret = kvm->callbacks->powerpc_dcr_write(vcpu, + run->dcr.dcrn, + run->dcr.data); + else + ret = kvm->callbacks->powerpc_dcr_read(vcpu, + run->dcr.dcrn, + &(run->dcr.data)); + + return ret; +} + +void kvm_show_code(kvm_vcpu_context_t vcpu) +{ + fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__); +} + +void kvm_show_regs(kvm_vcpu_context_t vcpu) +{ + struct kvm_regs regs; + int i; + + if (kvm_get_regs(vcpu, ®s)) + return; + + fprintf(stderr,"guest vcpu #%d\n", vcpu); + fprintf(stderr,"pc: %016"PRIx64" msr: %016"PRIx64"\n", + regs.pc, regs.msr); + fprintf(stderr,"lr: %016"PRIx64" ctr: %016"PRIx64"\n", + regs.lr, regs.ctr); + fprintf(stderr,"srr0: %016"PRIx64" srr1: %016"PRIx64"\n", + regs.srr0, regs.srr1); + for (i=0; i<32; i+=4) + { + fprintf(stderr, "gpr%02d: %016"PRIx64" %016"PRIx64" %016"PRIx64 + " %016"PRIx64"\n", i, + regs.gpr[i], + regs.gpr[i+1], + regs.gpr[i+2], + regs.gpr[i+3]); + } + + fflush(stdout); +} + +int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, + void **vm_mem) +{ + int r; + + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) + return r; + + return 0; +} + +int kvm_arch_run(kvm_vcpu_context_t vcpu) +{ + int ret = 0; + + switch (vcpu->run->exit_reason){ + case KVM_EXIT_DCR: + ret = handle_dcr(vcpu); + break; + default: + ret = 1; + break; + } + return ret; +} diff --git a/target-ppc/libkvm.h b/target-ppc/libkvm.h new file mode 100644 index 000000000..80b6b06c8 --- /dev/null +++ b/target-ppc/libkvm.h @@ -0,0 +1,36 @@ +/* + * This header is for functions & variables that will ONLY be + * used inside libkvm for powerpc. + * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE + * WITHIN LIBKVM. + * + * Copyright (C) 2006 Qumranet, Inc. + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + * Copyright 2007 IBM Corporation. + * Added by: Jerone Young <jyoung5@us.ibm.com> + * + * This work is licensed under the GNU LGPL license, version 2. + */ + +#ifndef KVM_POWERPC_H +#define KVM_POWERPC_H + +#include "libkvm-all.h" + +extern int kvm_page_size; + +#define PAGE_SIZE kvm_page_size +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +static inline void eieio(void) +{ + asm volatile("eieio" : : : "memory"); +} + +#define smp_wmb() eieio() + +#endif diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 4897c8a4d..ead38e180 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -1,6 +1,7 @@ #include "hw/hw.h" #include "hw/boards.h" #include "kvm.h" +#include "qemu-kvm.h" void cpu_save(QEMUFile *f, void *opaque) { @@ -157,6 +157,8 @@ int main(int argc, char **argv) #include "qemu-option.h" #include "qemu-config.h" #include "qemu-objects.h" +#include "qemu-kvm.h" +#include "hw/device-assignment.h" #include "disas.h" @@ -179,6 +181,7 @@ const char *bios_name = NULL; to store the VM snapshots */ struct drivelist drives = QTAILQ_HEAD_INITIALIZER(drives); struct driveoptlist driveopts = QTAILQ_HEAD_INITIALIZER(driveopts); +DriveInfo *extboot_drive = NULL; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; static DisplayState *display_state; DisplayType display_type = DT_DEFAULT; @@ -215,13 +218,17 @@ int rtc_td_hack = 0; #endif int usb_enabled = 0; int singlestep = 0; +const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE]; +int assigned_devices_index; int smp_cpus = 1; int max_cpus = 0; int smp_cores = 1; int smp_threads = 1; const char *vnc_display; int acpi_enabled = 1; +#ifdef TARGET_I386 int no_hpet = 0; +#endif int fd_bootchk = 1; int no_reboot = 0; int no_shutdown = 0; @@ -235,6 +242,12 @@ const char *watchdog; const char *option_rom[MAX_OPTION_ROMS]; int nb_option_roms; int semihosting_enabled = 0; +int time_drift_fix = 0; +unsigned int kvm_shadow_memory = 0; +const char *mem_path = NULL; +#ifdef MAP_POPULATE +int mem_prealloc = 1; /* force preallocation of physical target memory */ +#endif #ifdef TARGET_ARM int old_param = 0; #endif @@ -245,6 +258,7 @@ int ctrl_grab = 0; unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; #endif +const char *nvram = NULL; int boot_menu; int nb_numa_nodes; @@ -2109,6 +2123,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque, int on_read_error, on_write_error; const char *devaddr; DriveInfo *dinfo; + int is_extboot = 0; int snapshot = 0; *fatal_error = 1; @@ -2267,6 +2282,12 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque, } } + is_extboot = qemu_opt_get_bool(opts, "boot", 0); + if (is_extboot && extboot_drive) { + fprintf(stderr, "qemu: two bootable drives specified\n"); + return NULL; + } + on_write_error = BLOCK_ERR_STOP_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO) { @@ -2378,6 +2399,9 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque, if (serial) strncpy(dinfo->serial, serial, sizeof(serial)); QTAILQ_INSERT_TAIL(&drives, dinfo, next); + if (is_extboot) { + extboot_drive = dinfo; + } switch(type) { case IF_IDE: @@ -2851,6 +2875,7 @@ int qemu_set_fd_handler2(int fd, ioh->opaque = opaque; ioh->deleted = 0; } + qemu_notify_event(); return 0; } @@ -2973,6 +2998,15 @@ static int ram_save_block(QEMUFile *f) int found = 0; while (addr < last_ram_offset) { + if (kvm_enabled() && current_addr == 0) { + int r; + r = kvm_update_dirty_pages_log(); + if (r) { + fprintf(stderr, "%s: update dirty pages log failed %d\n", __FUNCTION__, r); + qemu_file_set_error(f); + return 0; + } + } if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { uint8_t *p; @@ -3277,6 +3311,13 @@ static int powerdown_requested; static int debug_requested; static int vmstop_requested; +int qemu_no_shutdown(void) +{ + int r = no_shutdown; + no_shutdown = 0; + return r; +} + int qemu_shutdown_requested(void) { int r = shutdown_requested; @@ -3361,6 +3402,9 @@ void qemu_system_reset_request(void) } else { reset_requested = 1; } + if (cpu_single_env) { + cpu_single_env->stopped = 1; + } qemu_notify_event(); } @@ -3513,13 +3557,19 @@ void qemu_notify_event(void) { CPUState *env = cpu_single_env; + if (kvm_enabled()) { + qemu_kvm_notify_work(); + return; + } if (env) { cpu_exit(env); } } +#if defined(KVM_UPSTREAM) || !defined(CONFIG_KVM) void qemu_mutex_lock_iothread(void) {} void qemu_mutex_unlock_iothread(void) {} +#endif void vm_stop(int reason) { @@ -3947,6 +3997,8 @@ void main_loop_wait(int timeout) for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { ioh->fd_read(ioh->opaque); + if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque))) + FD_CLR(ioh->fd, &rfds); } if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { ioh->fd_write(ioh->opaque); @@ -4154,6 +4206,12 @@ static void main_loop(void) { int r; + if (kvm_enabled()) { + kvm_main_loop(); + cpu_disable_ticks(); + return; + } + #ifdef CONFIG_IOTHREAD qemu_system_ready = 1; qemu_cond_broadcast(&qemu_system_cond); @@ -4931,6 +4989,8 @@ int main(int argc, char **argv, char **envp) node_cpumask[i] = 0; } + assigned_devices_index = 0; + nb_numa_nodes = 0; nb_nics = 0; @@ -5438,9 +5498,41 @@ int main(int argc, char **argv, char **envp) break; #endif #ifdef CONFIG_KVM +#ifdef KVM_UPSTREAM case QEMU_OPTION_enable_kvm: kvm_allowed = 1; +#endif break; + case QEMU_OPTION_no_kvm: + kvm_allowed = 0; + break; + case QEMU_OPTION_no_kvm_irqchip: { + kvm_irqchip = 0; + kvm_pit = 0; + break; + } + case QEMU_OPTION_no_kvm_pit: { + kvm_pit = 0; + break; + } + case QEMU_OPTION_no_kvm_pit_reinjection: { + kvm_pit_reinject = 0; + break; + } + case QEMU_OPTION_enable_nesting: { + kvm_nested = 1; + break; + } +#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__) + case QEMU_OPTION_pcidevice: + if (assigned_devices_index >= MAX_DEV_ASSIGN_CMDLINE) { + fprintf(stderr, "Too many assigned devices\n"); + exit(1); + } + assigned_devices[assigned_devices_index] = optarg; + assigned_devices_index++; + break; +#endif #endif case QEMU_OPTION_usb: usb_enabled = 1; @@ -5522,6 +5614,20 @@ int main(int argc, char **argv, char **envp) semihosting_enabled = 1; break; #endif + case QEMU_OPTION_tdf: + time_drift_fix = 1; + break; + case QEMU_OPTION_kvm_shadow_memory: + kvm_shadow_memory = (int64_t)atoi(optarg) * 1024 * 1024 / 4096; + break; + case QEMU_OPTION_mempath: + mem_path = optarg; + break; +#ifdef MAP_POPULATE + case QEMU_OPTION_mem_prealloc: + mem_prealloc = !mem_prealloc; + break; +#endif case QEMU_OPTION_name: qemu_name = qemu_strdup(optarg); { @@ -5600,6 +5706,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_runas: run_as = optarg; break; + case QEMU_OPTION_nvram: + nvram = optarg; + break; #endif #ifdef CONFIG_XEN case QEMU_OPTION_xen_domid: @@ -5789,8 +5898,12 @@ int main(int argc, char **argv, char **envp) ret = kvm_init(smp_cpus); if (ret < 0) { +#if defined(KVM_UPSTREAM) || defined(CONFIG_NO_CPU_EMULATION) fprintf(stderr, "failed to initialize KVM\n"); exit(1); +#endif + fprintf(stderr, "Could not initialize KVM, will disable KVM support\n"); + kvm_allowed = 0; } } @@ -68,7 +68,7 @@ typedef void VncSendHextileTile(VncState *vs, void *last_fg, int *has_bg, int *has_fg); -#define VNC_MAX_WIDTH 2048 +#define VNC_MAX_WIDTH 2560 #define VNC_MAX_HEIGHT 2048 #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) |