diff options
273 files changed, 64828 insertions, 242 deletions
@@ -51,6 +51,18 @@ endif SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) 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-%: $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) @@ -90,6 +102,10 @@ block-nested-$(CONFIG_CURL) += curl.o block-obj-y += $(addprefix block/, $(block-nested-y)) +ifdef CONFIG_AIO +block-obj-y += compatfd.o +endif + ###################################################################### # libqemu_common.a: Target independent part of system emulation. The # long term path is to suppress *all* target specific code in case of @@ -263,6 +279,7 @@ video.x openbios-sparc32 openbios-sparc64 openbios-ppc \ pxe-ne2k_pci.bin pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin \ bamboo.dtb petalogix-s3adsp1800.dtb \ multiboot.bin +BLOBS += extboot.bin else BLOBS= endif @@ -285,7 +302,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" @@ -295,6 +317,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 @@ -412,6 +437,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 \ @@ -420,3 +446,10 @@ tarbin: # Include automatically generated dependency files -include $(wildcard *.d audio/*.d slirp/*.d block/*.d) + +build-targets-i386 = $(build-targets-x86) +build-targets-x86_64 = $(build-targets-x86) +build-targets-x86 = +build-targets-ia64 = + +all: $(build-targets-$(ARCH)) diff --git a/Makefile.target b/Makefile.target index f9cd42a94..72f14f6de 100644 --- a/Makefile.target +++ b/Makefile.target @@ -1,6 +1,11 @@ +CFLAGS= +LDFLAGS= + include config.mak include $(SRC_PATH)/rules.mak +LDFLAGS_BASE:=$(LDFLAGS) + TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw CPPFLAGS=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) -MMD -MT $@ -MP -DNEED_CPU_H @@ -54,6 +59,7 @@ endif CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE CPPFLAGS+=-U_FORTIFY_SOURCE +CPPFLAGS+=-D__user= LIBS+=-lm ifdef CONFIG_WIN32 LIBS+=-lwinmm -lws2_32 -liphlpapi @@ -70,20 +76,30 @@ endif kvm.o: CFLAGS+=$(KVM_CFLAGS) kvm-all.o: CFLAGS+=$(KVM_CFLAGS) +CFLAGS += $(KVM_CFLAGS) + all: $(PROGS) # Dummy command so that make thinks it has done something @true ######################################################### # cpu emulator library -libobj-y = exec.o translate-all.o cpu-exec.o translate.o host-utils.o +libobj-y = exec.o cpu-exec.o host-utils.o +ifeq ($(NO_CPU_EMULATION), 1) +libobj-y += fack-exec.o +else +libobj-y += translate-all.o translate.o +endif libobj-$(CONFIG_KQEMU) += kqemu.o # TCG code generator +ifneq ($(NO_CPU_EMULATION), 1) libobj-y += tcg/tcg.o tcg/tcg-runtime.o CPPFLAGS+=-I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/$(ARCH) +endif ifeq ($(ARCH),sparc64) CPPFLAGS+=-I$(SRC_PATH)/tcg/sparc endif + ifdef CONFIG_SOFTFLOAT libobj-y += fpu/softfloat.o else @@ -92,6 +108,18 @@ endif CPPFLAGS+=-I$(SRC_PATH)/fpu libobj-y += op_helper.o helper.o +ifeq ($(TARGET_BASE_ARCH), i386) +libobj-y += helper.o +libobj-$(CONFIG_KVM) += kvm-tpr-opt.o +libobj-$(CONFIG_KVM) += qemu-kvm-helper.o +endif + +libobj-y += op_helper.o + +ifneq ($(TARGET_ARCH), ia64) +libobj-y += helper.o +endif + ifeq ($(TARGET_BASE_ARCH), arm) libobj-y += neon_helper.o iwmmxt_helper.o endif @@ -100,6 +128,11 @@ ifeq ($(TARGET_BASE_ARCH), alpha) libobj-y += alpha_palcode.o endif +ifeq ($(TARGET_BASE_ARCH), ia64) +libobj-y += op_helper.o firmware.o +libobj-$(CONFIG_KVM) += qemu-kvm-ia64.o +endif + ifeq ($(TARGET_BASE_ARCH), cris) libobj-y += cris-dis.o @@ -108,6 +141,7 @@ libobj-y += mmu.o endif endif + # NOTE: the disassembler code is only needed for debugging libobj-y += disas.o ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386) @@ -167,6 +201,9 @@ op_helper.o: CFLAGS += $(HELPER_CFLAGS) cpu-exec.o: CFLAGS += $(HELPER_CFLAGS) +qemu-kvm-helper.o: qemu-kvm-helper.c + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + ######################################################### # Linux user emulator target @@ -494,13 +531,33 @@ 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 +obj-i386-y += extboot.o +ifeq ($(USE_KVM_PIT), 1) +obj-i386-y += i8254-kvm.o +endif +ifeq ($(USE_KVM_DEVICE_ASSIGNMENT), 1) +obj-i386-y += device-assignment.o +LIBS+=-lpci +endif ifeq ($(TARGET_BASE_ARCH), i386) CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE endif +# 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 + +ifeq ($(USE_KVM_DEVICE_ASSIGNMENT), 1) +obj-ia64-y += device-assignment.o +LIBS+=-lpci +endif + # shared objects obj-ppc-y = ppc.o ide.o vga.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 @@ -650,6 +707,10 @@ ifdef TARGET_GPROF LDFLAGS+=-p main.o: CFLAGS+=-p endif +ifeq ($(TARGET_ARCH), ia64) +firmware.o: firmware.c + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< +endif vl.o: CFLAGS+=$(SDL_CFLAGS) @@ -660,6 +721,9 @@ monitor.o: qemu-monitor.h LIBS += $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) $(VDE_LIBS) $(CURL_LIBS) ARLIBS=../libqemu_common.a libqemu.a $(HWLIB) +$(QEMU_PROG): ARLIBS += $(DEPLIBS) +$(QEMU_PROG): $(DEPLIBS) + endif # !CONFIG_USER_ONLY $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(ARLIBS) diff --git a/block/raw-posix.c b/block/raw-posix.c index 3e1a87532..74821506a 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> #ifdef CONFIG_AIO #include "posix-aio-compat.h" #endif @@ -502,7 +504,7 @@ typedef struct RawAIOCB { typedef struct PosixAioState { - int rfd, wfd; + int fd; RawAIOCB *first_aio; } PosixAioState; @@ -511,18 +513,29 @@ static void posix_aio_read(void *opaque) PosixAioState *s = opaque; RawAIOCB *acb, **pacb; int ret; - ssize_t len; - - /* read all bytes from signal pipe */ - for (;;) { - char bytes[16]; - - len = read(s->rfd, bytes, sizeof(bytes)); + size_t offset; + union { + struct qemu_signalfd_siginfo siginfo; + char buf[128]; + } sig; + + /* 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->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; } for(;;) { @@ -569,22 +582,10 @@ 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 int posix_aio_init(void) { - struct sigaction act; + sigset_t mask; PosixAioState *s; - int fds[2]; struct qemu_paioinit ai; if (posix_aio_state) @@ -592,24 +593,21 @@ static int posix_aio_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 (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 -errno; } - 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, s); + qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush, s); memset(&ai, 0, sizeof(ai)); ai.aio_threads = 64; 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/compatfd.c b/compatfd.c new file mode 100644 index 000000000..36e37e5d1 --- /dev/null +++ b/compatfd.c @@ -0,0 +1,131 @@ +/* + * 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; + } + + 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) + return ret; +#endif + + return qemu_signalfd_compat(mask); +} + +int qemu_eventfd(int *fds) +{ +#if defined(CONFIG_eventfd) + int ret; + + ret = syscall(SYS_eventfd, 0); + if (ret >= 0) { + fds[0] = ret; + if ((fds[1] = dup(ret)) == -1) { + close(ret); + return -1; + } + return 0; + } +#endif + + return pipe(fds); +} diff --git a/compatfd.h b/compatfd.h new file mode 100644 index 000000000..55a111a57 --- /dev/null +++ b/compatfd.h @@ -0,0 +1,28 @@ +/* + * 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; + uint8_t pad[124]; +}; + +int qemu_signalfd(const sigset_t *mask); + +int qemu_eventfd(int *fds); + +#endif @@ -102,7 +102,7 @@ else cpu=`uname -m` fi -target_list="" +target_list="x86_64-softmmu" case "$cpu" in i386|i486|i586|i686|i86pc|BePC) cpu="i386" @@ -159,6 +159,17 @@ case "$cpu" in cpu="unknown" ;; esac + +kvm_version() { + local fname="$(dirname "$0")/KVM_VERSION" + + if test -f "$fname"; then + cat "$fname" + else + echo "kvm-devel" + fi +} + gprof="no" debug_tcg="no" debug="no" @@ -195,6 +206,9 @@ nptl="yes" mixemu="no" bluez="yes" kvm="no" +kvm_trace="no" +kvm_cap_pit="no" +kvm_cap_device_assignment="no" kerneldir="" aix="no" blobs="yes" @@ -202,7 +216,11 @@ fdt="yes" sdl="yes" sdl_x11="no" xen="yes" -pkgversion="" +pkgversion=" ($(kvm_version))" +signalfd="no" +eventfd="no" +cpu_emulation="yes" +kvm_kmod="no" # OS specific if check_define __linux__ ; then @@ -245,6 +263,7 @@ audio_drv_list="oss" audio_possible_drivers="oss sdl esd pa" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" + kvm="yes" fi ;; DragonFly) @@ -337,6 +356,19 @@ kvm="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" audio_possible_drivers="$audio_possible_drivers fmod" + kvm="yes" + kqemu="no" +fi +if [ "$cpu" = "ia64" ] ; then + kvm="yes" + xen="no" + target_list="ia64-softmmu" + cpu_emulation="no" + gdbstub="no" + slirp="no" +fi +if [ "$cpu" = "powerpc" ]; then + kvm="yes" fi ;; esac @@ -501,10 +533,14 @@ for opt do ;; --kerneldir=*) kerneldir="$optarg" ;; + --with-kvm-trace) kvm_trace="yes" + ;; --with-pkgversion=*) pkgversion=" ($optarg)" ;; --disable-docs) build_docs="no" ;; + --disable-cpu-emulation) cpu_emulation="no" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -528,6 +564,8 @@ if test -z "$werror" ; then else werror="no" fi + # disable default werror for kvm + werror="no" fi if test "$werror" = "yes" ; then @@ -652,6 +690,8 @@ echo " --disable-aio disable 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 @@ -830,6 +870,75 @@ else fi ########################################## +# KVM probe + +case "$cpu" in + i386 | x86_64) + kvm_arch="x86" + ;; + ppc) + kvm_arch="powerpc" + ;; + *) + kvm_arch="$cpu" + ;; +esac + +kvm_cflags="" + +if test "$kvm" = "yes" ; then + +kvm_cflags="-I$source_path/kvm/include" +kvm_cflags="$kvm_cflags -I$source_path/kvm/include/$kvm_arch" + +# test for KVM_CAP_PIT + +cat > $TMPC <<EOF +#include <linux/kvm.h> +#ifndef KVM_CAP_PIT +#error "kvm no pit capability" +#endif +int main(void) { return 0; } +EOF + if $cc $ARCH_CFLAGS $CFLAGS $kvm_cflags -o $TMPE ${OS_CFLAGS} $TMPC 2> /dev/null ; then + kvm_cap_pit="yes" + fi + +# test for KVM_CAP_DEVICE_ASSIGNMENT + +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 $cc $ARCH_CFLAGS $CFLAGS $kvm_cflags -o $TMPE ${OS_CFLAGS} $TMPC 2> /dev/null ; then + kvm_cap_device_assignment="yes" + 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) { return 0; } +EOF + if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $TMPC 2>/dev/null ; then + : + else + echo + echo "Error: libpci check failed" + echo "Disable KVM Device Assignment capability." + echo + kvm_cap_device_assignment="no" + fi +fi + +########################################## # zlib check cat > $TMPC << EOF @@ -1152,19 +1261,6 @@ if test "$kvm" = "yes" ; then #endif int main(void) { return 0; } EOF - if test "$kerneldir" != "" ; then - kvm_cflags=-I"$kerneldir"/include - if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) \ - -a -d "$kerneldir/arch/x86/include" ; then - kvm_cflags="$kvm_cflags -I$kerneldir/arch/x86/include" - elif test "$cpu" = "ppc" -a -d "$kerneldir/arch/powerpc/include" ; then - kvm_cflags="$kvm_cflags -I$kerneldir/arch/powerpc/include" - elif test -d "$kerneldir/arch/$cpu/include" ; then - kvm_cflags="$kvm_cflags -I$kerneldir/arch/$cpu/include" - fi - else - kvm_cflags="" - fi if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $kvm_cflags $TMPC \ > /dev/null 2>/dev/null ; then : @@ -1347,6 +1443,33 @@ if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then splice=yes fi +########################################## +# signalfd probe +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 + +########################################## +# eventfd probe +cat > $TMPC << EOF +#define _GNU_SOURCE +#include <unistd.h> +#include <sys/syscall.h> +int main(void) { return syscall(SYS_eventfd, 0); } +EOF + +if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then + eventfd=yes +fi + # Check if tools are available to build documentation. if test "$build_docs" = "yes" -a \( ! -x "`which texi2html 2>/dev/null`" -o ! -x "`which pod2man 2>/dev/null`" \) ; then build_docs="no" @@ -1390,6 +1513,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" @@ -1441,6 +1578,7 @@ if test -n "$sparc_cpu"; then fi echo "kqemu support $kqemu" echo "xen support $xen" +echo "CPU emulation $cpu_emulation" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1451,6 +1589,7 @@ echo "AIO support $aio" echo "IO thread $io_thread" echo "Install blobs $blobs" echo -e "KVM support $kvm" +echo "KVM trace support $kvm_trace" echo "fdt support $fdt" echo "preadv support $preadv" @@ -1738,6 +1877,12 @@ if test "$fdt" = "yes" ; then echo "#define HAVE_FDT 1" >> $config_host_h echo "FDT_LIBS=-lfdt" >> $config_host_mak fi +if test "$signalfd" = "yes" ; then + echo "#define CONFIG_signalfd 1" >> $config_host_h +fi +if test "$eventfd" = "yes" ; then + echo "#define CONFIG_eventfd 1" >> $config_host_h +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then @@ -1797,6 +1942,13 @@ if test "$xen" = "yes" ; echo "CONFIG_XEN=y" >> $config_host_mak fi +# this is a temp hack needed for kvm +if test "$kvm" = "yes" ; then + echo "KVM_CFLAGS=$kvm_cflags" >> $config_host_mak +fi + +echo "KVM_KMOD=$kvm_kmod" >> $config_host_mak + tools= if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then tools="qemu-img\$(EXESUF) $tools" @@ -1890,6 +2042,28 @@ interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_arch2/g"` echo "CONFIG_QEMU_PREFIX=\"$interp_prefix1\"" >> $config_mak gdb_xml_files="" +disable_cpu_emulation() { + if test $cpu_emulation = "no"; then + echo "#define NO_CPU_EMULATION 1" >> $config_host_h + echo "NO_CPU_EMULATION=1" >> $config_host_mak + fi +} + +configure_kvm() { + if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \ + \( "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "ia64" -o "$cpu" = "powerpc" \); then + echo "CONFIG_KVM=y" >> $config_mak + echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak + if test $kvm_cap_pit = "yes" ; then + echo "USE_KVM_PIT=1" >> $config_mak + fi + if test $kvm_cap_device_assignment = "yes" ; then + echo "USE_KVM_DEVICE_ASSIGNMENT=1" >> $config_mak + fi + disable_cpu_emulation + fi +} + TARGET_ARCH="$target_arch2" TARGET_BASE_ARCH="" TARGET_ABI_DIR="" @@ -1902,6 +2076,12 @@ case "$target_arch2" in TARGET_BASE_ARCH=i386 target_phys_bits=64 ;; + ia64) + echo "TARGET_ARCH=ia64" >> $config_host_mak + echo "#define TARGET_ARCH \"ia64\"" >> $config_host_h + echo "#define TARGET_IA64 1" >> $config_host_h + target_phys_bits=64 + ;; alpha) target_phys_bits=64 ;; @@ -2028,6 +2208,7 @@ case "$target_arch2" in \( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then echo "CONFIG_KVM=y" >> $config_mak echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak + configure_kvm fi esac echo "HWLIB=../libhw$target_phys_bits/libqemuhw$target_phys_bits.a" >> $config_mak @@ -844,6 +844,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-defs.h b/cpu-defs.h index d73ec0ab3..db17bd07b 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 "sys-queue.h" #include "targphys.h" @@ -134,6 +135,16 @@ typedef struct CPUWatchpoint { TAILQ_ENTRY(CPUWatchpoint) entry; } CPUWatchpoint; +/* forward decleration */ +struct qemu_work_item; + +struct KVMCPUState { + pthread_t thread; + int signalled; + void *vcpu_ctx; + struct qemu_work_item *queued_work_first, *queued_work_last; +}; + #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. */ \ @@ -186,6 +195,7 @@ typedef struct CPUWatchpoint { uint32_t host_tid; /* host thread ID */ \ int numa_node; /* NUMA node this cpu is belonging to */ \ int running; /* Nonzero if cpu is currently running(usermode). */ \ + int thread_id; \ /* user data */ \ void *opaque; \ \ @@ -195,6 +205,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 2385d56bc..e6709dc3a 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(HOST_SOLARIS) // Work around ugly bugs in glibc that mangle global register contents #undef env @@ -249,6 +253,7 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) #elif defined(TARGET_CRIS) +#elif defined(TARGET_IA64) /* XXXXX */ #else #error unsupported target CPU @@ -316,6 +321,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 } @@ -710,6 +717,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) /* XXXXX */ diff --git a/create_config b/create_config index cac0edbbb..b7b33bb3d 100755 --- a/create_config +++ b/create_config @@ -50,6 +50,9 @@ case $line in name=${line%=*} echo "#define $name 1" ;; + USE_KVM_*) + echo "#define $(echo "$line" | sed 's/=/ /')" + ;; esac done # read @@ -177,6 +177,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) && !defined(CONFIG_KQEMU) #define TARGET_PHYS_ADDR_SPACE_BITS 36 +#elif defined(TARGET_IA64) +#define TARGET_PHYS_ADDR_SPACE_BITS 36 #else /* Note: for compatibility with kqemu, we use 32 bits for x86_64 */ #define TARGET_PHYS_ADDR_SPACE_BITS 32 @@ -112,6 +120,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 { @@ -413,6 +422,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; @@ -577,6 +589,11 @@ void cpu_exec_init(CPUState *env) env->numa_node = 0; TAILQ_INIT(&env->breakpoints); TAILQ_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(); @@ -1542,6 +1559,8 @@ void cpu_interrupt(CPUState *env, int mask) old_mask = env->interrupt_request; env->interrupt_request |= mask; + if (kvm_enabled() && !qemu_kvm_irqchip_in_kernel()) + kvm_update_interrupt_request(env); #ifndef CONFIG_USER_ONLY /* @@ -1892,7 +1911,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); } @@ -2438,6 +2456,113 @@ static ram_addr_t kqemu_ram_alloc(ram_addr_t size) } #endif +#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; @@ -2451,7 +2576,10 @@ 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 = qemu_vmalloc(size); + new_block->host = file_ram_alloc(size, mem_path); + if (!new_block->host) { + new_block->host = qemu_vmalloc(size); + } new_block->offset = last_ram_offset; new_block->length = size; @@ -3156,6 +3284,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 && @@ -3348,6 +3481,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); @@ -3365,7 +3500,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; } @@ -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 @@ -519,6 +521,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); @@ -567,12 +576,14 @@ void qemu_system_powerdown(void) #endif #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 { @@ -595,6 +606,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); @@ -637,6 +652,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); @@ -713,22 +732,72 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val) #endif } +static const char *model; + static void piix4_device_hot_add(int bus, int slot, int state); -void piix4_acpi_system_hot_add_init(void) +void piix4_acpi_system_hot_add_init(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, NULL); register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, NULL); + model = cpu_model; + qemu_system_device_hot_add_register(piix4_device_hot_add); } +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)); +} + +#if defined(TARGET_I386) || defined(TARGET_X86_64) +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() && qemu_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); @@ -864,11 +874,103 @@ 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->kvm_cpu_state.vcpu_ctx, 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->kvm_cpu_state.vcpu_ctx, klapic); +} + +#endif + +void qemu_kvm_load_lapic(CPUState *env) +{ +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_vcpu_inited(env) && qemu_kvm_irqchip_in_kernel()) { + kvm_kernel_lapic_load_from_user(env->apic_state); + } +#endif +} + static void apic_save(QEMUFile *f, void *opaque) { APICState *s = opaque; int i; +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + kvm_kernel_lapic_save_to_user(s); + } +#endif + qemu_put_be32s(f, &s->apicbase); qemu_put_8s(f, &s->id); qemu_put_8s(f, &s->arb_id); @@ -931,6 +1033,9 @@ static int apic_load(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; } @@ -953,8 +1058,8 @@ static void apic_reset(void *opaque) */ s->lvt[APIC_LVT_LINT0] = 0x700; } - cpu_synchronize_state(s->cpu_env, 1); + qemu_kvm_load_lapic(s->cpu_env); } static CPUReadMemoryFunc *apic_mem_read[3] = { diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 0460f9713..9e775a992 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" /* * TODO: @@ -2587,6 +2588,7 @@ static CPUWriteMemoryFunc *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; @@ -2596,13 +2598,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((VGAState *)s); 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, @@ -2614,17 +2622,21 @@ 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(isa_mem_base + 0xa0000, 0x20000, s->vga.vga_io_memory); + + vga_dirty_log_start(&s->vga); } /* Compute the memory access functions */ @@ -3265,6 +3277,8 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int region_num, { CirrusVGAState *s = &((PCICirrusVGAState *)d)->cirrus_vga; + vga_dirty_log_stop((VGAState *)s); + /* XXX: add byte swapping apertures */ cpu_register_physical_memory(addr, s->vga.vram_size, s->cirrus_linear_io_addr); @@ -3296,10 +3310,14 @@ static void pci_cirrus_write_config(PCIDevice *d, PCICirrusVGAState *pvs = container_of(d, PCICirrusVGAState, dev); CirrusVGAState *s = &pvs->cirrus_vga; + vga_dirty_log_stop((VGAState *)s); + pci_default_write_config(d, address, val, len); if (s->vga.map_addr && pvs->dev.io_regions[0].addr == -1) s->vga.map_addr = 0; cirrus_update_memory_access(s); + + vga_dirty_log_start((VGAState *)s); } void pci_cirrus_vga_init(PCIBus *bus) diff --git a/hw/device-assignment.c b/hw/device-assignment.c new file mode 100644 index 000000000..75db54625 --- /dev/null +++ b/hw/device-assignment.c @@ -0,0 +1,1442 @@ +/* + * 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 <pci/pci.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" + +/* 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 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, + uint32_t e_phys, uint32_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]; + uint32_t old_ephys = region->e_physbase; + uint32_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, + uint32_t addr, uint32_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_ADDRESS_SPACE_MEM_PREFETCH + : PCI_ADDRESS_SPACE_MEM; + + /* 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_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, sizeof(pci_dev->dev.config)); + 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 LIST_HEAD(, AssignedDevInfo) adev_head; + +#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(AssignedDevInfo *adev) +{ + AssignedDevice *dev = adev->assigned_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; + } + + pci_unregister_device(&dev->dev, 1); +#ifdef KVM_CAP_IRQ_ROUTING + free_dev_irq_entries(dev); +#endif + adev->assigned_dev = dev = NULL; + } + + LIST_REMOVE(adev, next); + qemu_free(adev); +} + +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(AssignedDevInfo *adev) +{ + struct kvm_assigned_pci_dev assigned_dev_data; + AssignedDevice *dev = adev->assigned_dev; + 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 if present + * (or when not disabled on the command line) + */ + r = kvm_check_extension(kvm_state, KVM_CAP_IOMMU); + if (r && !adev->disable_iommu) + assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU; +#endif + + r = kvm_assign_pci_device(kvm_context, &assigned_dev_data); + if (r < 0) + fprintf(stderr, "Failed to assign device \"%s\" : %s\n", + adev->name, strerror(-r)); + return r; +} + +static int assign_irq(AssignedDevInfo *adev) +{ + struct kvm_assigned_irq assigned_irq_data; + AssignedDevice *dev = adev->assigned_dev; + 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", + adev->name, 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(AssignedDevInfo *adev) +{ +#ifdef KVM_CAP_DEVICE_DEASSIGNMENT + struct kvm_assigned_pci_dev assigned_dev_data; + AssignedDevice *dev = adev->assigned_dev; + 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", + adev->name, strerror(-r)); +#endif +} + +void remove_assigned_device(AssignedDevInfo *adev) +{ + deassign_device(adev); + free_assigned_device(adev); +} + +AssignedDevInfo *get_assigned_device(int pcibus, int slot) +{ + AssignedDevice *assigned_dev = NULL; + AssignedDevInfo *adev = NULL; + + LIST_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; +} + +/* The pci config space got updated. Check if irq numbers have changed + * for our devices + */ +void assigned_dev_update_irqs() +{ + AssignedDevInfo *adev; + + adev = LIST_FIRST(&adev_head); + while (adev) { + AssignedDevInfo *next = LIST_NEXT(adev, next); + int r; + + r = assign_irq(adev); + if (r < 0) + remove_assigned_device(adev); + + adev = 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; +} + +struct PCIDevice *init_assigned_device(AssignedDevInfo *adev, + const char *devaddr) +{ + PCIBus *bus; + int devfn; + int r; + AssignedDevice *dev; + PCIDevice *pci_dev; + struct pci_access *pacc; + uint8_t e_device, e_intx; + + DEBUG("Registering real physical device %s (bus=%x dev=%x func=%x)\n", + adev->name, adev->bus, adev->dev, adev->func); + + bus = pci_get_bus_devfn(&devfn, devaddr); + pci_dev = pci_register_device(bus, adev->name, + sizeof(AssignedDevice), devfn, assigned_dev_pci_read_config, + assigned_dev_pci_write_config); + dev = container_of(pci_dev, AssignedDevice, dev); + + if (NULL == dev) { + fprintf(stderr, "%s: Error: Couldn't register real device %s\n", + __func__, adev->name); + return NULL; + } + + adev->assigned_dev = dev; + + if (get_real_device(dev, adev->bus, adev->dev, adev->func)) { + fprintf(stderr, "%s: Error: Couldn't get real device (%s)!\n", + __func__, adev->name); + 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 = adev->bus; + dev->h_devfn = PCI_DEVFN(adev->dev, adev->func); + + pacc = pci_alloc(); + pci_init(pacc); + dev->pdev = pci_get_dev(pacc, 0, adev->bus, adev->dev, adev->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(adev); + if (r < 0) + goto assigned_out; + + /* assign irq for the device */ + r = assign_irq(adev); + 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)) + return NULL; + + return &dev->dev; + +assigned_out: + deassign_device(adev); +out: + free_assigned_device(adev); + return NULL; +} + +/* + * 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. + */ +AssignedDevInfo *add_assigned_device(const char *arg) +{ + char device[16]; + char dma[6]; + int r; + AssignedDevInfo *adev; + + adev = qemu_mallocz(sizeof(AssignedDevInfo)); + if (adev == NULL) { + fprintf(stderr, "%s: Out of memory\n", __func__); + return NULL; + } + r = get_param_value(device, sizeof(device), "host", arg); + if (!r) + goto bad; + + r = pci_parse_host_devaddr(device, &adev->bus, &adev->dev, &adev->func); + if (r) + goto bad; + + r = get_param_value(adev->name, sizeof(adev->name), "name", arg); + if (!r) + snprintf(adev->name, sizeof(adev->name), "%s", device); + +#ifdef KVM_CAP_IOMMU + r = get_param_value(dma, sizeof(dma), "dma", arg); + if (r && !strncmp(dma, "none", 4)) + adev->disable_iommu = 1; +#endif + + LIST_INSERT_HEAD(&adev_head, adev, next); + return adev; +bad: + fprintf(stderr, "pcidevice argument parse error; " + "please check the help text for usage\n"); + qemu_free(adev); + return NULL; +} + +void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices) +{ + int i; + + for (i = 0; i < n_devices; i++) { + struct AssignedDevInfo *adev; + + adev = add_assigned_device(devices[i]); + if (!adev) { + fprintf(stderr, "Could not add assigned device %s\n", devices[i]); + exit(1); + } + + if (!init_assigned_device(adev, NULL)) { + fprintf(stderr, "Failed to initialize assigned device %s\n", + devices[i]); + exit(1); + } + } +} + +/* 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 int scan_option_rom(uint8_t devfn, void *roms, ram_addr_t offset) +{ + int i, size, total_size; + uint8_t csum; + ram_addr_t addr; + 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 0; + + found: + /* The size should be both 2K-aligned and page-aligned */ + total_size = (TARGET_PAGE_SIZE < 2048) + ? OPTION_ROM_ALIGN(size + 1) + : TARGET_PAGE_ALIGN(size + 1); + + /* Size of all available ram space is 0x10000 (0xd0000 to 0xe0000) */ + if ((offset + total_size) > 0x10000u) { + fprintf(stderr, "Option ROM size %x exceeds available space\n", size); + return 0; + } + + addr = qemu_ram_alloc(total_size); + cpu_register_physical_memory(0xd0000 + offset, total_size, addr | IO_MEM_ROM); + + /* Write ROM data and devfn to phys_addr */ + cpu_physical_memory_write_rom(0xd0000 + offset, (uint8_t *)rom, size); + cpu_physical_memory_write_rom(0xd0000 + offset + size, &devfn, 1); + + return total_size; +} + +/* + * 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. + */ +ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset) +{ + ram_addr_t offset = rom_base_offset; + AssignedDevInfo *adev; + + LIST_FOREACH(adev, &adev_head, next) { + 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", + adev->bus, adev->dev, adev->func); + + if (access(rom_file, F_OK)) + continue; + + /* Write something to the ROM file to enable it */ + fp = fopen(rom_file, "wb"); + if (fp == NULL) + continue; + len = fwrite(&i, 1, 1, fp); + fclose(fp); + if (len != 1) + continue; + + /* The file has to be closed and reopened, otherwise it won't work */ + fp = fopen(rom_file, "rb"); + if (fp == NULL) + continue; + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buf = malloc(size); + if (buf == NULL) { + fclose(fp); + continue; + } + + fread(buf, size, 1, fp); + if (!feof(fp) || ferror(fp)) { + free(buf); + fclose(fp); + continue; + } + + /* Copy ROM contents into the space backing the ROM BAR */ + if (adev->assigned_dev->v_addrs[PCI_ROM_SLOT].r_size >= size && + adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase) { + mprotect(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + size, PROT_READ | PROT_WRITE); + memcpy(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + buf, size); + mprotect(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + size, PROT_READ); + } + + /* Scan the buffer for suitable ROMs and increase the offset */ + offset += scan_option_rom(adev->assigned_dev->dev.devfn, buf, offset); + + free(buf); + fclose(fp); + } + + return offset; +} diff --git a/hw/device-assignment.h b/hw/device-assignment.h new file mode 100644 index 000000000..0be62f450 --- /dev/null +++ b/hw/device-assignment.h @@ -0,0 +1,124 @@ +/* + * 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 "sys-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 { + 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 { + target_phys_addr_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[] */ + uint32_t e_size; /* emulated size of region in bytes */ + uint32_t r_size; /* real size of region in bytes */ +} AssignedDevRegion; + +typedef struct { + PCIDevice dev; + 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; +} AssignedDevice; + +typedef struct AssignedDevInfo AssignedDevInfo; + +struct AssignedDevInfo { + char name[15]; + int bus; + int dev; + int func; + AssignedDevice *assigned_dev; + LIST_ENTRY(AssignedDevInfo) next; + int disable_iommu; +}; + +PCIDevice *init_assigned_device(AssignedDevInfo *adev, const char *devaddr); +AssignedDevInfo *add_assigned_device(const char *arg); +void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices); +void remove_assigned_device(AssignedDevInfo *adev); +AssignedDevInfo *get_assigned_device(int pcibus, int slot); +ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset); +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); +} @@ -205,6 +205,9 @@ static int hpet_load(QEMUFile *f, void *opaque, int version_id) qemu_get_timer(f, s->timer[i].qemu_timer); } } + if (hpet_in_legacy_mode()) { + hpet_disable_pit(); + } return 0; } @@ -474,9 +477,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: @@ -560,7 +565,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; } diff --git a/hw/i8254-kvm.c b/hw/i8254-kvm.c new file mode 100644 index 000000000..c54052cb9 --- /dev/null +++ b/hw/i8254-kvm.c @@ -0,0 +1,117 @@ +/* + * 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" + +static PITState pit_state; + +static void kvm_pit_save(QEMUFile *f, void *opaque) +{ + PITState *s = 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; + } + + pit_save(f, s); +} + +static int kvm_pit_load(QEMUFile *f, void *opaque, int version_id) +{ + PITState *s = opaque; + struct kvm_pit_state2 pit2; + struct kvm_pit_channel_state *c; + struct PITChannelState *sc; + int i; + + pit_load(f, s, version_id); + + 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; +} + +PITState *kvm_pit_init(int base, qemu_irq irq) +{ + PITState *pit = &pit_state; + + register_savevm(PIT_SAVEVM_NAME, base, PIT_SAVEVM_VERSION, + kvm_pit_save, kvm_pit_load, pit); + + qemu_register_reset(pit_reset, pit); + pit_reset(pit); + + return pit; +} diff --git a/hw/i8254.c b/hw/i8254.c index 44e453139..34a716c1b 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); @@ -224,13 +197,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 */ @@ -290,17 +268,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; } @@ -360,6 +338,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; @@ -370,16 +353,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) / 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) @@ -389,12 +391,13 @@ static void pit_irq_timer(void *opaque) pit_irq_timer_update(s, s->next_transition_time); } -static void pit_save(QEMUFile *f, void *opaque) +void pit_save(QEMUFile *f, void *opaque) { PITState *pit = opaque; PITChannelState *s; int i; + qemu_put_be32(f, pit->flags); for(i = 0; i < 3; i++) { s = &pit->channels[i]; qemu_put_be32(f, s->count); @@ -417,15 +420,16 @@ static void pit_save(QEMUFile *f, void *opaque) } } -static int pit_load(QEMUFile *f, void *opaque, int version_id) +int pit_load(QEMUFile *f, void *opaque, int version_id) { PITState *pit = opaque; 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,44 +450,71 @@ static int pit_load(QEMUFile *f, void *opaque, int version_id) qemu_get_timer(f, s->irq_timer); } } + return 0; } -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) { @@ -495,7 +526,8 @@ PITState *pit_init(int base, qemu_irq irq) s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s); s->irq = irq; - register_savevm("i8254", base, 1, pit_save, pit_load, pit); + register_savevm(PIT_SAVEVM_NAME, base, PIT_SAVEVM_VERSION, + pit_save, pit_load, pit); qemu_register_reset(pit_reset, pit); register_ioport_write(base, 4, 1, pit_ioport_write, pit); 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 0b9fab5b4..19c9d170a 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 @@ -184,7 +186,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) @@ -218,18 +219,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) { @@ -451,7 +469,7 @@ static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1) return s->elcr; } -static void pic_save(QEMUFile *f, void *opaque) +static void pic_save_common(QEMUFile *f, void *opaque) { PicState *s = opaque; @@ -473,7 +491,12 @@ static void pic_save(QEMUFile *f, void *opaque) qemu_put_8s(f, &s->elcr); } -static int pic_load(QEMUFile *f, void *opaque, int version_id) +static void pic_save(QEMUFile *f, void *opaque) +{ + pic_save_common(f, opaque); +} + +static int pic_load_common(QEMUFile *f, void *opaque, int version_id) { PicState *s = opaque; @@ -496,9 +519,15 @@ static int pic_load(QEMUFile *f, void *opaque, int version_id) qemu_get_8s(f, &s->init4); qemu_get_8s(f, &s->single_mode); qemu_get_8s(f, &s->elcr); + return 0; } +static int pic_load(QEMUFile *f, void *opaque, int version_id) +{ + return pic_load_common(f, opaque, version_id); +} + /* XXX: add generic master/slave system */ static void pic_init1(int io_addr, int elcr_addr, PicState *s) { @@ -569,3 +598,118 @@ void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, s->alt_irq_func = alt_irq_func; s->alt_irq_opaque = alt_irq_opaque; } + +#ifdef KVM_CAP_IRQCHIP +static void kvm_kernel_pic_save_to_user(PicState *s) +{ +#if defined(TARGET_I386) + 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 void kvm_kernel_pic_load_from_user(PicState *s) +{ +#if defined(TARGET_I386) + 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 +} + +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_save(QEMUFile *f, void *opaque) +{ + PicState *s = opaque; + + kvm_kernel_pic_save_to_user(s); + pic_save_common(f, opaque); +} + +static int kvm_pic_load(QEMUFile *f, void *opaque, int version_id) +{ + PicState *s = opaque; + int r = pic_load_common(f, s, version_id); + if (r == 0) + kvm_kernel_pic_load_from_user(s); + return r; +} + +static void kvm_pic_init1(int io_addr, PicState *s) +{ + register_savevm("i8259", io_addr, 1, kvm_pic_save, kvm_pic_load, 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, 16); +} +#endif + + diff --git a/hw/ioapic.c b/hw/ioapic.c index a5cdd5d9a..32019de66 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,13 +197,62 @@ 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_save(QEMUFile *f, void *opaque) { IOAPICState *s = opaque; int i; + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + kvm_kernel_ioapic_save_to_user(s); + } + qemu_put_8s(f, &s->id); qemu_put_8s(f, &s->ioregsel); + qemu_put_be64s(f, &s->base_address); + qemu_put_be32s(f, &s->irr); for (i = 0; i < IOAPIC_NUM_PINS; i++) { qemu_put_be64s(f, &s->ioredtbl[i]); } @@ -208,14 +263,29 @@ static int ioapic_load(QEMUFile *f, void *opaque, int version_id) IOAPICState *s = opaque; int i; - if (version_id != 1) + if (version_id < 1 || version_id > 2) return -EINVAL; qemu_get_8s(f, &s->id); qemu_get_8s(f, &s->ioregsel); + if (version_id == 2) { + /* for version 2, we get this data off of the wire */ + qemu_get_be64s(f, &s->base_address); + qemu_get_be32s(f, &s->irr); + } + else { + /* in case we are doing version 1, we just set these to sane values */ + s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS; + s->irr = 0; + } for (i = 0; i < IOAPIC_NUM_PINS; i++) { qemu_get_be64s(f, &s->ioredtbl[i]); } + + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + kvm_kernel_ioapic_load_from_user(s); + } + return 0; } @@ -225,8 +295,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() && qemu_kvm_irqchip_in_kernel()) { + kvm_kernel_ioapic_load_from_user(s); + } +#endif } static CPUReadMemoryFunc *ioapic_mem_read[3] = { @@ -253,7 +329,7 @@ IOAPICState *ioapic_init(void) ioapic_mem_write, s); cpu_register_physical_memory(0xfec00000, 0x1000, io_memory); - register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s); + register_savevm("ioapic", 0, 2, ioapic_save, ioapic_load, s); qemu_register_reset(ioapic_reset, s); return s; diff --git a/hw/ipf.c b/hw/ipf.c new file mode 100644 index 000000000..04b7b2c50 --- /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 USE_KVM_DEVICE_ASSIGNMENT + if (kvm_enabled()) + add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index); +#endif /* USE_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,7 @@ #include "hw.h" #include "msix.h" #include "pci.h" +#include "qemu-kvm.h" /* Declaration from linux/pci_regs.h */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ @@ -62,6 +63,110 @@ /* 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; + + 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. @@ -109,6 +214,9 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, static void msix_free_irq_entries(PCIDevice *dev) { int vector; + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + kvm_msix_free(dev); + } for (vector = 0; vector < dev->msix_entries_nr; ++vector) dev->msix_entry_used[vector] = 0; @@ -181,7 +289,11 @@ static void msix_mmio_writel(void *opaque, target_phys_addr_t addr, PCIDevice *dev = opaque; unsigned int offset = addr & (MSIX_PAGE_SIZE - 1); int vector = offset / MSIX_ENTRY_SIZE; + int was_masked = msix_is_masked(dev, vector); memcpy(dev->msix_table_page + offset, &val, 4); + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + kvm_msix_update(dev, vector, was_masked, msix_is_masked(dev, vector)); + } if (!msix_is_masked(dev, vector) && msix_is_pending(dev, vector)) { msix_clr_pending(dev, vector); msix_notify(dev, vector); @@ -234,6 +346,12 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, if (nentries > MSIX_MAX_ENTRIES) return -EINVAL; +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && qemu_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); @@ -279,6 +397,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; } @@ -287,10 +407,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); } @@ -300,6 +423,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; } @@ -344,6 +470,13 @@ void msix_notify(PCIDevice *dev, unsigned vector) return; } +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && qemu_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); @@ -370,15 +503,29 @@ 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() && qemu_kvm_irqchip_in_kernel()) { + ret = kvm_msix_add(dev, vector); + if (ret) { + return ret; + } + } + ++dev->msix_entry_used[vector]; return 0; } /* Mark vector as unused. */ void msix_vector_unuse(PCIDevice *dev, unsigned vector) { - if (vector < dev->msix_entries_nr && dev->msix_entry_used[vector]) + if (vector < dev->msix_entries_nr && dev->msix_entry_used[vector]) { --dev->msix_entry_used[vector]; + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + kvm_msix_del(dev, vector); + } + } } @@ -36,6 +36,9 @@ #include "hpet_emul.h" #include "watchdog.h" #include "smbios.h" +#include "device-assignment.h" + +#include "qemu-kvm.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -46,6 +49,7 @@ #define BIOS_FILENAME "bios.bin" #define VGABIOS_FILENAME "vgabios.bin" #define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin" +#define EXTBOOT_FILENAME "extboot.bin" #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024) @@ -54,6 +58,7 @@ #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) #define MAX_IDE_BUS 2 @@ -476,6 +481,7 @@ static void *bochs_bios_init(void) fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, (uint8_t *)acpi_tables, acpi_tables_len); + fw_cfg_add_bytes(fw_cfg, FW_CFG_IRQ0_OVERRIDE, &irq0override, 1); smbios_table = smbios_get_table(&smbios_len); if (smbios_table) @@ -1081,22 +1087,25 @@ 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; + CPUState *env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find x86 CPU definition\n"); + exit(1); + } + if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) { + env->cpuid_apic_id = env->cpu_index; + /* APIC reset callback resets cpu */ + apic_init(env); + } else { + qemu_register_reset((QEMUResetHandler*)cpu_reset, env); + } - env = cpu_init(cpu_model); - if (!env) { - fprintf(stderr, "Unable to find x86 CPU definition\n"); - exit(1); - } - if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) { - env->cpuid_apic_id = env->cpu_index; - /* APIC reset callback resets cpu */ - apic_init(env); - } 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; } @@ -1114,6 +1123,7 @@ static void pc_init1(ram_addr_t ram_size, ram_addr_t ram_addr, bios_offset, option_rom_offset; ram_addr_t below_4g_mem_size, above_4g_mem_size = 0; int bios_size, isa_bios_size, oprom_area_size; + int pci_option_rom_offset = 0; PCIBus *pci_bus; PCIDevice *pci_dev; int piix3_devfn = -1; @@ -1144,6 +1154,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); } @@ -1151,18 +1164,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) { @@ -1204,11 +1210,16 @@ 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 != -1) { + option_rom[nb_option_roms++] = qemu_strdup(EXTBOOT_FILENAME); + } option_rom_offset = qemu_ram_alloc(0x20000); oprom_area_size = 0; @@ -1223,6 +1234,7 @@ static void pc_init1(ram_addr_t ram_size, vgabios_filename = VGABIOS_FILENAME; } oprom_area_size = load_option_rom(vgabios_filename, 0xc0000, 0xe0000); + pci_option_rom_offset = oprom_area_size; } /* Although video roms can grow larger than 0x8000, the area between * 0xc0000 - 0xc8000 is reserved for them. It means we won't be looking @@ -1255,7 +1267,7 @@ static void pc_init1(ram_addr_t ram_size, continue; if (model == NULL) - model = "ne2k_pci"; + model = "rtl8139"; snprintf(nic_oprom, sizeof(nic_oprom), "pxe-%s.bin", model); oprom_area_size += load_option_rom(nic_oprom, 0xc0000 + oprom_area_size, @@ -1263,7 +1275,12 @@ 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]); +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) { + i8259 = kvm_i8259_init(cpu_irq[0]); + } else +#endif + i8259 = i8259_init(cpu_irq[0]); ferr_irq = i8259[13]; if (pci_enabled) { @@ -1307,7 +1324,12 @@ static void pc_init1(ram_addr_t ram_size, if (pci_enabled) { ioapic = ioapic_init(); } - pit = pit_init(0x40, i8259[0]); +#ifdef USE_KVM_PIT + if (kvm_enabled() && qemu_kvm_pit_in_kernel()) + pit = kvm_pit_init(0x40, i8259[0]); + else +#endif + pit = pit_init(0x40, i8259[0]); pcspk_init(pit); if (!no_hpet) { hpet_init(i8259); @@ -1338,10 +1360,10 @@ 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, i8259); else - pci_nic_init(nd, "ne2k_pci", NULL); + pci_nic_init(nd, "rtl8139", NULL); } - piix4_acpi_system_hot_add_init(); + piix4_acpi_system_hot_add_init(cpu_model); if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { fprintf(stderr, "qemu: too many IDE bus\n"); @@ -1428,6 +1450,18 @@ static void pc_init1(ram_addr_t ram_size, } } + if (extboot_drive != -1) { + DriveInfo *info = &drives_table[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 balloon device */ if (pci_enabled && virtio_balloon) { pci_dev = pci_create("virtio-balloon-pci", virtio_balloon_devaddr); @@ -1442,6 +1476,13 @@ static void pc_init1(ram_addr_t ram_size, } } } + +#ifdef USE_KVM_DEVICE_ASSIGNMENT + if (kvm_enabled()) { + add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index); + assigned_dev_load_option_roms(pci_option_rom_offset); + } +#endif /* USE_KVM_DEVICE_ASSIGNMENT */ } static void pc_init_pci(ram_addr_t ram_size, @@ -32,6 +32,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); void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, void *alt_irq_opaque); int pic_read_irq(PicState2 *s); @@ -54,6 +55,7 @@ IOAPICState *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 */ @@ -68,8 +70,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); @@ -102,6 +108,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; @@ -115,7 +122,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(void); +void piix4_acpi_system_hot_add_init(const char *model); /* hpet.c */ extern int no_hpet; @@ -125,6 +132,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 + PCIBus *i440fx_init(PCIDevice **pi440fx_state, qemu_irq *pic); void i440fx_set_smm(PCIDevice *d, int val); int piix3_init(PCIBus *bus, int devfn); @@ -133,6 +143,10 @@ void i440fx_init_memory_mappings(PCIDevice *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, @@ -165,5 +179,10 @@ void pci_piix4_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn, void isa_ne2000_init(int base, qemu_irq 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 d0f2911d6..1180758da 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -31,6 +31,7 @@ #include "monitor.h" #include "block_int.h" #include "virtio-blk.h" +#include "device-assignment.h" #if defined(TARGET_I386) || defined(TARGET_X86_64) static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, @@ -143,6 +144,45 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, return dev; } +#ifdef USE_KVM_DEVICE_ASSIGNMENT +static PCIDevice *qemu_pci_hot_assign_device(Monitor *mon, + const char *devaddr, + const char *opts) +{ + AssignedDevInfo *adev; + PCIDevice *ret; + + adev = add_assigned_device(opts); + if (adev == NULL) { + monitor_printf(mon, "Error adding device; check syntax\n"); + return NULL; + } + + ret = init_assigned_device(adev, devaddr); + if (ret == NULL) { + monitor_printf(mon, "Failed to assign device\n"); + return NULL; + } + + monitor_printf(mon, + "Registered host PCI device %02x:%02x.%1x " + "(\"%s\") as guest device %s\n", + adev->bus, adev->dev, adev->func, adev->name, devaddr); + + return ret; +} + +static void qemu_pci_hot_deassign_device(Monitor *mon, AssignedDevInfo *adev) +{ + remove_assigned_device(adev); + + monitor_printf(mon, + "Unregister host PCI device %02x:%02x.%1x " + "(\"%s\") from guest\n", + adev->bus, adev->dev, adev->func, adev->name); +} +#endif /* USE_KVM_DEVICE_ASSIGNMENT */ + void pci_device_hot_add(Monitor *mon, const char *pci_addr, const char *type, const char *opts) { @@ -164,6 +204,10 @@ void pci_device_hot_add(Monitor *mon, const char *pci_addr, const char *type, 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 USE_KVM_DEVICE_ASSIGNMENT + else if (strcmp(type, "host") == 0) + dev = qemu_pci_hot_assign_device(mon, pci_addr, opts); +#endif /* USE_KVM_DEVICE_ASSIGNMENT */ else monitor_printf(mon, "invalid type: %s\n", type); @@ -212,12 +256,23 @@ void pci_device_hot_remove_success(int pcibus, int slot) { PCIDevice *d = pci_find_device(pcibus, slot, 0); int class_code; +#ifdef USE_KVM_DEVICE_ASSIGNMENT + AssignedDevInfo *adev; +#endif if (!d) { monitor_printf(cur_mon, "invalid slot %d\n", slot); return; } +#ifdef USE_KVM_DEVICE_ASSIGNMENT + adev = get_assigned_device(pcibus, slot); + if (adev) { + qemu_pci_hot_deassign_device(cur_mon, adev); + return; + } +#endif /* USE_KVM_DEVICE_ASSIGNMENT */ + class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1); switch(class_code) { @@ -229,6 +284,6 @@ void pci_device_hot_remove_success(int pcibus, int slot) break; } - pci_unregister_device(d); + pci_unregister_device(d, 0); } @@ -26,6 +26,9 @@ #include "monitor.h" #include "net.h" #include "sysemu.h" +#include "pc.h" +#include "qemu-kvm.h" +#include "device-assignment.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -206,6 +209,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) @@ -254,6 +258,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) { @@ -268,7 +321,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, return 0; } -static PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) +PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) { int dom, bus; unsigned slot; @@ -380,7 +433,7 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev) } } -int pci_unregister_device(PCIDevice *pci_dev) +int pci_unregister_device(PCIDevice *pci_dev, int assigned) { int ret = 0; @@ -393,7 +446,11 @@ int pci_unregister_device(PCIDevice *pci_dev) qemu_free_irqs(pci_dev->irq); pci_dev->bus->devices[pci_dev->devfn] = NULL; - qdev_free(&pci_dev->qdev); + + if (assigned) + qemu_free(pci_dev); + else + qdev_free(&pci_dev->qdev); return 0; } @@ -513,8 +570,8 @@ 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; @@ -539,21 +596,74 @@ uint32_t pci_default_read_config(PCIDevice *d, return val; } +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); +} + +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len) +{ + if (pci_access_cap_config(d, address, len)) + return d->cap.config_read(d, address, len); + + return pci_read_config(d, address, len); +} + void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) { uint8_t orig[PCI_CONFIG_SPACE_SIZE]; int i; + if (pci_access_cap_config(d, addr, l)) { + d->cap.config_write(d, addr, val, l); + return; + } + /* not efficient, but simple */ memcpy(orig, d->config, PCI_CONFIG_SPACE_SIZE); for(i = 0; i < l && addr < PCI_CONFIG_SPACE_SIZE; val >>= 8, ++i, ++addr) { uint8_t wmask = d->wmask[addr]; d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask); } + +#ifdef USE_KVM_DEVICE_ASSIGNMENT + if (kvm_enabled() && qemu_kvm_irqchip_in_kernel() && + addr >= PIIX_CONFIG_IRQ_ROUTE && + addr < PIIX_CONFIG_IRQ_ROUTE + 4) + assigned_dev_update_irqs(); +#endif /* USE_KVM_DEVICE_ASSIGNMENT */ + if (memcmp(orig + PCI_BASE_ADDRESS_0, d->config + PCI_BASE_ADDRESS_0, 24) || ((orig[PCI_COMMAND] ^ d->config[PCI_COMMAND]) & (PCI_COMMAND_MEMORY | PCI_COMMAND_IO))) pci_update_mappings(d); + } void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len) @@ -636,6 +746,11 @@ static void pci_set_irq(void *opaque, int irq_num, int level) return; pci_dev->irq_state[irq_num] = level; + +#if defined(TARGET_IA64) + ioapic_set_irq(pci_dev, irq_num, level); +#endif + for (;;) { bus = pci_dev->bus; irq_num = bus->map_irq(pci_dev, irq_num); @@ -647,6 +762,11 @@ static void pci_set_irq(void *opaque, int irq_num, int level) bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); } +int pci_map_irq(PCIDevice *pci_dev, int pin) +{ + return pci_dev->bus->map_irq(pci_dev, pin); +} + /***********************************************************/ /* monitor info on PCI */ @@ -936,6 +1056,37 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) return (PCIDevice *)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 offset = PCI_CONFIG_HEADER_SIZE; @@ -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)) @@ -78,6 +83,12 @@ typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_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); + #define PCI_ADDRESS_SPACE_MEM 0x00 #define PCI_ADDRESS_SPACE_IO 0x01 #define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08 @@ -133,10 +144,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 | \ @@ -160,6 +180,11 @@ enum { QEMU_PCI_CAP_MSIX = 0x1, }; +#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 */ @@ -209,18 +234,35 @@ struct PCIDevice { unsigned *msix_entry_used; /* Region including the MSI-X table */ uint32_t msix_bar_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, int instance_size, int devfn, PCIConfigReadFunc *config_read, PCIConfigWriteFunc *config_write); -int pci_unregister_device(PCIDevice *pci_dev); +int pci_unregister_device(PCIDevice *pci_dev, int assigned); void pci_register_bar(PCIDevice *pci_dev, int region_num, uint32_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); @@ -229,13 +271,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)(qemu_irq *pic, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); @@ -255,6 +301,10 @@ PCIDevice *pci_find_device(int bus_num, int slot, int function); 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); +PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); + 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..3d3eba44a 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 USE_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 fce01d484..2a5fb01e8 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -26,6 +26,8 @@ #include "pc.h" #include "pci.h" +#include "qemu-kvm.h" + typedef uint32_t pci_addr_t; #include "pci_host.h" @@ -91,6 +93,10 @@ static void i440fx_update_memory_mappings(PCIDevice *d) int i, r; uint32_t smram, addr; + if (kvm_enabled()) { + /* FIXME: Support remappings and protection changes. */ + return; + } update_pam(d, 0xf0000, 0x100000, (d->config[0x59] >> 4) & 3); for(i = 0; i < 12; i++) { r = (d->config[(i >> 1) + 0x5a] >> ((i & 1) * 4)) & 3; @@ -232,6 +238,16 @@ static void piix3_set_irq(qemu_irq *pic, int irq_num, int level) } } +int piix_get_irq(int pin) +{ + if (piix3_dev) + return piix3_dev->config[0x60+pin]; + if (piix4_dev) + return piix4_dev->config[0x60+pin]; + + return 0; +} + static void piix3_reset(void *opaque) { PCIDevice *d = opaque; diff --git a/hw/ppc440.c b/hw/ppc440.c index 5171988b6..aefc0191f 100644 --- a/hw/ppc440.c +++ b/hw/ppc440.c @@ -19,6 +19,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 d9ef3eca8..c74aa2f39 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -22,6 +22,7 @@ #include "kvm.h" #include "kvm_ppc.h" #include "device_tree.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 c0e367de6..db52cdd06 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -29,6 +29,7 @@ #include "device_tree.h" #include "openpic.h" #include "ppce500.h" +#include "qemu-kvm.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 @@ -29,6 +29,7 @@ #include "pixel_ops.h" #include "qemu-timer.h" #include "kvm.h" +#include "qemu-kvm.h" //#define DEBUG_VGA //#define DEBUG_VGA_MEM @@ -1281,6 +1282,8 @@ static void vga_draw_text(VGAState *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; @@ -1579,6 +1582,7 @@ static void vga_sync_dirty_bitmap(VGAState *s) cpu_physical_sync_dirty_bitmap(isa_mem_base + 0xa0000, 0xa8000); cpu_physical_sync_dirty_bitmap(isa_mem_base + 0xa8000, 0xb0000); } + vga_dirty_log_start(s); } /* @@ -1810,6 +1814,7 @@ static void vga_draw_blank(VGAState *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)]; @@ -1855,6 +1860,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: @@ -2226,17 +2234,48 @@ typedef struct PCIVGAState { VGAState vga_state; } PCIVGAState; +static int s1, s2; + +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(VGAState *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; } } +void vga_dirty_log_stop(VGAState *s) +{ + 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 && s2) { + kvm_log_stop(isa_mem_base + 0xa0000, 0x8000); + kvm_log_stop(isa_mem_base + 0xa8000, 0x8000); + } + s1 = s2 = 0; +} + static void vga_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { @@ -2246,10 +2285,12 @@ static void vga_map(PCIDevice *pci_dev, int region_num, cpu_register_physical_memory(addr, s->bios_size, s->bios_offset); } else { cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); - s->map_addr = addr; - s->map_end = addr + s->vram_size; - vga_dirty_log_start(s); } + + s->map_addr = addr; + s->map_end = addr + VGA_RAM_SIZE; + + vga_dirty_log_start(s); } void vga_common_init(VGAState *s, int vga_ram_size) @@ -2477,9 +2518,11 @@ static void pci_vga_write_config(PCIDevice *d, PCIVGAState *pvs = container_of(d, PCIVGAState, dev); VGAState *s = &pvs->vga_state; + 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); } int pci_vga_init(PCIBus *bus, diff --git a/hw/vga_int.h b/hw/vga_int.h index 8fdf51dc6..be174f3f3 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -30,8 +30,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 @@ -195,6 +195,7 @@ void vga_init(VGAState *s); void vga_reset(void *s); void vga_dirty_log_start(VGAState *s); +void vga_dirty_log_stop(VGAState *s); uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr); void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val); @@ -217,5 +218,5 @@ void vga_draw_cursor_line_32(uint8_t *d1, const uint8_t *src1, 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) 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 04f8873b3..663c8b9d1 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/virtio-net.c b/hw/virtio-net.c index 7a7eafe90..ce8e6cb7a 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -15,6 +15,11 @@ #include "net.h" #include "qemu-timer.h" #include "virtio-net.h" +#ifdef CONFIG_KVM +#include "qemu-kvm.h" +#endif + +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10 @@ -130,6 +135,25 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 << VIRTIO_NET_F_CTRL_VLAN) | (1 << VIRTIO_NET_F_CTRL_RX_EXTRA); +#ifdef TAP_VNET_HDR + VirtIONet *n = to_virtio_net(vdev); + VLANClientState *host = n->vc->vlan->first_client; + + if (tap_has_vnet_hdr(host)) { + tap_using_vnet_hdr(host, 1); + features |= (1 << VIRTIO_NET_F_CSUM); + features |= (1 << VIRTIO_NET_F_GUEST_CSUM); + features |= (1 << VIRTIO_NET_F_GUEST_TSO4); + features |= (1 << VIRTIO_NET_F_GUEST_TSO6); + features |= (1 << VIRTIO_NET_F_GUEST_ECN); + features |= (1 << VIRTIO_NET_F_HOST_TSO4); + features |= (1 << VIRTIO_NET_F_HOST_TSO6); + features |= (1 << VIRTIO_NET_F_HOST_ECN); + features |= (1 << VIRTIO_NET_F_MRG_RXBUF); + /* Kernel can't actually handle UFO in software currently. */ + } +#endif + return features; } @@ -151,8 +175,22 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); +#ifdef TAP_VNET_HDR + VLANClientState *host = n->vc->vlan->first_client; +#endif n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); + +#ifdef TAP_VNET_HDR + if (!tap_has_vnet_hdr(host) || !host->set_offload) + return; + + host->set_offload(host, + (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, + (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, + (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, + (features >> VIRTIO_NET_F_GUEST_ECN) & 1); +#endif } static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, @@ -305,6 +343,10 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = to_virtio_net(vdev); qemu_flush_queued_packets(n->vc); + + /* We now have RX buffers, signal to the IO thread to break out of the + * select to re-poll the tap file descriptor */ + qemu_notify_event(); } static int do_virtio_net_can_receive(VirtIONet *n, int bufsize) @@ -331,6 +373,36 @@ static int virtio_net_can_receive(VLANClientState *vc) return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE); } +#ifdef TAP_VNET_HDR +/* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so + * it never finds out that the packets don't have valid checksums. This + * causes dhclient to get upset. Fedora's carried a patch for ages to + * fix this with Xen but it hasn't appeared in an upstream release of + * dhclient yet. + * + * To avoid breaking existing guests, we catch udp packets and add + * checksums. This is terrible but it's better than hacking the guest + * kernels. + * + * N.B. if we introduce a zero-copy API, this operation is no longer free so + * we should provide a mechanism to disable it to avoid polluting the host + * cache. + */ +static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, + const uint8_t *buf, size_t size) +{ + if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ + (size > 27 && size < 1500) && /* normal sized MTU */ + (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ + (buf[23] == 17) && /* ip.protocol == UDP */ + (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ + /* FIXME this cast is evil */ + net_checksum_calculate((uint8_t *)buf, size); + hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; + } +} +#endif + static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) { int offset, i; @@ -347,7 +419,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) } static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, - const void *buf, size_t size, size_t hdr_len) + const void *buf, size_t size, size_t hdr_len, int raw) { struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)iov[0].iov_base; int offset = 0; @@ -355,6 +427,18 @@ static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, hdr->flags = 0; hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; +#ifdef TAP_VNET_HDR + if (tap_has_vnet_hdr(n->vc->vlan->first_client)) { + if (!raw) { + memcpy(hdr, buf, sizeof(*hdr)); + } else { + memset(hdr, 0, sizeof(*hdr)); + } + offset = sizeof(*hdr); + work_around_broken_dhclient(hdr, buf + offset, size - offset); + } +#endif + /* We only ever receive a struct virtio_net_hdr from the tapfd, * but we may be passing along a larger header to the guest. */ @@ -374,6 +458,11 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) if (n->promisc) return 1; +#ifdef TAP_VNET_HDR + if (tap_has_vnet_hdr(n->vc->vlan->first_client)) + ptr += sizeof(struct virtio_net_hdr); +#endif + if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff; if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) @@ -413,7 +502,7 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) return 0; } -static ssize_t virtio_net_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size_t size, int raw) { VirtIONet *n = vc->opaque; struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; @@ -463,7 +552,7 @@ static ssize_t virtio_net_receive(VLANClientState *vc, const uint8_t *buf, size_ mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; offset += receive_header(n, sg, elem.in_num, - buf + offset, size - offset, hdr_len); + buf + offset, size - offset, hdr_len, raw); total += hdr_len; } @@ -502,11 +591,25 @@ static void virtio_net_tx_complete(VLANClientState *vc, ssize_t len) virtio_net_flush_tx(n, n->tx_vq); } +static ssize_t virtio_net_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +{ + return virtio_net_receive2(vc, buf, size, 0); +} + +static ssize_t virtio_net_receive_raw(VLANClientState *vc, const uint8_t *buf, size_t size) +{ + return virtio_net_receive2(vc, buf, size, 1); +} + /* TX */ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) { VirtQueueElement elem; +#ifdef TAP_VNET_HDR + int has_vnet_hdr = tap_has_vnet_hdr(n->vc->vlan->first_client); +#else int has_vnet_hdr = 0; +#endif if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) return; @@ -606,7 +709,13 @@ static void virtio_net_save(QEMUFile *f, void *opaque) qemu_put_be32(f, n->mac_table.in_use); qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); - qemu_put_be32(f, 0); /* vnet-hdr placeholder */ + +#ifdef TAP_VNET_HDR + qemu_put_be32(f, tap_has_vnet_hdr(n->vc->vlan->first_client)); +#else + qemu_put_be32(f, 0); +#endif + qemu_put_byte(f, n->mac_table.multi_overflow); qemu_put_byte(f, n->mac_table.uni_overflow); qemu_put_byte(f, n->alluni); @@ -659,9 +768,13 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); if (version_id >= 7 && qemu_get_be32(f)) { +#ifdef TAP_VNET_HDR + tap_using_vnet_hdr(n->vc->vlan->first_client, 1); +#else fprintf(stderr, "virtio-net: saved image requires vnet header support\n"); exit(1); +#endif } if (version_id >= 9) { @@ -734,6 +847,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) virtio_net_receive, NULL, virtio_net_cleanup, n); n->vc->link_status_changed = virtio_net_set_link_status; + n->vc->receive_raw = virtio_net_receive_raw; qemu_format_nic_info_str(n->vc, n->mac); 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) @@ -9,7 +9,7 @@ SEARCH_DIR("/usr/ia64-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/li 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 @@ -382,6 +383,7 @@ int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) return ret; } +#endif int kvm_check_extension(KVMState *s, unsigned int extension) { int ret; @@ -393,6 +395,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension) return ret; } +#ifdef KVM_UPSTREAM int kvm_init(int smp_cpus) { @@ -792,6 +795,7 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr, } } +#endif int kvm_ioctl(KVMState *s, int type, ...) { int ret; @@ -826,6 +830,7 @@ int kvm_vm_ioctl(KVMState *s, int type, ...) return ret; } +#ifdef KVM_UPSTREAM int kvm_vcpu_ioctl(CPUState *env, int type, ...) { int ret; @@ -872,6 +877,8 @@ void kvm_setup_guest_memory(void *start, size_t size) } } +#endif /* KVM_UPSTREAM */ + #ifdef KVM_CAP_SET_GUEST_DEBUG struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, target_ulong pc) @@ -890,6 +897,7 @@ int kvm_sw_breakpoints_active(CPUState *env) return !TAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); } +#ifdef KVM_UPSTREAM int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) { struct kvm_guest_debug dbg; @@ -903,6 +911,7 @@ int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) return kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg); } +#endif int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, target_ulong len, int type) @@ -1027,3 +1036,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..f7b6f3bb4 --- /dev/null +++ b/kvm-tpr-opt.c @@ -0,0 +1,390 @@ +/* + * 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->kvm_cpu_state.vcpu_ctx, &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->kvm_cpu_state.vcpu_ctx, &sregs); + stb_phys(map_addr(&sregs, virt, NULL), b); +} + +static __u64 kvm_rsp_read(CPUState *env) +{ + struct kvm_regs regs; + + kvm_get_regs(env->kvm_cpu_state.vcpu_ctx, ®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 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; + + if (bios_enabled) + return 1; + + kvm_get_sregs(env->kvm_cpu_state.vcpu_ctx, &sregs); + + probe = (rip & 0xf0000000) + 0xe0000; + phys = map_addr(&sregs, probe, &perms); + if (phys != 0xe0000) + 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; + + kvm_save_registers(env); + + if (cpu_memory_rw_debug(env, env->segs[R_FS].base + 0x51, &b, 1, 0) < 0) + return -1; + + return (int)b; +} + +static int 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->kvm_cpu_state.vcpu_ctx, vapic_phys + (pcr_cpu << 7)); + cpu_physical_memory_rw(vapic_phys + (pcr_cpu << 7) + 4, &one, 1, 1); + bios_enabled = 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 (!enable_vapic(env)) + return; + patch_instruction(env, rip); +} + +void kvm_tpr_vcpu_start(CPUState *env) +{ + kvm_enable_tpr_access_reporting(env->kvm_cpu_state.vcpu_ctx); + if (bios_enabled) + 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_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->kvm_cpu_state.vcpu_ctx, ®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->kvm_cpu_state.vcpu_ctx, &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(); + 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); +} + @@ -16,6 +16,9 @@ #include "config.h" #include "sys-queue.h" +#include "qemu-kvm.h" + +#ifdef KVM_UPSTREAM #ifdef CONFIG_KVM extern int kvm_allowed; @@ -139,3 +142,5 @@ static inline void cpu_synchronize_state(CPUState *env, int modified) } #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/bios/.cvsignore b/kvm/bios/.cvsignore new file mode 100644 index 000000000..f3c7a7c5d --- /dev/null +++ b/kvm/bios/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/kvm/bios/Makefile b/kvm/bios/Makefile new file mode 100644 index 000000000..434d64e8b --- /dev/null +++ b/kvm/bios/Makefile @@ -0,0 +1,136 @@ +# Copyright (C) 2001 MandrakeSoft S.A. +# +# MandrakeSoft S.A. +# 43, rue d'Aboukir +# 75002 Paris - France +# http://www.linux-mandrake.com/ +# http://www.mandrakesoft.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 + +# Makefile for the BIOS component of bochs + + +.SUFFIXES: .cc + +srcdir = . + + +SHELL = /bin/sh + +CXX = g++ +CXXFLAGS = -g -O2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES + +# cc-option, copied from user/Makefile +# 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 = -m32 +CFLAGS += $(call cc-option, -fno-stack-protector, "") +CFLAGS += $(call cc-option, -fno-stack-protector-all, "") + +LDFLAGS = +LIBS = -lm +RANLIB = ranlib + +BCC = bcc +GCC = gcc $(CFLAGS) +HOST_CC = gcc +AS86 = as86 + +BX_INCDIRS = -I.. -I$(srcdir)/.. -I../iodev -I$(srcdir)/../iodev +LOCAL_CXXFLAGS = + +BUILDDATE = `date '+%m/%d/%y'` +BIOS_BUILD_DATE = "-DBIOS_BUILD_DATE=\"$(BUILDDATE)\"" +# +# -------- end configurable options -------------------------- +# + + +.cc.o: + $(CXX) -c $(BX_INCDIRS) $(CXXFLAGS) $(LOCAL_CXXFLAGS) $< -o $@ + + +bios: biossums BIOS-bochs-latest BIOS-bochs-legacy + +clean: + rm -f *.o *.a *.s _rombios*_.c rombios*.txt rombios*.sym + rm -f usage biossums rombios16.bin + rm -f rombios32.bin rombios32.out acpi-dsdt.hex acpi-ssdt.hex + +dist-clean: clean + rm -f Makefile + +bios-clean: + rm -f BIOS-bochs-* + +BIOS-bochs-legacy: rombios.c apmbios.S biossums rombios.h + $(GCC) $(BIOS_BUILD_DATE) -DLEGACY -E -P $< > _rombiosl_.c + $(BCC) -o rombiosl.s -C-c -D__i86__ -0 -S _rombiosl_.c + sed -e 's/^\.text//' -e 's/^\.data//' rombiosl.s > _rombiosl_.s + $(AS86) _rombiosl_.s -b tmpl.bin -u- -w- -g -0 -j -O -l rombiosl.txt + -perl ${srcdir}/makesym.perl < rombiosl.txt > rombiosl.sym + mv tmpl.bin $@ + ./biossums $@ + rm -f _rombiosl_.s + + +rombios16.bin: rombios.c apmbios.S biossums rombios.h + $(GCC) $(BIOS_BUILD_DATE) -E -P $< > _rombios_.c + $(BCC) -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c + sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s + $(AS86) _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt + -perl ${srcdir}/makesym.perl < rombios.txt > rombios.sym + mv tmp.bin rombios16.bin + ./biossums rombios16.bin + rm -f _rombios_.s + + +rombios32.bin: rombios32.out rombios.h + objcopy -O binary $< $@ + ./biossums -pad $@ + +rombios32.out: rombios32start.o rombios32.o vapic.o rombios32.ld + ld -o $@ -T rombios32.ld rombios32start.o vapic.o rombios32.o + +rombios32.o: rombios32.c acpi-dsdt.hex acpi-ssdt.hex + $(GCC) -m32 -O2 -Wall -c -o $@ $< + +acpi-dsdt.hex: acpi-dsdt.dsl + cpp -P $< $<.i + iasl -tc -p $@ $<.i + sed -i -e's/^unsigned char AmlCode/const unsigned char DSDTCode/' $@ + rm $<.i + +acpi-ssdt.hex: acpi-ssdt.dsl + cpp -P $< $<.i + iasl -tc -p $@ $<.i + sed -i -e's/^unsigned char AmlCode/const unsigned char SSDTCode/' $@ + rm $<.i + +rombios32start.o: rombios32start.S + $(GCC) -m32 -c -o $@ $< + +vapic.o: vapic.S + $(GCC) -m32 -c -o $@ $< + +BIOS-bochs-latest: rombios16.bin rombios32.bin + cat rombios32.bin rombios16.bin > $@ + +biossums: biossums.c + $(HOST_CC) -o biossums biossums.c diff --git a/kvm/bios/Makefile.in b/kvm/bios/Makefile.in new file mode 100644 index 000000000..28ada752b --- /dev/null +++ b/kvm/bios/Makefile.in @@ -0,0 +1,120 @@ +# Copyright (C) 2001 MandrakeSoft S.A. +# +# MandrakeSoft S.A. +# 43, rue d'Aboukir +# 75002 Paris - France +# http://www.linux-mandrake.com/ +# http://www.mandrakesoft.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Makefile for the BIOS component of bochs + + +@SUFFIX_LINE@ + +srcdir = @srcdir@ +VPATH = @srcdir@ + +SHELL = /bin/sh + +@SET_MAKE@ + +CXX = @CXX@ +CXXFLAGS = @CXXFLAGS@ + +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +RANLIB = @RANLIB@ + +BCC = bcc +GCC = gcc +GCC32 = gcc -m32 -fno-stack-protector +AS86 = as86 + +BX_INCDIRS = -I.. -I$(srcdir)/.. -I../iodev -I$(srcdir)/../iodev +LOCAL_CXXFLAGS = + +BUILDDATE = `date '+%m/%d/%y'` +BIOS_BUILD_DATE = "-DBIOS_BUILD_DATE=\"$(BUILDDATE)\"" +# +# -------- end configurable options -------------------------- +# + + +.@CPP_SUFFIX@.o: + $(CXX) -c $(BX_INCDIRS) $(CXXFLAGS) $(LOCAL_CXXFLAGS) @CXXFP@$< @OFP@$@ + + +bios: biossums BIOS-bochs-latest BIOS-bochs-legacy + +clean: + @RMCOMMAND@ *.o *.a *.s _rombios*_.c rombios*.txt rombios*.sym + @RMCOMMAND@ usage biossums rombios16.bin + @RMCOMMAND@ rombios32.bin rombios32.out + +dist-clean: clean + @RMCOMMAND@ Makefile + +bios-clean: + @RMCOMMAND@ BIOS-bochs-* + +BIOS-bochs-legacy: rombios.c apmbios.S biossums rombios.h + $(GCC32) $(BIOS_BUILD_DATE) -DLEGACY -E -P $< > _rombiosl_.c + $(BCC) -o rombiosl.s -C-c -D__i86__ -0 -S _rombiosl_.c + sed -e 's/^\.text//' -e 's/^\.data//' rombiosl.s > _rombiosl_.s + $(AS86) _rombiosl_.s -b tmpl.bin -u- -w- -g -0 -j -O -l rombiosl.txt + -perl ${srcdir}/makesym.perl < rombiosl.txt > rombiosl.sym + mv tmpl.bin $@ + ./biossums $@ + @RMCOMMAND@ _rombiosl_.s + + +rombios16.bin: rombios.c apmbios.S biossums rombios.h + $(GCC32) $(BIOS_BUILD_DATE) -E -P $< > _rombios_.c + $(BCC) -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c + sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s + $(AS86) _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt + -perl ${srcdir}/makesym.perl < rombios.txt > rombios.sym + mv tmp.bin rombios16.bin + ./biossums rombios16.bin + @RMCOMMAND@ _rombios_.s + + +rombios32.bin: rombios32.out rombios.h + objcopy -O binary $< $@ + ./biossums -pad $@ + +rombios32.out: rombios32start.o rombios32.o rombios32.ld + ld -o $@ -T $(srcdir)/rombios32.ld rombios32start.o rombios32.o + +rombios32.o: rombios32.c acpi-dsdt.hex + $(GCC32) -O2 -Wall -c -o $@ $< + +ifeq ("1", "0") +acpi-dsdt.hex: acpi-dsdt.dsl + cpp -P $< $<.i + iasl -tc -p $@ $<.i + rm $<.i + sed -i -e's/^unsigned/const unsigned/' $@ +endif + +rombios32start.o: rombios32start.S + $(GCC32) -c -o $@ $< + +BIOS-bochs-latest: rombios16.bin rombios32.bin + cat rombios32.bin rombios16.bin > $@ + +biossums: biossums.o diff --git a/kvm/bios/acpi-dsdt.dsl b/kvm/bios/acpi-dsdt.dsl new file mode 100755 index 000000000..26fc7add7 --- /dev/null +++ b/kvm/bios/acpi-dsdt.dsl @@ -0,0 +1,747 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ +DefinitionBlock ( + "acpi-dsdt.aml", // Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + Scope (\) + { + /* Debug Output */ + OperationRegion (DBG, SystemIO, 0xb044, 0x04) + Field (DBG, DWordAcc, NoLock, Preserve) + { + DBGL, 32, + } + } + + + /* PCI Bus definition */ + Scope(\_SB) { + Device(PCI0) { + Name (_HID, EisaId ("PNP0A03")) + Name (_ADR, 0x00) + Name (_UID, 1) + Name(_PRT, Package() { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + +#define prt_slot(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot0(nr) prt_slot(nr, LNKD, LNKA, LNKB, LNKC) +#define prt_slot1(nr) prt_slot(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot2(nr) prt_slot(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot3(nr) prt_slot(nr, LNKC, LNKD, LNKA, LNKB) + + prt_slot0(0x0000), + prt_slot1(0x0001), + prt_slot2(0x0002), + prt_slot3(0x0003), + prt_slot0(0x0004), + prt_slot1(0x0005), + prt_slot2(0x0006), + prt_slot3(0x0007), + prt_slot0(0x0008), + prt_slot1(0x0009), + prt_slot2(0x000a), + prt_slot3(0x000b), + prt_slot0(0x000c), + prt_slot1(0x000d), + prt_slot2(0x000e), + prt_slot3(0x000f), + prt_slot0(0x0010), + prt_slot1(0x0011), + prt_slot2(0x0012), + prt_slot3(0x0013), + prt_slot0(0x0014), + prt_slot1(0x0015), + prt_slot2(0x0016), + prt_slot3(0x0017), + prt_slot0(0x0018), + prt_slot1(0x0019), + prt_slot2(0x001a), + prt_slot3(0x001b), + prt_slot0(0x001c), + prt_slot1(0x001d), + prt_slot2(0x001e), + prt_slot3(0x001f), + }) + + OperationRegion(PCST, SystemIO, 0xae00, 0x08) + Field (PCST, DWordAcc, NoLock, WriteAsZeros) + { + PCIU, 32, + PCID, 32, + } + + OperationRegion(SEJ, SystemIO, 0xae08, 0x04) + Field (SEJ, DWordAcc, NoLock, WriteAsZeros) + { + B0EJ, 32, + } + +#define hotplug_slot(name, nr) \ + Device (S##name) { \ + Name (_ADR, nr##0000) \ + Method (_EJ0,1) { \ + Store(ShiftLeft(1, nr), B0EJ) \ + Return (0x0) \ + } \ + Name (_SUN, name) \ + } + + hotplug_slot(1, 0x0001) + hotplug_slot(2, 0x0002) + hotplug_slot(3, 0x0003) + hotplug_slot(4, 0x0004) + hotplug_slot(5, 0x0005) + hotplug_slot(6, 0x0006) + hotplug_slot(7, 0x0007) + hotplug_slot(8, 0x0008) + hotplug_slot(9, 0x0009) + hotplug_slot(10, 0x000a) + hotplug_slot(11, 0x000b) + hotplug_slot(12, 0x000c) + hotplug_slot(13, 0x000d) + hotplug_slot(14, 0x000e) + hotplug_slot(15, 0x000f) + hotplug_slot(16, 0x0010) + hotplug_slot(17, 0x0011) + hotplug_slot(18, 0x0012) + hotplug_slot(19, 0x0013) + hotplug_slot(20, 0x0014) + hotplug_slot(21, 0x0015) + hotplug_slot(22, 0x0016) + hotplug_slot(23, 0x0017) + hotplug_slot(24, 0x0018) + hotplug_slot(25, 0x0019) + hotplug_slot(26, 0x001a) + hotplug_slot(27, 0x001b) + hotplug_slot(28, 0x001c) + hotplug_slot(29, 0x001d) + hotplug_slot(30, 0x001e) + hotplug_slot(31, 0x001f) + + Name (_CRS, ResourceTemplate () + { + WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x00FF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0100, // Address Length + ,, ) + IO (Decode16, + 0x0CF8, // Address Range Minimum + 0x0CF8, // Address Range Maximum + 0x01, // Address Alignment + 0x08, // Address Length + ) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x0CF7, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0CF8, // Address Length + ,, , TypeStatic) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0D00, // Address Range Minimum + 0xFFFF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0xF300, // Address Length + ,, , TypeStatic) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x000A0000, // Address Range Minimum + 0x000BFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x00020000, // Address Length + ,, , AddressRangeMemory, TypeStatic) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0xE0000000, // Address Range Minimum + 0xFEBFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x1EC00000, // Address Length + ,, , AddressRangeMemory, TypeStatic) + }) + } +#ifdef BX_QEMU + Device(HPET) { + Name(_HID, EISAID("PNP0103")) + Name(_UID, 0) + Method (_STA, 0, NotSerialized) { + Return(0x0F) + } + Name(_CRS, ResourceTemplate() { + DWordMemory( + ResourceConsumer, PosDecode, MinFixed, MaxFixed, + NonCacheable, ReadWrite, + 0x00000000, + 0xFED00000, + 0xFED003FF, + 0x00000000, + 0x00000400 /* 1K memory: FED00000 - FED003FF */ + ) + }) + } +#endif + } + + Scope(\_SB.PCI0) { + Device (VGA) { + Name (_ADR, 0x00020000) + Method (_S1D, 0, NotSerialized) + { + Return (0x00) + } + Method (_S2D, 0, NotSerialized) + { + Return (0x00) + } + Method (_S3D, 0, NotSerialized) + { + Return (0x00) + } + } + + /* PIIX3 ISA bridge */ + Device (ISA) { + Name (_ADR, 0x00010000) + + /* PIIX PCI to ISA irq remapping */ + OperationRegion (P40C, PCI_Config, 0x60, 0x04) + + /* Real-time clock */ + Device (RTC) + { + Name (_HID, EisaId ("PNP0B00")) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, 0x0070, 0x0070, 0x10, 0x02) + IRQNoFlags () {8} + IO (Decode16, 0x0072, 0x0072, 0x02, 0x06) + }) + } + + /* Keyboard seems to be important for WinXP install */ + Device (KBD) + { + Name (_HID, EisaId ("PNP0303")) + Method (_STA, 0, NotSerialized) + { + Return (0x0f) + } + + Method (_CRS, 0, NotSerialized) + { + Name (TMP, ResourceTemplate () + { + IO (Decode16, + 0x0060, // Address Range Minimum + 0x0060, // Address Range Maximum + 0x01, // Address Alignment + 0x01, // Address Length + ) + IO (Decode16, + 0x0064, // Address Range Minimum + 0x0064, // Address Range Maximum + 0x01, // Address Alignment + 0x01, // Address Length + ) + IRQNoFlags () + {1} + }) + Return (TMP) + } + } + + /* PS/2 mouse */ + Device (MOU) + { + Name (_HID, EisaId ("PNP0F13")) + Method (_STA, 0, NotSerialized) + { + Return (0x0f) + } + + Method (_CRS, 0, NotSerialized) + { + Name (TMP, ResourceTemplate () + { + IRQNoFlags () {12} + }) + Return (TMP) + } + } + + /* PS/2 floppy controller */ + Device (FDC0) + { + Name (_HID, EisaId ("PNP0700")) + Method (_STA, 0, NotSerialized) + { + Return (0x0F) + } + Method (_CRS, 0, NotSerialized) + { + Name (BUF0, ResourceTemplate () + { + IO (Decode16, 0x03F2, 0x03F2, 0x00, 0x04) + IO (Decode16, 0x03F7, 0x03F7, 0x00, 0x01) + IRQNoFlags () {6} + DMA (Compatibility, NotBusMaster, Transfer8) {2} + }) + Return (BUF0) + } + } + + /* Parallel port */ + Device (LPT) + { + Name (_HID, EisaId ("PNP0400")) + Method (_STA, 0, NotSerialized) + { + Store (\_SB.PCI0.PX13.DRSA, Local0) + And (Local0, 0x80000000, Local0) + If (LEqual (Local0, 0)) + { + Return (0x00) + } + Else + { + Return (0x0F) + } + } + Method (_CRS, 0, NotSerialized) + { + Name (BUF0, ResourceTemplate () + { + IO (Decode16, 0x0378, 0x0378, 0x08, 0x08) + IRQNoFlags () {7} + }) + Return (BUF0) + } + } + + /* Serial Ports */ + Device (COM1) + { + Name (_HID, EisaId ("PNP0501")) + Name (_UID, 0x01) + Method (_STA, 0, NotSerialized) + { + Store (\_SB.PCI0.PX13.DRSC, Local0) + And (Local0, 0x08000000, Local0) + If (LEqual (Local0, 0)) + { + Return (0x00) + } + Else + { + Return (0x0F) + } + } + Method (_CRS, 0, NotSerialized) + { + Name (BUF0, ResourceTemplate () + { + IO (Decode16, 0x03F8, 0x03F8, 0x00, 0x08) + IRQNoFlags () {4} + }) + Return (BUF0) + } + } + + Device (COM2) + { + Name (_HID, EisaId ("PNP0501")) + Name (_UID, 0x02) + Method (_STA, 0, NotSerialized) + { + Store (\_SB.PCI0.PX13.DRSC, Local0) + And (Local0, 0x80000000, Local0) + If (LEqual (Local0, 0)) + { + Return (0x00) + } + Else + { + Return (0x0F) + } + } + Method (_CRS, 0, NotSerialized) + { + Name (BUF0, ResourceTemplate () + { + IO (Decode16, 0x02F8, 0x02F8, 0x00, 0x08) + IRQNoFlags () {3} + }) + Return (BUF0) + } + } + } + + /* PIIX4 PM */ + Device (PX13) { + Name (_ADR, 0x00010003) + + OperationRegion (P13C, PCI_Config, 0x5c, 0x24) + Field (P13C, DWordAcc, NoLock, Preserve) + { + DRSA, 32, + DRSB, 32, + DRSC, 32, + DRSE, 32, + DRSF, 32, + DRSG, 32, + DRSH, 32, + DRSI, 32, + DRSJ, 32 + } + } + } + + /* PCI IRQs */ + Scope(\_SB) { + Field (\_SB.PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) + { + PRQ0, 8, + PRQ1, 8, + PRQ2, 8, + PRQ3, 8 + } + + Device(LNKA){ + Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link + Name(_UID, 1) + Name(_PRS, ResourceTemplate(){ + Interrupt (, Level, ActiveHigh, Shared) + { 5, 10, 11 } + }) + Method (_STA, 0, NotSerialized) + { + Store (0x0B, Local0) + If (And (0x80, PRQ0, Local1)) + { + Store (0x09, Local0) + } + Return (Local0) + } + Method (_DIS, 0, NotSerialized) + { + Or (PRQ0, 0x80, PRQ0) + } + Method (_CRS, 0, NotSerialized) + { + Name (PRR0, ResourceTemplate () + { + Interrupt (, Level, ActiveHigh, Shared) + {1} + }) + CreateDWordField (PRR0, 0x05, TMP) + Store (PRQ0, Local0) + If (LLess (Local0, 0x80)) + { + Store (Local0, TMP) + } + Else + { + Store (Zero, TMP) + } + Return (PRR0) + } + Method (_SRS, 1, NotSerialized) + { + CreateDWordField (Arg0, 0x05, TMP) + Store (TMP, PRQ0) + } + } + Device(LNKB){ + Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link + Name(_UID, 2) + Name(_PRS, ResourceTemplate(){ + Interrupt (, Level, ActiveHigh, Shared) + { 5, 10, 11 } + }) + Method (_STA, 0, NotSerialized) + { + Store (0x0B, Local0) + If (And (0x80, PRQ1, Local1)) + { + Store (0x09, Local0) + } + Return (Local0) + } + Method (_DIS, 0, NotSerialized) + { + Or (PRQ1, 0x80, PRQ1) + } + Method (_CRS, 0, NotSerialized) + { + Name (PRR0, ResourceTemplate () + { + Interrupt (, Level, ActiveHigh, Shared) + {1} + }) + CreateDWordField (PRR0, 0x05, TMP) + Store (PRQ1, Local0) + If (LLess (Local0, 0x80)) + { + Store (Local0, TMP) + } + Else + { + Store (Zero, TMP) + } + Return (PRR0) + } + Method (_SRS, 1, NotSerialized) + { + CreateDWordField (Arg0, 0x05, TMP) + Store (TMP, PRQ1) + } + } + Device(LNKC){ + Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link + Name(_UID, 3) + Name(_PRS, ResourceTemplate(){ + Interrupt (, Level, ActiveHigh, Shared) + { 5, 10, 11 } + }) + Method (_STA, 0, NotSerialized) + { + Store (0x0B, Local0) + If (And (0x80, PRQ2, Local1)) + { + Store (0x09, Local0) + } + Return (Local0) + } + Method (_DIS, 0, NotSerialized) + { + Or (PRQ2, 0x80, PRQ2) + } + Method (_CRS, 0, NotSerialized) + { + Name (PRR0, ResourceTemplate () + { + Interrupt (, Level, ActiveHigh, Shared) + {1} + }) + CreateDWordField (PRR0, 0x05, TMP) + Store (PRQ2, Local0) + If (LLess (Local0, 0x80)) + { + Store (Local0, TMP) + } + Else + { + Store (Zero, TMP) + } + Return (PRR0) + } + Method (_SRS, 1, NotSerialized) + { + CreateDWordField (Arg0, 0x05, TMP) + Store (TMP, PRQ2) + } + } + Device(LNKD){ + Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link + Name(_UID, 4) + Name(_PRS, ResourceTemplate(){ + Interrupt (, Level, ActiveHigh, Shared) + { 5, 10, 11 } + }) + Method (_STA, 0, NotSerialized) + { + Store (0x0B, Local0) + If (And (0x80, PRQ3, Local1)) + { + Store (0x09, Local0) + } + Return (Local0) + } + Method (_DIS, 0, NotSerialized) + { + Or (PRQ3, 0x80, PRQ3) + } + Method (_CRS, 0, NotSerialized) + { + Name (PRR0, ResourceTemplate () + { + Interrupt (, Level, ActiveHigh, Shared) + {1} + }) + CreateDWordField (PRR0, 0x05, TMP) + Store (PRQ3, Local0) + If (LLess (Local0, 0x80)) + { + Store (Local0, TMP) + } + Else + { + Store (Zero, TMP) + } + Return (PRR0) + } + Method (_SRS, 1, NotSerialized) + { + CreateDWordField (Arg0, 0x05, TMP) + Store (TMP, PRQ3) + } + } + } + + /* + * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: + * must match piix4 emulation. + */ + Name (\_S3, Package (0x04) + { + 0x01, /* PM1a_CNT.SLP_TYP */ + 0x01, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + Name (\_S4, Package (0x04) + { + Zero, /* PM1a_CNT.SLP_TYP */ + Zero, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + Name (\_S5, Package (0x04) + { + Zero, /* PM1a_CNT.SLP_TYP */ + Zero, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + + Scope (\_GPE) + { + Name(_HID, "ACPI0006") + + Method(_L00) { + Return(0x01) + } + +#define gen_pci_hotplug(nr) \ + If (And(\_SB.PCI0.PCIU, ShiftLeft(1, nr))) { \ + Notify(\_SB.PCI0.S##nr, 1) \ + } \ + If (And(\_SB.PCI0.PCID, ShiftLeft(1, nr))) { \ + Notify(\_SB.PCI0.S##nr, 3) \ + } + + Method(_L01) { + gen_pci_hotplug(1) + gen_pci_hotplug(2) + gen_pci_hotplug(3) + gen_pci_hotplug(4) + gen_pci_hotplug(5) + gen_pci_hotplug(6) + gen_pci_hotplug(7) + gen_pci_hotplug(8) + gen_pci_hotplug(9) + gen_pci_hotplug(10) + gen_pci_hotplug(11) + gen_pci_hotplug(12) + gen_pci_hotplug(13) + gen_pci_hotplug(14) + gen_pci_hotplug(15) + gen_pci_hotplug(16) + gen_pci_hotplug(17) + gen_pci_hotplug(18) + gen_pci_hotplug(19) + gen_pci_hotplug(20) + gen_pci_hotplug(21) + gen_pci_hotplug(22) + gen_pci_hotplug(23) + gen_pci_hotplug(24) + gen_pci_hotplug(25) + gen_pci_hotplug(26) + gen_pci_hotplug(27) + gen_pci_hotplug(28) + gen_pci_hotplug(29) + gen_pci_hotplug(30) + gen_pci_hotplug(31) + + Return(0x01) + } + + /* + * Method _02 will be provided by the SSDT as it needs to call + * into the Processor methods (_PR.PRSC()). + */ + + Method(_L03) { + Return(0x01) + } + Method(_L04) { + Return(0x01) + } + Method(_L05) { + Return(0x01) + } + Method(_L06) { + Return(0x01) + } + Method(_L07) { + Return(0x01) + } + Method(_L08) { + Return(0x01) + } + Method(_L09) { + Return(0x01) + } + Method(_L0A) { + Return(0x01) + } + Method(_L0B) { + Return(0x01) + } + Method(_L0C) { + Return(0x01) + } + Method(_L0D) { + Return(0x01) + } + Method(_L0E) { + Return(0x01) + } + Method(_L0F) { + Return(0x01) + } + } +} diff --git a/kvm/bios/acpi-ssdt.dsl b/kvm/bios/acpi-ssdt.dsl new file mode 100644 index 000000000..d998867d7 --- /dev/null +++ b/kvm/bios/acpi-ssdt.dsl @@ -0,0 +1,140 @@ +/* + * Bochs/QEMU ACPI SSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2009 SGI, Jes Sorensen <jes@sgi.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ +DefinitionBlock ( + "acpi-ssdt.aml", // Output Filename + "SSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXSSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + Scope (\_PR) + { + /* pointer to first element of MADT APIC structures */ + OperationRegion(ATPR, SystemMemory, 0x0514, 4) + Field (ATPR, DwordAcc, NoLock, Preserve) + { + ATP, 32 + } + +#define madt_addr(nr) Add (ATP, Multiply(nr, 8)) + +#define gen_processor(nr, name) \ + Processor (C##name, nr, 0x0000b010, 0x06) { \ + OperationRegion (MATR, SystemMemory, madt_addr(nr), 8) \ + Field (MATR, ByteAcc, NoLock, Preserve) \ + { \ + MAT, 64 \ + } \ + Field (MATR, ByteAcc, NoLock, Preserve) \ + { \ + Offset(4), \ + FLG, 1 \ + } \ + Method(_MAT, 0) { \ + Return(MAT) \ + } \ + Method (_STA) { \ + If (FLG) { Return(0xF) } Else { Return(0x9) } \ + } \ + } \ + + + gen_processor(0, 0) + gen_processor(1, 1) + gen_processor(2, 2) + gen_processor(3, 3) + gen_processor(4, 4) + gen_processor(5, 5) + gen_processor(6, 6) + gen_processor(7, 7) + gen_processor(8, 8) + gen_processor(9, 9) + gen_processor(10, A) + gen_processor(11, B) + gen_processor(12, C) + gen_processor(13, D) + gen_processor(14, E) + + Method (NTFY, 2) { +#define gen_ntfy(nr) \ + If (LEqual(Arg0, 0x##nr)) { \ + If (LNotEqual(Arg1, \_PR.C##nr.FLG)) { \ + Store (Arg1, \_PR.C##nr.FLG) \ + If (LEqual(Arg1, 1)) { \ + Notify(C##nr, 1) \ + } Else { \ + Notify(C##nr, 3) \ + } \ + } \ + } + gen_ntfy(0) + gen_ntfy(1) + gen_ntfy(2) + gen_ntfy(3) + gen_ntfy(4) + gen_ntfy(5) + gen_ntfy(6) + gen_ntfy(7) + gen_ntfy(8) + gen_ntfy(9) + gen_ntfy(A) + gen_ntfy(B) + gen_ntfy(C) + gen_ntfy(D) + gen_ntfy(E) + Return(One) + } + + OperationRegion(PRST, SystemIO, 0xaf00, 32) + Field (PRST, ByteAcc, NoLock, Preserve) + { + PRS, 256 + } + + Method(PRSC, 0) { + Store(PRS, Local3) + Store(Zero, Local0) + While(LLess(Local0, 32)) { + Store(Zero, Local1) + Store(DerefOf(Index(Local3, Local0)), Local2) + While(LLess(Local1, 8)) { + NTFY(Add(Multiply(Local0, 8), Local1), + And(Local2, 1)) + ShiftRight(Local2, 1, Local2) + Increment(Local1) + } + Increment(Local0) + } + Return(One) + } + } + + /* + * Add the missing _L02 method for CPU notification + */ + Scope (\_GPE) + { + Method(_L02) { + Return(\_PR.PRSC()) + } + } +} diff --git a/kvm/bios/apmbios.S b/kvm/bios/apmbios.S new file mode 100644 index 000000000..41c9e7ef1 --- /dev/null +++ b/kvm/bios/apmbios.S @@ -0,0 +1,365 @@ +// APM BIOS support for the Bochs BIOS +// Copyright (C) 2004 Fabrice Bellard +// +// Debugging extensions, 16-bit interface and extended power options +// Copyright (C) 2005 Struan Bartlett +// +// 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +#if defined(APM_REAL) +#define APMSYM(s) apmreal_ ## s +#elif defined(APM_PROT16) +#define APMSYM(s) apm16_ ## s +#elif defined(APM_PROT32) +#define APMSYM(s) apm32_ ## s +#else +#error unsupported APM mode +#endif + +APMSYM(out_str): + push eax + push ebx + mov ebx, eax +APMSYM(out_str1): + SEG CS + mov al, byte ptr [bx] + cmp al, #0 + je APMSYM(out_str2) + outb dx, al + inc ebx + jmp APMSYM(out_str1) +APMSYM(out_str2): + pop ebx + pop eax + ret + +APMSYM(07_poweroff_str): + .ascii "Shutdown" + db 0 +APMSYM(07_suspend_str): + .ascii "Suspend" + db 0 +APMSYM(07_standby_str): + .ascii "Standby" + db 0 + +#if DEBUG_APM +APMSYM(put_str): + push edx + mov dx, #INFO_PORT + call APMSYM(out_str) + pop edx + ret + +; print the hex number in eax +APMSYM(put_num): + push eax + push ebx + push ecx + push edx + mov ecx, eax + mov bx, #8 + mov dx, #INFO_PORT +APMSYM(put_num1): + mov eax, ecx + shr eax, #28 + add al, #0x30 + cmp al, #0x39 + jbe APMSYM(put_num2) + add al, #0x27 +APMSYM(put_num2): + outb dx, al + shl ecx, #4 + dec bx + jne APMSYM(put_num1) + pop edx + pop ecx + pop ebx + pop eax + ret + +APMSYM(put_reg): + outb dx, al + shr eax, #8 + outb dx, al + shr eax, #8 + outb dx, al + shr eax, #8 + outb dx, al + + mov eax,ebx + call APMSYM(put_num) + + mov al, #0x3b + outb dx,al + mov al, #0x20 + outb dx,al + ret + +APMSYM(put_regs): + push eax + push edx + push ebx + mov dx, #INFO_PORT + + mov ebx, eax + mov eax, #0x3d584145 // 'EAX=' + call APMSYM(put_reg) + pop ebx + push ebx + mov eax, #0x3d584245 // 'EBX=' + call APMSYM(put_reg) + mov ebx, ecx + mov eax, #0x3d584345 // 'ECX=' + call APMSYM(put_reg) + mov ebx, edx + mov eax, #0x3d584445 // 'EDX=' + call APMSYM(put_reg) + mov ebx, esi + mov eax, #0x3d495345 // 'ESI=' + call APMSYM(put_reg) + mov ebx, edi + mov eax, #0x3d494445 // 'EDI=' + call APMSYM(put_reg) + + mov al, #0x0a + outb dx, al + pop ebx + pop edx + pop eax + ret +#endif + +#if defined(APM_PROT32) +_apm32_entry: +#endif +#if defined(APM_PROT16) +_apm16_entry: +#endif + pushf + +#if defined(APM_REAL) +_apmreal_entry: +#endif + +#if DEBUG_APM + call APMSYM(put_regs) +#endif + +#if defined(APM_REAL) +;----------------- +; APM installation check +APMSYM(00): + cmp al, #0x00 + jne APMSYM(01) + + mov ah, #1 // APM major version + mov al, #2 // APM minor version + + mov bh, #0x50 // 'P' + mov bl, #0x4d // 'M' + + // bit 0 : 16 bit interface supported + // bit 1 : 32 bit interface supported + mov cx, #0x3 + jmp APMSYM(ok) + +;----------------- +; APM real mode interface connect +APMSYM(01): + cmp al, #0x01 + jne APMSYM(02) + jmp APMSYM(ok) + +;----------------- +; APM 16 bit protected mode interface connect +APMSYM(02): + cmp al, #0x02 + jne APMSYM(03) + + mov bx, #_apm16_entry + + mov ax, #0xf000 // 16 bit code segment base + mov si, #0xfff0 // 16 bit code segment size + mov cx, #0xf000 // data segment address + mov di, #0xfff0 // data segment length + jmp APMSYM(ok) + +;----------------- +; APM 32 bit protected mode interface connect +APMSYM(03): + cmp al, #0x03 + jne APMSYM(04) + mov ax, #0xf000 // 32 bit code segment base + mov ebx, #_apm32_entry + mov cx, #0xf000 // 16 bit code segment base + // 32 bit code segment size (low 16 bits) + // 16 bit code segment size (high 16 bits) + mov esi, #0xfff0fff0 + mov dx, #0xf000 // data segment address + mov di, #0xfff0 // data segment length + jmp APMSYM(ok) +#endif + +;----------------- +; APM interface disconnect +APMSYM(04): + cmp al, #0x04 + jne APMSYM(05) + jmp APMSYM(ok) + +;----------------- +; APM cpu idle +APMSYM(05): + cmp al, #0x05 + jne APMSYM(07) + sti + hlt + jmp APMSYM(ok) + +;----------------- +; APM Set Power State +APMSYM(07): + cmp al, #0x07 + jne APMSYM(08) + + cmp bx, #1 + jne APMSYM(ok) + + cmp cx, #3 + je APMSYM(07_poweroff) + + cmp cx, #2 + je APMSYM(07_suspend) + + cmp cx, #1 + je APMSYM(07_standby) + + jne APMSYM(ok) + +APMSYM(07_poweroff): + // send power off event to emulator + cli + mov dx, #0x8900 + mov ax, #APMSYM(07_poweroff_str) + call APMSYM(out_str) + +APMSYM(07_1): + hlt + jmp APMSYM(07_1) + +APMSYM(07_suspend): + push edx + mov dx, #0x8900 + mov ax, #APMSYM(07_suspend_str) + call APMSYM(out_str) + pop edx + jmp APMSYM(ok) + +APMSYM(07_standby): + push edx + mov dx, #0x8900 + mov ax, #APMSYM(07_standby_str) + call APMSYM(out_str) + pop edx + jmp APMSYM(ok) + +;----------------- +; APM Enable / Disable +APMSYM(08): + cmp al, #0x08 + jne APMSYM(0a) + + jmp APMSYM(ok) + +;----------------- +; Get Power Status +APMSYM(0a): + cmp al, #0x0a + jne APMSYM(0b) + mov bh, #0x01 // on line + // mov bh, #0x02 // battery + mov bl, #0xff // unknown battery status + // mov bl, #0x03 // charging + mov ch, #0x80 // no system battery + // mov ch, #0x8 // charging + mov cl, #0xff // unknown remaining time + // mov cl, #50 + mov dx, #0xffff // unknown remaining time + mov si, #0 // zero battery + // mov si, #1 // one battery + jmp APMSYM(ok) + +;----------------- +; Get PM Event +APMSYM(0b): + cmp al, #0x0b + jne APMSYM(0e) + mov ah, #0x80 // no event pending + jmp APMSYM(error) + +;----------------- +; APM Driver Version +APMSYM(0e): + cmp al, #0x0e + jne APMSYM(0f) + + mov ah, #1 + mov al, #2 + + jmp APMSYM(ok) + +;----------------- +; APM Engage / Disengage +APMSYM(0f): + cmp al, #0x0f + jne APMSYM(10) + + jmp APMSYM(ok) + +;----------------- +; APM Get Capabilities +APMSYM(10): + cmp al, #0x10 + jne APMSYM(unimplemented) + + mov bl, #0 + mov cx, #0 + + jmp APMSYM(ok) + +;----------------- +APMSYM(ok): + popf + clc +#if defined(APM_REAL) + jmp iret_modify_cf +#else + retf +#endif +APMSYM(unimplemented): +APMSYM(error): + popf + stc +#if defined(APM_REAL) + jmp iret_modify_cf +#else + retf +#endif + +#undef APM_PROT32 +#undef APM_PROT16 +#undef APM_REAL +#undef APMSYM diff --git a/kvm/bios/bios_usage b/kvm/bios/bios_usage new file mode 100644 index 000000000..8019ef630 --- /dev/null +++ b/kvm/bios/bios_usage @@ -0,0 +1,4 @@ +#!/bin/csh -f +gcc -E rombios.c | grep "^\.org" | sed -e 's/\.org //' | sed -e 's/ .*//' | sort >! temp.usage +usage rombios.bin temp.usage +/bin/rm temp.usage diff --git a/kvm/bios/biossums.c b/kvm/bios/biossums.c new file mode 100644 index 000000000..032ac23c3 --- /dev/null +++ b/kvm/bios/biossums.c @@ -0,0 +1,504 @@ +/* + * $Id: biossums.c,v 1.4 2007/05/28 08:09:13 vruppert Exp $ + * + * 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 + */ + +/* biossums.c --- written by Eike W. for the Bochs BIOS */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +typedef unsigned char byte; + +void check( int value, char* message ); + +#define LEN_BIOS_DATA 0x10000 +#define MAX_OFFSET (LEN_BIOS_DATA - 1) + + +#define BIOS_OFFSET 0xFFFF + +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 _32__LEN 9 +#define _32__CHKSUM 10 + +#define _32__MINHDR 16 + +long chksum__32__get_offset( byte* data, long offset ); +byte chksum__32__calc_value( byte* data, long offset ); +byte chksum__32__get_value( byte* data, long offset ); +void chksum__32__set_value( byte* data, long offset, byte value ); + + +#define _MP__LEN 8 +#define _MP__CHKSUM 10 + +#define _MP__MINHDR 16 + +long chksum__mp__get_offset( byte* data, long offset ); +byte chksum__mp__calc_value( byte* data, long offset ); +byte chksum__mp__get_value( byte* data, long offset ); +void chksum__mp__set_value( byte* data, long offset, byte value ); + + +#define PCMP_BASELEN 4 +#define PCMP_CHKSUM 7 +#define PCMP_EXT_LEN 40 +#define PCMP_EXT_CHKSUM 42 + +#define PCMP_MINHDR 42 + +long chksum_pcmp_get_offset( byte* data, long offset ); +byte chksum_pcmp_calc_value( byte* data, long offset ); +byte chksum_pcmp_get_value( byte* data, long offset ); +void chksum_pcmp_set_value( byte* data, long offset, byte value ); + + +#define _PIR_LEN 6 +#define _PIR_CHKSUM 31 + +#define _PIR_MINHDR 32 + +long chksum__pir_get_offset( byte *data, long offset ); +byte chksum__pir_calc_value( byte* data, long offset ); +byte chksum__pir_get_value( byte* data, long offset ); +void chksum__pir_set_value( byte* data, long offset, byte value ); + + +byte bios_data[LEN_BIOS_DATA]; +long bios_len; + + +int main(int argc, char* argv[]) { + + FILE* stream; + long offset, tmp_offset; + byte cur_val = 0, new_val = 0; + int arg = 1, hits, pad = 0; + + + if ((argc == 3) && (!strcmp(argv[1], "-pad"))) { + pad = 1; + arg = 2; + } else if (argc != 2) { + printf("Error. Need a file-name as an argument.\n"); + exit(EXIT_FAILURE); + } + memset(bios_data, 0xff, LEN_BIOS_DATA); + + if ((stream = fopen(argv[arg], "rb")) == NULL) { + printf("Error opening %s for reading.\n", argv[arg]); + exit(EXIT_FAILURE); + } + bios_len = fread(bios_data, 1, LEN_BIOS_DATA, stream); + if ((bios_len < LEN_BIOS_DATA) && (pad == 0)) { + printf("Error reading 64KBytes from %s.\n", argv[arg]); + fclose(stream); + exit(EXIT_FAILURE); + } + fclose(stream); + if (pad == 1) goto write_bios; + + hits = 0; + offset = 0L; + while( (tmp_offset = chksum__32__get_offset( bios_data, offset )) != -1L ) { + offset = tmp_offset; + cur_val = chksum__32__get_value( bios_data, offset ); + new_val = chksum__32__calc_value( bios_data, offset ); + printf( "\n\nPCI-Bios header 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__32__set_value( bios_data, offset, new_val ); + } + if( hits >= 2 ) { + printf( "Multiple PCI headers! No checksum set." ); + } + if( hits ) { + printf( "\n" ); + } + + + hits = 0; + offset = 0L; + while( (tmp_offset = chksum__mp__get_offset( bios_data, offset )) != -1L ) { + offset = tmp_offset; + cur_val = chksum__mp__get_value( bios_data, offset ); + new_val = chksum__mp__calc_value( bios_data, offset ); + printf( "\n\nMP header 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__mp__set_value( bios_data, offset, new_val ); + } + if( hits >= 2 ) { + printf( "Warning! Multiple MP headers. No checksum set." ); + } + if( hits ) { + printf( "\n" ); + } + + + hits = 0; + offset = 0L; + while( (tmp_offset = chksum_pcmp_get_offset( bios_data, offset )) != -1L ) { + offset = tmp_offset; + cur_val = chksum_pcmp_get_value( bios_data, offset ); + new_val = chksum_pcmp_calc_value( bios_data, offset ); + printf( "\n\nPCMP header 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_pcmp_set_value( bios_data, offset, new_val ); + } + if( hits >= 2 ) { + printf( "Warning! Multiple PCMP headers. No checksum set." ); + } + if( hits ) { + printf( "\n" ); + } + + + hits = 0; + offset = 0L; + while( (tmp_offset = chksum__pir_get_offset( bios_data, offset )) != -1L ) { + offset = tmp_offset; + cur_val = chksum__pir_get_value( bios_data, offset ); + new_val = chksum__pir_calc_value( bios_data, offset ); + printf( "\n\n$PIR header at: 0x%4lX\n", offset ); + printf( "Current checksum: 0x%02X\n", cur_val ); + printf( "Calculated checksum: 0x%02X\n ", new_val ); + hits++; + } + if( hits == 1 && cur_val != new_val ) { + printf( "Setting checksum." ); + chksum__pir_set_value( bios_data, offset, new_val ); + } + if( hits >= 2 ) { + printf( "Warning! Multiple $PIR headers. No checksum set." ); + } + if( hits ) { + printf( "\n" ); + } + + + offset = 0L; + 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 ); + printf( "\n\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 ); + } + printf( "\n" ); + +write_bios: + if ((stream = fopen(argv[arg], "wb")) == NULL) { + printf("Error opening %s for writing.\n", argv[arg]); + exit(EXIT_FAILURE); + } + if (fwrite(bios_data, 1, LEN_BIOS_DATA, stream) < LEN_BIOS_DATA) { + printf("Error writing 64KBytes to %s.\n", argv[arg]); + 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_OFFSET ); +} + + +byte chksum_bios_calc_value( byte* data, long offset ) { + + int i; + byte sum; + + sum = 0; + for( i = 0; i < MAX_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 + BIOS_OFFSET ) ); +} + + +void chksum_bios_set_value( byte* data, long offset, byte value ) { + + *( data + BIOS_OFFSET ) = value; +} + + +byte chksum__32__calc_value( byte* data, long offset ) { + + int i; + int len; + byte sum; + + check( offset + _32__MINHDR <= MAX_OFFSET, "_32_ header out of bounds" ); + len = *( data + offset + _32__LEN ) << 4; + check( offset + len <= MAX_OFFSET, "_32_ header-length out of bounds" ); + sum = 0; + for( i = 0; i < len; i++ ) { + if( i != _32__CHKSUM ) { + sum = sum + *( data + offset + i ); + } + } + sum = -sum; + return( sum ); +} + + +long chksum__32__get_offset( byte* data, long offset ) { + + long result = -1L; + + offset = offset + 0x0F; + offset = offset & ~( 0x0F ); + while( offset + 16 < MAX_OFFSET ) { + offset = offset + 16; + if( *( data + offset + 0 ) == '_' && \ + *( data + offset + 1 ) == '3' && \ + *( data + offset + 2 ) == '2' && \ + *( data + offset + 3 ) == '_' ) { + result = offset; + break; + } + } + return( result ); +} + + +byte chksum__32__get_value( byte* data, long offset ) { + + check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" ); + return( *( data + offset + _32__CHKSUM ) ); +} + + +void chksum__32__set_value( byte* data, long offset, byte value ) { + + check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" ); + *( data + offset + _32__CHKSUM ) = value; +} + + +byte chksum__mp__calc_value( byte* data, long offset ) { + + int i; + int len; + byte sum; + + check( offset + _MP__MINHDR <= MAX_OFFSET, "_MP_ header out of bounds" ); + len = *( data + offset + _MP__LEN ) << 4; + check( offset + len <= MAX_OFFSET, "_MP_ header-length out of bounds" ); + sum = 0; + for( i = 0; i < len; i++ ) { + if( i != _MP__CHKSUM ) { + sum = sum + *( data + offset + i ); + } + } + sum = -sum; + return( sum ); +} + + +long chksum__mp__get_offset( byte* data, long offset ) { + + long result = -1L; + + offset = offset + 0x0F; + offset = offset & ~( 0x0F ); + while( offset + 16 < MAX_OFFSET ) { + offset = offset + 16; + if( *( data + offset + 0 ) == '_' && \ + *( data + offset + 1 ) == 'M' && \ + *( data + offset + 2 ) == 'P' && \ + *( data + offset + 3 ) == '_' ) { + result = offset; + break; + } + } + return( result ); +} + + +byte chksum__mp__get_value( byte* data, long offset ) { + + check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" ); + return( *( data + offset + _MP__CHKSUM ) ); +} + + +void chksum__mp__set_value( byte* data, long offset, byte value ) { + + check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" ); + *( data + offset + _MP__CHKSUM ) = value; +} + + +byte chksum_pcmp_calc_value( byte* data, long offset ) { + + int i; + int len; + byte sum; + + check( offset + PCMP_MINHDR <= MAX_OFFSET, "PCMP header out of bounds" ); + len = *( data + offset + PCMP_BASELEN ) + \ + ( *( data + offset + PCMP_BASELEN + 1 ) << 8 ); + check( offset + len <= MAX_OFFSET, "PCMP header-length out of bounds" ); + if( *( data + offset + PCMP_EXT_LEN ) | \ + *( data + offset + PCMP_EXT_LEN + 1 ) | \ + *( data + offset + PCMP_EXT_CHKSUM ) ) { + check( 0, "PCMP header indicates extended tables (unsupported)" ); + } + sum = 0; + for( i = 0; i < len; i++ ) { + if( i != PCMP_CHKSUM ) { + sum = sum + *( data + offset + i ); + } + } + sum = -sum; + return( sum ); +} + + +long chksum_pcmp_get_offset( byte* data, long offset ) { + + long result = -1L; + + offset = offset + 0x0F; + offset = offset & ~( 0x0F ); + while( offset + 16 < MAX_OFFSET ) { + offset = offset + 16; + if( *( data + offset + 0 ) == 'P' && \ + *( data + offset + 1 ) == 'C' && \ + *( data + offset + 2 ) == 'M' && \ + *( data + offset + 3 ) == 'P' ) { + result = offset; + break; + } + } + return( result ); +} + + +byte chksum_pcmp_get_value( byte* data, long offset ) { + + check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" ); + return( *( data + offset + PCMP_CHKSUM ) ); +} + + +void chksum_pcmp_set_value( byte* data, long offset, byte value ) { + + check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" ); + *( data + offset + PCMP_CHKSUM ) = value; +} + + +byte chksum__pir_calc_value( byte* data, long offset ) { + + int i; + int len; + byte sum; + + check( offset + _PIR_MINHDR <= MAX_OFFSET, "$PIR header out of bounds" ); + len = *( data + offset + _PIR_LEN ) + \ + ( *( data + offset + _PIR_LEN + 1 ) << 8 ); + check( offset + len <= MAX_OFFSET, "$PIR header-length out of bounds" ); + sum = 0; + for( i = 0; i < len; i++ ) { + if( i != _PIR_CHKSUM ) { + sum = sum + *( data + offset + i ); + } + } + sum = -sum; + return( sum ); +} + + +long chksum__pir_get_offset( byte* data, long offset ) { + + long result = -1L; + + offset = offset + 0x0F; + offset = offset & ~( 0x0F ); + while( offset + 16 < MAX_OFFSET ) { + offset = offset + 16; + if( *( data + offset + 0 ) == '$' && \ + *( data + offset + 1 ) == 'P' && \ + *( data + offset + 2 ) == 'I' && \ + *( data + offset + 3 ) == 'R' ) { + result = offset; + break; + } + } + return( result ); +} + + +byte chksum__pir_get_value( byte* data, long offset ) { + + check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" ); + return( *( data + offset + _PIR_CHKSUM ) ); +} + + +void chksum__pir_set_value( byte* data, long offset, byte value ) { + + check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" ); + *( data + offset + _PIR_CHKSUM ) = value; +} + diff --git a/kvm/bios/makesym.perl b/kvm/bios/makesym.perl new file mode 100755 index 000000000..631265404 --- /dev/null +++ b/kvm/bios/makesym.perl @@ -0,0 +1,31 @@ +#!/usr/bin/perl +# +# $Id: makesym.perl,v 1.1 2002/11/24 22:45:40 bdenney Exp $ +# +# Read output file from as86 (e.g. rombios.txt) and write out a symbol +# table suitable for the Bochs debugger. +# + +$WHERE_BEFORE_SYM_TABLE = 0; +$WHERE_IN_SYM_TABLE = 1; +$WHERE_AFTER_SYM_TABLE = 2; + +$where = $WHERE_BEFORE_SYM_TABLE; +while (<STDIN>) { + chop; + if ($where == WHERE_BEFORE_SYM_TABLE && /^Symbols:/) { + $where = $WHERE_IN_SYM_TABLE; + } elsif ($where == $WHERE_IN_SYM_TABLE && /^$/) { + $where = $WHERE_AFTER_SYM_TABLE; + } + if ($where == $WHERE_IN_SYM_TABLE) { + @F = split (/\s+/); + ($name[0], $junk, $addr[0], $junk, $name[1], $junk, $addr[1]) = @F; + foreach $col (0,1) { + next if length $addr[$col] < 1; + $addr[$col] =~ tr/A-Z/a-z/; + $addr[$col] = "000f" . $addr[$col]; + print "$addr[$col] $name[$col]\n"; + } + } +} diff --git a/kvm/bios/notes b/kvm/bios/notes new file mode 100644 index 000000000..ae1073e98 --- /dev/null +++ b/kvm/bios/notes @@ -0,0 +1,44 @@ +#################### +# Read Disk Sector # +#################### + +System programming: +------------------- + +(Int 13h, ah=2) +in 1f7 until BSY cleared +out 1f2, AL # number of sectors +out 1f3, cl (bits 0-5) # starting sector number +out 1f4, ch # cylinder number bits 0..7, 0 based +out 1f5, cl (bits 6,7) & dh (bits 6,7) # cyl, bits 8..9, 10..11 +out 1f6, dh (bits 0..3) --> bits 0..3 # head number + dh (bits 4..5) --> ??? # head number + dl (bit 0) --> bit 4 # drive number +out 1f7, 0x20 # read sectors command normal + + + +Drive response: +--------------- + +* drive sets the busy bit in Status Reg to 1 +* if command parameters are wrong: + > drive sets the aborted-command bit in the Error register and + error bit in the Status register to 1. + > Drive also sets the busy bit in the Status register to 0. + > Drive then generates an interrupt to the system. +* else: + > drive executes an implied seek to desired track and + reads sectors into sector buffer + > when sector buffer is filled and the data is ready to be + transferred, the drive sets the data-request bit to 1, sets + the busy bit to 0, and generates an interrupt. + > on a single-sector transfer, after the system has transferred + the data, the drive sets the data-request bit and the busy bit to 0. + > on a multiple-sector transfer, after the system has transferred + the first sector of data, the drive sets the data-request bit to 0, + and the busy bit to 1. When each subsequent sector is ready to be + transferred, the drive sets the data-request bit to 1, the busy bit to 0, + and generates an interrupt. When the system has tranferred the last sector, + the drive sets the data-request bit and busy bit to 0. + diff --git a/kvm/bios/rombios.c b/kvm/bios/rombios.c new file mode 100644 index 000000000..6e1d446dc --- /dev/null +++ b/kvm/bios/rombios.c @@ -0,0 +1,11456 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: rombios.c,v 1.182 2007/08/01 17:09:51 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +// ROM BIOS for use with Bochs/Plex86/QEMU emulation environment + + +// ROM BIOS compatability entry points: +// =================================== +// $e05b ; POST Entry Point +// $e2c3 ; NMI Handler Entry Point +// $e3fe ; INT 13h Fixed Disk Services Entry Point +// $e401 ; Fixed Disk Parameter Table +// $e6f2 ; INT 19h Boot Load Service Entry Point +// $e6f5 ; Configuration Data Table +// $e729 ; Baud Rate Generator Table +// $e739 ; INT 14h Serial Communications Service Entry Point +// $e82e ; INT 16h Keyboard Service Entry Point +// $e987 ; INT 09h Keyboard Service Entry Point +// $ec59 ; INT 13h Diskette Service Entry Point +// $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point +// $efc7 ; Diskette Controller Parameter Table +// $efd2 ; INT 17h Printer Service Entry Point +// $f045 ; INT 10 Functions 0-Fh Entry Point +// $f065 ; INT 10h Video Support Service Entry Point +// $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh) +// $f841 ; INT 12h Memory Size Service Entry Point +// $f84d ; INT 11h Equipment List Service Entry Point +// $f859 ; INT 15h System Services Entry Point +// $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters) +// $fe6e ; INT 1Ah Time-of-day Service Entry Point +// $fea5 ; INT 08h System Timer ISR Entry Point +// $fef3 ; Initial Interrupt Vector Offsets Loaded by POST +// $ff53 ; IRET Instruction for Dummy Interrupt Handler +// $ff54 ; INT 05h Print Screen Service Entry Point +// $fff0 ; Power-up Entry Point +// $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY +// $fffe ; System Model ID + +// NOTES for ATA/ATAPI driver (cbbochs@free.fr) +// Features +// - supports up to 4 ATA interfaces +// - device/geometry detection +// - 16bits/32bits device access +// - pchs/lba access +// - datain/dataout/packet command support +// +// NOTES for El-Torito Boot (cbbochs@free.fr) +// - CD-ROM booting is only available if ATA/ATAPI Driver is available +// - Current code is only able to boot mono-session cds +// - Current code can not boot and emulate a hard-disk +// the bios will panic otherwise +// - Current code also use memory in EBDA segement. +// - I used cmos byte 0x3D to store extended information on boot-device +// - Code has to be modified modified to handle multiple cdrom drives +// - Here are the cdrom boot failure codes: +// 1 : no atapi device found +// 2 : no atapi cdrom found +// 3 : can not read cd - BRVD +// 4 : cd is not eltorito (BRVD) +// 5 : cd is not eltorito (ISO TAG) +// 6 : cd is not eltorito (ELTORITO TAG) +// 7 : can not read cd - boot catalog +// 8 : boot catalog : bad header +// 9 : boot catalog : bad platform +// 10 : boot catalog : bad signature +// 11 : boot catalog : bootable flag not set +// 12 : can not read cd - boot image +// +// ATA driver +// - EBDA segment. +// I used memory starting at 0x121 in the segment +// - the translation policy is defined in cmos regs 0x39 & 0x3a +// +// TODO : +// +// int74 +// - needs to be reworked. Uses direct [bp] offsets. (?) +// +// int13: +// - f04 (verify sectors) isn't complete (?) +// - f02/03/04 should set current cyl,etc in BDA (?) +// - rewrite int13_relocated & clean up int13 entry code +// +// NOTES: +// - NMI access (bit7 of addr written to 70h) +// +// ATA driver +// - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c) +// - could send the multiple-sector read/write commands +// +// El-Torito +// - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk" +// - Implement remaining int13_cdemu functions (as defined by El-Torito specs) +// - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded" +// - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13. +// This is ok. But DL should be reincremented afterwards. +// - Fix all "FIXME ElTorito Various" +// - should be able to boot any cdrom instead of the first one +// +// BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7) + +#include "rombios.h" + +#define DEBUG_ATA 0 +#define DEBUG_INT13_HD 0 +#define DEBUG_INT13_CD 0 +#define DEBUG_INT13_ET 0 +#define DEBUG_INT13_FL 0 +#define DEBUG_INT15 0 +#define DEBUG_INT16 0 +#define DEBUG_INT1A 0 +#define DEBUG_INT74 0 +#define DEBUG_APM 0 + +#define BX_CPU 3 +#define BX_USE_PS2_MOUSE 1 +#define BX_CALL_INT15_4F 1 +#define BX_USE_EBDA 1 +#define BX_SUPPORT_FLOPPY 1 +#define BX_FLOPPY_ON_CNT 37 /* 2 seconds */ +#define BX_PCIBIOS 1 +#define BX_APM 1 + +#define BX_USE_ATADRV 1 +#define BX_ELTORITO_BOOT 1 + +#define BX_MAX_ATA_INTERFACES 4 +#define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2) + +#define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */ +#define BX_DEBUG_SERIAL 0 /* output to COM1 */ + + /* model byte 0xFC = AT */ +#define SYS_MODEL_ID 0xFC +#define SYS_SUBMODEL_ID 0x00 +#define BIOS_REVISION 1 +#define BIOS_CONFIG_TABLE 0xe6f5 + +#ifndef BIOS_BUILD_DATE +# define BIOS_BUILD_DATE "06/23/99" +#endif + + // 1K of base memory used for Extended Bios Data Area (EBDA) + // EBDA is used for PS/2 mouse support, and IDE BIOS, etc. +#define EBDA_SEG 0x9FC0 +#define EBDA_SIZE 1 // In KiB +#define BASE_MEM_IN_K (640 - EBDA_SIZE) + +/* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */ +#define IPL_SEG 0x9ff0 +#define IPL_TABLE_OFFSET 0x0000 +#define IPL_TABLE_ENTRIES 8 +#define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */ +#define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */ +#define IPL_BOOTFIRST_OFFSET 0x0084 /* u16: user selected device */ +#define IPL_SIZE 0xff +#define IPL_TYPE_FLOPPY 0x01 +#define IPL_TYPE_HARDDISK 0x02 +#define IPL_TYPE_CDROM 0x03 +#define IPL_TYPE_BEV 0x80 + + // Sanity Checks +#if BX_USE_ATADRV && BX_CPU<3 +# error The ATA/ATAPI Driver can only to be used with a 386+ cpu +#endif +#if BX_USE_ATADRV && !BX_USE_EBDA +# error ATA/ATAPI Driver can only be used if EBDA is available +#endif +#if BX_ELTORITO_BOOT && !BX_USE_ATADRV +# error El-Torito Boot can only be use if ATA/ATAPI Driver is available +#endif +#if BX_PCIBIOS && BX_CPU<3 +# error PCI BIOS can only be used with 386+ cpu +#endif +#if BX_APM && BX_CPU<3 +# error APM BIOS can only be used with 386+ cpu +#endif + +// define this if you want to make PCIBIOS working on a specific bridge only +// undef enables PCIBIOS when at least one PCI device is found +// i440FX is emulated by Bochs and QEMU +#define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge + +// #20 is dec 20 +// #$20 is hex 20 = 32 +// #0x20 is hex 20 = 32 +// LDA #$20 +// JSR $E820 +// LDD .i,S +// JSR $C682 +// mov al, #$20 + +// all hex literals should be prefixed with '0x' +// grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c +// no mov SEG-REG, #value, must mov register into seg-reg +// grep -i "mov[ ]*.s" rombios.c + +// This is for compiling with gcc2 and gcc3 +#define ASM_START #asm +#define ASM_END #endasm + +ASM_START +.rom + +.org 0x0000 + +#if BX_CPU >= 3 +use16 386 +#else +use16 286 +#endif + +MACRO HALT + ;; the HALT macro is called with the line number of the HALT call. + ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex + ;; to print a BX_PANIC message. This will normally halt the simulation + ;; with a message such as "BIOS panic at rombios.c, line 4091". + ;; However, users can choose to make panics non-fatal and continue. +#if BX_VIRTUAL_PORTS + mov dx,#PANIC_PORT + mov ax,#?1 + out dx,ax +#else + mov dx,#0x80 + mov ax,#?1 + out dx,al +#endif +MEND + +MACRO JMP_AP + db 0xea + dw ?2 + dw ?1 +MEND + +MACRO SET_INT_VECTOR + mov ax, ?3 + mov ?1*4, ax + mov ax, ?2 + mov ?1*4+2, ax +MEND + +ASM_END + +typedef unsigned char Bit8u; +typedef unsigned short Bit16u; +typedef unsigned short bx_bool; +typedef unsigned long Bit32u; + + + void memsetb(seg,offset,value,count); + void memcpyb(dseg,doffset,sseg,soffset,count); + void memcpyd(dseg,doffset,sseg,soffset,count); + + // memset of count bytes + 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 + test cx, cx + 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 + } + + // memcpy of count bytes + 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 + test cx, cx + 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 + } + + // memcpy of count dword + void + memcpyd(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 + test cx, cx + je memcpyd_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 + movsd + + memcpyd_end: + pop si + pop ds + pop di + pop es + pop cx + pop ax + + pop bp + ASM_END + } + + // read_dword and write_dword functions + static Bit32u read_dword(); + static void write_dword(); + + Bit32u + read_dword(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] + add bx, #2 + mov dx, [bx] + ;; ax = return value (word) + ;; dx = return value (word) + pop ds + pop bx + + pop bp + ASM_END + } + + void + write_dword(seg, offset, data) + Bit16u seg; + Bit16u offset; + Bit32u 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 + add bx, #2 + mov ax, 10[bp] ; data word + mov [bx], ax ; write data word + pop ds + pop bx + pop ax + + pop bp + ASM_END + } + + // Bit32u (unsigned long) and long helper functions + ASM_START + + ;; and function + landl: + landul: + SEG SS + and ax,[di] + SEG SS + and bx,2[di] + ret + + ;; add function + laddl: + laddul: + SEG SS + add ax,[di] + SEG SS + adc bx,2[di] + ret + + ;; cmp function + lcmpl: + lcmpul: + and eax, #0x0000FFFF + shl ebx, #16 + or eax, ebx + shr ebx, #16 + SEG SS + cmp eax, dword ptr [di] + ret + + ;; sub function + lsubl: + lsubul: + SEG SS + sub ax,[di] + SEG SS + sbb bx,2[di] + ret + + ;; mul function + lmull: + lmulul: + and eax, #0x0000FFFF + shl ebx, #16 + or eax, ebx + SEG SS + mul eax, dword ptr [di] + mov ebx, eax + shr ebx, #16 + ret + + ;; dec function + ldecl: + ldecul: + SEG SS + dec dword ptr [bx] + ret + + ;; or function + lorl: + lorul: + SEG SS + or ax,[di] + SEG SS + or bx,2[di] + ret + + ;; inc function + lincl: + lincul: + SEG SS + inc dword ptr [bx] + ret + + ;; tst function + ltstl: + ltstul: + and eax, #0x0000FFFF + shl ebx, #16 + or eax, ebx + shr ebx, #16 + test eax, eax + ret + + ;; sr function + lsrul: + mov cx,di + jcxz lsr_exit + and eax, #0x0000FFFF + shl ebx, #16 + or eax, ebx + lsr_loop: + shr eax, #1 + loop lsr_loop + mov ebx, eax + shr ebx, #16 + lsr_exit: + ret + + ;; sl function + lsll: + lslul: + mov cx,di + jcxz lsl_exit + and eax, #0x0000FFFF + shl ebx, #16 + or eax, ebx + lsl_loop: + shl eax, #1 + loop lsl_loop + mov ebx, eax + shr ebx, #16 + lsl_exit: + ret + + idiv_: + cwd + idiv bx + ret + + idiv_u: + xor dx,dx + div bx + ret + + ldivul: + and eax, #0x0000FFFF + shl ebx, #16 + or eax, ebx + xor edx, edx + SEG SS + mov bx, 2[di] + shl ebx, #16 + SEG SS + mov bx, [di] + div ebx + mov ebx, eax + shr ebx, #16 + ret + + ASM_END + +// for access to RAM area which is used by interrupt vectors +// and BIOS Data Area + +typedef struct { + unsigned char filler1[0x400]; + unsigned char filler2[0x6c]; + Bit16u ticks_low; + Bit16u ticks_high; + Bit8u midnight_flag; + } bios_data_t; + +#define BiosData ((bios_data_t *) 0) + +#if BX_USE_ATADRV + typedef struct { + Bit16u heads; // # heads + Bit16u cylinders; // # cylinders + Bit16u spt; // # sectors / track + } chs_t; + + // DPTE definition + typedef struct { + Bit16u iobase1; + Bit16u iobase2; + Bit8u prefix; + Bit8u unused; + Bit8u irq; + Bit8u blkcount; + Bit8u dma; + Bit8u pio; + Bit16u options; + Bit16u reserved; + Bit8u revision; + Bit8u checksum; + } dpte_t; + + typedef struct { + Bit8u iface; // ISA or PCI + Bit16u iobase1; // IO Base 1 + Bit16u iobase2; // IO Base 2 + Bit8u irq; // IRQ + } ata_channel_t; + + typedef struct { + Bit8u type; // Detected type of ata (ata/atapi/none/unknown) + Bit8u device; // Detected type of attached devices (hd/cd/none) + Bit8u removable; // Removable device flag + Bit8u lock; // Locks for removable devices + Bit8u mode; // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA + Bit16u blksize; // block size + + Bit8u translation; // type of translation + chs_t lchs; // Logical CHS + chs_t pchs; // Physical CHS + + Bit32u sectors_low; // Total sectors count + Bit32u sectors_high; + } ata_device_t; + + typedef struct { + // ATA channels info + ata_channel_t channels[BX_MAX_ATA_INTERFACES]; + + // ATA devices info + ata_device_t devices[BX_MAX_ATA_DEVICES]; + // + // map between (bios hd id - 0x80) and ata channels + Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES]; + + // map between (bios cd id - 0xE0) and ata channels + Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES]; + + // Buffer for DPTE table + dpte_t dpte; + + // Count of transferred sectors and bytes + Bit16u trsfsectors; + Bit32u trsfbytes; + + } ata_t; + +#if BX_ELTORITO_BOOT + // ElTorito Device Emulation data + typedef struct { + Bit8u active; + Bit8u media; + Bit8u emulated_drive; + Bit8u controller_index; + Bit16u device_spec; + Bit32u ilba; + Bit16u buffer_segment; + Bit16u load_segment; + Bit16u sector_count; + + // Virtual device + chs_t vdevice; + } cdemu_t; +#endif // BX_ELTORITO_BOOT + + // for access to EBDA area + // The EBDA structure should conform to + // http://www.frontiernet.net/~fys/rombios.htm document + // I made the ata and cdemu structs begin at 0x121 in the EBDA seg + // EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot + // device tables are at IPL_SEG + typedef struct { + unsigned char filler1[0x3D]; + + // FDPT - Can be splitted in data members if needed + unsigned char fdpt0[0x10]; + unsigned char fdpt1[0x10]; + + unsigned char filler2[0xC4]; + + // ATA Driver data + ata_t ata; + +#if BX_ELTORITO_BOOT + // El Torito Emulation data + cdemu_t cdemu; +#endif // BX_ELTORITO_BOOT + + } ebda_data_t; + + #define EbdaData ((ebda_data_t *) 0) + + // for access to the int13ext structure + typedef struct { + Bit8u size; + Bit8u reserved; + Bit16u count; + Bit16u offset; + Bit16u segment; + Bit32u lba1; + Bit32u lba2; + } int13ext_t; + + #define Int13Ext ((int13ext_t *) 0) + + // Disk Physical Table definition + typedef struct { + Bit16u size; + Bit16u infos; + Bit32u cylinders; + Bit32u heads; + Bit32u spt; + Bit32u sector_count1; + Bit32u sector_count2; + Bit16u blksize; + Bit16u dpte_offset; + Bit16u dpte_segment; + Bit16u key; + Bit8u dpi_length; + Bit8u reserved1; + Bit16u reserved2; + Bit8u host_bus[4]; + Bit8u iface_type[8]; + Bit8u iface_path[8]; + Bit8u device_path[8]; + Bit8u reserved3; + Bit8u checksum; + } dpt_t; + + #define Int13DPT ((dpt_t *) 0) + +#endif // BX_USE_ATADRV + +typedef struct { + union { + struct { + Bit16u di, si, bp, sp; + Bit16u bx, dx, cx, ax; + } r16; + struct { + Bit16u filler[4]; + Bit8u bl, bh, dl, dh, cl, ch, al, ah; + } r8; + } u; + } pusha_regs_t; + +typedef struct { + union { + struct { + Bit32u edi, esi, ebp, esp; + Bit32u ebx, edx, ecx, eax; + } r32; + struct { + Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4; + Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8; + } r16; + struct { + Bit32u filler[4]; + Bit8u bl, bh; + Bit16u filler1; + Bit8u dl, dh; + Bit16u filler2; + Bit8u cl, ch; + Bit16u filler3; + Bit8u al, ah; + Bit16u filler4; + } r8; + } u; +} pushad_regs_t; + +typedef struct { + union { + struct { + Bit16u flags; + } r16; + struct { + Bit8u flagsl; + Bit8u flagsh; + } r8; + } u; + } flags_t; + +#define SetCF(x) x.u.r8.flagsl |= 0x01 +#define SetZF(x) x.u.r8.flagsl |= 0x40 +#define ClearCF(x) x.u.r8.flagsl &= 0xfe +#define ClearZF(x) x.u.r8.flagsl &= 0xbf +#define GetCF(x) (x.u.r8.flagsl & 0x01) + +typedef struct { + Bit16u ip; + Bit16u cs; + flags_t flags; + } iret_addr_t; + +typedef struct { + Bit16u type; + Bit16u flags; + Bit32u vector; + Bit32u description; + Bit32u reserved; + } ipl_entry_t; + + + +static Bit8u inb(); +static Bit8u inb_cmos(); +static void outb(); +static void outb_cmos(); +static Bit16u inw(); +static void outw(); +static void init_rtc(); +static bx_bool rtc_updating(); + +static Bit8u read_byte(); +static Bit16u read_word(); +static void write_byte(); +static void write_word(); +static void bios_printf(); + +static Bit8u inhibit_mouse_int_and_events(); +static void enable_mouse_int_and_events(); +static Bit8u send_to_mouse_ctrl(); +static Bit8u get_mouse_data(); +static void set_kbd_command_byte(); + +static void int09_function(); +static void int13_harddisk(); +static void int13_cdrom(); +static void int13_cdemu(); +static void int13_eltorito(); +static void int13_diskette_function(); +static void int14_function(); +static void int15_function(); +static void int16_function(); +static void int17_function(); +static void int19_function(); +static void int1a_function(); +static void int70_function(); +static void int74_function(); +static Bit16u get_CS(); +static Bit16u get_SS(); +static unsigned int enqueue_key(); +static unsigned int dequeue_key(); +static void get_hd_geometry(); +static void set_diskette_ret_status(); +static void set_diskette_current_cyl(); +static void determine_floppy_media(); +static bx_bool floppy_drive_exists(); +static bx_bool floppy_drive_recal(); +static bx_bool floppy_media_known(); +static bx_bool floppy_media_sense(); +static bx_bool set_enable_a20(); +static void debugger_on(); +static void debugger_off(); +static void keyboard_init(); +static void keyboard_panic(); +static void shutdown_status_panic(); +static void nmi_handler_msg(); +static void delay_ticks(); +static void delay_ticks_and_check_for_keystroke(); + +static void interactive_bootkey(); +static void print_bios_banner(); +static void print_boot_device(); +static void print_boot_failure(); +static void print_cdromboot_failure(); + +# if BX_USE_ATADRV + +// ATA / ATAPI driver +void ata_init(); +void ata_detect(); +void ata_reset(); + +Bit16u ata_cmd_non_data(); +Bit16u ata_cmd_data_in(); +Bit16u ata_cmd_data_out(); +Bit16u ata_cmd_packet(); + +Bit16u atapi_get_sense(); +Bit16u atapi_is_ready(); +Bit16u atapi_is_cdrom(); + +#endif // BX_USE_ATADRV + +#if BX_ELTORITO_BOOT + +void cdemu_init(); +Bit8u cdemu_isactive(); +Bit8u cdemu_emulated_drive(); + +Bit16u cdrom_boot(); + +#endif // BX_ELTORITO_BOOT + +static char bios_cvs_version_string[] = "$Revision: 1.182 $ $Date: 2007/08/01 17:09:51 $"; + +#define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team." + +#if DEBUG_ATA +# define BX_DEBUG_ATA(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_ATA(a...) +#endif +#if DEBUG_INT13_HD +# define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT13_HD(a...) +#endif +#if DEBUG_INT13_CD +# define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT13_CD(a...) +#endif +#if DEBUG_INT13_ET +# define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT13_ET(a...) +#endif +#if DEBUG_INT13_FL +# define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT13_FL(a...) +#endif +#if DEBUG_INT15 +# define BX_DEBUG_INT15(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT15(a...) +#endif +#if DEBUG_INT16 +# define BX_DEBUG_INT16(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT16(a...) +#endif +#if DEBUG_INT1A +# define BX_DEBUG_INT1A(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT1A(a...) +#endif +#if DEBUG_INT74 +# define BX_DEBUG_INT74(a...) BX_DEBUG(a) +#else +# define BX_DEBUG_INT74(a...) +#endif + +#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 GET_ELDL() ( ELDX & 0x00ff ) +#define GET_ELDH() ( ELDX >> 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 UNSUPPORTED_FUNCTION 0x86 + +#define none 0 +#define MAX_SCAN_CODE 0x58 + +static struct { + Bit16u normal; + Bit16u shift; + Bit16u control; + Bit16u alt; + Bit8u lock_flags; + } scan_to_scanascii[MAX_SCAN_CODE + 1] = { + { none, none, none, none, none }, + { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */ + { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */ + { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */ + { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */ + { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */ + { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */ + { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */ + { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */ + { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */ + { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */ + { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */ + { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */ + { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */ + { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */ + { 0x0f09, 0x0f00, none, none, none }, /* tab */ + { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */ + { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */ + { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */ + { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */ + { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */ + { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */ + { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */ + { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */ + { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */ + { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */ + { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */ + { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */ + { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */ + { none, none, none, none, none }, /* L Ctrl */ + { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */ + { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */ + { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */ + { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */ + { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */ + { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */ + { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */ + { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */ + { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */ + { 0x273b, 0x273a, none, none, none }, /* ;: */ + { 0x2827, 0x2822, none, none, none }, /* '" */ + { 0x2960, 0x297e, none, none, none }, /* `~ */ + { none, none, none, none, none }, /* L shift */ + { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */ + { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */ + { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */ + { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */ + { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */ + { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */ + { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */ + { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */ + { 0x332c, 0x333c, none, none, none }, /* ,< */ + { 0x342e, 0x343e, none, none, none }, /* .> */ + { 0x352f, 0x353f, none, none, none }, /* /? */ + { none, none, none, none, none }, /* R Shift */ + { 0x372a, 0x372a, none, none, none }, /* * */ + { none, none, none, none, none }, /* L Alt */ + { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */ + { none, none, none, none, none }, /* caps lock */ + { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */ + { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */ + { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */ + { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */ + { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */ + { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */ + { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */ + { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */ + { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */ + { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */ + { none, none, none, none, none }, /* Num Lock */ + { none, none, none, none, none }, /* Scroll Lock */ + { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */ + { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */ + { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */ + { 0x4a2d, 0x4a2d, none, none, none }, /* - */ + { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */ + { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */ + { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */ + { 0x4e2b, 0x4e2b, none, none, none }, /* + */ + { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */ + { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */ + { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */ + { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */ + { 0x5300, 0x532e, none, none, 0x20 }, /* Del */ + { none, none, none, none, none }, + { none, none, none, none, none }, + { 0x565c, 0x567c, none, none, none }, /* \| */ + { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */ + { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */ + }; + + 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 +} + +#if BX_USE_ATADRV + 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 +} +#endif + + 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 +} + +#if BX_USE_ATADRV + 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 +} +#endif + + void +outb_cmos(cmos_reg, val) + Bit8u cmos_reg; + Bit8u val; +{ +ASM_START + push bp + mov bp, sp + + mov al, 4[bp] ;; cmos_reg + out 0x70, al + mov al, 6[bp] ;; val + out 0x71, al + + pop bp +ASM_END +} + + Bit8u +inb_cmos(cmos_reg) + Bit8u cmos_reg; +{ +ASM_START + push bp + mov bp, sp + + mov al, 4[bp] ;; cmos_reg + out 0x70, al + in al, 0x71 + + pop bp +ASM_END +} + + void +init_rtc() +{ + outb_cmos(0x0a, 0x26); + outb_cmos(0x0b, 0x02); + inb_cmos(0x0c); + inb_cmos(0x0d); +} + + bx_bool +rtc_updating() +{ + // This function checks to see if the update-in-progress bit + // is set in CMOS Status Register A. If not, it returns 0. + // If it is set, it tries to wait until there is a transition + // to 0, and will return 0 if such a transition occurs. A 1 + // is returned only after timing out. The maximum period + // that this bit should be set is constrained to 244useconds. + // The count I use below guarantees coverage or more than + // this time, with any reasonable IPS setting. + + Bit16u count; + + count = 25000; + while (--count != 0) { + if ( (inb_cmos(0x0a) & 0x80) == 0 ) + return(0); + } + return(1); // update-in-progress never transitioned to 0 +} + + + 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 +} + + 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 +} + + 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 +} + + 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 +} + + Bit16u +get_CS() +{ +ASM_START + mov ax, cs +ASM_END +} + + Bit16u +get_SS() +{ +ASM_START + mov ax, ss +ASM_END +} + +#if BX_DEBUG_SERIAL +/* serial debug port*/ +#define BX_DEBUG_PORT 0x03f8 + +/* data */ +#define UART_RBR 0x00 +#define UART_THR 0x00 + +/* control */ +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/* status */ +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 + +int uart_can_tx_byte(base_port) + Bit16u base_port; +{ + return inb(base_port + UART_LSR) & 0x20; +} + +void uart_wait_to_tx_byte(base_port) + Bit16u base_port; +{ + while (!uart_can_tx_byte(base_port)); +} + +void uart_wait_until_sent(base_port) + Bit16u base_port; +{ + while (!(inb(base_port + UART_LSR) & 0x40)); +} + +void uart_tx_byte(base_port, data) + Bit16u base_port; + Bit8u data; +{ + uart_wait_to_tx_byte(base_port); + outb(base_port + UART_THR, data); + uart_wait_until_sent(base_port); +} +#endif + + void +wrch(c) + Bit8u c; +{ + ASM_START + push bp + mov bp, sp + + push bx + mov ah, #0x0e + mov al, 4[bp] + xor bx,bx + int #0x10 + pop bx + + pop bp + ASM_END +} + + void +send(action, c) + Bit16u action; + Bit8u c; +{ +#if BX_DEBUG_SERIAL + if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r'); + uart_tx_byte(BX_DEBUG_PORT, c); +#endif +#if BX_VIRTUAL_PORTS + if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c); + if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c); +#endif + if (action & BIOS_PRINTF_SCREEN) { + if (c == '\n') wrch('\r'); + wrch(c); + } +} + + void +put_int(action, val, width, neg) + Bit16u action; + short val, width; + bx_bool neg; +{ + short nval = val / 10; + if (nval) + put_int(action, nval, width - 1, neg); + else { + while (--width > 0) send(action, ' '); + if (neg) send(action, '-'); + } + send(action, val - (nval * 10) + '0'); +} + + void +put_uint(action, val, width, neg) + Bit16u action; + unsigned short val; + short width; + bx_bool neg; +{ + unsigned short nval = val / 10; + if (nval) + put_uint(action, nval, width - 1, neg); + else { + while (--width > 0) send(action, ' '); + if (neg) send(action, '-'); + } + send(action, val - (nval * 10) + '0'); +} + + void +put_luint(action, val, width, neg) + Bit16u action; + unsigned long val; + short width; + bx_bool neg; +{ + unsigned long nval = val / 10; + if (nval) + put_luint(action, nval, width - 1, neg); + else { + while (--width > 0) send(action, ' '); + if (neg) send(action, '-'); + } + send(action, val - (nval * 10) + '0'); +} + +void put_str(action, segment, offset) + Bit16u action; + Bit16u segment; + Bit16u offset; +{ + Bit8u c; + + while (c = read_byte(segment, offset)) { + send(action, c); + offset++; + } +} + + void +delay_ticks(ticks) + Bit16u ticks; +{ + long ticks_to_wait, delta; + Bit32u prev_ticks, t; + + /* + * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock. + * We also have to be careful about interrupt storms. + */ +ASM_START + pushf + sti +ASM_END + ticks_to_wait = ticks; + prev_ticks = read_dword(0x0, 0x46c); + do + { +ASM_START + hlt +ASM_END + t = read_dword(0x0, 0x46c); + if (t > prev_ticks) + { + delta = t - prev_ticks; /* The temp var is required or bcc screws up. */ + ticks_to_wait -= delta; + } + else if (t < prev_ticks) + { + ticks_to_wait -= t; /* wrapped */ + } + + prev_ticks = t; + } while (ticks_to_wait > 0); +ASM_START + cli + popf +ASM_END +} + + Bit8u +check_for_keystroke() +{ +ASM_START + mov ax, #0x100 + int #0x16 + jz no_key + mov al, #1 + jmp done +no_key: + xor al, al +done: +ASM_END +} + + Bit8u +get_keystroke() +{ +ASM_START + mov ax, #0x0 + int #0x16 + xchg ah, al +ASM_END +} + + void +delay_ticks_and_check_for_keystroke(ticks, count) + Bit16u ticks, count; +{ + Bit16u i; + for (i = 1; i <= count; i++) { + delay_ticks(ticks); + if (check_for_keystroke()) + break; + } +} + +//-------------------------------------------------------------------------- +// bios_printf() +// A compact variable argument printf function. +// +// Supports %[format_width][length]format +// where format can be x,X,u,d,s,S,c +// and the optional length modifier is l (ell) +//-------------------------------------------------------------------------- + void +bios_printf(action, s) + Bit16u action; + Bit8u *s; +{ + Bit8u c, format_char; + bx_bool in_format; + short i; + Bit16u *arg_ptr; + Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd; + + arg_ptr = &s; + arg_seg = get_SS(); + + in_format = 0; + format_width = 0; + + if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) { +#if BX_VIRTUAL_PORTS + outb(PANIC_PORT2, 0x00); +#endif + bios_printf (BIOS_PRINTF_SCREEN, "FATAL: "); + } + + while (c = read_byte(get_CS(), 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 { + arg_ptr++; // increment to next arg + arg = read_word(arg_seg, arg_ptr); + if (c == 'x' || c == 'X') { + if (format_width == 0) + format_width = 4; + if (c == 'x') + hexadd = 'a'; + else + hexadd = 'A'; + for (i=format_width-1; i>=0; i--) { + nibble = (arg >> (4 * i)) & 0x000f; + send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd)); + } + } + else if (c == 'u') { + put_uint(action, arg, format_width, 0); + } + else if (c == 'l') { + s++; + c = read_byte(get_CS(), s); /* is it ld,lx,lu? */ + arg_ptr++; /* increment to next arg */ + hibyte = read_word(arg_seg, arg_ptr); + if (c == 'd') { + if (hibyte & 0x8000) + put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1); + else + put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0); + } + else if (c == 'u') { + put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0); + } + else if (c == 'x' || c == 'X') + { + if (format_width == 0) + format_width = 8; + if (c == 'x') + hexadd = 'a'; + else + hexadd = 'A'; + for (i=format_width-1; i>=0; i--) { + nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f; + send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd)); + } + } + } + else if (c == 'd') { + if (arg & 0x8000) + put_int(action, -arg, format_width - 1, 1); + else + put_int(action, arg, format_width, 0); + } + else if (c == 's') { + put_str(action, get_CS(), arg); + } + else if (c == 'S') { + hibyte = arg; + arg_ptr++; + arg = read_word(arg_seg, arg_ptr); + put_str(action, hibyte, arg); + } + else if (c == 'c') { + send(action, arg); + } + else + BX_PANIC("bios_printf: unknown format\n"); + in_format = 0; + } + } + else { + send(action, c); + } + s ++; + } + + if (action & BIOS_PRINTF_HALT) { + // freeze in a busy loop. +ASM_START + cli + halt2_loop: + hlt + jmp halt2_loop +ASM_END + } +} + +//-------------------------------------------------------------------------- +// keyboard_init +//-------------------------------------------------------------------------- +// this file is based on LinuxBIOS implementation of keyboard.c +// could convert to #asm to gain space + void +keyboard_init() +{ + Bit16u max; + + /* ------------------- Flush buffers ------------------------*/ + /* Wait until buffer is empty */ + max=0xffff; + while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00); + + /* flush incoming keys */ + max=0x2000; + while (--max > 0) { + outb(0x80, 0x00); + if (inb(0x64) & 0x01) { + inb(0x60); + max = 0x2000; + } + } + + // Due to timer issues, and if the IPS setting is > 15000000, + // the incoming keys might not be flushed here. That will + // cause a panic a few lines below. See sourceforge bug report : + // [ 642031 ] FATAL: Keyboard RESET error:993 + + /* ------------------- controller side ----------------------*/ + /* send cmd = 0xAA, self test 8042 */ + outb(0x64, 0xaa); + + /* Wait until buffer is empty */ + max=0xffff; + while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00); + if (max==0x0) keyboard_panic(00); + + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01); + if (max==0x0) keyboard_panic(01); + + /* read self-test result, 0x55 should be returned from 0x60 */ + if ((inb(0x60) != 0x55)){ + keyboard_panic(991); + } + + /* send cmd = 0xAB, keyboard interface test */ + outb(0x64,0xab); + + /* Wait until buffer is empty */ + max=0xffff; + while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10); + if (max==0x0) keyboard_panic(10); + + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11); + if (max==0x0) keyboard_panic(11); + + /* read keyboard interface test result, */ + /* 0x00 should be returned form 0x60 */ + if ((inb(0x60) != 0x00)) { + keyboard_panic(992); + } + + /* Enable Keyboard clock */ + outb(0x64,0xae); + outb(0x64,0xa8); + + /* ------------------- keyboard side ------------------------*/ + /* reset kerboard and self test (keyboard side) */ + outb(0x60, 0xff); + + /* Wait until buffer is empty */ + max=0xffff; + while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20); + if (max==0x0) keyboard_panic(20); + + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21); + if (max==0x0) keyboard_panic(21); + + /* keyboard should return ACK */ + if ((inb(0x60) != 0xfa)) { + keyboard_panic(993); + } + + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31); + if (max==0x0) keyboard_panic(31); + + if ((inb(0x60) != 0xaa)) { + keyboard_panic(994); + } + + /* Disable keyboard */ + outb(0x60, 0xf5); + + /* Wait until buffer is empty */ + max=0xffff; + while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40); + if (max==0x0) keyboard_panic(40); + + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41); + if (max==0x0) keyboard_panic(41); + + /* keyboard should return ACK */ + if ((inb(0x60) != 0xfa)) { + keyboard_panic(995); + } + + /* Write Keyboard Mode */ + outb(0x64, 0x60); + + /* Wait until buffer is empty */ + max=0xffff; + while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50); + if (max==0x0) keyboard_panic(50); + + /* send cmd: scan code convert, disable mouse, enable IRQ 1 */ + outb(0x60, 0x61); + + /* Wait until buffer is empty */ + max=0xffff; + while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60); + if (max==0x0) keyboard_panic(60); + + /* Enable keyboard */ + outb(0x60, 0xf4); + + /* Wait until buffer is empty */ + max=0xffff; + while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70); + if (max==0x0) keyboard_panic(70); + + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71); + if (max==0x0) keyboard_panic(70); + + /* keyboard should return ACK */ + if ((inb(0x60) != 0xfa)) { + keyboard_panic(996); + } + + outb(0x80, 0x77); +} + +//-------------------------------------------------------------------------- +// keyboard_panic +//-------------------------------------------------------------------------- + void +keyboard_panic(status) + Bit16u status; +{ + // If you're getting a 993 keyboard panic here, + // please see the comment in keyboard_init + + BX_PANIC("Keyboard error:%u\n",status); +} + +//-------------------------------------------------------------------------- +// shutdown_status_panic +// called when the shutdown statsu is not implemented, displays the status +//-------------------------------------------------------------------------- + void +shutdown_status_panic(status) + Bit16u status; +{ + BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status); +} + +void s3_resume_panic() +{ + BX_PANIC("Returned from s3_resume.\n"); +} + +//-------------------------------------------------------------------------- +// print_bios_banner +// displays a the bios version +//-------------------------------------------------------------------------- +void +print_bios_banner() +{ + printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ", + BIOS_BUILD_DATE, bios_cvs_version_string); + printf( +#if BX_APM + "apmbios " +#endif +#if BX_PCIBIOS + "pcibios " +#endif +#if BX_ELTORITO_BOOT + "eltorito " +#endif +#if BX_ROMBIOS32 + "rombios32 " +#endif + "\n\n"); +} + +//-------------------------------------------------------------------------- +// BIOS Boot Specification 1.0.1 compatibility +// +// Very basic support for the BIOS Boot Specification, which allows expansion +// ROMs to register themselves as boot devices, instead of just stealing the +// INT 19h boot vector. +// +// This is a hack: to do it properly requires a proper PnP BIOS and we aren't +// one; we just lie to the option ROMs to make them behave correctly. +// We also don't support letting option ROMs register as bootable disk +// drives (BCVs), only as bootable devices (BEVs). +// +// http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm +//-------------------------------------------------------------------------- + +static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"}; + +static void +init_boot_vectors() +{ + ipl_entry_t e; + Bit16u count = 0; + Bit16u ss = get_SS(); + + /* Clear out the IPL table. */ + memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE); + + /* User selected device not set */ + write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF); + + /* Floppy drive */ + e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; + memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); + count++; + + /* First HDD */ + e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; + memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); + count++; + +#if BX_ELTORITO_BOOT + /* CDROM */ + e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; + memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); + count++; +#endif + + /* Remember how many devices we have */ + write_word(IPL_SEG, IPL_COUNT_OFFSET, count); + /* Not tried booting anything yet */ + write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff); +} + +static Bit8u +get_boot_vector(i, e) +Bit16u i; ipl_entry_t *e; +{ + Bit16u count; + Bit16u ss = get_SS(); + /* Get the count of boot devices, and refuse to overrun the array */ + count = read_word(IPL_SEG, IPL_COUNT_OFFSET); + if (i >= count) return 0; + /* OK to read this device */ + memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e)); + return 1; +} + +#if BX_ELTORITO_BOOT +#ifdef BX_QEMU +int +qemu_cfg_probe_bootkey() +{ + outw(QEMU_CFG_CTL_PORT, QEMU_CFG_SIGNATURE); + if (inb(QEMU_CFG_DATA_PORT) != 'Q' || + inb(QEMU_CFG_DATA_PORT) != 'E' || + inb(QEMU_CFG_DATA_PORT) != 'M' || + inb(QEMU_CFG_DATA_PORT) != 'U') return 1; + + outw(QEMU_CFG_CTL_PORT, QEMU_CFG_BOOT_MENU); + return inb(QEMU_CFG_DATA_PORT); +} +#endif // BX_QEMU + + void +interactive_bootkey() +{ + ipl_entry_t e; + Bit16u count; + char description[33]; + Bit8u scan_code; + Bit8u i; + Bit16u ss = get_SS(); + Bit16u valid_choice = 0; + +#ifdef BX_QEMU + if (!qemu_cfg_probe_bootkey()) return; +#endif + + while (check_for_keystroke()) + get_keystroke(); + + printf("Press F12 for boot menu.\n\n"); + + delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */ + if (check_for_keystroke()) + { + scan_code = get_keystroke(); + if (scan_code == 0x86) /* F12 */ + { + while (check_for_keystroke()) + get_keystroke(); + + printf("Select boot device:\n\n"); + + count = read_word(IPL_SEG, IPL_COUNT_OFFSET); + for (i = 0; i < count; i++) + { + memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e)); + printf("%d. ", i+1); + switch(e.type) + { + case IPL_TYPE_FLOPPY: + case IPL_TYPE_HARDDISK: + case IPL_TYPE_CDROM: + printf("%s\n", drivetypes[e.type]); + break; + case IPL_TYPE_BEV: + printf("%s", drivetypes[4]); + if (e.description != 0) + { + memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32); + description[32] = 0; + printf(" [%S]", ss, description); + } + printf("\n"); + break; + } + } + + count++; + while (!valid_choice) { + scan_code = get_keystroke(); + if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */ + { + valid_choice = 1; + } + else if (scan_code <= count) + { + valid_choice = 1; + scan_code -= 1; + /* Set user selected device */ + write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code); + } + } + printf("\n"); + } + } +} +#endif // BX_ELTORITO_BOOT + +//-------------------------------------------------------------------------- +// print_boot_device +// displays the boot device +//-------------------------------------------------------------------------- + +void +print_boot_device(e) + ipl_entry_t *e; +{ + Bit16u type; + char description[33]; + Bit16u ss = get_SS(); + type = e->type; + /* NIC appears as type 0x80 */ + if (type == IPL_TYPE_BEV) type = 0x4; + if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n"); + printf("Booting from %s", drivetypes[type]); + /* print product string if BEV */ + if (type == 4 && e->description != 0) { + /* first 32 bytes are significant */ + memcpyb(ss, &description, (Bit16u)(e->description >> 16), (Bit16u)(e->description & 0xffff), 32); + /* terminate string */ + description[32] = 0; + printf(" [%S]", ss, description); + } + printf("...\n"); +} + +//-------------------------------------------------------------------------- +// print_boot_failure +// displays the reason why boot failed +//-------------------------------------------------------------------------- + void +print_boot_failure(type, reason) + Bit16u type; Bit8u reason; +{ + if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n"); + + printf("Boot failed"); + if (type < 4) { + /* Report the reason too */ + if (reason==0) + printf(": not a bootable disk"); + else + printf(": could not read the boot disk"); + } + printf("\n\n"); +} + +//-------------------------------------------------------------------------- +// print_cdromboot_failure +// displays the reason why boot failed +//-------------------------------------------------------------------------- + void +print_cdromboot_failure( code ) + Bit16u code; +{ + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code); + + return; +} + +void +nmi_handler_msg() +{ + BX_PANIC("NMI Handler called\n"); +} + +void +int18_panic_msg() +{ + BX_PANIC("INT18: BOOT FAILURE\n"); +} + +void +log_bios_start() +{ +#if BX_DEBUG_SERIAL + outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */ +#endif + BX_INFO("%s\n", bios_cvs_version_string); +} + + bx_bool +set_enable_a20(val) + bx_bool val; +{ + Bit8u oldval; + + // Use PS2 System Control port A to set A20 enable + + // get current setting first + oldval = inb(0x92); + + // change A20 status + if (val) + outb(0x92, oldval | 0x02); + else + outb(0x92, oldval & 0xfd); + + return((oldval & 0x02) != 0); +} + + void +debugger_on() +{ + outb(0xfedc, 0x01); +} + + void +debugger_off() +{ + outb(0xfedc, 0x00); +} + +int +s3_resume() +{ + Bit32u s3_wakeup_vector; + Bit8u s3_resume_flag; + + s3_resume_flag = read_byte(0x40, 0xb0); + s3_wakeup_vector = read_dword(0x40, 0xb2); + + BX_INFO("S3 resume called %x 0x%lx\n", s3_resume_flag, s3_wakeup_vector); + if (s3_resume_flag != 0xFE || !s3_wakeup_vector) + return 0; + + write_byte(0x40, 0xb0, 0); + + /* setup wakeup vector */ + write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */ + write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */ + + BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4), + (s3_wakeup_vector & 0xF)); +ASM_START + mov sp, #0 ;; disable tpr patching on boot CPU + jmpf [0x04b6] +ASM_END + return 1; +} + +#if BX_USE_ATADRV + +// --------------------------------------------------------------------------- +// Start of ATA/ATAPI Driver +// --------------------------------------------------------------------------- + +// Global defines -- ATA register and register bits. +// command block & control block regs +#define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0 +#define ATA_CB_ERR 1 // error in pio_base_addr1+1 +#define ATA_CB_FR 1 // feature reg out pio_base_addr1+1 +#define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2 +#define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3 +#define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4 +#define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5 +#define ATA_CB_DH 6 // device head in/out pio_base_addr1+6 +#define ATA_CB_STAT 7 // primary status in pio_base_addr1+7 +#define ATA_CB_CMD 7 // command out pio_base_addr1+7 +#define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6 +#define ATA_CB_DC 6 // device control out pio_base_addr2+6 +#define ATA_CB_DA 7 // device address in pio_base_addr2+7 + +#define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC +#define ATA_CB_ER_BBK 0x80 // ATA bad block +#define ATA_CB_ER_UNC 0x40 // ATA uncorrected error +#define ATA_CB_ER_MC 0x20 // ATA media change +#define ATA_CB_ER_IDNF 0x10 // ATA id not found +#define ATA_CB_ER_MCR 0x08 // ATA media change request +#define ATA_CB_ER_ABRT 0x04 // ATA command aborted +#define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found +#define ATA_CB_ER_NDAM 0x01 // ATA address mark not found + +#define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask) +#define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request +#define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort +#define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media +#define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication + +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC) +#define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask) +#define ATA_CB_SC_P_REL 0x04 // ATAPI release +#define ATA_CB_SC_P_IO 0x02 // ATAPI I/O +#define ATA_CB_SC_P_CD 0x01 // ATAPI C/D + +// bits 7-4 of the device/head (CB_DH) reg +#define ATA_CB_DH_DEV0 0xa0 // select device 0 +#define ATA_CB_DH_DEV1 0xb0 // select device 1 +#define ATA_CB_DH_LBA 0x40 // use LBA + +// status reg (CB_STAT and CB_ASTAT) bits +#define ATA_CB_STAT_BSY 0x80 // busy +#define ATA_CB_STAT_RDY 0x40 // ready +#define ATA_CB_STAT_DF 0x20 // device fault +#define ATA_CB_STAT_WFT 0x20 // write fault (old name) +#define ATA_CB_STAT_SKC 0x10 // seek complete +#define ATA_CB_STAT_SERV 0x10 // service +#define ATA_CB_STAT_DRQ 0x08 // data request +#define ATA_CB_STAT_CORR 0x04 // corrected +#define ATA_CB_STAT_IDX 0x02 // index +#define ATA_CB_STAT_ERR 0x01 // error (ATA) +#define ATA_CB_STAT_CHK 0x01 // check (ATAPI) + +// device control reg (CB_DC) bits +#define ATA_CB_DC_HD15 0x08 // bit should always be set to one +#define ATA_CB_DC_SRST 0x04 // soft reset +#define ATA_CB_DC_NIEN 0x02 // disable interrupts + +// Most mandtory and optional ATA commands (from ATA-3), +#define ATA_CMD_CFA_ERASE_SECTORS 0xC0 +#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define ATA_CMD_CHECK_POWER_MODE1 0xE5 +#define ATA_CMD_CHECK_POWER_MODE2 0x98 +#define ATA_CMD_DEVICE_RESET 0x08 +#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define ATA_CMD_FLUSH_CACHE 0xE7 +#define ATA_CMD_FORMAT_TRACK 0x50 +#define ATA_CMD_IDENTIFY_DEVICE 0xEC +#define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define ATA_CMD_IDLE1 0xE3 +#define ATA_CMD_IDLE2 0x97 +#define ATA_CMD_IDLE_IMMEDIATE1 0xE1 +#define ATA_CMD_IDLE_IMMEDIATE2 0x95 +#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define ATA_CMD_NOP 0x00 +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_READ_BUFFER 0xE4 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_QUEUED 0xC7 +#define ATA_CMD_READ_MULTIPLE 0xC4 +#define ATA_CMD_READ_SECTORS 0x20 +#define ATA_CMD_READ_VERIFY_SECTORS 0x40 +#define ATA_CMD_RECALIBRATE 0x10 +#define ATA_CMD_REQUEST_SENSE 0x03 +#define ATA_CMD_SEEK 0x70 +#define ATA_CMD_SET_FEATURES 0xEF +#define ATA_CMD_SET_MULTIPLE_MODE 0xC6 +#define ATA_CMD_SLEEP1 0xE6 +#define ATA_CMD_SLEEP2 0x99 +#define ATA_CMD_STANDBY1 0xE2 +#define ATA_CMD_STANDBY2 0x96 +#define ATA_CMD_STANDBY_IMMEDIATE1 0xE0 +#define ATA_CMD_STANDBY_IMMEDIATE2 0x94 +#define ATA_CMD_WRITE_BUFFER 0xE8 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_QUEUED 0xCC +#define ATA_CMD_WRITE_MULTIPLE 0xC5 +#define ATA_CMD_WRITE_SECTORS 0x30 +#define ATA_CMD_WRITE_VERIFY 0x3C + +#define ATA_IFACE_NONE 0x00 +#define ATA_IFACE_ISA 0x00 +#define ATA_IFACE_PCI 0x01 + +#define ATA_TYPE_NONE 0x00 +#define ATA_TYPE_UNKNOWN 0x01 +#define ATA_TYPE_ATA 0x02 +#define ATA_TYPE_ATAPI 0x03 + +#define ATA_DEVICE_NONE 0x00 +#define ATA_DEVICE_HD 0xFF +#define ATA_DEVICE_CDROM 0x05 + +#define ATA_MODE_NONE 0x00 +#define ATA_MODE_PIO16 0x00 +#define ATA_MODE_PIO32 0x01 +#define ATA_MODE_ISADMA 0x02 +#define ATA_MODE_PCIDMA 0x03 +#define ATA_MODE_USEIRQ 0x10 + +#define ATA_TRANSLATION_NONE 0 +#define ATA_TRANSLATION_LBA 1 +#define ATA_TRANSLATION_LARGE 2 +#define ATA_TRANSLATION_RECHS 3 + +#define ATA_DATA_NO 0x00 +#define ATA_DATA_IN 0x01 +#define ATA_DATA_OUT 0x02 + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : initialization +// --------------------------------------------------------------------------- +void ata_init( ) +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u channel, device; + + // Channels info init. + for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) { + write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE); + write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0); + write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0); + write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0); + } + + // Devices info init. + for (device=0; device<BX_MAX_ATA_DEVICES; device++) { + write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE); + write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE); + write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0); + write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0); + write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE); + write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0); + write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE); + write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0); + write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0); + write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0); + write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0); + write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0); + write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0); + + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L); + } + + // hdidmap and cdidmap init. + for (device=0; device<BX_MAX_ATA_DEVICES; device++) { + write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES); + write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES); + } + + write_byte(ebda_seg,&EbdaData->ata.hdcount,0); + write_byte(ebda_seg,&EbdaData->ata.cdcount,0); +} + +#define TIMEOUT 0 +#define BSY 1 +#define NOT_BSY 2 +#define NOT_BSY_DRQ 3 +#define NOT_BSY_NOT_DRQ 4 +#define NOT_BSY_RDY 5 + +#define IDE_TIMEOUT 32000u //32 seconds max for IDE ops + +int await_ide(); +static int await_ide(when_done,base,timeout) + Bit8u when_done; + Bit16u base; + Bit16u timeout; +{ + Bit32u time=0,last=0; + Bit16u status; + Bit8u result; + status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away + for(;;) { + status = inb(base+ATA_CB_STAT); + time++; + if (when_done == BSY) + result = status & ATA_CB_STAT_BSY; + else if (when_done == NOT_BSY) + result = !(status & ATA_CB_STAT_BSY); + else if (when_done == NOT_BSY_DRQ) + result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ); + else if (when_done == NOT_BSY_NOT_DRQ) + result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ); + else if (when_done == NOT_BSY_RDY) + result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY); + else if (when_done == TIMEOUT) + result = 0; + + if (result) return 0; + if (time>>16 != last) // mod 2048 each 16 ms + { + last = time >>16; + BX_DEBUG_ATA("await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout); + } + if (status & ATA_CB_STAT_ERR) + { + BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout); + return -1; + } + if ((timeout == 0) || ((time>>11) > timeout)) break; + } + BX_INFO("IDE time out\n"); + return -1; +} + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : device detection +// --------------------------------------------------------------------------- + +void ata_detect( ) +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u hdcount, cdcount, device, type; + Bit8u buffer[0x0200]; + +#if BX_MAX_ATA_INTERFACES > 0 + write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA); + write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0); + write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0); + write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14); +#endif +#if BX_MAX_ATA_INTERFACES > 1 + write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA); + write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170); + write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370); + write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15); +#endif +#if BX_MAX_ATA_INTERFACES > 2 + write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA); + write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8); + write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0); + write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12); +#endif +#if BX_MAX_ATA_INTERFACES > 3 + write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA); + write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168); + write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360); + write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11); +#endif +#if BX_MAX_ATA_INTERFACES > 4 +#error Please fill the ATA interface informations +#endif + + // Device detection + hdcount=cdcount=0; + + for(device=0; device<BX_MAX_ATA_DEVICES; device++) { + Bit16u iobase1, iobase2; + Bit8u channel, slave, shift; + Bit8u sc, sn, cl, ch, st; + + channel = device / 2; + slave = device % 2; + + iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1); + iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2); + + // Disable interrupts + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); + + // Look for device + outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0); + outb(iobase1+ATA_CB_SC, 0x55); + outb(iobase1+ATA_CB_SN, 0xaa); + outb(iobase1+ATA_CB_SC, 0xaa); + outb(iobase1+ATA_CB_SN, 0x55); + outb(iobase1+ATA_CB_SC, 0x55); + outb(iobase1+ATA_CB_SN, 0xaa); + + // If we found something + sc = inb(iobase1+ATA_CB_SC); + sn = inb(iobase1+ATA_CB_SN); + + if ( (sc == 0x55) && (sn == 0xaa) ) { + write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN); + + // reset the channel + ata_reset(device); + + // check for ATA or ATAPI + outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0); + sc = inb(iobase1+ATA_CB_SC); + sn = inb(iobase1+ATA_CB_SN); + if ((sc==0x01) && (sn==0x01)) { + cl = inb(iobase1+ATA_CB_CL); + ch = inb(iobase1+ATA_CB_CH); + st = inb(iobase1+ATA_CB_STAT); + + if ((cl==0x14) && (ch==0xeb)) { + write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI); + } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) { + write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA); + } else if ((cl==0xff) && (ch==0xff)) { + write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE); + } + } + } + + type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type); + + // Now we send a IDENTIFY command to ATA device + if(type == ATA_TYPE_ATA) { + Bit32u sectors_low, sectors_high; + Bit16u cylinders, heads, spt, blksize; + Bit8u translation, removable, mode; + + //Temporary values to do the transfer + write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD); + write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16); + + if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 ) + BX_PANIC("ata-detect: Failed to detect ATA device\n"); + + removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0; + mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16; + blksize = read_word(get_SS(),buffer+10); + + cylinders = read_word(get_SS(),buffer+(1*2)); // word 1 + heads = read_word(get_SS(),buffer+(3*2)); // word 3 + spt = read_word(get_SS(),buffer+(6*2)); // word 6 + + if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support + sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101 + sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103 + } else { + sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61 + sectors_high = 0; + } + + write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD); + write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable); + write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode); + write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize); + write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads); + write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders); + write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high); + BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt); + + translation = inb_cmos(0x39 + channel/2); + for (shift=device%4; shift>0; shift--) translation >>= 2; + translation &= 0x03; + + write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation); + + switch (translation) { + case ATA_TRANSLATION_NONE: + BX_INFO("none"); + break; + case ATA_TRANSLATION_LBA: + BX_INFO("lba"); + break; + case ATA_TRANSLATION_LARGE: + BX_INFO("large"); + break; + case ATA_TRANSLATION_RECHS: + BX_INFO("r-echs"); + break; + } + switch (translation) { + case ATA_TRANSLATION_NONE: + break; + case ATA_TRANSLATION_LBA: + spt = 63; + sectors_low /= 63; + heads = sectors_low / 1024; + if (heads>128) heads = 255; + else if (heads>64) heads = 128; + else if (heads>32) heads = 64; + else if (heads>16) heads = 32; + else heads=16; + cylinders = sectors_low / heads; + break; + case ATA_TRANSLATION_RECHS: + // Take care not to overflow + if (heads==16) { + if(cylinders>61439) cylinders=61439; + heads=15; + cylinders = (Bit16u)((Bit32u)(cylinders)*16/15); + } + // then go through the large bitshift process + case ATA_TRANSLATION_LARGE: + while(cylinders > 1024) { + cylinders >>= 1; + heads <<= 1; + + // If we max out the head count + if (heads > 127) break; + } + break; + } + // clip to 1024 cylinders in lchs + if (cylinders > 1024) cylinders=1024; + BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt); + + write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads); + write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders); + write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt); + + // fill hdidmap + write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device); + hdcount++; + } + + // Now we send a IDENTIFY command to ATAPI device + if(type == ATA_TYPE_ATAPI) { + + Bit8u type, removable, mode; + Bit16u blksize; + + //Temporary values to do the transfer + write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM); + write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16); + + if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0) + BX_PANIC("ata-detect: Failed to detect ATAPI device\n"); + + type = read_byte(get_SS(),buffer+1) & 0x1f; + removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0; + mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16; + blksize = 2048; + + write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type); + write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable); + write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode); + write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize); + + // fill cdidmap + write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device); + cdcount++; + } + + { + Bit32u sizeinmb; + Bit16u ataversion; + Bit8u c, i, version, model[41]; + + switch (type) { + case ATA_TYPE_ATA: + sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21) + | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11); + case ATA_TYPE_ATAPI: + // Read ATA/ATAPI version + ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160); + for(version=15;version>0;version--) { + if((ataversion&(1<<version))!=0) + break; + } + + // Read model name + for(i=0;i<20;i++){ + write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1)); + write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54)); + } + + // Reformat + write_byte(get_SS(),model+40,0x00); + for(i=39;i>0;i--){ + if(read_byte(get_SS(),model+i)==0x20) + write_byte(get_SS(),model+i,0x00); + else break; + } + if (i>36) { + write_byte(get_SS(),model+36,0x00); + for(i=35;i>32;i--){ + write_byte(get_SS(),model+i,0x2E); + } + } + break; + } + + switch (type) { + case ATA_TYPE_ATA: + printf("ata%d %s: ",channel,slave?" slave":"master"); + i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c); + if (sizeinmb < (1UL<<16)) + printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb); + else + printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10)); + break; + case ATA_TYPE_ATAPI: + printf("ata%d %s: ",channel,slave?" slave":"master"); + i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c); + if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM) + printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version); + else + printf(" ATAPI-%d Device\n",version); + break; + case ATA_TYPE_UNKNOWN: + printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master"); + break; + } + } + } + + // Store the devices counts + write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount); + write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount); + write_byte(0x40,0x75, hdcount); + + printf("\n"); + + // FIXME : should use bios=cmos|auto|disable bits + // FIXME : should know about translation bits + // FIXME : move hard_drive_post here + +} + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : software reset +// --------------------------------------------------------------------------- +// ATA-3 +// 8.2.1 Software reset - Device 0 + +void ata_reset(device) +Bit16u device; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit16u iobase1, iobase2; + Bit8u channel, slave, sn, sc; + Bit8u type; + Bit16u max; + + channel = device / 2; + slave = device % 2; + + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); + + // Reset + +// 8.2.1 (a) -- set SRST in DC + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST); + +// 8.2.1 (b) -- wait for BSY + await_ide(BSY, iobase1, 20); + +// 8.2.1 (f) -- clear SRST + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); + + type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type); + if (type != ATA_TYPE_NONE) { + +// 8.2.1 (g) -- check for sc==sn==0x01 + // select device + outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0); + sc = inb(iobase1+ATA_CB_SC); + sn = inb(iobase1+ATA_CB_SN); + + if ( (sc==0x01) && (sn==0x01) ) { + if (type == ATA_TYPE_ATA) //ATA + await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT); + else //ATAPI + await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); + } + +// 8.2.1 (h) -- wait for not BSY + await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); + } + + // Enable interrupts + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); +} + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : execute a non data command +// --------------------------------------------------------------------------- + +Bit16u ata_cmd_non_data() +{return 0;} + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : execute a data-in command +// --------------------------------------------------------------------------- + // returns + // 0 : no error + // 1 : BUSY bit set + // 2 : read error + // 3 : expected DRQ=1 + // 4 : no sectors left to read/verify + // 5 : more sectors to read/verify + // 6 : no sectors left to write + // 7 : more sectors to write +Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset) +Bit16u device, command, count, cylinder, head, sector, segment, offset; +Bit32u lba_low, lba_high; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit16u iobase1, iobase2, blksize; + Bit8u channel, slave; + Bit8u status, current, mode; + + channel = device / 2; + slave = device % 2; + + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); + mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); + blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); + if (mode == ATA_MODE_PIO32) blksize>>=2; + else blksize>>=1; + + // Reset count of transferred data + write_word(ebda_seg, &EbdaData->ata.trsfsectors,0); + write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L); + current = 0; + + status = inb(iobase1 + ATA_CB_STAT); + if (status & ATA_CB_STAT_BSY) return 1; + + outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); + + // sector will be 0 only on lba access. Convert to lba-chs + if (sector == 0) { + if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) { + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); + outb(iobase1 + ATA_CB_SN, lba_low >> 24); + outb(iobase1 + ATA_CB_CL, lba_high & 0xff); + outb(iobase1 + ATA_CB_CH, lba_high >> 8); + command |= 0x04; + count &= (1UL << 8) - 1; + lba_low &= (1UL << 24) - 1; + } + sector = (Bit16u) (lba_low & 0x000000ffL); + cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL); + head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; + } + + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, count); + outb(iobase1 + ATA_CB_SN, sector); + outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff); + outb(iobase1 + ATA_CB_CH, cylinder >> 8); + outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head ); + outb(iobase1 + ATA_CB_CMD, command); + + await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); + status = inb(iobase1 + ATA_CB_STAT); + + if (status & ATA_CB_STAT_ERR) { + BX_DEBUG_ATA("ata_cmd_data_in : read error\n"); + return 2; + } else if ( !(status & ATA_CB_STAT_DRQ) ) { + BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status); + return 3; + } + + // FIXME : move seg/off translation here + +ASM_START + sti ;; enable higher priority interrupts +ASM_END + + while (1) { + +ASM_START + push bp + mov bp, sp + mov di, _ata_cmd_data_in.offset + 2[bp] + mov ax, _ata_cmd_data_in.segment + 2[bp] + mov cx, _ata_cmd_data_in.blksize + 2[bp] + + ;; adjust if there will be an overrun. 2K max sector size + cmp di, #0xf800 ;; + jbe ata_in_no_adjust + +ata_in_adjust: + sub di, #0x0800 ;; sub 2 kbytes from offset + add ax, #0x0080 ;; add 2 Kbytes to segment + +ata_in_no_adjust: + mov es, ax ;; segment in es + + mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port + + mov ah, _ata_cmd_data_in.mode + 2[bp] + cmp ah, #ATA_MODE_PIO32 + je ata_in_32 + +ata_in_16: + rep + insw ;; CX words transfered from port(DX) to ES:[DI] + jmp ata_in_done + +ata_in_32: + rep + insd ;; CX dwords transfered from port(DX) to ES:[DI] + +ata_in_done: + mov _ata_cmd_data_in.offset + 2[bp], di + mov _ata_cmd_data_in.segment + 2[bp], es + pop bp +ASM_END + + current++; + write_word(ebda_seg, &EbdaData->ata.trsfsectors,current); + count--; + await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); + status = inb(iobase1 + ATA_CB_STAT); + if (count == 0) { + if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) + != ATA_CB_STAT_RDY ) { + BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status); + return 4; + } + break; + } + else { + if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) + != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) { + BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status); + return 5; + } + continue; + } + } + // Enable interrupts + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); + return 0; +} + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : execute a data-out command +// --------------------------------------------------------------------------- + // returns + // 0 : no error + // 1 : BUSY bit set + // 2 : read error + // 3 : expected DRQ=1 + // 4 : no sectors left to read/verify + // 5 : more sectors to read/verify + // 6 : no sectors left to write + // 7 : more sectors to write +Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset) +Bit16u device, command, count, cylinder, head, sector, segment, offset; +Bit32u lba_low, lba_high; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit16u iobase1, iobase2, blksize; + Bit8u channel, slave; + Bit8u status, current, mode; + + channel = device / 2; + slave = device % 2; + + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); + mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); + blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); + if (mode == ATA_MODE_PIO32) blksize>>=2; + else blksize>>=1; + + // Reset count of transferred data + write_word(ebda_seg, &EbdaData->ata.trsfsectors,0); + write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L); + current = 0; + + status = inb(iobase1 + ATA_CB_STAT); + if (status & ATA_CB_STAT_BSY) return 1; + + outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); + + // sector will be 0 only on lba access. Convert to lba-chs + if (sector == 0) { + if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) { + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); + outb(iobase1 + ATA_CB_SN, lba_low >> 24); + outb(iobase1 + ATA_CB_CL, lba_high & 0xff); + outb(iobase1 + ATA_CB_CH, lba_high >> 8); + command |= 0x04; + count &= (1UL << 8) - 1; + lba_low &= (1UL << 24) - 1; + } + sector = (Bit16u) (lba_low & 0x000000ffL); + cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL); + head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; + } + + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, count); + outb(iobase1 + ATA_CB_SN, sector); + outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff); + outb(iobase1 + ATA_CB_CH, cylinder >> 8); + outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head ); + outb(iobase1 + ATA_CB_CMD, command); + + await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); + status = inb(iobase1 + ATA_CB_STAT); + + if (status & ATA_CB_STAT_ERR) { + BX_DEBUG_ATA("ata_cmd_data_out : read error\n"); + return 2; + } else if ( !(status & ATA_CB_STAT_DRQ) ) { + BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status); + return 3; + } + + // FIXME : move seg/off translation here + +ASM_START + sti ;; enable higher priority interrupts +ASM_END + + while (1) { + +ASM_START + push bp + mov bp, sp + mov si, _ata_cmd_data_out.offset + 2[bp] + mov ax, _ata_cmd_data_out.segment + 2[bp] + mov cx, _ata_cmd_data_out.blksize + 2[bp] + + ;; adjust if there will be an overrun. 2K max sector size + cmp si, #0xf800 ;; + jbe ata_out_no_adjust + +ata_out_adjust: + sub si, #0x0800 ;; sub 2 kbytes from offset + add ax, #0x0080 ;; add 2 Kbytes to segment + +ata_out_no_adjust: + mov es, ax ;; segment in es + + mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port + + mov ah, _ata_cmd_data_out.mode + 2[bp] + cmp ah, #ATA_MODE_PIO32 + je ata_out_32 + +ata_out_16: + seg ES + rep + outsw ;; CX words transfered from port(DX) to ES:[SI] + jmp ata_out_done + +ata_out_32: + seg ES + rep + outsd ;; CX dwords transfered from port(DX) to ES:[SI] + +ata_out_done: + mov _ata_cmd_data_out.offset + 2[bp], si + mov _ata_cmd_data_out.segment + 2[bp], es + pop bp +ASM_END + + current++; + write_word(ebda_seg, &EbdaData->ata.trsfsectors,current); + count--; + status = inb(iobase1 + ATA_CB_STAT); + if (count == 0) { + if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) + != ATA_CB_STAT_RDY ) { + BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status); + return 6; + } + break; + } + else { + if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) + != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) { + BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status); + return 7; + } + continue; + } + } + // Enable interrupts + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); + return 0; +} + +// --------------------------------------------------------------------------- +// ATA/ATAPI driver : execute a packet command +// --------------------------------------------------------------------------- + // returns + // 0 : no error + // 1 : error in parameters + // 2 : BUSY bit set + // 3 : error + // 4 : not ready +Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff) +Bit8u cmdlen,inout; +Bit16u device,cmdseg, cmdoff, bufseg, bufoff; +Bit16u header; +Bit32u length; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit16u iobase1, iobase2; + Bit16u lcount, lbefore, lafter, count; + Bit8u channel, slave; + Bit8u status, mode, lmode; + Bit32u total, transfer; + + channel = device / 2; + slave = device % 2; + + // Data out is not supported yet + if (inout == ATA_DATA_OUT) { + BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n"); + return 1; + } + + // The header length must be even + if (header & 1) { + BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header); + return 1; + } + + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); + mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); + transfer= 0L; + + if (cmdlen < 12) cmdlen=12; + if (cmdlen > 12) cmdlen=16; + cmdlen>>=1; + + // Reset count of transferred data + write_word(ebda_seg, &EbdaData->ata.trsfsectors,0); + write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L); + + status = inb(iobase1 + ATA_CB_STAT); + if (status & ATA_CB_STAT_BSY) return 2; + + outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, 0x00); + outb(iobase1 + ATA_CB_SN, 0x00); + outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff); + outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8); + outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0); + outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET); + + // Device should ok to receive command + await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); + status = inb(iobase1 + ATA_CB_STAT); + + if (status & ATA_CB_STAT_ERR) { + BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status); + return 3; + } else if ( !(status & ATA_CB_STAT_DRQ) ) { + BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status); + return 4; + } + + // Normalize address + cmdseg += (cmdoff / 16); + cmdoff %= 16; + + // Send command to device +ASM_START + sti ;; enable higher priority interrupts + + push bp + mov bp, sp + + mov si, _ata_cmd_packet.cmdoff + 2[bp] + mov ax, _ata_cmd_packet.cmdseg + 2[bp] + mov cx, _ata_cmd_packet.cmdlen + 2[bp] + mov es, ax ;; segment in es + + mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port + + seg ES + rep + outsw ;; CX words transfered from port(DX) to ES:[SI] + + pop bp +ASM_END + + if (inout == ATA_DATA_NO) { + await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); + status = inb(iobase1 + ATA_CB_STAT); + } + else { + Bit16u loops = 0; + Bit8u sc; + while (1) { + + if (loops == 0) {//first time through + status = inb(iobase2 + ATA_CB_ASTAT); + await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); + } + else + await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); + loops++; + + status = inb(iobase1 + ATA_CB_STAT); + sc = inb(iobase1 + ATA_CB_SC); + + // Check if command completed + if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) && + ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break; + + if (status & ATA_CB_STAT_ERR) { + BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status); + return 3; + } + + // Normalize address + bufseg += (bufoff / 16); + bufoff %= 16; + + // Get the byte count + lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL); + + // adjust to read what we want + if(header>lcount) { + lbefore=lcount; + header-=lcount; + lcount=0; + } + else { + lbefore=header; + header=0; + lcount-=lbefore; + } + + if(lcount>length) { + lafter=lcount-length; + lcount=length; + length=0; + } + else { + lafter=0; + length-=lcount; + } + + // Save byte count + count = lcount; + + BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter); + BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff); + + // If counts not dividable by 4, use 16bits mode + lmode = mode; + if (lbefore & 0x03) lmode=ATA_MODE_PIO16; + if (lcount & 0x03) lmode=ATA_MODE_PIO16; + if (lafter & 0x03) lmode=ATA_MODE_PIO16; + + // adds an extra byte if count are odd. before is always even + if (lcount & 0x01) { + lcount+=1; + if ((lafter > 0) && (lafter & 0x01)) { + lafter-=1; + } + } + + if (lmode == ATA_MODE_PIO32) { + lcount>>=2; lbefore>>=2; lafter>>=2; + } + else { + lcount>>=1; lbefore>>=1; lafter>>=1; + } + + ; // FIXME bcc bug + +ASM_START + push bp + mov bp, sp + + mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port + + mov cx, _ata_cmd_packet.lbefore + 2[bp] + jcxz ata_packet_no_before + + mov ah, _ata_cmd_packet.lmode + 2[bp] + cmp ah, #ATA_MODE_PIO32 + je ata_packet_in_before_32 + +ata_packet_in_before_16: + in ax, dx + loop ata_packet_in_before_16 + jmp ata_packet_no_before + +ata_packet_in_before_32: + push eax +ata_packet_in_before_32_loop: + in eax, dx + loop ata_packet_in_before_32_loop + pop eax + +ata_packet_no_before: + mov cx, _ata_cmd_packet.lcount + 2[bp] + jcxz ata_packet_after + + mov di, _ata_cmd_packet.bufoff + 2[bp] + mov ax, _ata_cmd_packet.bufseg + 2[bp] + mov es, ax + + mov ah, _ata_cmd_packet.lmode + 2[bp] + cmp ah, #ATA_MODE_PIO32 + je ata_packet_in_32 + +ata_packet_in_16: + rep + insw ;; CX words transfered tp port(DX) to ES:[DI] + jmp ata_packet_after + +ata_packet_in_32: + rep + insd ;; CX dwords transfered to port(DX) to ES:[DI] + +ata_packet_after: + mov cx, _ata_cmd_packet.lafter + 2[bp] + jcxz ata_packet_done + + mov ah, _ata_cmd_packet.lmode + 2[bp] + cmp ah, #ATA_MODE_PIO32 + je ata_packet_in_after_32 + +ata_packet_in_after_16: + in ax, dx + loop ata_packet_in_after_16 + jmp ata_packet_done + +ata_packet_in_after_32: + push eax +ata_packet_in_after_32_loop: + in eax, dx + loop ata_packet_in_after_32_loop + pop eax + +ata_packet_done: + pop bp +ASM_END + + // Compute new buffer address + bufoff += count; + + // Save transferred bytes count + transfer += count; + write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer); + } + } + + // Final check, device must be ready + if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) + != ATA_CB_STAT_RDY ) { + BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status); + return 4; + } + + // Enable interrupts + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); + return 0; +} + +// --------------------------------------------------------------------------- +// End of ATA/ATAPI Driver +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// Start of ATA/ATAPI generic functions +// --------------------------------------------------------------------------- + + Bit16u +atapi_get_sense(device, seg, asc, ascq) + Bit16u device; +{ + Bit8u atacmd[12]; + Bit8u buffer[18]; + Bit8u i; + + memsetb(get_SS(),atacmd,0,12); + + // Request SENSE + atacmd[0]=ATA_CMD_REQUEST_SENSE; + atacmd[4]=sizeof(buffer); + if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0) + return 0x0002; + + write_byte(seg,asc,buffer[12]); + write_byte(seg,ascq,buffer[13]); + + return 0; +} + + Bit16u +atapi_is_ready(device) + Bit16u device; +{ + Bit8u packet[12]; + Bit8u buf[8]; + Bit32u block_len; + Bit32u sectors; + Bit32u timeout; //measured in ms + Bit32u time; + Bit8u asc, ascq; + Bit8u in_progress; + Bit16u ebda_seg = read_word(0x0040,0x000E); + if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) { + printf("not implemented for non-ATAPI device\n"); + return -1; + } + + BX_DEBUG_ATA("ata_detect_medium: begin\n"); + memsetb(get_SS(),packet, 0, sizeof packet); + packet[0] = 0x25; /* READ CAPACITY */ + + /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT + * is reported by the device. If the device reports "IN PROGRESS", + * 30 seconds is added. */ + timeout = 5000; + time = 0; + in_progress = 0; + while (time < timeout) { + if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0) + goto ok; + + if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) { + if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ + BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (asc == 0x04 && ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for device to detect medium... "); + /* Allow 30 seconds more */ + timeout = 30000; + in_progress = 1; + } + } + time += 100; + } + BX_DEBUG_ATA("read capacity failed\n"); + return -1; +ok: + + block_len = (Bit32u) buf[4] << 24 + | (Bit32u) buf[5] << 16 + | (Bit32u) buf[6] << 8 + | (Bit32u) buf[7] << 0; + BX_DEBUG_ATA("block_len=%u\n", block_len); + + if (block_len!= 2048 && block_len!= 512) + { + printf("Unsupported sector size %u\n", block_len); + return -1; + } + write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len); + + sectors = (Bit32u) buf[0] << 24 + | (Bit32u) buf[1] << 16 + | (Bit32u) buf[2] << 8 + | (Bit32u) buf[3] << 0; + + BX_DEBUG_ATA("sectors=%u\n", sectors); + if (block_len == 2048) + sectors <<= 2; /* # of sectors in 512-byte "soft" sector */ + if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low)) + printf("%dMB medium detected\n", sectors>>(20-9)); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors); + return 0; +} + + Bit16u +atapi_is_cdrom(device) + Bit8u device; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + + if (device >= BX_MAX_ATA_DEVICES) + return 0; + + if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) + return 0; + + if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM) + return 0; + + return 1; +} + +// --------------------------------------------------------------------------- +// End of ATA/ATAPI generic functions +// --------------------------------------------------------------------------- + +#endif // BX_USE_ATADRV + +#if BX_ELTORITO_BOOT + +// --------------------------------------------------------------------------- +// Start of El-Torito boot functions +// --------------------------------------------------------------------------- + + void +cdemu_init() +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + + // the only important data is this one for now + write_byte(ebda_seg,&EbdaData->cdemu.active,0x00); +} + + Bit8u +cdemu_isactive() +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + + return(read_byte(ebda_seg,&EbdaData->cdemu.active)); +} + + Bit8u +cdemu_emulated_drive() +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + + return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)); +} + +static char isotag[6]="CD001"; +static char eltorito[24]="EL TORITO SPECIFICATION"; +// +// Returns ah: emulated drive, al: error code +// + Bit16u +cdrom_boot() +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u atacmd[12], buffer[2048]; + Bit32u lba; + Bit16u boot_segment, nbsectors, i, error; + Bit8u device; + + // Find out the first cdrom + for (device=0; device<BX_MAX_ATA_DEVICES;device++) { + if (atapi_is_cdrom(device)) break; + } + + // if not found + if(device >= BX_MAX_ATA_DEVICES) return 2; + + if(error = atapi_is_ready(device) != 0) + BX_INFO("ata_is_ready returned %d\n",error); + + // Read the Boot Record Volume Descriptor + memsetb(get_SS(),atacmd,0,12); + atacmd[0]=0x28; // READ command + atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors + atacmd[8]=(0x01 & 0x00ff); // Sectors + atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA + atacmd[3]=(0x11 & 0x00ff0000) >> 16; + atacmd[4]=(0x11 & 0x0000ff00) >> 8; + atacmd[5]=(0x11 & 0x000000ff); + if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0) + return 3; + + // Validity checks + if(buffer[0]!=0)return 4; + for(i=0;i<5;i++){ + if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5; + } + for(i=0;i<23;i++) + if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6; + + // ok, now we calculate the Boot catalog address + lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47]; + + // And we read the Boot Catalog + memsetb(get_SS(),atacmd,0,12); + atacmd[0]=0x28; // READ command + atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors + atacmd[8]=(0x01 & 0x00ff); // Sectors + atacmd[2]=(lba & 0xff000000) >> 24; // LBA + atacmd[3]=(lba & 0x00ff0000) >> 16; + atacmd[4]=(lba & 0x0000ff00) >> 8; + atacmd[5]=(lba & 0x000000ff); + if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0) + return 7; + + // Validation entry + if(buffer[0x00]!=0x01)return 8; // Header + if(buffer[0x01]!=0x00)return 9; // Platform + if(buffer[0x1E]!=0x55)return 10; // key 1 + if(buffer[0x1F]!=0xAA)return 10; // key 2 + + // Initial/Default Entry + if(buffer[0x20]!=0x88)return 11; // Bootable + + write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]); + if(buffer[0x21]==0){ + // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. + // Win2000 cd boot needs to know it booted from cd + write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0); + } + else if(buffer[0x21]<4) + write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00); + else + write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80); + + write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2); + write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2); + + boot_segment=buffer[0x23]*0x100+buffer[0x22]; + if(boot_segment==0x0000)boot_segment=0x07C0; + + write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment); + write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000); + + nbsectors=buffer[0x27]*0x100+buffer[0x26]; + write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors); + + lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28]; + write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba); + + // And we read the image in memory + memsetb(get_SS(),atacmd,0,12); + atacmd[0]=0x28; // READ command + atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors + atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors + atacmd[2]=(lba & 0xff000000) >> 24; // LBA + atacmd[3]=(lba & 0x00ff0000) >> 16; + atacmd[4]=(lba & 0x0000ff00) >> 8; + atacmd[5]=(lba & 0x000000ff); + if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0) + return 12; + + // Remember the media type + switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) { + case 0x01: // 1.2M floppy + write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2); + break; + case 0x02: // 1.44M floppy + write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2); + break; + case 0x03: // 2.88M floppy + write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2); + break; + case 0x04: // Harddrive + write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders, + (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1); + write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1); + break; + } + + if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) { + // Increase bios installed hardware number of devices + if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00) + write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41); + else + write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1); + } + + + // everything is ok, so from now on, the emulation is active + if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) + write_byte(ebda_seg,&EbdaData->cdemu.active,0x01); + + // return the boot drive + no error + return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0; +} + +// --------------------------------------------------------------------------- +// End of El-Torito boot functions +// --------------------------------------------------------------------------- +#endif // BX_ELTORITO_BOOT + + void +int14_function(regs, ds, iret_addr) + pusha_regs_t regs; // regs pushed from PUSHA instruction + Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper + iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call +{ + Bit16u addr,timer,val16; + Bit8u timeout; + + ASM_START + sti + ASM_END + + addr = read_word(0x0040, (regs.u.r16.dx << 1)); + timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx); + if ((regs.u.r16.dx < 4) && (addr > 0)) { + switch (regs.u.r8.ah) { + case 0: + outb(addr+3, inb(addr+3) | 0x80); + if (regs.u.r8.al & 0xE0 == 0) { + outb(addr, 0x17); + outb(addr+1, 0x04); + } else { + val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5); + outb(addr, val16 & 0xFF); + outb(addr+1, val16 >> 8); + } + outb(addr+3, regs.u.r8.al & 0x1F); + regs.u.r8.ah = inb(addr+5); + regs.u.r8.al = inb(addr+6); + ClearCF(iret_addr.flags); + break; + case 1: + timer = read_word(0x0040, 0x006C); + while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) { + val16 = read_word(0x0040, 0x006C); + if (val16 != timer) { + timer = val16; + timeout--; + } + } + if (timeout) outb(addr, regs.u.r8.al); + regs.u.r8.ah = inb(addr+5); + if (!timeout) regs.u.r8.ah |= 0x80; + ClearCF(iret_addr.flags); + break; + case 2: + timer = read_word(0x0040, 0x006C); + while (((inb(addr+5) & 0x01) == 0) && (timeout)) { + val16 = read_word(0x0040, 0x006C); + if (val16 != timer) { + timer = val16; + timeout--; + } + } + if (timeout) { + regs.u.r8.ah = 0; + regs.u.r8.al = inb(addr); + } else { + regs.u.r8.ah = inb(addr+5); + } + ClearCF(iret_addr.flags); + break; + case 3: + regs.u.r8.ah = inb(addr+5); + regs.u.r8.al = inb(addr+6); + ClearCF(iret_addr.flags); + break; + default: + SetCF(iret_addr.flags); // Unsupported + } + } else { + SetCF(iret_addr.flags); // Unsupported + } +} + + void +int15_function(regs, ES, DS, FLAGS) + pusha_regs_t regs; // REGS pushed via pusha + Bit16u ES, DS, FLAGS; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + bx_bool prev_a20_enable; + Bit16u base15_00; + Bit8u base23_16; + Bit16u ss; + Bit16u CX,DX; + + Bit16u bRegister; + Bit8u irqDisable; + +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax); + + switch (regs.u.r8.ah) { + case 0x24: /* A20 Control */ + switch (regs.u.r8.al) { + case 0x00: + set_enable_a20(0); + CLEAR_CF(); + regs.u.r8.ah = 0; + break; + case 0x01: + set_enable_a20(1); + CLEAR_CF(); + regs.u.r8.ah = 0; + break; + case 0x02: + regs.u.r8.al = (inb(0x92) >> 1) & 0x01; + CLEAR_CF(); + regs.u.r8.ah = 0; + break; + case 0x03: + CLEAR_CF(); + regs.u.r8.ah = 0; + regs.u.r16.bx = 3; + break; + default: + BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + break; + + case 0x41: + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; + + case 0x4f: + /* keyboard intercept */ +#if BX_CPU < 2 + regs.u.r8.ah = UNSUPPORTED_FUNCTION; +#else + // nop +#endif + SET_CF(); + break; + + case 0x52: // removable media eject + CLEAR_CF(); + regs.u.r8.ah = 0; // "ok ejection may proceed" + break; + + case 0x83: { + if( regs.u.r8.al == 0 ) { + // Set Interval requested. + if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) { + // Interval not already set. + write_byte( 0x40, 0xA0, 1 ); // Set status byte. + write_word( 0x40, 0x98, ES ); // Byte location, segment + write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset + write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay + write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay. + CLEAR_CF( ); + irqDisable = inb( 0xA1 ); + outb( 0xA1, irqDisable & 0xFE ); + bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through. + outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer + } else { + // Interval already set. + BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" ); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + } else if( regs.u.r8.al == 1 ) { + // Clear Interval requested + write_byte( 0x40, 0xA0, 0 ); // Clear status byte + CLEAR_CF( ); + bRegister = inb_cmos( 0xB ); + outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer + } else { + BX_DEBUG_INT15("int15: Func 83h, failed.\n" ); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + regs.u.r8.al--; + } + + break; + } + + case 0x87: +#if BX_CPU < 3 +# error "Int15 function 87h not supported on < 80386" +#endif + // +++ should probably have descriptor checks + // +++ should have exception handlers + + // turn off interrupts +ASM_START + cli +ASM_END + + prev_a20_enable = set_enable_a20(1); // enable A20 line + + // 128K max of transfer on 386+ ??? + // source == destination ??? + + // ES:SI points to descriptor table + // offset use initially comments + // ============================================== + // 00..07 Unused zeros Null descriptor + // 08..0f GDT zeros filled in by BIOS + // 10..17 source ssssssss source of data + // 18..1f dest dddddddd destination of data + // 20..27 CS zeros filled in by BIOS + // 28..2f SS zeros filled in by BIOS + + //es:si + //eeee0 + //0ssss + //----- + +// check for access rights of source & dest here + + // Initialize GDT descriptor + base15_00 = (ES << 4) + regs.u.r16.si; + base23_16 = ES >> 12; + if (base15_00 < (ES<<4)) + base23_16++; + write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor + write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00 + write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16 + write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access + write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16 + + // Initialize CS descriptor + write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit + write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00 + write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16 + write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access + write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16 + + // Initialize SS descriptor + ss = get_SS(); + base15_00 = ss << 4; + base23_16 = ss >> 12; + write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit + write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00 + write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16 + write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access + write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16 + + CX = regs.u.r16.cx; +ASM_START + // Compile generates locals offset info relative to SP. + // Get CX (word count) from stack. + mov bx, sp + SEG SS + mov cx, _int15_function.CX [bx] + + // since we need to set SS:SP, save them to the BDA + // for future restore + push eax + xor eax, eax + mov ds, ax + mov 0x0469, ss + mov 0x0467, sp + + SEG ES + lgdt [si + 0x08] + SEG CS + lidt [pmode_IDT_info] + ;; perhaps do something with IDT here + + ;; set PE bit in CR0 + mov eax, cr0 + or al, #0x01 + mov cr0, eax + ;; far jump to flush CPU queue after transition to protected mode + JMP_AP(0x0020, protected_mode) + +protected_mode: + ;; GDT points to valid descriptor table, now load SS, DS, ES + mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00 + mov ss, ax + mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00 + mov ds, ax + mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00 + mov es, ax + xor si, si + xor di, di + cld + rep + movsw ;; move CX words from DS:SI to ES:DI + + ;; make sure DS and ES limits are 64KB + mov ax, #0x28 + mov ds, ax + mov es, ax + + ;; reset PG bit in CR0 ??? + mov eax, cr0 + and al, #0xFE + mov cr0, eax + + ;; far jump to flush CPU queue after transition to real mode + JMP_AP(0xf000, real_mode) + +real_mode: + ;; restore IDT to normal real-mode defaults + SEG CS + lidt [rmode_IDT_info] + + // restore SS:SP from the BDA + xor ax, ax + mov ds, ax + mov ss, 0x0469 + mov sp, 0x0467 + pop eax +ASM_END + + set_enable_a20(prev_a20_enable); + + // turn back on interrupts +ASM_START + sti +ASM_END + + regs.u.r8.ah = 0; + CLEAR_CF(); + break; + + + case 0x88: + // Get the amount of extended memory (above 1M) +#if BX_CPU < 2 + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + SET_CF(); +#else + regs.u.r8.al = inb_cmos(0x30); + regs.u.r8.ah = inb_cmos(0x31); + + // According to Ralf Brown's interrupt the limit should be 15M, + // but real machines mostly return max. 63M. + if(regs.u.r16.ax > 0xffc0) + regs.u.r16.ax = 0xffc0; + + CLEAR_CF(); +#endif + break; + + case 0x90: + /* Device busy interrupt. Called by Int 16h when no key available */ + break; + + case 0x91: + /* Interrupt complete. Called by Int 16h when key becomes available */ + break; + + case 0xbf: + BX_INFO("*** int 15h function AH=bf not yet supported!\n"); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; + + case 0xC0: +#if 0 + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; +#endif + CLEAR_CF(); + regs.u.r8.ah = 0; + regs.u.r16.bx = BIOS_CONFIG_TABLE; + ES = 0xF000; + break; + + case 0xc1: + ES = ebda_seg; + CLEAR_CF(); + break; + + case 0xd8: + bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n"); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; + + default: + BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", + (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; + } +} + +#if BX_USE_PS2_MOUSE + void +int15_function_mouse(regs, ES, DS, FLAGS) + pusha_regs_t regs; // REGS pushed via pusha + Bit16u ES, DS, FLAGS; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u mouse_flags_1, mouse_flags_2; + Bit16u mouse_driver_seg; + Bit16u mouse_driver_offset; + Bit8u comm_byte, prev_command_byte; + Bit8u ret, mouse_data1, mouse_data2, mouse_data3; + +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax); + + switch (regs.u.r8.ah) { + case 0xC2: + // Return Codes status in AH + // ========================= + // 00: success + // 01: invalid subfunction (AL > 7) + // 02: invalid input value (out of allowable range) + // 03: interface error + // 04: resend command received from mouse controller, + // device driver should attempt command again + // 05: cannot enable mouse, since no far call has been installed + // 80/86: mouse service not implemented + + switch (regs.u.r8.al) { + case 0: // Disable/Enable Mouse +BX_DEBUG_INT15("case 0:\n"); + switch (regs.u.r8.bh) { + case 0: // Disable Mouse +BX_DEBUG_INT15("case 0: disable mouse\n"); + inhibit_mouse_int_and_events(); // disable IRQ12 and packets + ret = send_to_mouse_ctrl(0xF5); // disable mouse command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if ( (ret == 0) || (mouse_data1 == 0xFA) ) { + CLEAR_CF(); + regs.u.r8.ah = 0; + return; + } + } + + // error + SET_CF(); + regs.u.r8.ah = ret; + return; + break; + + case 1: // Enable Mouse +BX_DEBUG_INT15("case 1: enable mouse\n"); + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + if ( (mouse_flags_2 & 0x80) == 0 ) { + BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n"); + SET_CF(); // error + regs.u.r8.ah = 5; // no far call installed + return; + } + inhibit_mouse_int_and_events(); // disable IRQ12 and packets + ret = send_to_mouse_ctrl(0xF4); // enable mouse command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if ( (ret == 0) && (mouse_data1 == 0xFA) ) { + enable_mouse_int_and_events(); // turn IRQ12 and packet generation on + CLEAR_CF(); + regs.u.r8.ah = 0; + return; + } + } + SET_CF(); + regs.u.r8.ah = ret; + return; + + default: // invalid subfunction + BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh); + SET_CF(); // error + regs.u.r8.ah = 1; // invalid subfunction + return; + } + break; + + case 1: // Reset Mouse + case 5: // Initialize Mouse +BX_DEBUG_INT15("case 1 or 5:\n"); + if (regs.u.r8.al == 5) { + if (regs.u.r8.bh != 3) { + SET_CF(); + regs.u.r8.ah = 0x02; // invalid input + return; + } + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh; + mouse_flags_1 = 0x00; + write_byte(ebda_seg, 0x0026, mouse_flags_1); + write_byte(ebda_seg, 0x0027, mouse_flags_2); + } + + inhibit_mouse_int_and_events(); // disable IRQ12 and packets + ret = send_to_mouse_ctrl(0xFF); // reset mouse command + if (ret == 0) { + ret = get_mouse_data(&mouse_data3); + // if no mouse attached, it will return RESEND + if (mouse_data3 == 0xfe) { + SET_CF(); + return; + } + if (mouse_data3 != 0xfa) + BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data1); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data2); + if ( ret == 0 ) { + // turn IRQ12 and packet generation on + enable_mouse_int_and_events(); + CLEAR_CF(); + regs.u.r8.ah = 0; + regs.u.r8.bl = mouse_data1; + regs.u.r8.bh = mouse_data2; + return; + } + } + } + } + + // error + SET_CF(); + regs.u.r8.ah = ret; + return; + + case 2: // Set Sample Rate +BX_DEBUG_INT15("case 2:\n"); + switch (regs.u.r8.bh) { + case 0: mouse_data1 = 10; break; // 10 reports/sec + case 1: mouse_data1 = 20; break; // 20 reports/sec + case 2: mouse_data1 = 40; break; // 40 reports/sec + case 3: mouse_data1 = 60; break; // 60 reports/sec + case 4: mouse_data1 = 80; break; // 80 reports/sec + case 5: mouse_data1 = 100; break; // 100 reports/sec (default) + case 6: mouse_data1 = 200; break; // 200 reports/sec + default: mouse_data1 = 0; + } + if (mouse_data1 > 0) { + ret = send_to_mouse_ctrl(0xF3); // set sample rate command + if (ret == 0) { + ret = get_mouse_data(&mouse_data2); + ret = send_to_mouse_ctrl(mouse_data1); + ret = get_mouse_data(&mouse_data2); + CLEAR_CF(); + regs.u.r8.ah = 0; + } else { + // error + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + } else { + // error + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + break; + + case 3: // Set Resolution +BX_DEBUG_INT15("case 3:\n"); + // BH: + // 0 = 25 dpi, 1 count per millimeter + // 1 = 50 dpi, 2 counts per millimeter + // 2 = 100 dpi, 4 counts per millimeter + // 3 = 200 dpi, 8 counts per millimeter + comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets + if (regs.u.r8.bh < 4) { + ret = send_to_mouse_ctrl(0xE8); // set resolution command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if (mouse_data1 != 0xfa) + BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); + ret = send_to_mouse_ctrl(regs.u.r8.bh); + ret = get_mouse_data(&mouse_data1); + if (mouse_data1 != 0xfa) + BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); + CLEAR_CF(); + regs.u.r8.ah = 0; + } else { + // error + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + } else { + // error + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable + break; + + case 4: // Get Device ID +BX_DEBUG_INT15("case 4:\n"); + inhibit_mouse_int_and_events(); // disable IRQ12 and packets + ret = send_to_mouse_ctrl(0xF2); // get mouse ID command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + ret = get_mouse_data(&mouse_data2); + CLEAR_CF(); + regs.u.r8.ah = 0; + regs.u.r8.bh = mouse_data2; + } else { + // error + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + break; + + case 6: // Return Status & Set Scaling Factor... +BX_DEBUG_INT15("case 6:\n"); + switch (regs.u.r8.bh) { + case 0: // Return Status + comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets + ret = send_to_mouse_ctrl(0xE9); // get mouse info command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if (mouse_data1 != 0xfa) + BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data2); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data3); + if ( ret == 0 ) { + CLEAR_CF(); + regs.u.r8.ah = 0; + regs.u.r8.bl = mouse_data1; + regs.u.r8.cl = mouse_data2; + regs.u.r8.dl = mouse_data3; + set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable + return; + } + } + } + } + } + + // error + SET_CF(); + regs.u.r8.ah = ret; + set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable + return; + + case 1: // Set Scaling Factor to 1:1 + case 2: // Set Scaling Factor to 2:1 + comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets + if (regs.u.r8.bh == 1) { + ret = send_to_mouse_ctrl(0xE6); + } else { + ret = send_to_mouse_ctrl(0xE7); + } + if (ret == 0) { + get_mouse_data(&mouse_data1); + ret = (mouse_data1 != 0xFA); + } + if (ret == 0) { + CLEAR_CF(); + regs.u.r8.ah = 0; + } else { + // error + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + } + set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable + break; + + default: + BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh); + } + break; + + case 7: // Set Mouse Handler Address +BX_DEBUG_INT15("case 7:\n"); + mouse_driver_seg = ES; + mouse_driver_offset = regs.u.r16.bx; + write_word(ebda_seg, 0x0022, mouse_driver_offset); + write_word(ebda_seg, 0x0024, mouse_driver_seg); + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + if (mouse_driver_offset == 0 && mouse_driver_seg == 0) { + /* remove handler */ + if ( (mouse_flags_2 & 0x80) != 0 ) { + mouse_flags_2 &= ~0x80; + inhibit_mouse_int_and_events(); // disable IRQ12 and packets + } + } + else { + /* install handler */ + mouse_flags_2 |= 0x80; + } + write_byte(ebda_seg, 0x0027, mouse_flags_2); + CLEAR_CF(); + regs.u.r8.ah = 0; + break; + + default: +BX_DEBUG_INT15("case default:\n"); + regs.u.r8.ah = 1; // invalid function + SET_CF(); + } + break; + + default: + BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", + (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; + } +} +#endif // BX_USE_PS2_MOUSE + + +void set_e820_range(ES, DI, start, end, extra_start, extra_end, type) + Bit16u ES; + Bit16u DI; + Bit32u start; + Bit32u end; + Bit8u extra_start; + Bit8u extra_end; + Bit16u type; +{ + write_word(ES, DI, start); + write_word(ES, DI+2, start >> 16); + write_word(ES, DI+4, extra_start); + write_word(ES, DI+6, 0x00); + + end -= start; + extra_end -= extra_start; + write_word(ES, DI+8, end); + write_word(ES, DI+10, end >> 16); + write_word(ES, DI+12, extra_end); + write_word(ES, DI+14, 0x0000); + + write_word(ES, DI+16, type); + write_word(ES, DI+18, 0x0); +} + + void +int15_function32(regs, ES, DS, FLAGS) + pushad_regs_t regs; // REGS pushed via pushad + Bit16u ES, DS, FLAGS; +{ + Bit32u extended_memory_size=0; // 64bits long + Bit32u extra_lowbits_memory_size=0; + Bit16u CX,DX; + Bit8u extra_highbits_memory_size=0; + +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax); + + switch (regs.u.r8.ah) { + case 0x86: + // Wait for CX:DX microseconds. currently using the + // refresh request port 0x61 bit4, toggling every 15usec + + CX = regs.u.r16.cx; + DX = regs.u.r16.dx; + +ASM_START + sti + + ;; Get the count in eax + mov bx, sp + SEG SS + mov ax, _int15_function32.CX [bx] + shl eax, #16 + SEG SS + mov ax, _int15_function32.DX [bx] + + ;; convert to numbers of 15usec ticks + mov ebx, #15 + xor edx, edx + div eax, ebx + mov ecx, eax + + ;; wait for ecx number of refresh requests + in al, #0x61 + and al,#0x10 + mov ah, al + + or ecx, ecx + je int1586_tick_end +int1586_tick: + in al, #0x61 + and al,#0x10 + cmp al, ah + je int1586_tick + mov ah, al + dec ecx + jnz int1586_tick +int1586_tick_end: +ASM_END + + break; + + case 0xe8: + switch(regs.u.r8.al) + { + case 0x20: // coded by osmaker aka K.J. + if(regs.u.r32.edx == 0x534D4150) + { + extended_memory_size = inb_cmos(0x35); + extended_memory_size <<= 8; + extended_memory_size |= inb_cmos(0x34); + extended_memory_size *= 64; + // greater than EFF00000??? + if(extended_memory_size > 0x3bc000) { + extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000 + } + extended_memory_size *= 1024; + extended_memory_size += (16L * 1024 * 1024); + + if(extended_memory_size <= (16L * 1024 * 1024)) { + extended_memory_size = inb_cmos(0x31); + extended_memory_size <<= 8; + extended_memory_size |= inb_cmos(0x30); + extended_memory_size *= 1024; + extended_memory_size += (1L * 1024 * 1024); + } + + extra_lowbits_memory_size = inb_cmos(0x5c); + extra_lowbits_memory_size <<= 8; + extra_lowbits_memory_size |= inb_cmos(0x5b); + extra_lowbits_memory_size *= 64; + extra_lowbits_memory_size *= 1024; + extra_highbits_memory_size = inb_cmos(0x5d); + + switch(regs.u.r16.bx) + { + case 0: + set_e820_range(ES, regs.u.r16.di, + 0x0000000L, 0x0009f000L, 0, 0, 1); + regs.u.r32.ebx = 1; + break; + case 1: + set_e820_range(ES, regs.u.r16.di, + 0x0009f000L, 0x000a0000L, 0, 0, 2); + regs.u.r32.ebx = 2; + break; + case 2: + set_e820_range(ES, regs.u.r16.di, + 0x000e8000L, 0x00100000L, 0, 0, 2); + regs.u.r32.ebx = 3; + break; + case 3: +#if BX_ROMBIOS32 + set_e820_range(ES, regs.u.r16.di, + 0x00100000L, + extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1); + regs.u.r32.ebx = 4; +#else + set_e820_range(ES, regs.u.r16.di, + 0x00100000L, + extended_memory_size, 1); + regs.u.r32.ebx = 5; +#endif + break; + case 4: + set_e820_range(ES, regs.u.r16.di, + extended_memory_size - ACPI_DATA_SIZE, + extended_memory_size ,0, 0, 3); // ACPI RAM + regs.u.r32.ebx = 5; + break; + case 5: + /* 4 pages before the bios, 3 pages for vmx tss pages, + * the other page for EPT real mode pagetable */ + set_e820_range(ES, regs.u.r16.di, 0xfffbc000L, + 0xfffc0000L, 0, 0, 2); + regs.u.r32.ebx = 6; + break; + case 6: + /* 256KB BIOS area at the end of 4 GB */ + set_e820_range(ES, regs.u.r16.di, + 0xfffc0000L, 0x00000000L ,0, 0, 2); + if (extra_highbits_memory_size || extra_lowbits_memory_size) + regs.u.r32.ebx = 7; + else + regs.u.r32.ebx = 0; + break; + case 7: + /* Maping of memory above 4 GB */ + set_e820_range(ES, regs.u.r16.di, 0x00000000L, + extra_lowbits_memory_size, 1, extra_highbits_memory_size + + 1, 1); + regs.u.r32.ebx = 0; + break; + default: /* AX=E820, DX=534D4150, BX unrecognized */ + goto int15_unimplemented; + break; + } + regs.u.r32.eax = 0x534D4150; + regs.u.r32.ecx = 0x14; + CLEAR_CF(); + } else { + // if DX != 0x534D4150) + goto int15_unimplemented; + } + break; + + case 0x01: + // do we have any reason to fail here ? + CLEAR_CF(); + + // my real system sets ax and bx to 0 + // this is confirmed by Ralph Brown list + // but syslinux v1.48 is known to behave + // strangely if ax is set to 0 + // regs.u.r16.ax = 0; + // regs.u.r16.bx = 0; + + // Get the amount of extended memory (above 1M) + regs.u.r8.cl = inb_cmos(0x30); + regs.u.r8.ch = inb_cmos(0x31); + + // limit to 15M + if(regs.u.r16.cx > 0x3c00) + { + regs.u.r16.cx = 0x3c00; + } + + // Get the amount of extended memory above 16M in 64k blocs + regs.u.r8.dl = inb_cmos(0x34); + regs.u.r8.dh = inb_cmos(0x35); + + // Set configured memory equal to extended memory + regs.u.r16.ax = regs.u.r16.cx; + regs.u.r16.bx = regs.u.r16.dx; + break; + default: /* AH=0xE8?? but not implemented */ + goto int15_unimplemented; + } + break; + int15_unimplemented: + // fall into the default + default: + BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", + (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); + SET_CF(); + regs.u.r8.ah = UNSUPPORTED_FUNCTION; + break; + } +} + + void +int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS) + Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS; +{ + Bit8u scan_code, ascii_code, shift_flags, led_flags, count; + Bit16u kbd_code, max; + + BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX); + + shift_flags = read_byte(0x0040, 0x17); + led_flags = read_byte(0x0040, 0x97); + if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) { +ASM_START + cli +ASM_END + outb(0x60, 0xed); + while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21); + if ((inb(0x60) == 0xfa)) { + led_flags &= 0xf8; + led_flags |= ((shift_flags >> 4) & 0x07); + outb(0x60, led_flags & 0x07); + while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21); + inb(0x60); + write_byte(0x0040, 0x97, led_flags); + } +ASM_START + sti +ASM_END + } + + switch (GET_AH()) { + case 0x00: /* read keyboard input */ + + if ( !dequeue_key(&scan_code, &ascii_code, 1) ) { + BX_PANIC("KBD: int16h: out of keyboard input\n"); + } + if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; + else if (ascii_code == 0xE0) ascii_code = 0; + AX = (scan_code << 8) | ascii_code; + break; + + case 0x01: /* check keyboard status */ + if ( !dequeue_key(&scan_code, &ascii_code, 0) ) { + SET_ZF(); + return; + } + if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; + else if (ascii_code == 0xE0) ascii_code = 0; + AX = (scan_code << 8) | ascii_code; + CLEAR_ZF(); + break; + + case 0x02: /* get shift flag status */ + shift_flags = read_byte(0x0040, 0x17); + SET_AL(shift_flags); + break; + + case 0x05: /* store key-stroke into buffer */ + if ( !enqueue_key(GET_CH(), GET_CL()) ) { + SET_AL(1); + } + else { + SET_AL(0); + } + break; + + case 0x09: /* GET KEYBOARD FUNCTIONALITY */ + // bit Bochs Description + // 7 0 reserved + // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support) + // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support) + // 4 1 INT 16/AH=0Ah supported + // 3 0 INT 16/AX=0306h supported + // 2 0 INT 16/AX=0305h supported + // 1 0 INT 16/AX=0304h supported + // 0 0 INT 16/AX=0300h supported + // + SET_AL(0x30); + break; + + case 0x0A: /* GET KEYBOARD ID */ + count = 2; + kbd_code = 0x0; + outb(0x60, 0xf2); + /* Wait for data */ + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00); + if (max>0x0) { + if ((inb(0x60) == 0xfa)) { + do { + max=0xffff; + while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00); + if (max>0x0) { + kbd_code >>= 8; + kbd_code |= (inb(0x60) << 8); + } + } while (--count>0); + } + } + BX=kbd_code; + break; + + case 0x10: /* read MF-II keyboard input */ + + if ( !dequeue_key(&scan_code, &ascii_code, 1) ) { + BX_PANIC("KBD: int16h: out of keyboard input\n"); + } + if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; + AX = (scan_code << 8) | ascii_code; + break; + + case 0x11: /* check MF-II keyboard status */ + if ( !dequeue_key(&scan_code, &ascii_code, 0) ) { + SET_ZF(); + return; + } + if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; + AX = (scan_code << 8) | ascii_code; + CLEAR_ZF(); + break; + + case 0x12: /* get extended keyboard status */ + shift_flags = read_byte(0x0040, 0x17); + SET_AL(shift_flags); + shift_flags = read_byte(0x0040, 0x18) & 0x73; + shift_flags |= read_byte(0x0040, 0x96) & 0x0c; + SET_AH(shift_flags); + BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX); + break; + + case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */ + SET_AH(0x80); // function int16 ah=0x10-0x12 supported + break; + + case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */ + // don't change AH : function int16 ah=0x20-0x22 NOT supported + break; + + case 0x6F: + if (GET_AL() == 0x08) + SET_AH(0x02); // unsupported, aka normal keyboard + + default: + BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH()); + } +} + + unsigned int +dequeue_key(scan_code, ascii_code, incr) + Bit8u *scan_code; + Bit8u *ascii_code; + unsigned int incr; +{ + Bit16u buffer_start, buffer_end, buffer_head, buffer_tail; + Bit16u ss; + Bit8u acode, scode; + +#if BX_CPU < 2 + buffer_start = 0x001E; + buffer_end = 0x003E; +#else + buffer_start = read_word(0x0040, 0x0080); + buffer_end = read_word(0x0040, 0x0082); +#endif + + buffer_head = read_word(0x0040, 0x001a); + buffer_tail = read_word(0x0040, 0x001c); + + if (buffer_head != buffer_tail) { + ss = get_SS(); + acode = read_byte(0x0040, buffer_head); + scode = read_byte(0x0040, buffer_head+1); + write_byte(ss, ascii_code, acode); + write_byte(ss, scan_code, scode); + + if (incr) { + buffer_head += 2; + if (buffer_head >= buffer_end) + buffer_head = buffer_start; + write_word(0x0040, 0x001a, buffer_head); + } + return(1); + } + else { + return(0); + } +} + +static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n"; + + Bit8u +inhibit_mouse_int_and_events() +{ + Bit8u command_byte, prev_command_byte; + + // Turn off IRQ generation and aux data line + if ( inb(0x64) & 0x02 ) + BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse"); + outb(0x64, 0x20); // get command byte + while ( (inb(0x64) & 0x01) != 0x01 ); + prev_command_byte = inb(0x60); + command_byte = prev_command_byte; + //while ( (inb(0x64) & 0x02) ); + if ( inb(0x64) & 0x02 ) + BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse"); + command_byte &= 0xfd; // turn off IRQ 12 generation + command_byte |= 0x20; // disable mouse serial clock line + outb(0x64, 0x60); // write command byte + outb(0x60, command_byte); + return(prev_command_byte); +} + + void +enable_mouse_int_and_events() +{ + Bit8u command_byte; + + // Turn on IRQ generation and aux data line + if ( inb(0x64) & 0x02 ) + BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse"); + outb(0x64, 0x20); // get command byte + while ( (inb(0x64) & 0x01) != 0x01 ); + command_byte = inb(0x60); + //while ( (inb(0x64) & 0x02) ); + if ( inb(0x64) & 0x02 ) + BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse"); + command_byte |= 0x02; // turn on IRQ 12 generation + command_byte &= 0xdf; // enable mouse serial clock line + outb(0x64, 0x60); // write command byte + outb(0x60, command_byte); +} + + Bit8u +send_to_mouse_ctrl(sendbyte) + Bit8u sendbyte; +{ + Bit8u response; + + // wait for chance to write to ctrl + if ( inb(0x64) & 0x02 ) + BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse"); + outb(0x64, 0xD4); + outb(0x60, sendbyte); + return(0); +} + + + Bit8u +get_mouse_data(data) + Bit8u *data; +{ + Bit8u response; + Bit16u ss; + + while ( (inb(0x64) & 0x21) != 0x21 ) { + } + + response = inb(0x60); + + ss = get_SS(); + write_byte(ss, data, response); + return(0); +} + + void +set_kbd_command_byte(command_byte) + Bit8u command_byte; +{ + if ( inb(0x64) & 0x02 ) + BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm"); + outb(0x64, 0xD4); + + outb(0x64, 0x60); // write command byte + outb(0x60, command_byte); +} + + void +int09_function(DI, SI, BP, SP, BX, DX, CX, AX) + Bit16u DI, SI, BP, SP, BX, DX, CX, AX; +{ + Bit8u scancode, asciicode, shift_flags; + Bit8u mf2_flags, mf2_state; + + // + // DS has been set to F000 before call + // + + + scancode = GET_AL(); + + if (scancode == 0) { + BX_INFO("KBD: int09 handler: AL=0\n"); + return; + } + + + shift_flags = read_byte(0x0040, 0x17); + mf2_flags = read_byte(0x0040, 0x18); + mf2_state = read_byte(0x0040, 0x96); + asciicode = 0; + + switch (scancode) { + case 0x3a: /* Caps Lock press */ + shift_flags ^= 0x40; + write_byte(0x0040, 0x17, shift_flags); + mf2_flags |= 0x40; + write_byte(0x0040, 0x18, mf2_flags); + break; + case 0xba: /* Caps Lock release */ + mf2_flags &= ~0x40; + write_byte(0x0040, 0x18, mf2_flags); + break; + + case 0x2a: /* L Shift press */ + shift_flags |= 0x02; + write_byte(0x0040, 0x17, shift_flags); + break; + case 0xaa: /* L Shift release */ + shift_flags &= ~0x02; + write_byte(0x0040, 0x17, shift_flags); + break; + + case 0x36: /* R Shift press */ + shift_flags |= 0x01; + write_byte(0x0040, 0x17, shift_flags); + break; + case 0xb6: /* R Shift release */ + shift_flags &= ~0x01; + write_byte(0x0040, 0x17, shift_flags); + break; + + case 0x1d: /* Ctrl press */ + if ((mf2_state & 0x01) == 0) { + shift_flags |= 0x04; + write_byte(0x0040, 0x17, shift_flags); + if (mf2_state & 0x02) { + mf2_state |= 0x04; + write_byte(0x0040, 0x96, mf2_state); + } else { + mf2_flags |= 0x01; + write_byte(0x0040, 0x18, mf2_flags); + } + } + break; + case 0x9d: /* Ctrl release */ + if ((mf2_state & 0x01) == 0) { + shift_flags &= ~0x04; + write_byte(0x0040, 0x17, shift_flags); + if (mf2_state & 0x02) { + mf2_state &= ~0x04; + write_byte(0x0040, 0x96, mf2_state); + } else { + mf2_flags &= ~0x01; + write_byte(0x0040, 0x18, mf2_flags); + } + } + break; + + case 0x38: /* Alt press */ + shift_flags |= 0x08; + write_byte(0x0040, 0x17, shift_flags); + if (mf2_state & 0x02) { + mf2_state |= 0x08; + write_byte(0x0040, 0x96, mf2_state); + } else { + mf2_flags |= 0x02; + write_byte(0x0040, 0x18, mf2_flags); + } + break; + case 0xb8: /* Alt release */ + shift_flags &= ~0x08; + write_byte(0x0040, 0x17, shift_flags); + if (mf2_state & 0x02) { + mf2_state &= ~0x08; + write_byte(0x0040, 0x96, mf2_state); + } else { + mf2_flags &= ~0x02; + write_byte(0x0040, 0x18, mf2_flags); + } + break; + + case 0x45: /* Num Lock press */ + if ((mf2_state & 0x03) == 0) { + mf2_flags |= 0x20; + write_byte(0x0040, 0x18, mf2_flags); + shift_flags ^= 0x20; + write_byte(0x0040, 0x17, shift_flags); + } + break; + case 0xc5: /* Num Lock release */ + if ((mf2_state & 0x03) == 0) { + mf2_flags &= ~0x20; + write_byte(0x0040, 0x18, mf2_flags); + } + break; + + case 0x46: /* Scroll Lock press */ + mf2_flags |= 0x10; + write_byte(0x0040, 0x18, mf2_flags); + shift_flags ^= 0x10; + write_byte(0x0040, 0x17, shift_flags); + break; + + case 0xc6: /* Scroll Lock release */ + mf2_flags &= ~0x10; + write_byte(0x0040, 0x18, mf2_flags); + break; + + default: + if (scancode & 0x80) { + break; /* toss key releases ... */ + } + if (scancode > MAX_SCAN_CODE) { + BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode); + return; + } + if (shift_flags & 0x08) { /* ALT */ + asciicode = scan_to_scanascii[scancode].alt; + scancode = scan_to_scanascii[scancode].alt >> 8; + } else if (shift_flags & 0x04) { /* CONTROL */ + asciicode = scan_to_scanascii[scancode].control; + scancode = scan_to_scanascii[scancode].control >> 8; + } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) { + /* extended keys handling */ + asciicode = 0xe0; + scancode = scan_to_scanascii[scancode].normal >> 8; + } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */ + /* check if lock state should be ignored + * because a SHIFT key are pressed */ + + if (shift_flags & scan_to_scanascii[scancode].lock_flags) { + asciicode = scan_to_scanascii[scancode].normal; + scancode = scan_to_scanascii[scancode].normal >> 8; + } else { + asciicode = scan_to_scanascii[scancode].shift; + scancode = scan_to_scanascii[scancode].shift >> 8; + } + } else { + /* check if lock is on */ + if (shift_flags & scan_to_scanascii[scancode].lock_flags) { + asciicode = scan_to_scanascii[scancode].shift; + scancode = scan_to_scanascii[scancode].shift >> 8; + } else { + asciicode = scan_to_scanascii[scancode].normal; + scancode = scan_to_scanascii[scancode].normal >> 8; + } + } + if (scancode==0 && asciicode==0) { + BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n"); + } + enqueue_key(scancode, asciicode); + break; + } + if ((scancode & 0x7f) != 0x1d) { + mf2_state &= ~0x01; + } + mf2_state &= ~0x02; + write_byte(0x0040, 0x96, mf2_state); +} + + unsigned int +enqueue_key(scan_code, ascii_code) + Bit8u scan_code, ascii_code; +{ + Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail; + +#if BX_CPU < 2 + buffer_start = 0x001E; + buffer_end = 0x003E; +#else + buffer_start = read_word(0x0040, 0x0080); + buffer_end = read_word(0x0040, 0x0082); +#endif + + buffer_head = read_word(0x0040, 0x001A); + buffer_tail = read_word(0x0040, 0x001C); + + temp_tail = buffer_tail; + buffer_tail += 2; + if (buffer_tail >= buffer_end) + buffer_tail = buffer_start; + + if (buffer_tail == buffer_head) { + return(0); + } + + write_byte(0x0040, temp_tail, ascii_code); + write_byte(0x0040, temp_tail+1, scan_code); + write_word(0x0040, 0x001C, buffer_tail); + return(1); +} + + + void +int74_function(make_farcall, Z, Y, X, status) + Bit16u make_farcall, Z, Y, X, status; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u in_byte, index, package_count; + Bit8u mouse_flags_1, mouse_flags_2; + +BX_DEBUG_INT74("entering int74_function\n"); + make_farcall = 0; + + in_byte = inb(0x64); + if ( (in_byte & 0x21) != 0x21 ) { + return; + } + in_byte = inb(0x60); +BX_DEBUG_INT74("int74: read byte %02x\n", in_byte); + + mouse_flags_1 = read_byte(ebda_seg, 0x0026); + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + + if ( (mouse_flags_2 & 0x80) != 0x80 ) { + return; + } + + package_count = mouse_flags_2 & 0x07; + index = mouse_flags_1 & 0x07; + write_byte(ebda_seg, 0x28 + index, in_byte); + + if ( (index+1) >= package_count ) { +BX_DEBUG_INT74("int74_function: make_farcall=1\n"); + status = read_byte(ebda_seg, 0x0028 + 0); + X = read_byte(ebda_seg, 0x0028 + 1); + Y = read_byte(ebda_seg, 0x0028 + 2); + Z = 0; + mouse_flags_1 = 0; + // check if far call handler installed + if (mouse_flags_2 & 0x80) + make_farcall = 1; + } + else { + mouse_flags_1++; + } + write_byte(ebda_seg, 0x0026, mouse_flags_1); +} + +#define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status) + +#if BX_USE_ATADRV + + void +int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit32u lba_low, lba_high; + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit16u cylinder, head, sector; + Bit16u segment, offset; + Bit16u npc, nph, npspt, nlc, nlh, nlspt; + Bit16u size, count; + Bit8u device, status; + + BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); + + write_byte(0x0040, 0x008e, 0); // clear completion flag + + // basic check : device has to be defined + if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) { + BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL()); + goto int13_fail; + } + + // Get the ata channel + device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]); + + // basic check : device has to be valid + if (device >= BX_MAX_ATA_DEVICES) { + BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL()); + goto int13_fail; + } + + switch (GET_AH()) { + + case 0x00: /* disk controller reset */ + ata_reset (device); + goto int13_success; + break; + + case 0x01: /* read disk status */ + status = read_byte(0x0040, 0x0074); + SET_AH(status); + SET_DISK_RET_STATUS(0); + /* set CF if error status read */ + if (status) goto int13_fail_nostatus; + else goto int13_success_noah; + break; + + case 0x02: // read disk sectors + case 0x03: // write disk sectors + case 0x04: // verify disk sectors + + count = GET_AL(); + cylinder = GET_CH(); + cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300; + sector = (GET_CL() & 0x3f); + head = GET_DH(); + + segment = ES; + offset = BX; + + if ((count > 128) || (count == 0) || (sector == 0)) { + BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH()); + goto int13_fail; + } + + nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders); + nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads); + nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); + + // sanity check on cyl heads, sec + if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) { + BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector); + goto int13_fail; + } + + // FIXME verify + if ( GET_AH() == 0x04 ) goto int13_success; + + nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads); + npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt); + + // if needed, translate lchs to lba, and execute command + if ( (nph != nlh) || (npspt != nlspt)) { + lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1; + lba_high = 0; + sector = 0; // this forces the command to be lba + } + + if ( GET_AH() == 0x02 ) + status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset); + else + status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset); + + // Set nb of sector transferred + SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors)); + + if (status != 0) { + BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status); + SET_AH(0x0c); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x05: /* format disk track */ + BX_INFO("format disk track called\n"); + goto int13_success; + return; + break; + + case 0x08: /* read disk drive parameters */ + + // Get logical geometry from table + nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders); + nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads); + nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); + count = read_byte(ebda_seg, &EbdaData->ata.hdcount); + + nlc = nlc - 2; /* 0 based , last sector not used */ + SET_AL(0); + SET_CH(nlc & 0xff); + SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f)); + SET_DH(nlh - 1); + SET_DL(count); /* FIXME returns 0, 1, or n hard drives */ + + // FIXME should set ES & DI + + goto int13_success; + break; + + case 0x10: /* check drive ready */ + // should look at 40:8E also??? + + // Read the status from controller + status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT); + if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) { + goto int13_success; + } + else { + SET_AH(0xAA); + goto int13_fail_noah; + } + break; + + case 0x15: /* read disk drive size */ + + // Get logical geometry from table + nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders); + nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads); + nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); + + // Compute sector count seen by int13 + lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt; + CX = lba_low >> 16; + DX = lba_low & 0xffff; + + SET_AH(3); // hard disk accessible + goto int13_success_noah; + break; + + case 0x41: // IBM/MS installation check + BX=0xaa55; // install check + SET_AH(0x30); // EDD 3.0 + CX=0x0007; // ext disk access and edd, removable supported + goto int13_success_noah; + break; + + case 0x42: // IBM/MS extended read + case 0x43: // IBM/MS extended write + case 0x44: // IBM/MS verify + case 0x47: // IBM/MS extended seek + + count=read_word(DS, SI+(Bit16u)&Int13Ext->count); + segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment); + offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset); + + // Get 32 msb lba and check + lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); + if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) { + BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH()); + goto int13_fail; + } + + // Get 32 lsb lba and check + lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); + if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) + && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) { + BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH()); + goto int13_fail; + } + + // If verify or seek + if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 )) + goto int13_success; + + // Execute the command + if ( GET_AH() == 0x42 ) + status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset); + else + status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset); + + count=read_word(ebda_seg, &EbdaData->ata.trsfsectors); + write_word(DS, SI+(Bit16u)&Int13Ext->count, count); + + if (status != 0) { + BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status); + SET_AH(0x0c); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x45: // IBM/MS lock/unlock drive + case 0x49: // IBM/MS extended media change + goto int13_success; // Always success for HD + break; + + case 0x46: // IBM/MS eject media + SET_AH(0xb2); // Volume Not Removable + goto int13_fail_noah; // Always fail for HD + break; + + case 0x48: // IBM/MS get drive parameters + size=read_word(DS,SI+(Bit16u)&Int13DPT->size); + + // Buffer is too small + if(size < 0x1a) + goto int13_fail; + + // EDD 1.x + if(size >= 0x1a) { + Bit16u blksize; + + npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders); + nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads); + npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt); + lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low); + lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high); + blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); + + write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a); + if (lba_high || (lba_low/npspt)/nph > 0x3fff) + { + write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid + write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff); + } + else + { + write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid + write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc); + } + write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph); + write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt); + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low); + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high); + write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize); + } + + // EDD 2.x + if(size >= 0x1e) { + Bit8u channel, dev, irq, mode, checksum, i, translation; + Bit16u iobase1, iobase2, options; + + write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e); + + write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg); + write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte); + + // Fill in dpte + channel = device / 2; + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); + irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq); + mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); + translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation); + + options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation + options |= (1<<4); // lba translation + options |= (mode==ATA_MODE_PIO32?1:0)<<7; + options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9; + options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9; + + write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1); + write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC); + write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 ); + write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb ); + write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq ); + write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 ); + write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 ); + write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 ); + write_word(ebda_seg, &EbdaData->ata.dpte.options, options); + write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0); + if (size >=0x42) + write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11); + else + write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10); + + checksum=0; + for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i); + checksum = ~checksum; + write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum); + } + + // EDD 3.x + if(size >= 0x42) { + Bit8u channel, iface, checksum, i; + Bit16u iobase1; + + channel = device / 2; + iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface); + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + + write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42); + write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd); + write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24); + write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0); + write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0); + + if (iface==ATA_IFACE_ISA) { + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I'); + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S'); + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A'); + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0); + } + else { + // FIXME PCI + } + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A'); + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T'); + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A'); + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0); + + if (iface==ATA_IFACE_ISA) { + write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1); + write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0); + write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L); + } + else { + // FIXME PCI + } + write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2); + write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0); + write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0); + write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L); + + checksum=0; + for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i); + checksum = ~checksum; + write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum); + } + + goto int13_success; + break; + + case 0x4e: // // IBM/MS set hardware configuration + // DMA, prefetch, PIO maximum not supported + switch (GET_AL()) { + case 0x01: + case 0x03: + case 0x04: + case 0x06: + goto int13_success; + break; + default : + goto int13_fail; + } + break; + + case 0x09: /* initialize drive parameters */ + case 0x0c: /* seek to specified cylinder */ + case 0x0d: /* alternate disk reset */ + case 0x11: /* recalibrate */ + case 0x14: /* controller internal diagnostic */ + BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH()); + goto int13_success; + break; + + case 0x0a: /* read disk sectors with ECC */ + case 0x0b: /* write disk sectors with ECC */ + case 0x18: // set media type for format + case 0x50: // IBM/MS send packet command + default: + BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter +int13_fail_noah: + SET_DISK_RET_STATUS(GET_AH()); +int13_fail_nostatus: + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error +int13_success_noah: + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// Start of int13 for cdrom +// --------------------------------------------------------------------------- + + void +int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u device, status, locks; + Bit8u atacmd[12]; + Bit32u lba; + Bit16u count, segment, offset, i, size; + + BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); + + SET_DISK_RET_STATUS(0x00); + + /* basic check : device should be 0xE0+ */ + if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) { + BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL()); + goto int13_fail; + } + + // Get the ata channel + device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]); + + /* basic check : device has to be valid */ + if (device >= BX_MAX_ATA_DEVICES) { + BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL()); + goto int13_fail; + } + + switch (GET_AH()) { + + // all those functions return SUCCESS + case 0x00: /* disk controller reset */ + case 0x09: /* initialize drive parameters */ + case 0x0c: /* seek to specified cylinder */ + case 0x0d: /* alternate disk reset */ + case 0x10: /* check drive ready */ + case 0x11: /* recalibrate */ + case 0x14: /* controller internal diagnostic */ + case 0x16: /* detect disk change */ + goto int13_success; + break; + + // all those functions return disk write-protected + case 0x03: /* write disk sectors */ + case 0x05: /* format disk track */ + case 0x43: // IBM/MS extended write + SET_AH(0x03); + goto int13_fail_noah; + break; + + case 0x01: /* read disk status */ + status = read_byte(0x0040, 0x0074); + SET_AH(status); + SET_DISK_RET_STATUS(0); + + /* set CF if error status read */ + if (status) goto int13_fail_nostatus; + else goto int13_success_noah; + break; + + case 0x15: /* read disk drive size */ + SET_AH(0x02); + goto int13_fail_noah; + break; + + case 0x41: // IBM/MS installation check + BX=0xaa55; // install check + SET_AH(0x30); // EDD 2.1 + CX=0x0007; // ext disk access, removable and edd + goto int13_success_noah; + break; + + case 0x42: // IBM/MS extended read + case 0x44: // IBM/MS verify sectors + case 0x47: // IBM/MS extended seek + + count=read_word(DS, SI+(Bit16u)&Int13Ext->count); + segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment); + offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset); + + // Can't use 64 bits lba + lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); + if (lba != 0L) { + BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH()); + goto int13_fail; + } + + // Get 32 bits lba + lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); + + // If verify or seek + if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 )) + goto int13_success; + + memsetb(get_SS(),atacmd,0,12); + atacmd[0]=0x28; // READ command + atacmd[7]=(count & 0xff00) >> 8; // Sectors + atacmd[8]=(count & 0x00ff); // Sectors + atacmd[2]=(lba & 0xff000000) >> 24; // LBA + atacmd[3]=(lba & 0x00ff0000) >> 16; + atacmd[4]=(lba & 0x0000ff00) >> 8; + atacmd[5]=(lba & 0x000000ff); + status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset); + + count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11); + write_word(DS, SI+(Bit16u)&Int13Ext->count, count); + + if (status != 0) { + BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status); + SET_AH(0x0c); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x45: // IBM/MS lock/unlock drive + if (GET_AL() > 2) goto int13_fail; + + locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock); + + switch (GET_AL()) { + case 0 : // lock + if (locks == 0xff) { + SET_AH(0xb4); + SET_AL(1); + goto int13_fail_noah; + } + write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks); + SET_AL(1); + break; + case 1 : // unlock + if (locks == 0x00) { + SET_AH(0xb0); + SET_AL(0); + goto int13_fail_noah; + } + write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks); + SET_AL(locks==0?0:1); + break; + case 2 : // status + SET_AL(locks==0?0:1); + break; + } + goto int13_success; + break; + + case 0x46: // IBM/MS eject media + locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock); + + if (locks != 0) { + SET_AH(0xb1); // media locked + goto int13_fail_noah; + } + // FIXME should handle 0x31 no media in device + // FIXME should handle 0xb5 valid request failed + + // Call removable media eject + ASM_START + push bp + mov bp, sp + + mov ah, #0x52 + int #0x15 + mov _int13_cdrom.status + 2[bp], ah + jnc int13_cdrom_rme_end + mov _int13_cdrom.status, #1 +int13_cdrom_rme_end: + pop bp + ASM_END + + if (status != 0) { + SET_AH(0xb1); // media locked + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x48: // IBM/MS get drive parameters + size = read_word(DS,SI+(Bit16u)&Int13Ext->size); + + // Buffer is too small + if(size < 0x1a) + goto int13_fail; + + // EDD 1.x + if(size >= 0x1a) { + Bit16u cylinders, heads, spt, blksize; + + blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); + + write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a); + write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values + write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff); + write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff); + write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff); + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64 + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff); + write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize); + } + + // EDD 2.x + if(size >= 0x1e) { + Bit8u channel, dev, irq, mode, checksum, i; + Bit16u iobase1, iobase2, options; + + write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e); + + write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg); + write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte); + + // Fill in dpte + channel = device / 2; + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); + irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq); + mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); + + // FIXME atapi device + options = (1<<4); // lba translation + options |= (1<<5); // removable device + options |= (1<<6); // atapi device + options |= (mode==ATA_MODE_PIO32?1:0<<7); + + write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1); + write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC); + write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 ); + write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb ); + write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq ); + write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 ); + write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 ); + write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 ); + write_word(ebda_seg, &EbdaData->ata.dpte.options, options); + write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0); + write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11); + + checksum=0; + for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i); + checksum = ~checksum; + write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum); + } + + // EDD 3.x + if(size >= 0x42) { + Bit8u channel, iface, checksum, i; + Bit16u iobase1; + + channel = device / 2; + iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface); + iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); + + write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42); + write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd); + write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24); + write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0); + write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0); + + if (iface==ATA_IFACE_ISA) { + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I'); + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S'); + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A'); + write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0); + } + else { + // FIXME PCI + } + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A'); + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T'); + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A'); + write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0); + + if (iface==ATA_IFACE_ISA) { + write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1); + write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0); + write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L); + } + else { + // FIXME PCI + } + write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2); + write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0); + write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0); + write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L); + + checksum=0; + for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i); + checksum = ~checksum; + write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum); + } + + goto int13_success; + break; + + case 0x49: // IBM/MS extended media change + // always send changed ?? + SET_AH(06); + goto int13_fail_nostatus; + break; + + case 0x4e: // // IBM/MS set hardware configuration + // DMA, prefetch, PIO maximum not supported + switch (GET_AL()) { + case 0x01: + case 0x03: + case 0x04: + case 0x06: + goto int13_success; + break; + default : + goto int13_fail; + } + break; + + // all those functions return unimplemented + case 0x02: /* read sectors */ + case 0x04: /* verify sectors */ + case 0x08: /* read disk drive parameters */ + case 0x0a: /* read disk sectors with ECC */ + case 0x0b: /* write disk sectors with ECC */ + case 0x18: /* set media type for format */ + case 0x50: // ? - send packet command + default: + BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter +int13_fail_noah: + SET_DISK_RET_STATUS(GET_AH()); +int13_fail_nostatus: + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error +int13_success_noah: + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// End of int13 for cdrom +// --------------------------------------------------------------------------- + +#if BX_ELTORITO_BOOT +// --------------------------------------------------------------------------- +// Start of int13 for eltorito functions +// --------------------------------------------------------------------------- + + void +int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + + BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); + // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI); + + switch (GET_AH()) { + + // FIXME ElTorito Various. Should be implemented + case 0x4a: // ElTorito - Initiate disk emu + case 0x4c: // ElTorito - Initiate disk emu and boot + case 0x4d: // ElTorito - Return Boot catalog + BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX); + goto int13_fail; + break; + + case 0x4b: // ElTorito - Terminate disk emu + // FIXME ElTorito Hardcoded + write_byte(DS,SI+0x00,0x13); + write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media)); + write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)); + write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index)); + write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba)); + write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec)); + write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment)); + write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment)); + write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count)); + write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders)); + write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt)); + write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads)); + + // If we have to terminate emulation + if(GET_AL() == 0x00) { + // FIXME ElTorito Various. Should be handled accordingly to spec + write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye + } + + goto int13_success; + break; + + default: + BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter + SET_DISK_RET_STATUS(GET_AH()); + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// End of int13 for eltorito functions +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// Start of int13 when emulating a device from the cd +// --------------------------------------------------------------------------- + + void +int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit8u device, status; + Bit16u vheads, vspt, vcylinders; + Bit16u head, sector, cylinder, nbsectors; + Bit32u vlba, ilba, slba, elba; + Bit16u before, segment, offset; + Bit8u atacmd[12]; + + BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); + + /* at this point, we are emulating a floppy/harddisk */ + + // Recompute the device number + device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2; + device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec); + + SET_DISK_RET_STATUS(0x00); + + /* basic checks : emulation should be active, dl should equal the emulated drive */ + if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 ) + || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) { + BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL()); + goto int13_fail; + } + + switch (GET_AH()) { + + // all those functions return SUCCESS + case 0x00: /* disk controller reset */ + case 0x09: /* initialize drive parameters */ + case 0x0c: /* seek to specified cylinder */ + case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ? + case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ? + case 0x11: /* recalibrate */ + case 0x14: /* controller internal diagnostic */ + case 0x16: /* detect disk change */ + goto int13_success; + break; + + // all those functions return disk write-protected + case 0x03: /* write disk sectors */ + case 0x05: /* format disk track */ + SET_AH(0x03); + goto int13_fail_noah; + break; + + case 0x01: /* read disk status */ + status=read_byte(0x0040, 0x0074); + SET_AH(status); + SET_DISK_RET_STATUS(0); + + /* set CF if error status read */ + if (status) goto int13_fail_nostatus; + else goto int13_success_noah; + break; + + case 0x02: // read disk sectors + case 0x04: // verify disk sectors + vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); + vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders); + vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads); + + ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba); + + sector = GET_CL() & 0x003f; + cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH(); + head = GET_DH(); + nbsectors = GET_AL(); + segment = ES; + offset = BX; + + // no sector to read ? + if(nbsectors==0) goto int13_success; + + // sanity checks sco openserver needs this! + if ((sector > vspt) + || (cylinder >= vcylinders) + || (head >= vheads)) { + goto int13_fail; + } + + // After controls, verify do nothing + if (GET_AH() == 0x04) goto int13_success; + + segment = ES+(BX / 16); + offset = BX % 16; + + // calculate the virtual lba inside the image + vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1)); + + // In advance so we don't loose the count + SET_AL(nbsectors); + + // start lba on cd + slba = (Bit32u)vlba/4; + before= (Bit16u)vlba%4; + + // end lba on cd + elba = (Bit32u)(vlba+nbsectors-1)/4; + + memsetb(get_SS(),atacmd,0,12); + atacmd[0]=0x28; // READ command + atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors + atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors + atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA + atacmd[3]=(ilba+slba & 0x00ff0000) >> 16; + atacmd[4]=(ilba+slba & 0x0000ff00) >> 8; + atacmd[5]=(ilba+slba & 0x000000ff); + if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) { + BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status); + SET_AH(0x02); + SET_AL(0); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x08: /* read disk drive parameters */ + vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); + vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1; + vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1; + + SET_AL( 0x00 ); + SET_BL( 0x00 ); + SET_CH( vcylinders & 0xff ); + SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f )); + SET_DH( vheads ); + SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2 + // FIXME ElTorito Harddisk. should send the HD count + + switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) { + case 0x01: SET_BL( 0x02 ); break; + case 0x02: SET_BL( 0x04 ); break; + case 0x03: SET_BL( 0x06 ); break; + } + +ASM_START + push bp + mov bp, sp + mov ax, #diskette_param_table2 + mov _int13_cdemu.DI+2[bp], ax + mov _int13_cdemu.ES+2[bp], cs + pop bp +ASM_END + goto int13_success; + break; + + case 0x15: /* read disk drive size */ + // FIXME ElTorito Harddisk. What geometry to send ? + SET_AH(0x03); + goto int13_success_noah; + break; + + // all those functions return unimplemented + case 0x0a: /* read disk sectors with ECC */ + case 0x0b: /* write disk sectors with ECC */ + case 0x18: /* set media type for format */ + case 0x41: // IBM/MS installation check + // FIXME ElTorito Harddisk. Darwin would like to use EDD + case 0x42: // IBM/MS extended read + case 0x43: // IBM/MS extended write + case 0x44: // IBM/MS verify sectors + case 0x45: // IBM/MS lock/unlock drive + case 0x46: // IBM/MS eject media + case 0x47: // IBM/MS extended seek + case 0x48: // IBM/MS get drive parameters + case 0x49: // IBM/MS extended media change + case 0x4e: // ? - set hardware configuration + case 0x50: // ? - send packet command + default: + BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter +int13_fail_noah: + SET_DISK_RET_STATUS(GET_AH()); +int13_fail_nostatus: + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error +int13_success_noah: + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// End of int13 when emulating a device from the cd +// --------------------------------------------------------------------------- + +#endif // BX_ELTORITO_BOOT + +#else //BX_USE_ATADRV + + void +outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl) + Bit16u cylinder; + Bit16u hd_heads; + Bit16u head; + Bit16u hd_sectors; + Bit16u sector; + Bit16u dl; +{ +ASM_START + push bp + mov bp, sp + push eax + push ebx + push edx + xor eax,eax + mov ax,4[bp] // cylinder + xor ebx,ebx + mov bl,6[bp] // hd_heads + imul ebx + + mov bl,8[bp] // head + add eax,ebx + mov bl,10[bp] // hd_sectors + imul ebx + mov bl,12[bp] // sector + add eax,ebx + + dec eax + mov dx,#0x1f3 + out dx,al + mov dx,#0x1f4 + mov al,ah + out dx,al + shr eax,#16 + mov dx,#0x1f5 + out dx,al + and ah,#0xf + mov bl,14[bp] // dl + and bl,#1 + shl bl,#4 + or ah,bl + or ah,#0xe0 + mov al,ah + mov dx,#0x01f6 + out dx,al + pop edx + pop ebx + pop eax + pop bp +ASM_END +} + + void +int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit8u drive, num_sectors, sector, head, status, mod; + Bit8u drive_map; + Bit8u n_drives; + Bit16u cyl_mod, ax; + Bit16u max_cylinder, cylinder, total_sectors; + Bit16u hd_cylinders; + Bit8u hd_heads, hd_sectors; + Bit16u val16; + Bit8u sector_count; + unsigned int i; + Bit16u tempbx; + Bit16u dpsize; + + Bit16u count, segment, offset; + Bit32u lba; + Bit16u error; + + BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); + + write_byte(0x0040, 0x008e, 0); // clear completion flag + + /* at this point, DL is >= 0x80 to be passed from the floppy int13h + handler code */ + /* check how many disks first (cmos reg 0x12), return an error if + drive not present */ + drive_map = inb_cmos(0x12); + drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) | + (((drive_map & 0x0f)==0) ? 0 : 2); + n_drives = (drive_map==0) ? 0 : + ((drive_map==3) ? 2 : 1); + + if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */ + SET_AH(0x01); + SET_DISK_RET_STATUS(0x01); + SET_CF(); /* error occurred */ + return; + } + + switch (GET_AH()) { + + case 0x00: /* disk controller reset */ +BX_DEBUG_INT13_HD("int13_f00\n"); + + SET_AH(0); + SET_DISK_RET_STATUS(0); + set_diskette_ret_status(0); + set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */ + set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */ + CLEAR_CF(); /* successful */ + return; + break; + + case 0x01: /* read disk status */ +BX_DEBUG_INT13_HD("int13_f01\n"); + status = read_byte(0x0040, 0x0074); + SET_AH(status); + SET_DISK_RET_STATUS(0); + /* set CF if error status read */ + if (status) SET_CF(); + else CLEAR_CF(); + return; + break; + + case 0x04: // verify disk sectors + case 0x02: // read disk sectors + drive = GET_ELDL(); + get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); + + num_sectors = GET_AL(); + cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH(); + sector = (GET_CL() & 0x3f); + head = GET_DH(); + + + if (hd_cylinders > 1024) { + if (hd_cylinders <= 2048) { + cylinder <<= 1; + } + else if (hd_cylinders <= 4096) { + cylinder <<= 2; + } + else if (hd_cylinders <= 8192) { + cylinder <<= 3; + } + else { // hd_cylinders <= 16384 + cylinder <<= 4; + } + + ax = head / hd_heads; + cyl_mod = ax & 0xff; + head = ax >> 8; + cylinder |= cyl_mod; + } + + if ( (cylinder >= hd_cylinders) || + (sector > hd_sectors) || + (head >= hd_heads) ) { + SET_AH(1); + SET_DISK_RET_STATUS(1); + SET_CF(); /* error occurred */ + return; + } + + if ( (num_sectors > 128) || (num_sectors == 0) ) + BX_PANIC("int13_harddisk: num_sectors out of range!\n"); + + if (head > 15) + BX_PANIC("hard drive BIOS:(read/verify) head > 15\n"); + + if ( GET_AH() == 0x04 ) { + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); + return; + } + + status = inb(0x1f7); + if (status & 0x80) { + BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n"); + } + outb(0x01f2, num_sectors); + /* activate LBA? (tomv) */ + if (hd_heads > 16) { +BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector); + outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive); + } + else { + outb(0x01f3, sector); + outb(0x01f4, cylinder & 0x00ff); + outb(0x01f5, cylinder >> 8); + outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f)); + } + outb(0x01f7, 0x20); + + while (1) { + status = inb(0x1f7); + if ( !(status & 0x80) ) break; + } + + if (status & 0x01) { + BX_PANIC("hard drive BIOS:(read/verify) read error\n"); + } else if ( !(status & 0x08) ) { + BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status); + BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n"); + } + + sector_count = 0; + tempbx = BX; + +ASM_START + sti ;; enable higher priority interrupts +ASM_END + + while (1) { +ASM_START + ;; store temp bx in real DI register + push bp + mov bp, sp + mov di, _int13_harddisk.tempbx + 2 [bp] + pop bp + + ;; adjust if there will be an overrun + cmp di, #0xfe00 + jbe i13_f02_no_adjust +i13_f02_adjust: + sub di, #0x0200 ; sub 512 bytes from offset + mov ax, es + add ax, #0x0020 ; add 512 to segment + mov es, ax + +i13_f02_no_adjust: + mov cx, #0x0100 ;; counter (256 words = 512b) + mov dx, #0x01f0 ;; AT data read port + + rep + insw ;; CX words transfered from port(DX) to ES:[DI] + +i13_f02_done: + ;; store real DI register back to temp bx + push bp + mov bp, sp + mov _int13_harddisk.tempbx + 2 [bp], di + pop bp +ASM_END + + sector_count++; + num_sectors--; + if (num_sectors == 0) { + status = inb(0x1f7); + if ( (status & 0xc9) != 0x40 ) + BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status); + break; + } + else { + status = inb(0x1f7); + if ( (status & 0xc9) != 0x48 ) + BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status); + continue; + } + } + + SET_AH(0); + SET_DISK_RET_STATUS(0); + SET_AL(sector_count); + CLEAR_CF(); /* successful */ + return; + break; + + + case 0x03: /* write disk sectors */ +BX_DEBUG_INT13_HD("int13_f03\n"); + drive = GET_ELDL (); + get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); + + num_sectors = GET_AL(); + cylinder = GET_CH(); + cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300; + sector = (GET_CL() & 0x3f); + head = GET_DH(); + + if (hd_cylinders > 1024) { + if (hd_cylinders <= 2048) { + cylinder <<= 1; + } + else if (hd_cylinders <= 4096) { + cylinder <<= 2; + } + else if (hd_cylinders <= 8192) { + cylinder <<= 3; + } + else { // hd_cylinders <= 16384 + cylinder <<= 4; + } + + ax = head / hd_heads; + cyl_mod = ax & 0xff; + head = ax >> 8; + cylinder |= cyl_mod; + } + + if ( (cylinder >= hd_cylinders) || + (sector > hd_sectors) || + (head >= hd_heads) ) { + SET_AH( 1); + SET_DISK_RET_STATUS(1); + SET_CF(); /* error occurred */ + return; + } + + if ( (num_sectors > 128) || (num_sectors == 0) ) + BX_PANIC("int13_harddisk: num_sectors out of range!\n"); + + if (head > 15) + BX_PANIC("hard drive BIOS:(read) head > 15\n"); + + status = inb(0x1f7); + if (status & 0x80) { + BX_PANIC("hard drive BIOS:(read) BUSY bit set\n"); + } +// should check for Drive Ready Bit also in status reg + outb(0x01f2, num_sectors); + + /* activate LBA? (tomv) */ + if (hd_heads > 16) { +BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector); + outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL()); + } + else { + outb(0x01f3, sector); + outb(0x01f4, cylinder & 0x00ff); + outb(0x01f5, cylinder >> 8); + outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f)); + } + outb(0x01f7, 0x30); + + // wait for busy bit to turn off after seeking + while (1) { + status = inb(0x1f7); + if ( !(status & 0x80) ) break; + } + + if ( !(status & 0x08) ) { + BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status); + BX_PANIC("hard drive BIOS:(write) data-request bit not set\n"); + } + + sector_count = 0; + tempbx = BX; + +ASM_START + sti ;; enable higher priority interrupts +ASM_END + + while (1) { +ASM_START + ;; store temp bx in real SI register + push bp + mov bp, sp + mov si, _int13_harddisk.tempbx + 2 [bp] + pop bp + + ;; adjust if there will be an overrun + cmp si, #0xfe00 + jbe i13_f03_no_adjust +i13_f03_adjust: + sub si, #0x0200 ; sub 512 bytes from offset + mov ax, es + add ax, #0x0020 ; add 512 to segment + mov es, ax + +i13_f03_no_adjust: + mov cx, #0x0100 ;; counter (256 words = 512b) + mov dx, #0x01f0 ;; AT data read port + + seg ES + rep + outsw ;; CX words tranfered from ES:[SI] to port(DX) + + ;; store real SI register back to temp bx + push bp + mov bp, sp + mov _int13_harddisk.tempbx + 2 [bp], si + pop bp +ASM_END + + sector_count++; + num_sectors--; + if (num_sectors == 0) { + status = inb(0x1f7); + if ( (status & 0xe9) != 0x40 ) + BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status); + break; + } + else { + status = inb(0x1f7); + if ( (status & 0xc9) != 0x48 ) + BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status); + continue; + } + } + + SET_AH(0); + SET_DISK_RET_STATUS(0); + SET_AL(sector_count); + CLEAR_CF(); /* successful */ + return; + break; + + case 0x05: /* format disk track */ +BX_DEBUG_INT13_HD("int13_f05\n"); + BX_PANIC("format disk track called\n"); + /* nop */ + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + return; + break; + + case 0x08: /* read disk drive parameters */ +BX_DEBUG_INT13_HD("int13_f08\n"); + + drive = GET_ELDL (); + get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); + + // translate CHS + // + if (hd_cylinders <= 1024) { + // hd_cylinders >>= 0; + // hd_heads <<= 0; + } + else if (hd_cylinders <= 2048) { + hd_cylinders >>= 1; + hd_heads <<= 1; + } + else if (hd_cylinders <= 4096) { + hd_cylinders >>= 2; + hd_heads <<= 2; + } + else if (hd_cylinders <= 8192) { + hd_cylinders >>= 3; + hd_heads <<= 3; + } + else { // hd_cylinders <= 16384 + hd_cylinders >>= 4; + hd_heads <<= 4; + } + + max_cylinder = hd_cylinders - 2; /* 0 based */ + SET_AL(0); + SET_CH(max_cylinder & 0xff); + SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f)); + SET_DH(hd_heads - 1); + SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */ + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + + return; + break; + + case 0x09: /* initialize drive parameters */ +BX_DEBUG_INT13_HD("int13_f09\n"); + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + return; + break; + + case 0x0a: /* read disk sectors with ECC */ +BX_DEBUG_INT13_HD("int13_f0a\n"); + case 0x0b: /* write disk sectors with ECC */ +BX_DEBUG_INT13_HD("int13_f0b\n"); + BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n"); + return; + break; + + case 0x0c: /* seek to specified cylinder */ +BX_DEBUG_INT13_HD("int13_f0c\n"); + BX_INFO("int13h function 0ch (seek) not implemented!\n"); + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + return; + break; + + case 0x0d: /* alternate disk reset */ +BX_DEBUG_INT13_HD("int13_f0d\n"); + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + return; + break; + + case 0x10: /* check drive ready */ +BX_DEBUG_INT13_HD("int13_f10\n"); + //SET_AH(0); + //SET_DISK_RET_STATUS(0); + //CLEAR_CF(); /* successful */ + //return; + //break; + + // should look at 40:8E also??? + status = inb(0x01f7); + if ( (status & 0xc0) == 0x40 ) { + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); // drive ready + return; + } + else { + SET_AH(0xAA); + SET_DISK_RET_STATUS(0xAA); + SET_CF(); // not ready + return; + } + break; + + case 0x11: /* recalibrate */ +BX_DEBUG_INT13_HD("int13_f11\n"); + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + return; + break; + + case 0x14: /* controller internal diagnostic */ +BX_DEBUG_INT13_HD("int13_f14\n"); + SET_AH(0); + SET_DISK_RET_STATUS(0); + CLEAR_CF(); /* successful */ + SET_AL(0); + return; + break; + + case 0x15: /* read disk drive size */ + drive = GET_ELDL(); + get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); +ASM_START + push bp + mov bp, sp + mov al, _int13_harddisk.hd_heads + 2 [bp] + mov ah, _int13_harddisk.hd_sectors + 2 [bp] + mul al, ah ;; ax = heads * sectors + mov bx, _int13_harddisk.hd_cylinders + 2 [bp] + dec bx ;; use (cylinders - 1) ??? + mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors) + ;; now we need to move the 32bit result dx:ax to what the + ;; BIOS wants which is cx:dx. + ;; and then into CX:DX on the stack + mov _int13_harddisk.CX + 2 [bp], dx + mov _int13_harddisk.DX + 2 [bp], ax + pop bp +ASM_END + SET_AH(3); // hard disk accessible + SET_DISK_RET_STATUS(0); // ??? should this be 0 + CLEAR_CF(); // successful + return; + break; + + case 0x18: // set media type for format + case 0x41: // IBM/MS + case 0x42: // IBM/MS + case 0x43: // IBM/MS + case 0x44: // IBM/MS + case 0x45: // IBM/MS lock/unlock drive + case 0x46: // IBM/MS eject media + case 0x47: // IBM/MS extended seek + case 0x49: // IBM/MS extended media change + case 0x50: // IBM/MS send packet command + default: + BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH()); + + SET_AH(1); // code=invalid function in AH or invalid parameter + SET_DISK_RET_STATUS(1); + SET_CF(); /* unsuccessful */ + return; + break; + } +} + +static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n"; +static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n"; + + void +get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors) + Bit8u drive; + Bit16u *hd_cylinders; + Bit8u *hd_heads; + Bit8u *hd_sectors; +{ + Bit8u hd_type; + Bit16u ss; + Bit16u cylinders; + Bit8u iobase; + + ss = get_SS(); + if (drive == 0x80) { + hd_type = inb_cmos(0x12) & 0xf0; + if (hd_type != 0xf0) + BX_INFO(panic_msg_reg12h,0); + hd_type = inb_cmos(0x19); // HD0: extended type + if (hd_type != 47) + BX_INFO(panic_msg_reg19h,0,0x19); + iobase = 0x1b; + } else { + hd_type = inb_cmos(0x12) & 0x0f; + if (hd_type != 0x0f) + BX_INFO(panic_msg_reg12h,1); + hd_type = inb_cmos(0x1a); // HD1: extended type + if (hd_type != 47) + BX_INFO(panic_msg_reg19h,0,0x1a); + iobase = 0x24; + } + + // cylinders + cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8); + write_word(ss, hd_cylinders, cylinders); + + // heads + write_byte(ss, hd_heads, inb_cmos(iobase+2)); + + // sectors per track + write_byte(ss, hd_sectors, inb_cmos(iobase+8)); +} + +#endif //else BX_USE_ATADRV + +#if BX_SUPPORT_FLOPPY + +////////////////////// +// FLOPPY functions // +////////////////////// + +void floppy_reset_controller() +{ + Bit8u val8; + + // Reset controller + val8 = inb(0x03f2); + outb(0x03f2, val8 & ~0x04); + outb(0x03f2, val8 | 0x04); + + // Wait for controller to come out of reset + do { + val8 = inb(0x3f4); + } while ( (val8 & 0xc0) != 0x80 ); +} + +void floppy_prepare_controller(drive) + Bit16u drive; +{ + Bit8u val8, dor, prev_reset; + + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0040, 0x003e); + val8 &= 0x7f; + write_byte(0x0040, 0x003e, val8); + + // turn on motor of selected drive, DMA & int enabled, normal operation + prev_reset = inb(0x03f2) & 0x04; + if (drive) + dor = 0x20; + else + dor = 0x10; + dor |= 0x0c; + dor |= drive; + outb(0x03f2, dor); + + // reset the disk motor timeout value of INT 08 + write_byte(0x40,0x40, BX_FLOPPY_ON_CNT); + + // wait for drive readiness + do { + val8 = inb(0x3f4); + } while ( (val8 & 0xc0) != 0x80 ); + + if (prev_reset == 0) { + // turn on interrupts +ASM_START + sti +ASM_END + // wait on 40:3e bit 7 to become 1 + do { + val8 = read_byte(0x0040, 0x003e); + } while ( (val8 & 0x80) == 0 ); + val8 &= 0x7f; +ASM_START + cli +ASM_END + write_byte(0x0040, 0x003e, val8); + } +} + + bx_bool +floppy_media_known(drive) + Bit16u drive; +{ + Bit8u val8; + Bit16u media_state_offset; + + val8 = read_byte(0x0040, 0x003e); // diskette recal status + if (drive) + val8 >>= 1; + val8 &= 0x01; + if (val8 == 0) + return(0); + + media_state_offset = 0x0090; + if (drive) + media_state_offset += 1; + + val8 = read_byte(0x0040, media_state_offset); + val8 = (val8 >> 4) & 0x01; + if (val8 == 0) + return(0); + + // check pass, return KNOWN + return(1); +} + + bx_bool +floppy_media_sense(drive) + Bit16u drive; +{ + bx_bool retval; + Bit16u media_state_offset; + Bit8u drive_type, config_data, media_state; + + if (floppy_drive_recal(drive) == 0) { + return(0); + } + + // for now cheat and get drive type from CMOS, + // assume media is same as drive type + + // ** config_data ** + // Bitfields for diskette media control: + // Bit(s) Description (Table M0028) + // 7-6 last data rate set by controller + // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps + // 5-4 last diskette drive step rate selected + // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah + // 3-2 {data rate at start of operation} + // 1-0 reserved + + // ** media_state ** + // Bitfields for diskette drive media state: + // Bit(s) Description (Table M0030) + // 7-6 data rate + // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps + // 5 double stepping required (e.g. 360kB in 1.2MB) + // 4 media type established + // 3 drive capable of supporting 4MB media + // 2-0 on exit from BIOS, contains + // 000 trying 360kB in 360kB + // 001 trying 360kB in 1.2MB + // 010 trying 1.2MB in 1.2MB + // 011 360kB in 360kB established + // 100 360kB in 1.2MB established + // 101 1.2MB in 1.2MB established + // 110 reserved + // 111 all other formats/drives + + drive_type = inb_cmos(0x10); + if (drive == 0) + drive_type >>= 4; + else + drive_type &= 0x0f; + if ( drive_type == 1 ) { + // 360K 5.25" drive + config_data = 0x00; // 0000 0000 + media_state = 0x25; // 0010 0101 + retval = 1; + } + else if ( drive_type == 2 ) { + // 1.2 MB 5.25" drive + config_data = 0x00; // 0000 0000 + media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5) + retval = 1; + } + else if ( drive_type == 3 ) { + // 720K 3.5" drive + config_data = 0x00; // 0000 0000 ??? + media_state = 0x17; // 0001 0111 + retval = 1; + } + else if ( drive_type == 4 ) { + // 1.44 MB 3.5" drive + config_data = 0x00; // 0000 0000 + media_state = 0x17; // 0001 0111 + retval = 1; + } + else if ( drive_type == 5 ) { + // 2.88 MB 3.5" drive + config_data = 0xCC; // 1100 1100 + media_state = 0xD7; // 1101 0111 + retval = 1; + } + // + // Extended floppy size uses special cmos setting + else if ( drive_type == 6 ) { + // 160k 5.25" drive + config_data = 0x00; // 0000 0000 + media_state = 0x27; // 0010 0111 + retval = 1; + } + else if ( drive_type == 7 ) { + // 180k 5.25" drive + config_data = 0x00; // 0000 0000 + media_state = 0x27; // 0010 0111 + retval = 1; + } + else if ( drive_type == 8 ) { + // 320k 5.25" drive + config_data = 0x00; // 0000 0000 + media_state = 0x27; // 0010 0111 + retval = 1; + } + + else { + // not recognized + config_data = 0x00; // 0000 0000 + media_state = 0x00; // 0000 0000 + retval = 0; + } + + if (drive == 0) + media_state_offset = 0x90; + else + media_state_offset = 0x91; + write_byte(0x0040, 0x008B, config_data); + write_byte(0x0040, media_state_offset, media_state); + + return(retval); +} + + bx_bool +floppy_drive_recal(drive) + Bit16u drive; +{ + Bit8u val8; + Bit16u curr_cyl_offset; + + floppy_prepare_controller(drive); + + // send Recalibrate command (2 bytes) to controller + outb(0x03f5, 0x07); // 07: Recalibrate + outb(0x03f5, drive); // 0=drive0, 1=drive1 + + // turn on interrupts +ASM_START + sti +ASM_END + + // wait on 40:3e bit 7 to become 1 + do { + val8 = (read_byte(0x0040, 0x003e) & 0x80); + } while ( val8 == 0 ); + + val8 = 0; // separate asm from while() loop + // turn off interrupts +ASM_START + cli +ASM_END + + // set 40:3e bit 7 to 0, and calibrated bit + val8 = read_byte(0x0040, 0x003e); + val8 &= 0x7f; + if (drive) { + val8 |= 0x02; // Drive 1 calibrated + curr_cyl_offset = 0x0095; + } else { + val8 |= 0x01; // Drive 0 calibrated + curr_cyl_offset = 0x0094; + } + write_byte(0x0040, 0x003e, val8); + write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0 + + return(1); +} + + + + bx_bool +floppy_drive_exists(drive) + Bit16u drive; +{ + Bit8u drive_type; + + // check CMOS to see if drive exists + drive_type = inb_cmos(0x10); + if (drive == 0) + drive_type >>= 4; + else + drive_type &= 0x0f; + if ( drive_type == 0 ) + return(0); + else + return(1); +} + + void +int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit8u drive, num_sectors, track, sector, head, status; + Bit16u base_address, base_count, base_es; + Bit8u page, mode_register, val8, dor; + Bit8u return_status[7]; + Bit8u drive_type, num_floppies, ah; + Bit16u es, last_addr; + + BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); + + ah = GET_AH(); + + switch ( ah ) { + case 0x00: // diskette controller reset +BX_DEBUG_INT13_FL("floppy f00\n"); + drive = GET_ELDL(); + if (drive > 1) { + SET_AH(1); // invalid param + set_diskette_ret_status(1); + SET_CF(); + return; + } + drive_type = inb_cmos(0x10); + + if (drive == 0) + drive_type >>= 4; + else + drive_type &= 0x0f; + if (drive_type == 0) { + SET_AH(0x80); // drive not responding + set_diskette_ret_status(0x80); + SET_CF(); + return; + } + SET_AH(0); + set_diskette_ret_status(0); + CLEAR_CF(); // successful + set_diskette_current_cyl(drive, 0); // current cylinder + return; + + case 0x01: // Read Diskette Status + CLEAR_CF(); + val8 = read_byte(0x0000, 0x0441); + SET_AH(val8); + if (val8) { + SET_CF(); + } + return; + + case 0x02: // Read Diskette Sectors + case 0x03: // Write Diskette Sectors + case 0x04: // Verify Diskette Sectors + num_sectors = GET_AL(); + track = GET_CH(); + sector = GET_CL(); + head = GET_DH(); + drive = GET_ELDL(); + + if ((drive > 1) || (head > 1) || (sector == 0) || + (num_sectors == 0) || (num_sectors > 72)) { + BX_INFO("int13_diskette: read/write/verify: parameter out of range\n"); + SET_AH(1); + set_diskette_ret_status(1); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + + // see if drive exists + if (floppy_drive_exists(drive) == 0) { + SET_AH(0x80); // not responding + set_diskette_ret_status(0x80); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + + // see if media in drive, and type is known + if (floppy_media_known(drive) == 0) { + if (floppy_media_sense(drive) == 0) { + SET_AH(0x0C); // Media type not found + set_diskette_ret_status(0x0C); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + } + + if (ah == 0x02) { + // Read Diskette Sectors + + //----------------------------------- + // set up DMA controller for transfer + //----------------------------------- + + // es:bx = pointer to where to place information from diskette + // port 04: DMA-1 base and current address, channel 2 + // port 05: DMA-1 base and current count, channel 2 + page = (ES >> 12); // upper 4 bits + base_es = (ES << 4); // lower 16bits contributed by ES + base_address = base_es + BX; // lower 16 bits of address + // contributed by ES:BX + if ( base_address < base_es ) { + // in case of carry, adjust page by 1 + page++; + } + base_count = (num_sectors * 512) - 1; + + // check for 64K boundary overrun + last_addr = base_address + base_count; + if (last_addr < base_address) { + SET_AH(0x09); + set_diskette_ret_status(0x09); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + + BX_DEBUG_INT13_FL("masking DMA-1 c2\n"); + outb(0x000a, 0x06); + + BX_DEBUG_INT13_FL("clear flip-flop\n"); + outb(0x000c, 0x00); // clear flip-flop + outb(0x0004, base_address); + outb(0x0004, base_address>>8); + BX_DEBUG_INT13_FL("clear flip-flop\n"); + outb(0x000c, 0x00); // clear flip-flop + outb(0x0005, base_count); + outb(0x0005, base_count>>8); + + // port 0b: DMA-1 Mode Register + mode_register = 0x46; // single mode, increment, autoinit disable, + // transfer type=write, channel 2 + BX_DEBUG_INT13_FL("setting mode register\n"); + outb(0x000b, mode_register); + + BX_DEBUG_INT13_FL("setting page register\n"); + // port 81: DMA-1 Page Register, channel 2 + outb(0x0081, page); + + BX_DEBUG_INT13_FL("unmask chan 2\n"); + outb(0x000a, 0x02); // unmask channel 2 + + BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n"); + outb(0x000a, 0x02); + + //-------------------------------------- + // set up floppy controller for transfer + //-------------------------------------- + floppy_prepare_controller(drive); + + // send read-normal-data command (9 bytes) to controller + outb(0x03f5, 0xe6); // e6: read normal data + outb(0x03f5, (head << 2) | drive); // HD DR1 DR2 + outb(0x03f5, track); + outb(0x03f5, head); + outb(0x03f5, sector); + outb(0x03f5, 2); // 512 byte sector size + outb(0x03f5, sector + num_sectors - 1); // last sector to read on track + outb(0x03f5, 0); // Gap length + outb(0x03f5, 0xff); // Gap length + + // turn on interrupts + ASM_START + sti + ASM_END + + // wait on 40:3e bit 7 to become 1 + do { + val8 = read_byte(0x0040, 0x0040); + if (val8 == 0) { + floppy_reset_controller(); + SET_AH(0x80); // drive not ready (timeout) + set_diskette_ret_status(0x80); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + val8 = (read_byte(0x0040, 0x003e) & 0x80); + } while ( val8 == 0 ); + + val8 = 0; // separate asm from while() loop + // turn off interrupts + ASM_START + cli + ASM_END + + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0040, 0x003e); + val8 &= 0x7f; + write_byte(0x0040, 0x003e, val8); + + // check port 3f4 for accessibility to status bytes + val8 = inb(0x3f4); + if ( (val8 & 0xc0) != 0xc0 ) + BX_PANIC("int13_diskette: ctrl not ready\n"); + + // read 7 return status bytes from controller + // using loop index broken, have to unroll... + return_status[0] = inb(0x3f5); + return_status[1] = inb(0x3f5); + return_status[2] = inb(0x3f5); + return_status[3] = inb(0x3f5); + return_status[4] = inb(0x3f5); + return_status[5] = inb(0x3f5); + return_status[6] = inb(0x3f5); + // record in BIOS Data Area + write_byte(0x0040, 0x0042, return_status[0]); + write_byte(0x0040, 0x0043, return_status[1]); + write_byte(0x0040, 0x0044, return_status[2]); + write_byte(0x0040, 0x0045, return_status[3]); + write_byte(0x0040, 0x0046, return_status[4]); + write_byte(0x0040, 0x0047, return_status[5]); + write_byte(0x0040, 0x0048, return_status[6]); + + if ( (return_status[0] & 0xc0) != 0 ) { + SET_AH(0x20); + set_diskette_ret_status(0x20); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + + // ??? should track be new val from return_status[3] ? + set_diskette_current_cyl(drive, track); + // AL = number of sectors read (same value as passed) + SET_AH(0x00); // success + CLEAR_CF(); // success + return; + } else if (ah == 0x03) { + // Write Diskette Sectors + + //----------------------------------- + // set up DMA controller for transfer + //----------------------------------- + + // es:bx = pointer to where to place information from diskette + // port 04: DMA-1 base and current address, channel 2 + // port 05: DMA-1 base and current count, channel 2 + page = (ES >> 12); // upper 4 bits + base_es = (ES << 4); // lower 16bits contributed by ES + base_address = base_es + BX; // lower 16 bits of address + // contributed by ES:BX + if ( base_address < base_es ) { + // in case of carry, adjust page by 1 + page++; + } + base_count = (num_sectors * 512) - 1; + + // check for 64K boundary overrun + last_addr = base_address + base_count; + if (last_addr < base_address) { + SET_AH(0x09); + set_diskette_ret_status(0x09); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + + BX_DEBUG_INT13_FL("masking DMA-1 c2\n"); + outb(0x000a, 0x06); + + outb(0x000c, 0x00); // clear flip-flop + outb(0x0004, base_address); + outb(0x0004, base_address>>8); + outb(0x000c, 0x00); // clear flip-flop + outb(0x0005, base_count); + outb(0x0005, base_count>>8); + + // port 0b: DMA-1 Mode Register + mode_register = 0x4a; // single mode, increment, autoinit disable, + // transfer type=read, channel 2 + outb(0x000b, mode_register); + + // port 81: DMA-1 Page Register, channel 2 + outb(0x0081, page); + + BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n"); + outb(0x000a, 0x02); + + //-------------------------------------- + // set up floppy controller for transfer + //-------------------------------------- + floppy_prepare_controller(drive); + + // send write-normal-data command (9 bytes) to controller + outb(0x03f5, 0xc5); // c5: write normal data + outb(0x03f5, (head << 2) | drive); // HD DR1 DR2 + outb(0x03f5, track); + outb(0x03f5, head); + outb(0x03f5, sector); + outb(0x03f5, 2); // 512 byte sector size + outb(0x03f5, sector + num_sectors - 1); // last sector to write on track + outb(0x03f5, 0); // Gap length + outb(0x03f5, 0xff); // Gap length + + // turn on interrupts + ASM_START + sti + ASM_END + + // wait on 40:3e bit 7 to become 1 + do { + val8 = read_byte(0x0040, 0x0040); + if (val8 == 0) { + floppy_reset_controller(); + SET_AH(0x80); // drive not ready (timeout) + set_diskette_ret_status(0x80); + SET_AL(0); // no sectors written + SET_CF(); // error occurred + return; + } + val8 = (read_byte(0x0040, 0x003e) & 0x80); + } while ( val8 == 0 ); + + val8 = 0; // separate asm from while() loop + // turn off interrupts + ASM_START + cli + ASM_END + + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0040, 0x003e); + val8 &= 0x7f; + write_byte(0x0040, 0x003e, val8); + + // check port 3f4 for accessibility to status bytes + val8 = inb(0x3f4); + if ( (val8 & 0xc0) != 0xc0 ) + BX_PANIC("int13_diskette: ctrl not ready\n"); + + // read 7 return status bytes from controller + // using loop index broken, have to unroll... + return_status[0] = inb(0x3f5); + return_status[1] = inb(0x3f5); + return_status[2] = inb(0x3f5); + return_status[3] = inb(0x3f5); + return_status[4] = inb(0x3f5); + return_status[5] = inb(0x3f5); + return_status[6] = inb(0x3f5); + // record in BIOS Data Area + write_byte(0x0040, 0x0042, return_status[0]); + write_byte(0x0040, 0x0043, return_status[1]); + write_byte(0x0040, 0x0044, return_status[2]); + write_byte(0x0040, 0x0045, return_status[3]); + write_byte(0x0040, 0x0046, return_status[4]); + write_byte(0x0040, 0x0047, return_status[5]); + write_byte(0x0040, 0x0048, return_status[6]); + + if ( (return_status[0] & 0xc0) != 0 ) { + if ( (return_status[1] & 0x02) != 0 ) { + // diskette not writable. + // AH=status code=0x03 (tried to write on write-protected disk) + // AL=number of sectors written=0 + AX = 0x0300; + SET_CF(); + return; + } else { + BX_PANIC("int13_diskette_function: read error\n"); + } + } + + // ??? should track be new val from return_status[3] ? + set_diskette_current_cyl(drive, track); + // AL = number of sectors read (same value as passed) + SET_AH(0x00); // success + CLEAR_CF(); // success + return; + } else { // if (ah == 0x04) + // Verify Diskette Sectors + + // ??? should track be new val from return_status[3] ? + set_diskette_current_cyl(drive, track); + // AL = number of sectors verified (same value as passed) + CLEAR_CF(); // success + SET_AH(0x00); // success + return; + } + break; + + case 0x05: // format diskette track +BX_DEBUG_INT13_FL("floppy f05\n"); + + num_sectors = GET_AL(); + track = GET_CH(); + head = GET_DH(); + drive = GET_ELDL(); + + if ((drive > 1) || (head > 1) || (track > 79) || + (num_sectors == 0) || (num_sectors > 18)) { + SET_AH(1); + set_diskette_ret_status(1); + SET_CF(); // error occurred + } + + // see if drive exists + if (floppy_drive_exists(drive) == 0) { + SET_AH(0x80); // drive not responding + set_diskette_ret_status(0x80); + SET_CF(); // error occurred + return; + } + + // see if media in drive, and type is known + if (floppy_media_known(drive) == 0) { + if (floppy_media_sense(drive) == 0) { + SET_AH(0x0C); // Media type not found + set_diskette_ret_status(0x0C); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + } + + // set up DMA controller for transfer + page = (ES >> 12); // upper 4 bits + base_es = (ES << 4); // lower 16bits contributed by ES + base_address = base_es + BX; // lower 16 bits of address + // contributed by ES:BX + if ( base_address < base_es ) { + // in case of carry, adjust page by 1 + page++; + } + base_count = (num_sectors * 4) - 1; + + // check for 64K boundary overrun + last_addr = base_address + base_count; + if (last_addr < base_address) { + SET_AH(0x09); + set_diskette_ret_status(0x09); + SET_AL(0); // no sectors read + SET_CF(); // error occurred + return; + } + + outb(0x000a, 0x06); + outb(0x000c, 0x00); // clear flip-flop + outb(0x0004, base_address); + outb(0x0004, base_address>>8); + outb(0x000c, 0x00); // clear flip-flop + outb(0x0005, base_count); + outb(0x0005, base_count>>8); + mode_register = 0x4a; // single mode, increment, autoinit disable, + // transfer type=read, channel 2 + outb(0x000b, mode_register); + // port 81: DMA-1 Page Register, channel 2 + outb(0x0081, page); + outb(0x000a, 0x02); + + // set up floppy controller for transfer + floppy_prepare_controller(drive); + + // send format-track command (6 bytes) to controller + outb(0x03f5, 0x4d); // 4d: format track + outb(0x03f5, (head << 2) | drive); // HD DR1 DR2 + outb(0x03f5, 2); // 512 byte sector size + outb(0x03f5, num_sectors); // number of sectors per track + outb(0x03f5, 0); // Gap length + outb(0x03f5, 0xf6); // Fill byte + // turn on interrupts + ASM_START + sti + ASM_END + + // wait on 40:3e bit 7 to become 1 + do { + val8 = read_byte(0x0040, 0x0040); + if (val8 == 0) { + floppy_reset_controller(); + SET_AH(0x80); // drive not ready (timeout) + set_diskette_ret_status(0x80); + SET_CF(); // error occurred + return; + } + val8 = (read_byte(0x0040, 0x003e) & 0x80); + } while ( val8 == 0 ); + + val8 = 0; // separate asm from while() loop + // turn off interrupts + ASM_START + cli + ASM_END + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0040, 0x003e); + val8 &= 0x7f; + write_byte(0x0040, 0x003e, val8); + // check port 3f4 for accessibility to status bytes + val8 = inb(0x3f4); + if ( (val8 & 0xc0) != 0xc0 ) + BX_PANIC("int13_diskette: ctrl not ready\n"); + + // read 7 return status bytes from controller + // using loop index broken, have to unroll... + return_status[0] = inb(0x3f5); + return_status[1] = inb(0x3f5); + return_status[2] = inb(0x3f5); + return_status[3] = inb(0x3f5); + return_status[4] = inb(0x3f5); + return_status[5] = inb(0x3f5); + return_status[6] = inb(0x3f5); + // record in BIOS Data Area + write_byte(0x0040, 0x0042, return_status[0]); + write_byte(0x0040, 0x0043, return_status[1]); + write_byte(0x0040, 0x0044, return_status[2]); + write_byte(0x0040, 0x0045, return_status[3]); + write_byte(0x0040, 0x0046, return_status[4]); + write_byte(0x0040, 0x0047, return_status[5]); + write_byte(0x0040, 0x0048, return_status[6]); + + if ( (return_status[0] & 0xc0) != 0 ) { + if ( (return_status[1] & 0x02) != 0 ) { + // diskette not writable. + // AH=status code=0x03 (tried to write on write-protected disk) + // AL=number of sectors written=0 + AX = 0x0300; + SET_CF(); + return; + } else { + BX_PANIC("int13_diskette_function: write error\n"); + } + } + + SET_AH(0); + set_diskette_ret_status(0); + set_diskette_current_cyl(drive, 0); + CLEAR_CF(); // successful + return; + + + case 0x08: // read diskette drive parameters +BX_DEBUG_INT13_FL("floppy f08\n"); + drive = GET_ELDL(); + + if (drive > 1) { + AX = 0; + BX = 0; + CX = 0; + DX = 0; + ES = 0; + DI = 0; + SET_DL(num_floppies); + SET_CF(); + return; + } + + drive_type = inb_cmos(0x10); + num_floppies = 0; + if (drive_type & 0xf0) + num_floppies++; + if (drive_type & 0x0f) + num_floppies++; + + if (drive == 0) + drive_type >>= 4; + else + drive_type &= 0x0f; + + SET_BH(0); + SET_BL(drive_type); + SET_AH(0); + SET_AL(0); + SET_DL(num_floppies); + + switch (drive_type) { + case 0: // none + CX = 0; + SET_DH(0); // max head # + break; + + case 1: // 360KB, 5.25" + CX = 0x2709; // 40 tracks, 9 sectors + SET_DH(1); // max head # + break; + + case 2: // 1.2MB, 5.25" + CX = 0x4f0f; // 80 tracks, 15 sectors + SET_DH(1); // max head # + break; + + case 3: // 720KB, 3.5" + CX = 0x4f09; // 80 tracks, 9 sectors + SET_DH(1); // max head # + break; + + case 4: // 1.44MB, 3.5" + CX = 0x4f12; // 80 tracks, 18 sectors + SET_DH(1); // max head # + break; + + case 5: // 2.88MB, 3.5" + CX = 0x4f24; // 80 tracks, 36 sectors + SET_DH(1); // max head # + break; + + case 6: // 160k, 5.25" + CX = 0x2708; // 40 tracks, 8 sectors + SET_DH(0); // max head # + break; + + case 7: // 180k, 5.25" + CX = 0x2709; // 40 tracks, 9 sectors + SET_DH(0); // max head # + break; + + case 8: // 320k, 5.25" + CX = 0x2708; // 40 tracks, 8 sectors + SET_DH(1); // max head # + break; + + default: // ? + BX_PANIC("floppy: int13: bad floppy type\n"); + } + + /* set es & di to point to 11 byte diskette param table in ROM */ +ASM_START + push bp + mov bp, sp + mov ax, #diskette_param_table2 + mov _int13_diskette_function.DI+2[bp], ax + mov _int13_diskette_function.ES+2[bp], cs + pop bp +ASM_END + CLEAR_CF(); // success + /* disk status not changed upon success */ + return; + + + case 0x15: // read diskette drive type +BX_DEBUG_INT13_FL("floppy f15\n"); + drive = GET_ELDL(); + if (drive > 1) { + SET_AH(0); // only 2 drives supported + // set_diskette_ret_status here ??? + SET_CF(); + return; + } + drive_type = inb_cmos(0x10); + + if (drive == 0) + drive_type >>= 4; + else + drive_type &= 0x0f; + CLEAR_CF(); // successful, not present + if (drive_type==0) { + SET_AH(0); // drive not present + } + else { + SET_AH(1); // drive present, does not support change line + } + + return; + + case 0x16: // get diskette change line status +BX_DEBUG_INT13_FL("floppy f16\n"); + drive = GET_ELDL(); + if (drive > 1) { + SET_AH(0x01); // invalid drive + set_diskette_ret_status(0x01); + SET_CF(); + return; + } + + SET_AH(0x06); // change line not supported + set_diskette_ret_status(0x06); + SET_CF(); + return; + + case 0x17: // set diskette type for format(old) +BX_DEBUG_INT13_FL("floppy f17\n"); + /* not used for 1.44M floppies */ + SET_AH(0x01); // not supported + set_diskette_ret_status(1); /* not supported */ + SET_CF(); + return; + + case 0x18: // set diskette type for format(new) +BX_DEBUG_INT13_FL("floppy f18\n"); + SET_AH(0x01); // do later + set_diskette_ret_status(1); + SET_CF(); + return; + + default: + BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH()); + + // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) { + SET_AH(0x01); // ??? + set_diskette_ret_status(1); + SET_CF(); + return; + // } + } +} +#else // #if BX_SUPPORT_FLOPPY + void +int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) + Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; +{ + Bit8u val8; + + switch ( GET_AH() ) { + + case 0x01: // Read Diskette Status + CLEAR_CF(); + val8 = read_byte(0x0000, 0x0441); + SET_AH(val8); + if (val8) { + SET_CF(); + } + return; + + default: + SET_CF(); + write_byte(0x0000, 0x0441, 0x01); + SET_AH(0x01); + } +} +#endif // #if BX_SUPPORT_FLOPPY + + void +set_diskette_ret_status(value) + Bit8u value; +{ + write_byte(0x0040, 0x0041, value); +} + + void +set_diskette_current_cyl(drive, cyl) + Bit8u drive; + Bit8u cyl; +{ + if (drive > 1) + BX_PANIC("set_diskette_current_cyl(): drive > 1\n"); + write_byte(0x0040, 0x0094+drive, cyl); +} + + void +determine_floppy_media(drive) + Bit16u drive; +{ +#if 0 + Bit8u val8, DOR, ctrl_info; + + ctrl_info = read_byte(0x0040, 0x008F); + if (drive==1) + ctrl_info >>= 4; + else + ctrl_info &= 0x0f; + +#if 0 + if (drive == 0) { + DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0 + } + else { + DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1 + } +#endif + + if ( (ctrl_info & 0x04) != 0x04 ) { + // Drive not determined means no drive exists, done. + return; + } + +#if 0 + // check Main Status Register for readiness + val8 = inb(0x03f4) & 0x80; // Main Status Register + if (val8 != 0x80) + BX_PANIC("d_f_m: MRQ bit not set\n"); + + // change line + + // existing BDA values + + // turn on drive motor + outb(0x03f2, DOR); // Digital Output Register + // +#endif + BX_PANIC("d_f_m: OK so far\n"); +#endif +} + + void +int17_function(regs, ds, iret_addr) + pusha_regs_t regs; // regs pushed from PUSHA instruction + Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper + iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call +{ + Bit16u addr,timeout; + Bit8u val8; + + ASM_START + sti + ASM_END + + addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8); + if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) { + timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8; + if (regs.u.r8.ah == 0) { + outb(addr, regs.u.r8.al); + val8 = inb(addr+2); + outb(addr+2, val8 | 0x01); // send strobe + ASM_START + nop + ASM_END + outb(addr+2, val8 & ~0x01); + while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) { + timeout--; + } + } + if (regs.u.r8.ah == 1) { + val8 = inb(addr+2); + outb(addr+2, val8 & ~0x04); // send init + ASM_START + nop + ASM_END + outb(addr+2, val8 | 0x04); + } + val8 = inb(addr+1); + regs.u.r8.ah = (val8 ^ 0x48); + if (!timeout) regs.u.r8.ah |= 0x01; + ClearCF(iret_addr.flags); + } else { + SetCF(iret_addr.flags); // Unsupported + } +} + +void +int19_function(seq_nr) +Bit16u seq_nr; +{ + Bit16u ebda_seg=read_word(0x0040,0x000E); + Bit16u bootdev; + Bit8u bootdrv; + Bit8u bootchk; + Bit16u bootseg; + Bit16u bootip; + Bit16u status; + Bit16u bootfirst; + + ipl_entry_t e; + + // if BX_ELTORITO_BOOT is not defined, old behavior + // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL + // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:) + // 0: system boot sequence, first drive C: then A: + // 1: system boot sequence, first drive A: then C: + // else BX_ELTORITO_BOOT is defined + // CMOS regs 0x3D and 0x38 contain the boot sequence: + // CMOS reg 0x3D & 0x0f : 1st boot device + // CMOS reg 0x3D & 0xf0 : 2nd boot device + // CMOS reg 0x38 & 0xf0 : 3rd boot device + // boot device codes: + // 0x00 : not defined + // 0x01 : first floppy + // 0x02 : first harddrive + // 0x03 : first cdrom + // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot) + // else : boot failure + + // Get the boot sequence +#if BX_ELTORITO_BOOT + bootdev = inb_cmos(0x3d); + bootdev |= ((inb_cmos(0x38) & 0xf0) << 4); + bootdev >>= 4 * seq_nr; + bootdev &= 0xf; + + /* Read user selected device */ + bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET); + if (bootfirst != 0xFFFF) { + bootdev = bootfirst; + /* User selected device not set */ + write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF); + /* Reset boot sequence */ + write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF); + } else if (bootdev == 0) BX_PANIC("No bootable device.\n"); + + /* Translate from CMOS runes to an IPL table offset by subtracting 1 */ + bootdev -= 1; +#else + if (seq_nr ==2) BX_PANIC("No more boot devices."); + if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1)) + /* Boot from floppy if the bit is set or it's the second boot */ + bootdev = 0x00; + else + bootdev = 0x01; +#endif + + /* Read the boot device from the IPL table */ + if (get_boot_vector(bootdev, &e) == 0) { + BX_INFO("Invalid boot device (0x%x)\n", bootdev); + return; + } + + /* Do the loading, and set up vector as a far pointer to the boot + * address, and bootdrv as the boot drive */ + print_boot_device(&e); + + switch(e.type) { + case IPL_TYPE_FLOPPY: /* FDD */ + case IPL_TYPE_HARDDISK: /* HDD */ + + bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00; + bootseg = 0x07c0; + status = 0; + +ASM_START + push bp + mov bp, sp + push ax + push bx + push cx + push dx + + mov dl, _int19_function.bootdrv + 2[bp] + mov ax, _int19_function.bootseg + 2[bp] + mov es, ax ;; segment + xor bx, bx ;; offset + mov ah, #0x02 ;; function 2, read diskette sector + mov al, #0x01 ;; read 1 sector + mov ch, #0x00 ;; track 0 + mov cl, #0x01 ;; sector 1 + mov dh, #0x00 ;; head 0 + int #0x13 ;; read sector + jnc int19_load_done + mov ax, #0x0001 + mov _int19_function.status + 2[bp], ax + +int19_load_done: + pop dx + pop cx + pop bx + pop ax + pop bp +ASM_END + + if (status != 0) { + print_boot_failure(e.type, 1); + return; + } + + /* Always check the signature on a HDD boot sector; on FDD, only do + * the check if the CMOS doesn't tell us to skip it */ + if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) { + if (read_word(bootseg,0x1fe) != 0xaa55) { + print_boot_failure(e.type, 0); + return; + } + } + + /* Canonicalize bootseg:bootip */ + bootip = (bootseg & 0x0fff) << 4; + bootseg &= 0xf000; + break; + +#if BX_ELTORITO_BOOT + case IPL_TYPE_CDROM: /* CD-ROM */ + status = cdrom_boot(); + + // If failure + if ( (status & 0x00ff) !=0 ) { + print_cdromboot_failure(status); + print_boot_failure(e.type, 1); + return; + } + + bootdrv = (Bit8u)(status>>8); + bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment); + bootip = 0; + break; +#endif + + case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */ + bootseg = e.vector >> 16; + bootip = e.vector & 0xffff; + break; + + default: return; + } + + /* Debugging info */ + BX_INFO("Booting from %x:%x\n", bootseg, bootip); + + /* Jump to the boot vector */ +ASM_START + mov bp, sp + push cs + push #int18_handler + ;; Build an iret stack frame that will take us to the boot vector. + ;; iret pops ip, then cs, then flags, so push them in the opposite order. + pushf + mov ax, _int19_function.bootseg + 0[bp] + push ax + mov ax, _int19_function.bootip + 0[bp] + push ax + ;; Set the magic number in ax and the boot drive in dl. + mov ax, #0xaa55 + mov dl, _int19_function.bootdrv + 0[bp] + ;; Zero some of the other registers. + xor bx, bx + mov ds, bx + mov es, bx + mov bp, bx + ;; Go! + iret +ASM_END +} + + void +int1a_function(regs, ds, iret_addr) + pusha_regs_t regs; // regs pushed from PUSHA instruction + Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper + iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call +{ + Bit8u val8; + + BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n", regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds); + + ASM_START + sti + ASM_END + + switch (regs.u.r8.ah) { + case 0: // get current clock count + ASM_START + cli + ASM_END + regs.u.r16.cx = BiosData->ticks_high; + regs.u.r16.dx = BiosData->ticks_low; + regs.u.r8.al = BiosData->midnight_flag; + BiosData->midnight_flag = 0; // reset flag + ASM_START + sti + ASM_END + // AH already 0 + ClearCF(iret_addr.flags); // OK + break; + + case 1: // Set Current Clock Count + ASM_START + cli + ASM_END + BiosData->ticks_high = regs.u.r16.cx; + BiosData->ticks_low = regs.u.r16.dx; + BiosData->midnight_flag = 0; // reset flag + ASM_START + sti + ASM_END + regs.u.r8.ah = 0; + ClearCF(iret_addr.flags); // OK + break; + + + case 2: // Read CMOS Time + if (rtc_updating()) { + SetCF(iret_addr.flags); + break; + } + + regs.u.r8.dh = inb_cmos(0x00); // Seconds + regs.u.r8.cl = inb_cmos(0x02); // Minutes + regs.u.r8.ch = inb_cmos(0x04); // Hours + regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B + regs.u.r8.ah = 0; + regs.u.r8.al = regs.u.r8.ch; + ClearCF(iret_addr.flags); // OK + break; + + case 3: // Set CMOS Time + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 + // before 1111 1101 0111 1101 0000 0000 + // after 0110 0010 0110 0010 0000 0010 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = ((RegB & 01100000b) | 00000010b) + if (rtc_updating()) { + init_rtc(); + // fall through as if an update were not in progress + } + outb_cmos(0x00, regs.u.r8.dh); // Seconds + outb_cmos(0x02, regs.u.r8.cl); // Minutes + outb_cmos(0x04, regs.u.r8.ch); // Hours + // Set Daylight Savings time enabled bit to requested value + val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01); + // (reg B already selected) + outb_cmos(0x0b, val8); + regs.u.r8.ah = 0; + regs.u.r8.al = val8; // val last written to Reg B + ClearCF(iret_addr.flags); // OK + break; + + case 4: // Read CMOS Date + regs.u.r8.ah = 0; + if (rtc_updating()) { + SetCF(iret_addr.flags); + break; + } + regs.u.r8.cl = inb_cmos(0x09); // Year + regs.u.r8.dh = inb_cmos(0x08); // Month + regs.u.r8.dl = inb_cmos(0x07); // Day of Month + regs.u.r8.ch = inb_cmos(0x32); // Century + regs.u.r8.al = regs.u.r8.ch; + ClearCF(iret_addr.flags); // OK + break; + + case 5: // Set CMOS Date + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 try#4 + // before 1111 1101 0111 1101 0000 0010 0000 0000 + // after 0110 1101 0111 1101 0000 0010 0000 0000 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = (RegB & 01111111b) + if (rtc_updating()) { + init_rtc(); + SetCF(iret_addr.flags); + break; + } + outb_cmos(0x09, regs.u.r8.cl); // Year + outb_cmos(0x08, regs.u.r8.dh); // Month + outb_cmos(0x07, regs.u.r8.dl); // Day of Month + outb_cmos(0x32, regs.u.r8.ch); // Century + val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit + outb_cmos(0x0b, val8); + regs.u.r8.ah = 0; + regs.u.r8.al = val8; // AL = val last written to Reg B + ClearCF(iret_addr.flags); // OK + break; + + case 6: // Set Alarm Time in CMOS + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 + // before 1101 1111 0101 1111 0000 0000 + // after 0110 1111 0111 1111 0010 0000 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = ((RegB & 01111111b) | 00100000b) + val8 = inb_cmos(0x0b); // Get Status Reg B + regs.u.r16.ax = 0; + if (val8 & 0x20) { + // Alarm interrupt enabled already + SetCF(iret_addr.flags); // Error: alarm in use + break; + } + if (rtc_updating()) { + init_rtc(); + // fall through as if an update were not in progress + } + outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm + outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm + outb_cmos(0x05, regs.u.r8.ch); // Hours alarm + outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8 + // enable Status Reg B alarm bit, clear halt clock bit + outb_cmos(0x0b, (val8 & 0x7f) | 0x20); + ClearCF(iret_addr.flags); // OK + break; + + case 7: // Turn off Alarm + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 try#4 + // before 1111 1101 0111 1101 0010 0000 0010 0010 + // after 0100 0101 0101 0101 0000 0000 0000 0010 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = (RegB & 01010111b) + val8 = inb_cmos(0x0b); // Get Status Reg B + // clear clock-halt bit, disable alarm bit + outb_cmos(0x0b, val8 & 0x57); // disable alarm bit + regs.u.r8.ah = 0; + regs.u.r8.al = val8; // val last written to Reg B + ClearCF(iret_addr.flags); // OK + break; +#if BX_PCIBIOS + case 0xb1: + // real mode PCI BIOS functions now handled in assembler code + // this C code handles the error code for information only + if (regs.u.r8.bl == 0xff) { + BX_INFO("PCI BIOS: PCI not present\n"); + } else if (regs.u.r8.bl == 0x81) { + BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al); + } else if (regs.u.r8.bl == 0x83) { + BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx); + } else if (regs.u.r8.bl == 0x86) { + if (regs.u.r8.al == 0x02) { + BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si); + } else { + BX_INFO("no PCI device with class code 0x%02x%04x found at index %d\n", regs.u.r8.cl, regs.u.r16.dx, regs.u.r16.si); + } + } + regs.u.r8.ah = regs.u.r8.bl; + SetCF(iret_addr.flags); + break; +#endif + + default: + SetCF(iret_addr.flags); // Unsupported + } +} + + void +int70_function(regs, ds, iret_addr) + pusha_regs_t regs; // regs pushed from PUSHA instruction + Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper + iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call +{ + // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes + Bit8u registerB = 0, registerC = 0; + + // Check which modes are enabled and have occurred. + registerB = inb_cmos( 0xB ); + registerC = inb_cmos( 0xC ); + + if( ( registerB & 0x60 ) != 0 ) { + if( ( registerC & 0x20 ) != 0 ) { + // Handle Alarm Interrupt. +ASM_START + sti + int #0x4a + cli +ASM_END + } + if( ( registerC & 0x40 ) != 0 ) { + // Handle Periodic Interrupt. + + if( read_byte( 0x40, 0xA0 ) != 0 ) { + // Wait Interval (Int 15, AH=83) active. + Bit32u time, toggle; + + time = read_dword( 0x40, 0x9C ); // Time left in microseconds. + if( time < 0x3D1 ) { + // Done waiting. + Bit16u segment, offset; + + segment = read_word( 0x40, 0x98 ); + offset = read_word( 0x40, 0x9A ); + write_byte( 0x40, 0xA0, 0 ); // Turn of status byte. + outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt. + write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte. + } else { + // Continue waiting. + time -= 0x3D1; + write_dword( 0x40, 0x9C, time ); + } + } + } + } + +ASM_START + call eoi_both_pics +ASM_END +} + + +ASM_START +;------------------------------------------ +;- INT74h : PS/2 mouse hardware interrupt - +;------------------------------------------ +int74_handler: + sti + pusha + push ds ;; save DS + push #0x00 ;; placeholder for status + push #0x00 ;; placeholder for X + push #0x00 ;; placeholder for Y + push #0x00 ;; placeholder for Z + push #0x00 ;; placeholder for make_far_call boolean + call _int74_function + pop cx ;; remove make_far_call from stack + jcxz int74_done + + ;; make far call to EBDA:0022 + push #0x00 + pop ds + push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04) + pop ds + //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00) + call far ptr[0x22] +int74_done: + cli + call eoi_both_pics + add sp, #8 ;; pop status, x, y, z + + pop ds ;; restore DS + popa + iret + + +;; This will perform an IRET, but will retain value of current CF +;; by altering flags on stack. Better than RETF #02. +iret_modify_cf: + jc carry_set + push bp + mov bp, sp + and BYTE [bp + 0x06], #0xfe + pop bp + iret +carry_set: + push bp + mov bp, sp + or BYTE [bp + 0x06], #0x01 + pop bp + iret + + +;---------------------- +;- INT13h (relocated) - +;---------------------- +; +; int13_relocated is a little bit messed up since I played with it +; I have to rewrite it: +; - call a function that detect which function to call +; - make all called C function get the same parameters list +; +int13_relocated: + +#if BX_ELTORITO_BOOT + ;; check for an eltorito function + cmp ah,#0x4a + jb int13_not_eltorito + cmp ah,#0x4d + ja int13_not_eltorito + + pusha + push es + push ds + push ss + pop ds + + push #int13_out + jmp _int13_eltorito ;; ELDX not used + +int13_not_eltorito: + push ax + push bx + push cx + push dx + + ;; check if emulation active + call _cdemu_isactive + cmp al,#0x00 + je int13_cdemu_inactive + + ;; check if access to the emulated drive + call _cdemu_emulated_drive + pop dx + push dx + cmp al,dl ;; int13 on emulated drive + jne int13_nocdemu + + pop dx + pop cx + pop bx + pop ax + + pusha + push es + push ds + push ss + pop ds + + push #int13_out + jmp _int13_cdemu ;; ELDX not used + +int13_nocdemu: + and dl,#0xE0 ;; mask to get device class, including cdroms + cmp al,dl ;; al is 0x00 or 0x80 + jne int13_cdemu_inactive ;; inactive for device class + + pop dx + pop cx + pop bx + pop ax + + push ax + push cx + push dx + push bx + + dec dl ;; real drive is dl - 1 + jmp int13_legacy + +int13_cdemu_inactive: + pop dx + pop cx + pop bx + pop ax + +#endif // BX_ELTORITO_BOOT + +int13_noeltorito: + + push ax + push cx + push dx + push bx + +int13_legacy: + + push dx ;; push eltorito value of dx instead of sp + + push bp + push si + push di + + push es + push ds + push ss + pop ds + + ;; now the 16-bit registers can be restored with: + ;; pop ds; pop es; popa; iret + ;; arguments passed to functions should be + ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS + + test dl, #0x80 + jnz int13_notfloppy + + push #int13_out + jmp _int13_diskette_function + +int13_notfloppy: + +#if BX_USE_ATADRV + + cmp dl, #0xE0 + jb int13_notcdrom + + // ebx is modified: BSD 5.2.1 boot loader problem + // someone should figure out which 32 bit register that actually are used + + shr ebx, #16 + push bx + + call _int13_cdrom + + pop bx + shl ebx, #16 + + jmp int13_out + +int13_notcdrom: + +#endif + +int13_disk: + ;; int13_harddisk modifies high word of EAX + shr eax, #16 + push ax + call _int13_harddisk + pop ax + shl eax, #16 + +int13_out: + pop ds + pop es + popa + iret + +;---------- +;- INT18h - +;---------- +int18_handler: ;; Boot Failure recovery: try the next device. + + ;; Reset SP and SS + mov ax, #0xfffe + mov sp, ax + xor ax, ax + mov ss, ax + + ;; Get the boot sequence number out of the IPL memory + mov bx, #IPL_SEG + mov ds, bx ;; Set segment + mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number + inc bx ;; ++ + mov IPL_SEQUENCE_OFFSET, bx ;; Write it back + mov ds, ax ;; and reset the segment to zero. + + ;; Carry on in the INT 19h handler, using the new sequence number + push bx + + jmp int19_next_boot + +;---------- +;- INT19h - +;---------- +int19_relocated: ;; Boot function, relocated + + ;; int19 was beginning to be really complex, so now it + ;; just calls a C function that does the work + + push bp + mov bp, sp + + ;; Reset SS and SP + mov ax, #0xfffe + mov sp, ax + xor ax, ax + mov ss, ax + + ;; Start from the first boot device (0, in AX) + mov bx, #IPL_SEG + mov ds, bx ;; Set segment to write to the IPL memory + mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number + mov ds, ax ;; and reset the segment. + + push ax + +int19_next_boot: + + ;; Call the C code for the next boot device + call _int19_function + + ;; Boot failed: invoke the boot recovery function + int #0x18 + +;---------- +;- INT1Ch - +;---------- +int1c_handler: ;; User Timer Tick + iret + + +;---------------------- +;- POST: Floppy Drive - +;---------------------- +floppy_drive_post: + xor ax, ax + mov ds, ax + + mov al, #0x00 + mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred + + mov 0x043f, al ;; diskette motor status: read op, drive0, motors off + + mov 0x0440, al ;; diskette motor timeout counter: not active + mov 0x0441, al ;; diskette controller status return code + + mov 0x0442, al ;; disk & diskette controller status register 0 + mov 0x0443, al ;; diskette controller status register 1 + mov 0x0444, al ;; diskette controller status register 2 + mov 0x0445, al ;; diskette controller cylinder number + mov 0x0446, al ;; diskette controller head number + mov 0x0447, al ;; diskette controller sector number + mov 0x0448, al ;; diskette controller bytes written + + mov 0x048b, al ;; diskette configuration data + + ;; ----------------------------------------------------------------- + ;; (048F) diskette controller information + ;; + mov al, #0x10 ;; get CMOS diskette drive type + out 0x70, AL + in AL, 0x71 + mov ah, al ;; save byte to AH + +look_drive0: + shr al, #4 ;; look at top 4 bits for drive 0 + jz f0_missing ;; jump if no drive0 + mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line + jmp look_drive1 +f0_missing: + mov bl, #0x00 ;; no drive0 + +look_drive1: + mov al, ah ;; restore from AH + and al, #0x0f ;; look at bottom 4 bits for drive 1 + jz f1_missing ;; jump if no drive1 + or bl, #0x70 ;; drive1 determined, multi-rate, has changed line +f1_missing: + ;; leave high bits in BL zerod + mov 0x048f, bl ;; put new val in BDA (diskette controller information) + ;; ----------------------------------------------------------------- + + mov al, #0x00 + mov 0x0490, al ;; diskette 0 media state + mov 0x0491, al ;; diskette 1 media state + + ;; diskette 0,1 operational starting state + ;; drive type has not been determined, + ;; has no changed detection line + mov 0x0492, al + mov 0x0493, al + + mov 0x0494, al ;; diskette 0 current cylinder + mov 0x0495, al ;; diskette 1 current cylinder + + mov al, #0x02 + out #0x0a, al ;; clear DMA-1 channel 2 mask bit + + SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2) + SET_INT_VECTOR(0x40, #0xF000, #int13_diskette) + SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6 + + ret + + +;-------------------- +;- POST: HARD DRIVE - +;-------------------- +; relocated here because the primary POST area isnt big enough. +hard_drive_post: + // IRQ 14 = INT 76h + // INT 76h calls INT 15h function ax=9100 + + mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14 + mov dx, #0x03f6 + out dx, al + + xor ax, ax + mov ds, ax + mov 0x0474, al /* hard disk status of last operation */ + mov 0x0477, al /* hard disk port offset (XT only ???) */ + mov 0x048c, al /* hard disk status register */ + mov 0x048d, al /* hard disk error register */ + mov 0x048e, al /* hard disk task complete flag */ + mov al, #0x01 + mov 0x0475, al /* hard disk number attached */ + mov al, #0xc0 + mov 0x0476, al /* hard disk control byte */ + SET_INT_VECTOR(0x13, #0xF000, #int13_handler) + SET_INT_VECTOR(0x76, #0xF000, #int76_handler) + ;; INT 41h: hard disk 0 configuration pointer + ;; INT 46h: hard disk 1 configuration pointer + SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D) + SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D) + + ;; move disk geometry data from CMOS to EBDA disk parameter table(s) + mov al, #0x12 + out #0x70, al + in al, #0x71 + and al, #0xf0 + cmp al, #0xf0 + je post_d0_extended + jmp check_for_hd1 +post_d0_extended: + mov al, #0x19 + out #0x70, al + in al, #0x71 + cmp al, #47 ;; decimal 47 - user definable + je post_d0_type47 + HALT(__LINE__) +post_d0_type47: + ;; CMOS purpose param table offset + ;; 1b cylinders low 0 + ;; 1c cylinders high 1 + ;; 1d heads 2 + ;; 1e write pre-comp low 5 + ;; 1f write pre-comp high 6 + ;; 20 retries/bad map/heads>8 8 + ;; 21 landing zone low C + ;; 22 landing zone high D + ;; 23 sectors/track E + + mov ax, #EBDA_SEG + mov ds, ax + + ;;; Filling EBDA table for hard disk 0. + mov al, #0x1f + out #0x70, al + in al, #0x71 + mov ah, al + mov al, #0x1e + out #0x70, al + in al, #0x71 + mov (0x003d + 0x05), ax ;; write precomp word + + mov al, #0x20 + out #0x70, al + in al, #0x71 + mov (0x003d + 0x08), al ;; drive control byte + + mov al, #0x22 + out #0x70, al + in al, #0x71 + mov ah, al + mov al, #0x21 + out #0x70, al + in al, #0x71 + mov (0x003d + 0x0C), ax ;; landing zone word + + mov al, #0x1c ;; get cylinders word in AX + out #0x70, al + in al, #0x71 ;; high byte + mov ah, al + mov al, #0x1b + out #0x70, al + in al, #0x71 ;; low byte + mov bx, ax ;; BX = cylinders + + mov al, #0x1d + out #0x70, al + in al, #0x71 + mov cl, al ;; CL = heads + + mov al, #0x23 + out #0x70, al + in al, #0x71 + mov dl, al ;; DL = sectors + + cmp bx, #1024 + jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS + +hd0_post_physical_chs: + ;; no logical CHS mapping used, just physical CHS + ;; use Standard Fixed Disk Parameter Table (FDPT) + mov (0x003d + 0x00), bx ;; number of physical cylinders + mov (0x003d + 0x02), cl ;; number of physical heads + mov (0x003d + 0x0E), dl ;; number of physical sectors + jmp check_for_hd1 + +hd0_post_logical_chs: + ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT) + mov (0x003d + 0x09), bx ;; number of physical cylinders + mov (0x003d + 0x0b), cl ;; number of physical heads + mov (0x003d + 0x04), dl ;; number of physical sectors + mov (0x003d + 0x0e), dl ;; number of logical sectors (same) + mov al, #0xa0 + mov (0x003d + 0x03), al ;; A0h signature, indicates translated table + + cmp bx, #2048 + jnbe hd0_post_above_2048 + ;; 1024 < c <= 2048 cylinders + shr bx, #0x01 + shl cl, #0x01 + jmp hd0_post_store_logical + +hd0_post_above_2048: + cmp bx, #4096 + jnbe hd0_post_above_4096 + ;; 2048 < c <= 4096 cylinders + shr bx, #0x02 + shl cl, #0x02 + jmp hd0_post_store_logical + +hd0_post_above_4096: + cmp bx, #8192 + jnbe hd0_post_above_8192 + ;; 4096 < c <= 8192 cylinders + shr bx, #0x03 + shl cl, #0x03 + jmp hd0_post_store_logical + +hd0_post_above_8192: + ;; 8192 < c <= 16384 cylinders + shr bx, #0x04 + shl cl, #0x04 + +hd0_post_store_logical: + mov (0x003d + 0x00), bx ;; number of physical cylinders + mov (0x003d + 0x02), cl ;; number of physical heads + ;; checksum + mov cl, #0x0f ;; repeat count + mov si, #0x003d ;; offset to disk0 FDPT + mov al, #0x00 ;; sum +hd0_post_checksum_loop: + add al, [si] + inc si + dec cl + jnz hd0_post_checksum_loop + not al ;; now take 2s complement + inc al + mov [si], al +;;; Done filling EBDA table for hard disk 0. + + +check_for_hd1: + ;; is there really a second hard disk? if not, return now + mov al, #0x12 + out #0x70, al + in al, #0x71 + and al, #0x0f + jnz post_d1_exists + ret +post_d1_exists: + ;; check that the hd type is really 0x0f. + cmp al, #0x0f + jz post_d1_extended + HALT(__LINE__) +post_d1_extended: + ;; check that the extended type is 47 - user definable + mov al, #0x1a + out #0x70, al + in al, #0x71 + cmp al, #47 ;; decimal 47 - user definable + je post_d1_type47 + HALT(__LINE__) +post_d1_type47: + ;; Table for disk1. + ;; CMOS purpose param table offset + ;; 0x24 cylinders low 0 + ;; 0x25 cylinders high 1 + ;; 0x26 heads 2 + ;; 0x27 write pre-comp low 5 + ;; 0x28 write pre-comp high 6 + ;; 0x29 heads>8 8 + ;; 0x2a landing zone low C + ;; 0x2b landing zone high D + ;; 0x2c sectors/track E +;;; Fill EBDA table for hard disk 1. + mov ax, #EBDA_SEG + mov ds, ax + mov al, #0x28 + out #0x70, al + in al, #0x71 + mov ah, al + mov al, #0x27 + out #0x70, al + in al, #0x71 + mov (0x004d + 0x05), ax ;; write precomp word + + mov al, #0x29 + out #0x70, al + in al, #0x71 + mov (0x004d + 0x08), al ;; drive control byte + + mov al, #0x2b + out #0x70, al + in al, #0x71 + mov ah, al + mov al, #0x2a + out #0x70, al + in al, #0x71 + mov (0x004d + 0x0C), ax ;; landing zone word + + mov al, #0x25 ;; get cylinders word in AX + out #0x70, al + in al, #0x71 ;; high byte + mov ah, al + mov al, #0x24 + out #0x70, al + in al, #0x71 ;; low byte + mov bx, ax ;; BX = cylinders + + mov al, #0x26 + out #0x70, al + in al, #0x71 + mov cl, al ;; CL = heads + + mov al, #0x2c + out #0x70, al + in al, #0x71 + mov dl, al ;; DL = sectors + + cmp bx, #1024 + jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS + +hd1_post_physical_chs: + ;; no logical CHS mapping used, just physical CHS + ;; use Standard Fixed Disk Parameter Table (FDPT) + mov (0x004d + 0x00), bx ;; number of physical cylinders + mov (0x004d + 0x02), cl ;; number of physical heads + mov (0x004d + 0x0E), dl ;; number of physical sectors + ret + +hd1_post_logical_chs: + ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT) + mov (0x004d + 0x09), bx ;; number of physical cylinders + mov (0x004d + 0x0b), cl ;; number of physical heads + mov (0x004d + 0x04), dl ;; number of physical sectors + mov (0x004d + 0x0e), dl ;; number of logical sectors (same) + mov al, #0xa0 + mov (0x004d + 0x03), al ;; A0h signature, indicates translated table + + cmp bx, #2048 + jnbe hd1_post_above_2048 + ;; 1024 < c <= 2048 cylinders + shr bx, #0x01 + shl cl, #0x01 + jmp hd1_post_store_logical + +hd1_post_above_2048: + cmp bx, #4096 + jnbe hd1_post_above_4096 + ;; 2048 < c <= 4096 cylinders + shr bx, #0x02 + shl cl, #0x02 + jmp hd1_post_store_logical + +hd1_post_above_4096: + cmp bx, #8192 + jnbe hd1_post_above_8192 + ;; 4096 < c <= 8192 cylinders + shr bx, #0x03 + shl cl, #0x03 + jmp hd1_post_store_logical + +hd1_post_above_8192: + ;; 8192 < c <= 16384 cylinders + shr bx, #0x04 + shl cl, #0x04 + +hd1_post_store_logical: + mov (0x004d + 0x00), bx ;; number of physical cylinders + mov (0x004d + 0x02), cl ;; number of physical heads + ;; checksum + mov cl, #0x0f ;; repeat count + mov si, #0x004d ;; offset to disk0 FDPT + mov al, #0x00 ;; sum +hd1_post_checksum_loop: + add al, [si] + inc si + dec cl + jnz hd1_post_checksum_loop + not al ;; now take 2s complement + inc al + mov [si], al +;;; Done filling EBDA table for hard disk 1. + + ret + +;-------------------- +;- POST: EBDA segment +;-------------------- +; relocated here because the primary POST area isnt big enough. +ebda_post: +#if BX_USE_EBDA + mov ax, #EBDA_SEG + mov ds, ax + mov byte ptr [0x0], #EBDA_SIZE +#endif + xor ax, ax ; mov EBDA seg into 40E + mov ds, ax + mov word ptr [0x40E], #EBDA_SEG + ret;; + +;-------------------- +;- POST: EOI + jmp via [0x40:67) +;-------------------- +; relocated here because the primary POST area isnt big enough. +eoi_jmp_post: + mov al, #0x20 + out #0xA0, al ;; slave PIC EOI + mov al, #0x20 + out #0x20, al ;; master PIC EOI + +jmp_post_0x467: + xor ax, ax + mov ds, ax + + jmp far ptr [0x467] + +iret_post_0x467: + xor ax, ax + mov ds, ax + + mov sp, [0x467] + mov ss, [0x469] + iret + +retf_post_0x467: + xor ax, ax + mov ds, ax + + mov sp, [0x467] + mov ss, [0x469] + retf + +s3_post: + mov sp, #0xffe +#if BX_ROMBIOS32 + call rombios32_init +#endif + call _s3_resume + mov bl, #0x00 + and ax, ax + jz normal_post + call _s3_resume_panic + +;-------------------- +eoi_both_pics: + mov al, #0x20 + out #0xA0, al ;; slave PIC EOI +eoi_master_pic: + mov al, #0x20 + out #0x20, al ;; master PIC EOI + ret + +;-------------------- +BcdToBin: + ;; in: AL in BCD format + ;; out: AL in binary format, AH will always be 0 + ;; trashes BX + mov bl, al + and bl, #0x0f ;; bl has low digit + shr al, #4 ;; al has high digit + mov bh, #10 + mul al, bh ;; multiply high digit by 10 (result in AX) + add al, bl ;; then add low digit + ret + +;-------------------- +timer_tick_post: + ;; Setup the Timer Ticks Count (0x46C:dword) and + ;; Timer Ticks Roller Flag (0x470:byte) + ;; The Timer Ticks Count needs to be set according to + ;; the current CMOS time, as if ticks have been occurring + ;; at 18.2hz since midnight up to this point. Calculating + ;; this is a little complicated. Here are the factors I gather + ;; regarding this. 14,318,180 hz was the original clock speed, + ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU + ;; at the time, or 4 to drive the CGA video adapter. The div3 + ;; source was divided again by 4 to feed a 1.193Mhz signal to + ;; the timer. With a maximum 16bit timer count, this is again + ;; divided down by 65536 to 18.2hz. + ;; + ;; 14,318,180 Hz clock + ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU + ;; /4 = 1,193,181 Hz fed to timer + ;; /65536 (maximum timer count) = 18.20650736 ticks/second + ;; 1 second = 18.20650736 ticks + ;; 1 minute = 1092.390442 ticks + ;; 1 hour = 65543.42651 ticks + ;; + ;; Given the values in the CMOS clock, one could calculate + ;; the number of ticks by the following: + ;; ticks = (BcdToBin(seconds) * 18.206507) + + ;; (BcdToBin(minutes) * 1092.3904) + ;; (BcdToBin(hours) * 65543.427) + ;; To get a little more accuracy, since Im using integer + ;; arithmatic, I use: + ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 + + ;; (BcdToBin(minutes) * 10923904) / 10000 + + ;; (BcdToBin(hours) * 65543427) / 1000 + + ;; assuming DS=0000 + + ;; get CMOS seconds + xor eax, eax ;; clear EAX + mov al, #0x00 + out #0x70, al + in al, #0x71 ;; AL has CMOS seconds in BCD + call BcdToBin ;; EAX now has seconds in binary + mov edx, #18206507 + mul eax, edx + mov ebx, #1000000 + xor edx, edx + div eax, ebx + mov ecx, eax ;; ECX will accumulate total ticks + + ;; get CMOS minutes + xor eax, eax ;; clear EAX + mov al, #0x02 + out #0x70, al + in al, #0x71 ;; AL has CMOS minutes in BCD + call BcdToBin ;; EAX now has minutes in binary + mov edx, #10923904 + mul eax, edx + mov ebx, #10000 + xor edx, edx + div eax, ebx + add ecx, eax ;; add to total ticks + + ;; get CMOS hours + xor eax, eax ;; clear EAX + mov al, #0x04 + out #0x70, al + in al, #0x71 ;; AL has CMOS hours in BCD + call BcdToBin ;; EAX now has hours in binary + mov edx, #65543427 + mul eax, edx + mov ebx, #1000 + xor edx, edx + div eax, ebx + add ecx, eax ;; add to total ticks + + mov 0x46C, ecx ;; Timer Ticks Count + xor al, al + mov 0x470, al ;; Timer Ticks Rollover Flag + ret + +;-------------------- +int76_handler: + ;; record completion in BIOS task complete flag + push ax + push ds + mov ax, #0x0040 + mov ds, ax + mov 0x008E, #0xff + call eoi_both_pics + pop ds + pop ax + iret + + +;-------------------- +#if BX_APM + +use32 386 +#define APM_PROT32 +#include "apmbios.S" + +use16 386 +#define APM_PROT16 +#include "apmbios.S" + +#define APM_REAL +#include "apmbios.S" + +#endif + +;-------------------- +#if BX_PCIBIOS +use32 386 +.align 16 +bios32_structure: + db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature + dw bios32_entry_point, 0xf ;; 32 bit physical address + db 0 ;; revision level + ;; length in paragraphs and checksum stored in a word to prevent errors + dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \ + & 0xff) << 8) + 0x01 + db 0,0,0,0,0 ;; reserved + +.align 16 +bios32_entry_point: + pushfd + cmp eax, #0x49435024 ;; "$PCI" + jne unknown_service + mov eax, #0x80000000 + mov dx, #0x0cf8 + out dx, eax + mov dx, #0x0cfc + in eax, dx +#ifdef PCI_FIXED_HOST_BRIDGE + cmp eax, #PCI_FIXED_HOST_BRIDGE + jne unknown_service +#else + ;; say ok if a device is present + cmp eax, #0xffffffff + je unknown_service +#endif + mov ebx, #0x000f0000 + mov ecx, #0 + mov edx, #pcibios_protected + xor al, al + jmp bios32_end +unknown_service: + mov al, #0x80 +bios32_end: +#ifdef BX_QEMU + and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu +#endif + popfd + retf + +.align 16 +pcibios_protected: + pushfd + cli + push esi + push edi + cmp al, #0x01 ;; installation check + jne pci_pro_f02 + mov bx, #0x0210 + mov cx, #0 + mov edx, #0x20494350 ;; "PCI " + mov al, #0x01 + jmp pci_pro_ok +pci_pro_f02: ;; find pci device + cmp al, #0x02 + jne pci_pro_f03 + shl ecx, #16 + mov cx, dx + xor bx, bx + mov di, #0x00 +pci_pro_devloop: + call pci_pro_select_reg + mov dx, #0x0cfc + in eax, dx + cmp eax, ecx + jne pci_pro_nextdev + cmp si, #0 + je pci_pro_ok + dec si +pci_pro_nextdev: + inc bx + cmp bx, #0x0100 + jne pci_pro_devloop + mov ah, #0x86 + jmp pci_pro_fail +pci_pro_f03: ;; find class code + cmp al, #0x03 + jne pci_pro_f08 + xor bx, bx + mov di, #0x08 +pci_pro_devloop2: + call pci_pro_select_reg + mov dx, #0x0cfc + in eax, dx + shr eax, #8 + cmp eax, ecx + jne pci_pro_nextdev2 + cmp si, #0 + je pci_pro_ok + dec si +pci_pro_nextdev2: + inc bx + cmp bx, #0x0100 + jne pci_pro_devloop2 + mov ah, #0x86 + jmp pci_pro_fail +pci_pro_f08: ;; read configuration byte + cmp al, #0x08 + jne pci_pro_f09 + call pci_pro_select_reg + push edx + mov dx, di + and dx, #0x03 + add dx, #0x0cfc + in al, dx + pop edx + mov cl, al + jmp pci_pro_ok +pci_pro_f09: ;; read configuration word + cmp al, #0x09 + jne pci_pro_f0a + call pci_pro_select_reg + push edx + mov dx, di + and dx, #0x02 + add dx, #0x0cfc + in ax, dx + pop edx + mov cx, ax + jmp pci_pro_ok +pci_pro_f0a: ;; read configuration dword + cmp al, #0x0a + jne pci_pro_f0b + call pci_pro_select_reg + push edx + mov dx, #0x0cfc + in eax, dx + pop edx + mov ecx, eax + jmp pci_pro_ok +pci_pro_f0b: ;; write configuration byte + cmp al, #0x0b + jne pci_pro_f0c + call pci_pro_select_reg + push edx + mov dx, di + and dx, #0x03 + add dx, #0x0cfc + mov al, cl + out dx, al + pop edx + jmp pci_pro_ok +pci_pro_f0c: ;; write configuration word + cmp al, #0x0c + jne pci_pro_f0d + call pci_pro_select_reg + push edx + mov dx, di + and dx, #0x02 + add dx, #0x0cfc + mov ax, cx + out dx, ax + pop edx + jmp pci_pro_ok +pci_pro_f0d: ;; write configuration dword + cmp al, #0x0d + jne pci_pro_unknown + call pci_pro_select_reg + push edx + mov dx, #0x0cfc + mov eax, ecx + out dx, eax + pop edx + jmp pci_pro_ok +pci_pro_unknown: + mov ah, #0x81 +pci_pro_fail: + pop edi + pop esi +#ifdef BX_QEMU + and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu +#endif + popfd + stc + retf +pci_pro_ok: + xor ah, ah + pop edi + pop esi +#ifdef BX_QEMU + and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu +#endif + popfd + clc + retf + +pci_pro_select_reg: + push edx + mov eax, #0x800000 + mov ax, bx + shl eax, #8 + and di, #0xff + or ax, di + and al, #0xfc + mov dx, #0x0cf8 + out dx, eax + pop edx + ret + +use16 386 + +pcibios_real: + push eax + push dx + mov eax, #0x80000000 + mov dx, #0x0cf8 + out dx, eax + mov dx, #0x0cfc + in eax, dx +#ifdef PCI_FIXED_HOST_BRIDGE + cmp eax, #PCI_FIXED_HOST_BRIDGE + je pci_present +#else + ;; say ok if a device is present + cmp eax, #0xffffffff + jne pci_present +#endif + pop dx + pop eax + mov ah, #0xff + stc + ret +pci_present: + pop dx + pop eax + cmp al, #0x01 ;; installation check + jne pci_real_f02 + mov ax, #0x0001 + mov bx, #0x0210 + mov cx, #0 + mov edx, #0x20494350 ;; "PCI " + mov edi, #0xf0000 + mov di, #pcibios_protected + clc + ret +pci_real_f02: ;; find pci device + push esi + push edi + cmp al, #0x02 + jne pci_real_f03 + shl ecx, #16 + mov cx, dx + xor bx, bx + mov di, #0x00 +pci_real_devloop: + call pci_real_select_reg + mov dx, #0x0cfc + in eax, dx + cmp eax, ecx + jne pci_real_nextdev + cmp si, #0 + je pci_real_ok + dec si +pci_real_nextdev: + inc bx + cmp bx, #0x0100 + jne pci_real_devloop + mov dx, cx + shr ecx, #16 + mov ax, #0x8602 + jmp pci_real_fail +pci_real_f03: ;; find class code + cmp al, #0x03 + jne pci_real_f08 + xor bx, bx + mov di, #0x08 +pci_real_devloop2: + call pci_real_select_reg + mov dx, #0x0cfc + in eax, dx + shr eax, #8 + cmp eax, ecx + jne pci_real_nextdev2 + cmp si, #0 + je pci_real_ok + dec si +pci_real_nextdev2: + inc bx + cmp bx, #0x0100 + jne pci_real_devloop2 + mov dx, cx + shr ecx, #16 + mov ax, #0x8603 + jmp pci_real_fail +pci_real_f08: ;; read configuration byte + cmp al, #0x08 + jne pci_real_f09 + call pci_real_select_reg + push dx + mov dx, di + and dx, #0x03 + add dx, #0x0cfc + in al, dx + pop dx + mov cl, al + jmp pci_real_ok +pci_real_f09: ;; read configuration word + cmp al, #0x09 + jne pci_real_f0a + call pci_real_select_reg + push dx + mov dx, di + and dx, #0x02 + add dx, #0x0cfc + in ax, dx + pop dx + mov cx, ax + jmp pci_real_ok +pci_real_f0a: ;; read configuration dword + cmp al, #0x0a + jne pci_real_f0b + call pci_real_select_reg + push dx + mov dx, #0x0cfc + in eax, dx + pop dx + mov ecx, eax + jmp pci_real_ok +pci_real_f0b: ;; write configuration byte + cmp al, #0x0b + jne pci_real_f0c + call pci_real_select_reg + push dx + mov dx, di + and dx, #0x03 + add dx, #0x0cfc + mov al, cl + out dx, al + pop dx + jmp pci_real_ok +pci_real_f0c: ;; write configuration word + cmp al, #0x0c + jne pci_real_f0d + call pci_real_select_reg + push dx + mov dx, di + and dx, #0x02 + add dx, #0x0cfc + mov ax, cx + out dx, ax + pop dx + jmp pci_real_ok +pci_real_f0d: ;; write configuration dword + cmp al, #0x0d + jne pci_real_f0e + call pci_real_select_reg + push dx + mov dx, #0x0cfc + mov eax, ecx + out dx, eax + pop dx + jmp pci_real_ok +pci_real_f0e: ;; get irq routing options + cmp al, #0x0e + jne pci_real_unknown + SEG ES + cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start + jb pci_real_too_small + SEG ES + mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start + pushf + push ds + push es + push cx + push si + push di + cld + mov si, #pci_routing_table_structure_start + push cs + pop ds + SEG ES + mov cx, [di+2] + SEG ES + mov es, [di+4] + mov di, cx + mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start + rep + movsb + pop di + pop si + pop cx + pop es + pop ds + popf + mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used + jmp pci_real_ok +pci_real_too_small: + SEG ES + mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start + mov ah, #0x89 + jmp pci_real_fail + +pci_real_unknown: + mov ah, #0x81 +pci_real_fail: + pop edi + pop esi + stc + ret +pci_real_ok: + xor ah, ah + pop edi + pop esi + clc + ret + +pci_real_select_reg: + push dx + mov eax, #0x800000 + mov ax, bx + shl eax, #8 + and di, #0xff + or ax, di + and al, #0xfc + mov dx, #0x0cf8 + out dx, eax + pop dx + ret + +.align 16 +pci_routing_table_structure: + db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature + db 0, 1 ;; version + dw 32 + (6 * 16) ;; table size + db 0 ;; PCI interrupt router bus + db 0x08 ;; PCI interrupt router DevFunc + dw 0x0000 ;; PCI exclusive IRQs + dw 0x8086 ;; compatible PCI interrupt router vendor ID + dw 0x122e ;; compatible PCI interrupt router device ID + dw 0,0 ;; Miniport data + db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved + db 0x37 ;; checksum +pci_routing_table_structure_start: + ;; first slot entry PCI-to-ISA (embedded) + db 0 ;; pci bus number + db 0x08 ;; pci device number (bit 7-3) + db 0x60 ;; link value INTA#: pointer into PCI2ISA config space + dw 0xdef8 ;; IRQ bitmap INTA# + db 0x61 ;; link value INTB# + dw 0xdef8 ;; IRQ bitmap INTB# + db 0x62 ;; link value INTC# + dw 0xdef8 ;; IRQ bitmap INTC# + db 0x63 ;; link value INTD# + dw 0xdef8 ;; IRQ bitmap INTD# + db 0 ;; physical slot (0 = embedded) + db 0 ;; reserved + ;; second slot entry: 1st PCI slot + db 0 ;; pci bus number + db 0x10 ;; pci device number (bit 7-3) + db 0x61 ;; link value INTA# + dw 0xdef8 ;; IRQ bitmap INTA# + db 0x62 ;; link value INTB# + dw 0xdef8 ;; IRQ bitmap INTB# + db 0x63 ;; link value INTC# + dw 0xdef8 ;; IRQ bitmap INTC# + db 0x60 ;; link value INTD# + dw 0xdef8 ;; IRQ bitmap INTD# + db 1 ;; physical slot (0 = embedded) + db 0 ;; reserved + ;; third slot entry: 2nd PCI slot + db 0 ;; pci bus number + db 0x18 ;; pci device number (bit 7-3) + db 0x62 ;; link value INTA# + dw 0xdef8 ;; IRQ bitmap INTA# + db 0x63 ;; link value INTB# + dw 0xdef8 ;; IRQ bitmap INTB# + db 0x60 ;; link value INTC# + dw 0xdef8 ;; IRQ bitmap INTC# + db 0x61 ;; link value INTD# + dw 0xdef8 ;; IRQ bitmap INTD# + db 2 ;; physical slot (0 = embedded) + db 0 ;; reserved + ;; 4th slot entry: 3rd PCI slot + db 0 ;; pci bus number + db 0x20 ;; pci device number (bit 7-3) + db 0x63 ;; link value INTA# + dw 0xdef8 ;; IRQ bitmap INTA# + db 0x60 ;; link value INTB# + dw 0xdef8 ;; IRQ bitmap INTB# + db 0x61 ;; link value INTC# + dw 0xdef8 ;; IRQ bitmap INTC# + db 0x62 ;; link value INTD# + dw 0xdef8 ;; IRQ bitmap INTD# + db 3 ;; physical slot (0 = embedded) + db 0 ;; reserved + ;; 5th slot entry: 4rd PCI slot + db 0 ;; pci bus number + db 0x28 ;; pci device number (bit 7-3) + db 0x60 ;; link value INTA# + dw 0xdef8 ;; IRQ bitmap INTA# + db 0x61 ;; link value INTB# + dw 0xdef8 ;; IRQ bitmap INTB# + db 0x62 ;; link value INTC# + dw 0xdef8 ;; IRQ bitmap INTC# + db 0x63 ;; link value INTD# + dw 0xdef8 ;; IRQ bitmap INTD# + db 4 ;; physical slot (0 = embedded) + db 0 ;; reserved + ;; 6th slot entry: 5rd PCI slot + db 0 ;; pci bus number + db 0x30 ;; pci device number (bit 7-3) + db 0x61 ;; link value INTA# + dw 0xdef8 ;; IRQ bitmap INTA# + db 0x62 ;; link value INTB# + dw 0xdef8 ;; IRQ bitmap INTB# + db 0x63 ;; link value INTC# + dw 0xdef8 ;; IRQ bitmap INTC# + db 0x60 ;; link value INTD# + dw 0xdef8 ;; IRQ bitmap INTD# + db 5 ;; physical slot (0 = embedded) + db 0 ;; reserved +pci_routing_table_structure_end: + +#if !BX_ROMBIOS32 +pci_irq_list: + db 11, 10, 9, 5; + +pcibios_init_sel_reg: + push eax + mov eax, #0x800000 + mov ax, bx + shl eax, #8 + and dl, #0xfc + or al, dl + mov dx, #0x0cf8 + out dx, eax + pop eax + ret + +pcibios_init_iomem_bases: + push bp + mov bp, sp + mov eax, #0xe0000000 ;; base for memory init + push eax + mov ax, #0xc000 ;; base for i/o init + push ax + mov ax, #0x0010 ;; start at base address #0 + push ax + mov bx, #0x0008 +pci_init_io_loop1: + mov dl, #0x00 + call pcibios_init_sel_reg + mov dx, #0x0cfc + in ax, dx + cmp ax, #0xffff + jz next_pci_dev + mov dl, #0x04 ;; disable i/o and memory space access + call pcibios_init_sel_reg + mov dx, #0x0cfc + in al, dx + and al, #0xfc + out dx, al +pci_init_io_loop2: + mov dl, [bp-8] + call pcibios_init_sel_reg + mov dx, #0x0cfc + in eax, dx + test al, #0x01 + jnz init_io_base + mov ecx, eax + mov eax, #0xffffffff + out dx, eax + in eax, dx + cmp eax, ecx + je next_pci_base + xor eax, #0xffffffff + mov ecx, eax + mov eax, [bp-4] + out dx, eax + add eax, ecx ;; calculate next free mem base + add eax, #0x01000000 + and eax, #0xff000000 + mov [bp-4], eax + jmp next_pci_base +init_io_base: + mov cx, ax + mov ax, #0xffff + out dx, ax + in ax, dx + cmp ax, cx + je next_pci_base + xor ax, #0xfffe + mov cx, ax + mov ax, [bp-6] + out dx, ax + add ax, cx ;; calculate next free i/o base + add ax, #0x0100 + and ax, #0xff00 + mov [bp-6], ax +next_pci_base: + mov al, [bp-8] + add al, #0x04 + cmp al, #0x28 + je enable_iomem_space + mov byte ptr[bp-8], al + jmp pci_init_io_loop2 +enable_iomem_space: + mov dl, #0x04 ;; enable i/o and memory space access if available + call pcibios_init_sel_reg + mov dx, #0x0cfc + in al, dx + or al, #0x07 + out dx, al +next_pci_dev: + mov byte ptr[bp-8], #0x10 + inc bx + cmp bx, #0x0100 + jne pci_init_io_loop1 + mov sp, bp + pop bp + ret + +pcibios_init_set_elcr: + push ax + push cx + mov dx, #0x04d0 + test al, #0x08 + jz is_master_pic + inc dx + and al, #0x07 +is_master_pic: + mov cl, al + mov bl, #0x01 + shl bl, cl + in al, dx + or al, bl + out dx, al + pop cx + pop ax + ret + +pcibios_init_irqs: + push ds + push bp + mov ax, #0xf000 + mov ds, ax + mov dx, #0x04d0 ;; reset ELCR1 + ELCR2 + mov al, #0x00 + out dx, al + inc dx + out dx, al + mov si, #pci_routing_table_structure + mov bh, [si+8] + mov bl, [si+9] + mov dl, #0x00 + call pcibios_init_sel_reg + mov dx, #0x0cfc + in ax, dx + cmp ax, [si+12] ;; check irq router + jne pci_init_end + mov dl, [si+34] + call pcibios_init_sel_reg + push bx ;; save irq router bus + devfunc + mov dx, #0x0cfc + mov ax, #0x8080 + out dx, ax ;; reset PIRQ route control + add dx, #2 + out dx, ax + mov ax, [si+6] + sub ax, #0x20 + shr ax, #4 + mov cx, ax + add si, #0x20 ;; set pointer to 1st entry + mov bp, sp + mov ax, #pci_irq_list + push ax + xor ax, ax + push ax +pci_init_irq_loop1: + mov bh, [si] + mov bl, [si+1] +pci_init_irq_loop2: + mov dl, #0x00 + call pcibios_init_sel_reg + mov dx, #0x0cfc + in ax, dx + cmp ax, #0xffff + jnz pci_test_int_pin + test bl, #0x07 + jz next_pir_entry + jmp next_pci_func +pci_test_int_pin: + mov dl, #0x3c + call pcibios_init_sel_reg + mov dx, #0x0cfd + in al, dx + and al, #0x07 + jz next_pci_func + dec al ;; determine pirq reg + mov dl, #0x03 + mul al, dl + add al, #0x02 + xor ah, ah + mov bx, ax + mov al, [si+bx] + mov dl, al + mov bx, [bp] + call pcibios_init_sel_reg + mov dx, #0x0cfc + and al, #0x03 + add dl, al + in al, dx + cmp al, #0x80 + jb pirq_found + mov bx, [bp-2] ;; pci irq list pointer + mov al, [bx] + out dx, al + inc bx + mov [bp-2], bx + call pcibios_init_set_elcr +pirq_found: + mov bh, [si] + mov bl, [si+1] + add bl, [bp-3] ;; pci function number + mov dl, #0x3c + call pcibios_init_sel_reg + mov dx, #0x0cfc + out dx, al +next_pci_func: + inc byte ptr[bp-3] + inc bl + test bl, #0x07 + jnz pci_init_irq_loop2 +next_pir_entry: + add si, #0x10 + mov byte ptr[bp-3], #0x00 + loop pci_init_irq_loop1 + mov sp, bp + pop bx +pci_init_end: + pop bp + pop ds + ret +#endif // !BX_ROMBIOS32 +#endif // BX_PCIBIOS + +#if BX_ROMBIOS32 +rombios32_init: + ;; save a20 and enable it + in al, 0x92 + push ax + or al, #0x02 + out 0x92, al + + ;; save SS:SP to the BDA + xor ax, ax + mov ds, ax + mov 0x0469, ss + mov 0x0467, sp + + SEG CS + lidt [pmode_IDT_info] + SEG CS + lgdt [rombios32_gdt_48] + ;; set PE bit in CR0 + mov eax, cr0 + or al, #0x01 + mov cr0, eax + ;; start protected mode code: ljmpl 0x10:rombios32_init1 + db 0x66, 0xea + dw rombios32_05 + dw 0x000f ;; high 16 bit address + dw 0x0010 + +use32 386 +rombios32_05: + ;; init data segments + mov eax, #0x18 + mov ds, ax + mov es, ax + mov ss, ax + xor eax, eax + mov fs, ax + mov gs, ax + cld + + ;; init the stack pointer to point below EBDA + mov ax, [0x040e] + shl eax, #4 + mov esp, #-0x10 + add esp, eax + + ;; pass pointer to s3_resume_flag and s3_resume_vector to rombios32 + push #0x04b0 + push #0x04b2 + + ;; call rombios32 code + mov eax, #0x000e0000 + call eax + + ;; return to 16 bit protected mode first + db 0xea + dd rombios32_10 + dw 0x20 + +use16 386 +rombios32_10: + ;; restore data segment limits to 0xffff + mov ax, #0x28 + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + ;; reset PE bit in CR0 + mov eax, cr0 + and al, #0xFE + mov cr0, eax + + ;; far jump to flush CPU queue after transition to real mode + JMP_AP(0xf000, rombios32_real_mode) + +rombios32_real_mode: + ;; restore IDT to normal real-mode defaults + SEG CS + lidt [rmode_IDT_info] + + xor ax, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ;; restore SS:SP from the BDA + mov ss, 0x0469 + xor esp, esp + mov sp, 0x0467 + ;; restore a20 + pop ax + out 0x92, al + ret + +rombios32_gdt_48: + dw 0x30 + dw rombios32_gdt + dw 0x000f + +rombios32_gdt: + dw 0, 0, 0, 0 + dw 0, 0, 0, 0 + dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10) + dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18) + dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff + dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff +#endif // BX_ROMBIOS32 + + +; parallel port detection: base address in DX, index in BX, timeout in CL +detect_parport: + push dx + add dx, #2 + in al, dx + and al, #0xdf ; clear input mode + out dx, al + pop dx + mov al, #0xaa + out dx, al + in al, dx + cmp al, #0xaa + jne no_parport + push bx + shl bx, #1 + mov [bx+0x408], dx ; Parallel I/O address + pop bx + mov [bx+0x478], cl ; Parallel printer timeout + inc bx +no_parport: + ret + +; serial port detection: base address in DX, index in BX, timeout in CL +detect_serial: + push dx + inc dx + mov al, #0x02 + out dx, al + in al, dx + cmp al, #0x02 + jne no_serial + inc dx + in al, dx + cmp al, #0x02 + jne no_serial + dec dx + xor al, al + out dx, al + pop dx + push bx + shl bx, #1 + mov [bx+0x400], dx ; Serial I/O address + pop bx + mov [bx+0x47c], cl ; Serial timeout + inc bx + ret +no_serial: + pop dx + ret + +rom_checksum: + push ax + push bx + push cx + xor ax, ax + xor bx, bx + xor cx, cx + mov ch, [2] + shl cx, #1 +checksum_loop: + add al, [bx] + inc bx + loop checksum_loop + and al, #0xff + pop cx + pop bx + pop ax + ret + + +;; We need a copy of this string, but we are not actually a PnP BIOS, +;; so make sure it is *not* aligned, so OSes will not see it if they scan. +.align 16 + db 0 +pnp_string: + .ascii "$PnP" + + +rom_scan: + ;; Scan for existence of valid expansion ROMS. + ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments + ;; General ROM: from 0xC8000..0xDFFFF in 2k increments + ;; System ROM: only 0xE0000 + ;; + ;; Header: + ;; Offset Value + ;; 0 0x55 + ;; 1 0xAA + ;; 2 ROM length in 512-byte blocks + ;; 3 ROM initialization entry point (FAR CALL) + +rom_scan_loop: + push ax ;; Save AX + mov ds, cx + mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k + cmp [0], #0xAA55 ;; look for signature + jne rom_scan_increment + call rom_checksum + jnz rom_scan_increment + mov al, [2] ;; change increment to ROM length in 512-byte blocks + + ;; We want our increment in 512-byte quantities, rounded to + ;; the nearest 2k quantity, since we only scan at 2k intervals. + test al, #0x03 + jz block_count_rounded + and al, #0xfc ;; needs rounding up + add al, #0x04 +block_count_rounded: + + push ax ;; Save AX + push di ;; Save DI + ;; Push addr of ROM entry point + push cx ;; Push seg + push #0x0003 ;; Push offset + + ;; Get the BDF into ax before invoking the option ROM + mov bl, [2] + mov al, bl + shr al, #7 + cmp al, #1 + jne fetch_bdf + mov ax, ds ;; Increment the DS since rom size larger than an segment + add ax, #0x1000 + mov ds, ax +fetch_bdf: + shl bx, #9 + xor ax, ax + mov al, [bx] + + ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS. + ;; That should stop it grabbing INT 19h; we will use its BEV instead. + mov bx, #0xf000 + mov es, bx + lea di, pnp_string + + mov bp, sp ;; Call ROM init routine using seg:off on stack + db 0xff ;; call_far ss:[bp+0] + db 0x5e + db 0 + cli ;; In case expansion ROM BIOS turns IF on + add sp, #2 ;; Pop offset value + pop cx ;; Pop seg value (restore CX) + + ;; Look at the ROM's PnP Expansion header. Properly, we're supposed + ;; to init all the ROMs and then go back and build an IPL table of + ;; all the bootable devices, but we can get away with one pass. + mov ds, cx ;; ROM base + mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains... + mov ax, [bx] ;; the offset of PnP expansion header, where... + cmp ax, #0x5024 ;; we look for signature "$PnP" + jne no_bev + mov ax, 2[bx] + cmp ax, #0x506e + jne no_bev + + mov ax, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector + cmp ax, #0x0000 + je no_bcv + + ;; Option ROM has BCV. Run it now. + push cx ;; Push seg + push ax ;; Push offset + + ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS. + mov bx, #0xf000 + mov es, bx + lea di, pnp_string + /* jump to BCV function entry pointer */ + mov bp, sp ;; Call ROM BCV routine using seg:off on stack + db 0xff ;; call_far ss:[bp+0] + db 0x5e + db 0 + cli ;; In case expansion ROM BIOS turns IF on + add sp, #2 ;; Pop offset value + pop cx ;; Pop seg value (restore CX) + jmp no_bev + +no_bcv: + mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of... + cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none. + je no_bev + + ;; Found a device that thinks it can boot the system. Record its BEV and product name string. + mov di, 0x10[bx] ;; Pointer to the product name string or zero if none + mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives + mov ds, bx + mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far + cmp bx, #IPL_TABLE_ENTRIES + je no_bev ;; Get out if the table is full + shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes) + mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device + mov 6[bx], cx ;; Build a far pointer from the segment... + mov 4[bx], ax ;; and the offset + cmp di, #0x0000 + je no_prod_str + mov 0xA[bx], cx ;; Build a far pointer from the segment... + mov 8[bx], di ;; and the offset +no_prod_str: + shr bx, #0x4 ;; Turn the offset back into a count + inc bx ;; We have one more entry now + mov IPL_COUNT_OFFSET, bx ;; Remember that. + +no_bev: + pop di ;; Restore DI + pop ax ;; Restore AX +rom_scan_increment: + shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments + ;; because the segment selector is shifted left 4 bits. + add cx, ax + pop ax ;; Restore AX + cmp cx, ax + jbe rom_scan_loop + + xor ax, ax ;; Restore DS back to 0000: + mov ds, ax + ret + +post_enable_cache: + ;; enable cache + mov eax, cr0 + and eax, #0x9fffffff + mov cr0, eax + jmp post_enable_cache_done + +post_init_pic: + mov al, #0x11 ; send initialisation commands + out 0x20, al + out 0xa0, al + mov al, #0x08 + out 0x21, al + mov al, #0x70 + out 0xa1, al + mov al, #0x04 + out 0x21, al + mov al, #0x02 + out 0xa1, al + mov al, #0x01 + out 0x21, al + out 0xa1, al + mov al, #0xb8 + out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6 +#if BX_USE_PS2_MOUSE + mov al, #0x8f +#else + mov al, #0x9f +#endif + out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14 + ret + +;; the following area can be used to write dynamically generated tables + .align 16 +bios_table_area_start: + dd 0xaafb4442 + dd bios_table_area_end - bios_table_area_start - 8; + + +;-------- +;- POST - +;-------- +.org 0xe05b ; POST Entry Point +post: + jmp post_enable_cache ; hack: we have limited space before next .org, + ; so take this bit out-of-line +post_enable_cache_done: + xor ax, ax + + ;; first reset the DMA controllers + out 0x0d,al + out 0xda,al + + ;; then initialize the DMA controllers + mov al, #0xC0 + out 0xD6, al ; cascade mode of channel 4 enabled + mov al, #0x00 + out 0xD4, al ; unmask channel 4 + + ;; Examine CMOS shutdown status. + mov AL, #0x0f + out 0x70, AL + in AL, 0x71 + + ;; backup status + mov bl, al + + ;; Reset CMOS shutdown status. + mov AL, #0x0f + out 0x70, AL ; select CMOS register Fh + mov AL, #0x00 + out 0x71, AL ; set shutdown action to normal + + ;; Examine CMOS shutdown status. + mov al, bl + + ;; 0x00, 0x09, 0x0D+ = normal startup + cmp AL, #0x00 + jz normal_post + cmp AL, #0x0d + jae normal_post + cmp AL, #0x09 + je normal_post + + ;; 0x05 = eoi + jmp via [0x40:0x67] jump + cmp al, #0x05 + je eoi_jmp_post + + ;; 0x0A = jmp via [0x40:0x67] jump + cmp al, #0x0a + je jmp_post_0x467 + + ;; 0x0B = iret via [0x40:0x67] + cmp al, #0x0b + je iret_post_0x467 + + ;; 0x0C = retf via [0x40:0x67] + cmp al, #0x0c + je retf_post_0x467 + + ;; Examine CMOS shutdown status. + ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08 = Unimplemented shutdown status. + push bx + call _shutdown_status_panic + +#if 0 + HALT(__LINE__) + ; + ;#if 0 + ; 0xb0, 0x20, /* mov al, #0x20 */ + ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */ + ;#endif + ; + pop es + pop ds + popa + iret +#endif + +normal_post: + ; case 0: normal startup + + cli + mov ax, #0xfffe + mov sp, ax + xor ax, ax + mov ds, ax + mov ss, ax + + ;; Save shutdown status + mov 0x04b0, bl + + cmp bl, #0xfe + jz s3_post + + ;; zero out BIOS data area (40:00..40:ff) + mov es, ax + mov cx, #0x0080 ;; 128 words + mov di, #0x0400 + cld + rep + stosw + + call _log_bios_start + + ;; set all interrupts to default handler + xor bx, bx ;; offset index + mov cx, #0x0100 ;; counter (256 interrupts) + mov ax, #dummy_iret_handler + mov dx, #0xF000 + +post_default_ints: + mov [bx], ax + add bx, #2 + mov [bx], dx + add bx, #2 + loop post_default_ints + + ;; set vector 0x79 to zero + ;; this is used by 'gardian angel' protection system + SET_INT_VECTOR(0x79, #0, #0) + + ;; base memory in K 40:13 (word) + mov ax, #BASE_MEM_IN_K + mov 0x0413, ax + + + ;; Manufacturing Test 40:12 + ;; zerod out above + + ;; Warm Boot Flag 0040:0072 + ;; value of 1234h = skip memory checks + ;; zerod out above + + + ;; Printer Services vector + SET_INT_VECTOR(0x17, #0xF000, #int17_handler) + + ;; Bootstrap failure vector + SET_INT_VECTOR(0x18, #0xF000, #int18_handler) + + ;; Bootstrap Loader vector + SET_INT_VECTOR(0x19, #0xF000, #int19_handler) + + ;; User Timer Tick vector + SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler) + + ;; Memory Size Check vector + SET_INT_VECTOR(0x12, #0xF000, #int12_handler) + + ;; Equipment Configuration Check vector + SET_INT_VECTOR(0x11, #0xF000, #int11_handler) + + ;; System Services + SET_INT_VECTOR(0x15, #0xF000, #int15_handler) + + ;; EBDA setup + call ebda_post + + ;; PIT setup + SET_INT_VECTOR(0x08, #0xF000, #int08_handler) + ;; int 1C already points at dummy_iret_handler (above) + mov al, #0x34 ; timer0: binary count, 16bit count, mode 2 + out 0x43, al + mov al, #0x00 ; maximum count of 0000H = 18.2Hz + out 0x40, al + out 0x40, al + + ;; Keyboard + SET_INT_VECTOR(0x09, #0xF000, #int09_handler) + SET_INT_VECTOR(0x16, #0xF000, #int16_handler) + + xor ax, ax + mov ds, ax + mov 0x0417, al /* keyboard shift flags, set 1 */ + mov 0x0418, al /* keyboard shift flags, set 2 */ + mov 0x0419, al /* keyboard alt-numpad work area */ + mov 0x0471, al /* keyboard ctrl-break flag */ + mov 0x0497, al /* keyboard status flags 4 */ + mov al, #0x10 + mov 0x0496, al /* keyboard status flags 3 */ + + + /* keyboard head of buffer pointer */ + mov bx, #0x001E + mov 0x041A, bx + + /* keyboard end of buffer pointer */ + mov 0x041C, bx + + /* keyboard pointer to start of buffer */ + mov bx, #0x001E + mov 0x0480, bx + + /* keyboard pointer to end of buffer */ + mov bx, #0x003E + mov 0x0482, bx + + /* init the keyboard */ + call _keyboard_init + + ;; mov CMOS Equipment Byte to BDA Equipment Word + mov ax, 0x0410 + mov al, #0x14 + out 0x70, al + in al, 0x71 + mov 0x0410, ax + + + ;; Parallel setup + SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler) + xor ax, ax + mov ds, ax + xor bx, bx + mov cl, #0x14 ; timeout value + mov dx, #0x378 ; Parallel I/O address, port 1 + call detect_parport + mov dx, #0x278 ; Parallel I/O address, port 2 + call detect_parport + shl bx, #0x0e + mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports + and ax, #0x3fff + or ax, bx ; set number of parallel ports + mov 0x410, ax + + ;; Serial setup + SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler) + SET_INT_VECTOR(0x14, #0xF000, #int14_handler) + xor bx, bx + mov cl, #0x0a ; timeout value + mov dx, #0x03f8 ; Serial I/O address, port 1 + call detect_serial + mov dx, #0x02f8 ; Serial I/O address, port 2 + call detect_serial + mov dx, #0x03e8 ; Serial I/O address, port 3 + call detect_serial + mov dx, #0x02e8 ; Serial I/O address, port 4 + call detect_serial + shl bx, #0x09 + mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports + and ax, #0xf1ff + or ax, bx ; set number of serial port + mov 0x410, ax + + ;; CMOS RTC + SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler) + SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler) + SET_INT_VECTOR(0x70, #0xF000, #int70_handler) + ;; BIOS DATA AREA 0x4CE ??? + call timer_tick_post + + ;; PS/2 mouse setup + SET_INT_VECTOR(0x74, #0xF000, #int74_handler) + + ;; IRQ13 (FPU exception) setup + SET_INT_VECTOR(0x75, #0xF000, #int75_handler) + + ;; Video setup + SET_INT_VECTOR(0x10, #0xF000, #int10_handler) + + ;; PIC + call post_init_pic + + mov cx, #0xc000 ;; init vga bios + mov ax, #0xc780 + call rom_scan + + call _print_bios_banner + +#if BX_ROMBIOS32 + call rombios32_init +#else +#if BX_PCIBIOS + call pcibios_init_iomem_bases + call pcibios_init_irqs +#endif //BX_PCIBIOS +#endif + + ;; + ;; Floppy setup + ;; + call floppy_drive_post + + ;; + ;; Hard Drive setup + ;; + call hard_drive_post + +#if BX_USE_ATADRV + + ;; + ;; ATA/ATAPI driver setup + ;; + call _ata_init + call _ata_detect + ;; + +#endif // BX_USE_ATADRV + +#if BX_ELTORITO_BOOT + ;; + ;; eltorito floppy/harddisk emulation from cd + ;; + call _cdemu_init + ;; +#endif // BX_ELTORITO_BOOT + + call _init_boot_vectors + + mov cx, #0xc800 ;; init option roms + mov ax, #0xe000 + call rom_scan + +#if BX_ELTORITO_BOOT + call _interactive_bootkey +#endif // BX_ELTORITO_BOOT + + sti ;; enable interrupts + int #0x19 + +.org 0xe2c3 ; NMI Handler Entry Point +nmi: + ;; FIXME the NMI handler should not panic + ;; but iret when called from int75 (fpu exception) + call _nmi_handler_msg + iret + +int75_handler: + out 0xf0, al // clear irq13 + call eoi_both_pics // clear interrupt + int 2 // legacy nmi call + iret + +;------------------------------------------- +;- INT 13h Fixed Disk Services Entry Point - +;------------------------------------------- +.org 0xe3fe ; INT 13h Fixed Disk Services Entry Point +int13_handler: + //JMPL(int13_relocated) + jmp int13_relocated + +.org 0xe401 ; Fixed Disk Parameter Table + +;---------- +;- INT19h - +;---------- +.org 0xe6f2 ; INT 19h Boot Load Service Entry Point +int19_handler: + + jmp int19_relocated +;------------------------------------------- +;- System BIOS Configuration Data Table +;------------------------------------------- +.org BIOS_CONFIG_TABLE +db 0x08 ; Table size (bytes) -Lo +db 0x00 ; Table size (bytes) -Hi +db SYS_MODEL_ID +db SYS_SUBMODEL_ID +db BIOS_REVISION +; Feature byte 1 +; b7: 1=DMA channel 3 used by hard disk +; b6: 1=2 interrupt controllers present +; b5: 1=RTC present +; b4: 1=BIOS calls int 15h/4Fh every key +; b3: 1=wait for extern event supported (Int 15h/41h) +; b2: 1=extended BIOS data area used +; b1: 0=AT or ESDI bus, 1=MicroChannel +; b0: 1=Dual bus (MicroChannel + ISA) +db (0 << 7) | \ + (1 << 6) | \ + (1 << 5) | \ + (BX_CALL_INT15_4F << 4) | \ + (0 << 3) | \ + (BX_USE_EBDA << 2) | \ + (0 << 1) | \ + (0 << 0) +; Feature byte 2 +; b7: 1=32-bit DMA supported +; b6: 1=int16h, function 9 supported +; b5: 1=int15h/C6h (get POS data) supported +; b4: 1=int15h/C7h (get mem map info) supported +; b3: 1=int15h/C8h (en/dis CPU) supported +; b2: 1=non-8042 kb controller +; b1: 1=data streaming supported +; b0: reserved +db (0 << 7) | \ + (1 << 6) | \ + (0 << 5) | \ + (0 << 4) | \ + (0 << 3) | \ + (0 << 2) | \ + (0 << 1) | \ + (0 << 0) +; Feature byte 3 +; b7: not used +; b6: reserved +; b5: reserved +; b4: POST supports ROM-to-RAM enable/disable +; b3: SCSI on system board +; b2: info panel installed +; b1: Initial Machine Load (IML) system - BIOS on disk +; b0: SCSI supported in IML +db 0x00 +; Feature byte 4 +; b7: IBM private +; b6: EEPROM present +; b5-3: ABIOS presence (011 = not supported) +; b2: private +; b1: memory split above 16Mb supported +; b0: POSTEXT directly supported by POST +db 0x00 +; Feature byte 5 (IBM) +; b1: enhanced mouse +; b0: flash EPROM +db 0x00 + + + +.org 0xe729 ; Baud Rate Generator Table + +;---------- +;- INT14h - +;---------- +.org 0xe739 ; INT 14h Serial Communications Service Entry Point +int14_handler: + push ds + pusha + xor ax, ax + mov ds, ax + call _int14_function + popa + pop ds + iret + + +;---------------------------------------- +;- INT 16h Keyboard Service Entry Point - +;---------------------------------------- +.org 0xe82e +int16_handler: + + sti + push ds + pushf + pusha + + cmp ah, #0x00 + je int16_F00 + cmp ah, #0x10 + je int16_F00 + + mov bx, #0xf000 + mov ds, bx + call _int16_function + popa + popf + pop ds + jz int16_zero_set + +int16_zero_clear: + push bp + mov bp, sp + //SEG SS + and BYTE [bp + 0x06], #0xbf + pop bp + iret + +int16_zero_set: + push bp + mov bp, sp + //SEG SS + or BYTE [bp + 0x06], #0x40 + pop bp + iret + +int16_F00: + mov bx, #0x0040 + mov ds, bx + +int16_wait_for_key: + cli + mov bx, 0x001a + cmp bx, 0x001c + jne int16_key_found + sti + nop +#if 0 + /* no key yet, call int 15h, function AX=9002 */ + 0x50, /* push AX */ + 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */ + 0xcd, 0x15, /* int 15h */ + 0x58, /* pop AX */ + 0xeb, 0xea, /* jmp WAIT_FOR_KEY */ +#endif + jmp int16_wait_for_key + +int16_key_found: + mov bx, #0xf000 + mov ds, bx + call _int16_function + popa + popf + pop ds +#if 0 + /* notify int16 complete w/ int 15h, function AX=9102 */ + 0x50, /* push AX */ + 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */ + 0xcd, 0x15, /* int 15h */ + 0x58, /* pop AX */ +#endif + iret + + + +;------------------------------------------------- +;- INT09h : Keyboard Hardware Service Entry Point - +;------------------------------------------------- +.org 0xe987 +int09_handler: + cli + push ax + + mov al, #0xAD ;;disable keyboard + out #0x64, al + + mov al, #0x0B + out #0x20, al + in al, #0x20 + and al, #0x02 + jz int09_finish + + in al, #0x60 ;;read key from keyboard controller + sti + push ds + pusha +#ifdef BX_CALL_INT15_4F + mov ah, #0x4f ;; allow for keyboard intercept + stc + int #0x15 + jnc int09_done +#endif + + ;; check for extended key + cmp al, #0xe0 + jne int09_check_pause + xor ax, ax + mov ds, ax + mov al, BYTE [0x496] ;; mf2_state |= 0x02 + or al, #0x02 + mov BYTE [0x496], al + jmp int09_done + +int09_check_pause: ;; check for pause key + cmp al, #0xe1 + jne int09_process_key + xor ax, ax + mov ds, ax + mov al, BYTE [0x496] ;; mf2_state |= 0x01 + or al, #0x01 + mov BYTE [0x496], al + jmp int09_done + +int09_process_key: + mov bx, #0xf000 + mov ds, bx + call _int09_function + +int09_done: + popa + pop ds + cli + call eoi_master_pic + +int09_finish: + mov al, #0xAE ;;enable keyboard + out #0x64, al + pop ax + iret + + +;---------------------------------------- +;- INT 13h Diskette Service Entry Point - +;---------------------------------------- +.org 0xec59 +int13_diskette: + jmp int13_noeltorito + +;--------------------------------------------- +;- INT 0Eh Diskette Hardware ISR Entry Point - +;--------------------------------------------- +.org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point +int0e_handler: + push ax + push dx + mov dx, #0x03f4 + in al, dx + and al, #0xc0 + cmp al, #0xc0 + je int0e_normal + mov dx, #0x03f5 + mov al, #0x08 ; sense interrupt status + out dx, al +int0e_loop1: + mov dx, #0x03f4 + in al, dx + and al, #0xc0 + cmp al, #0xc0 + jne int0e_loop1 +int0e_loop2: + mov dx, #0x03f5 + in al, dx + mov dx, #0x03f4 + in al, dx + and al, #0xc0 + cmp al, #0xc0 + je int0e_loop2 +int0e_normal: + push ds + xor ax, ax ;; segment 0000 + mov ds, ax + call eoi_master_pic + mov al, 0x043e + or al, #0x80 ;; diskette interrupt has occurred + mov 0x043e, al + pop ds + pop dx + pop ax + iret + + +.org 0xefc7 ; Diskette Controller Parameter Table +diskette_param_table: +;; Since no provisions are made for multiple drive types, most +;; values in this table are ignored. I set parameters for 1.44M +;; floppy here +db 0xAF +db 0x02 ;; head load time 0000001, DMA used +db 0x25 +db 0x02 +db 18 +db 0x1B +db 0xFF +db 0x6C +db 0xF6 +db 0x0F +db 0x08 + + +;---------------------------------------- +;- INT17h : Printer Service Entry Point - +;---------------------------------------- +.org 0xefd2 +int17_handler: + push ds + pusha + xor ax, ax + mov ds, ax + call _int17_function + popa + pop ds + iret + +diskette_param_table2: +;; New diskette parameter table adding 3 parameters from IBM +;; Since no provisions are made for multiple drive types, most +;; values in this table are ignored. I set parameters for 1.44M +;; floppy here +db 0xAF +db 0x02 ;; head load time 0000001, DMA used +db 0x25 +db 0x02 +db 18 +db 0x1B +db 0xFF +db 0x6C +db 0xF6 +db 0x0F +db 0x08 +db 79 ;; maximum track +db 0 ;; data transfer rate +db 4 ;; drive type in cmos + +.org 0xf045 ; INT 10 Functions 0-Fh Entry Point + HALT(__LINE__) + iret + +;---------- +;- INT10h - +;---------- +.org 0xf065 ; INT 10h Video Support Service Entry Point +int10_handler: + ;; dont do anything, since the VGA BIOS handles int10h requests + iret + +.org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh) + +;---------- +;- INT12h - +;---------- +.org 0xf841 ; INT 12h Memory Size Service Entry Point +; ??? different for Pentium (machine check)? +int12_handler: + push ds + mov ax, #0x0040 + mov ds, ax + mov ax, 0x0013 + pop ds + iret + +;---------- +;- INT11h - +;---------- +.org 0xf84d ; INT 11h Equipment List Service Entry Point +int11_handler: + push ds + mov ax, #0x0040 + mov ds, ax + mov ax, 0x0010 + pop ds + iret + +;---------- +;- INT15h - +;---------- +.org 0xf859 ; INT 15h System Services Entry Point +int15_handler: + pushf +#if BX_APM + cmp ah, #0x53 + je apm_call +#endif + push ds + push es + cmp ah, #0x86 + je int15_handler32 + cmp ah, #0xE8 + je int15_handler32 + pusha +#if BX_USE_PS2_MOUSE + cmp ah, #0xC2 + je int15_handler_mouse +#endif + call _int15_function +int15_handler_mouse_ret: + popa +int15_handler32_ret: + pop es + pop ds + popf + jmp iret_modify_cf +#if BX_APM +apm_call: + jmp _apmreal_entry +#endif + +#if BX_USE_PS2_MOUSE +int15_handler_mouse: + call _int15_function_mouse + jmp int15_handler_mouse_ret +#endif + +int15_handler32: + pushad + call _int15_function32 + popad + jmp int15_handler32_ret + +;; Protected mode IDT descriptor +;; +;; I just make the limit 0, so the machine will shutdown +;; if an exception occurs during protected mode memory +;; transfers. +;; +;; Set base to f0000 to correspond to beginning of BIOS, +;; in case I actually define an IDT later +;; Set limit to 0 + +pmode_IDT_info: +dw 0x0000 ;; limit 15:00 +dw 0x0000 ;; base 15:00 +db 0x0f ;; base 23:16 + +;; Real mode IDT descriptor +;; +;; Set to typical real-mode values. +;; base = 000000 +;; limit = 03ff + +rmode_IDT_info: +dw 0x03ff ;; limit 15:00 +dw 0x0000 ;; base 15:00 +db 0x00 ;; base 23:16 + + +;---------- +;- INT1Ah - +;---------- +.org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point +int1a_handler: +#if BX_PCIBIOS + cmp ah, #0xb1 + jne int1a_normal + call pcibios_real + jc pcibios_error + retf 2 +pcibios_error: + mov bl, ah + mov ah, #0xb1 + push ds + pusha + mov ax, ss ; set readable descriptor to ds, for calling pcibios + mov ds, ax ; on 16bit protected mode. + jmp int1a_callfunction +int1a_normal: +#endif + push ds + pusha + xor ax, ax + mov ds, ax +int1a_callfunction: + call _int1a_function + popa + pop ds + iret + +;; +;; int70h: IRQ8 - CMOS RTC +;; +int70_handler: + push ds + pushad + xor ax, ax + mov ds, ax + call _int70_function + popad + pop ds + iret + +;--------- +;- INT08 - +;--------- +.org 0xfea5 ; INT 08h System Timer ISR Entry Point +int08_handler: + sti + push eax + push ds + xor ax, ax + mov ds, ax + + ;; time to turn off drive(s)? + mov al,0x0440 + or al,al + jz int08_floppy_off + dec al + mov 0x0440,al + jnz int08_floppy_off + ;; turn motor(s) off + push dx + mov dx,#0x03f2 + in al,dx + and al,#0xcf + out dx,al + pop dx +int08_floppy_off: + + mov eax, 0x046c ;; get ticks dword + inc eax + + ;; compare eax to one days worth of timer ticks at 18.2 hz + cmp eax, #0x001800B0 + jb int08_store_ticks + ;; there has been a midnight rollover at this point + xor eax, eax ;; zero out counter + inc BYTE 0x0470 ;; increment rollover flag + +int08_store_ticks: + mov 0x046c, eax ;; store new ticks dword + ;; chain to user timer tick INT #0x1c + //pushf + //;; call_ep [ds:loc] + //CALL_EP( 0x1c << 2 ) + int #0x1c + cli + call eoi_master_pic + pop ds + pop eax + iret + +.org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST + + +.org 0xff00 +.ascii BIOS_COPYRIGHT_STRING + +;------------------------------------------------ +;- IRET Instruction for Dummy Interrupt Handler - +;------------------------------------------------ +.org 0xff53 ; IRET Instruction for Dummy Interrupt Handler +dummy_iret_handler: + iret + +.org 0xff54 ; INT 05h Print Screen Service Entry Point + HALT(__LINE__) + iret + +.org 0xfff0 ; Power-up Entry Point + jmp 0xf000:post + +.org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY +.ascii BIOS_BUILD_DATE + +.org 0xfffe ; System Model ID +db SYS_MODEL_ID +db 0x00 ; filler + +.org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters) +ASM_END +/* + * This font comes from the fntcol16.zip package (c) by Joseph Gil + * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip + * This font is public domain + */ +static Bit8u vgafont8[128*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, +}; + +ASM_START +.org 0xcc00 +bios_table_area_end: +// bcc-generated data will be placed here +ASM_END diff --git a/kvm/bios/rombios.h b/kvm/bios/rombios.h new file mode 100644 index 000000000..c8f64f3f5 --- /dev/null +++ b/kvm/bios/rombios.h @@ -0,0 +1,83 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: rombios.h,v 1.4 2007/02/20 09:36:55 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2006 Volker Ruppert +// +// 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 + +/* define it to include QEMU specific code */ +#define BX_QEMU + +#ifndef LEGACY +# define BX_ROMBIOS32 1 +#else +# define BX_ROMBIOS32 0 +#endif +#define DEBUG_ROMBIOS 0 + +#define PANIC_PORT 0x400 +#define PANIC_PORT2 0x401 +#define INFO_PORT 0x402 +#define DEBUG_PORT 0x403 + +#define BIOS_PRINTF_HALT 1 +#define BIOS_PRINTF_SCREEN 2 +#define BIOS_PRINTF_INFO 4 +#define BIOS_PRINTF_DEBUG 8 +#define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO) +#define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT) + +#define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p) + +// Defines the output macros. +// BX_DEBUG goes to INFO port until we can easily choose debug info on a +// per-device basis. Debug info are sent only in debug mode +#if DEBUG_ROMBIOS +# define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p) +#else +# define BX_DEBUG(format, p...) +#endif +#define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p) +#define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p) + +#define ACPI_DATA_SIZE 0x00010000L +#define PM_IO_BASE 0xb000 +#define SMB_IO_BASE 0xb100 +#define SMP_MSR_ADDR 0x0510 +#define APIC_MADT_PTR 0x0514 + +#define MAX_CPUS 16 + +#define QEMU_CFG_CTL_PORT 0x510 +#define QEMU_CFG_DATA_PORT 0x511 +#define QEMU_CFG_SIGNATURE 0x00 +#define QEMU_CFG_ID 0x01 +#define QEMU_CFG_UUID 0x02 +#define QEMU_CFG_NUMA 0x0d +#define QEMU_CFG_BOOT_MENU 0x0e +#define QEMU_CFG_ARCH_LOCAL 0x8000 +#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) +#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) +#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) + + // Define the application NAME +#if defined(BX_QEMU) +# define BX_APPNAME "QEMU" +#elif defined(PLEX86) +# define BX_APPNAME "Plex86" +#else +# define BX_APPNAME "Bochs" +#endif diff --git a/kvm/bios/rombios32.c b/kvm/bios/rombios32.c new file mode 100755 index 000000000..0b3556cba --- /dev/null +++ b/kvm/bios/rombios32.c @@ -0,0 +1,2749 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: rombios32.c,v 1.11 2007/08/03 13:56:13 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// 32 bit Bochs BIOS init code +// Copyright (C) 2006 Fabrice Bellard +// +// 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 <stdarg.h> +#include <stddef.h> + +#include "rombios.h" + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +/* if true, put the MP float table and ACPI RSDT in EBDA and the MP + table in RAM. Unfortunately, Linux has bugs with that, so we prefer + to modify the BIOS in shadow RAM */ +//#define BX_USE_EBDA_TABLES + +/* define it if the (emulated) hardware supports SMM mode */ +//#define BX_USE_SMM + +#define cpuid(index, eax, ebx, ecx, edx) \ + asm volatile ("cpuid" \ + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \ + : "0" (index)) + +#define wbinvd() asm volatile("wbinvd") + +#define CPUID_MSR (1 << 5) +#define CPUID_APIC (1 << 9) +#define CPUID_MTRR (1 << 12) + +#define APIC_BASE ((uint8_t *)0xfee00000) +#define APIC_ICR_LOW 0x300 +#define APIC_SVR 0x0F0 +#define APIC_ID 0x020 +#define APIC_LVT3 0x370 + +/* IRQs 5,9,10,11 */ +#define PCI_ISA_IRQ_MASK 0x0e20U + +#define APIC_ENABLED 0x0100 + +#define AP_BOOT_ADDR 0x9f000 + +#define MPTABLE_MAX_SIZE 0x00002000 +#define SMI_CMD_IO_ADDR 0xb2 + +#define BIOS_TMP_STORAGE 0x00030000 /* 64 KB used to copy the BIOS to shadow RAM */ + +#define MSR_MTRRcap 0x000000fe +#define MSR_MTRRfix64K_00000 0x00000250 +#define MSR_MTRRfix16K_80000 0x00000258 +#define MSR_MTRRfix16K_A0000 0x00000259 +#define MSR_MTRRfix4K_C0000 0x00000268 +#define MSR_MTRRfix4K_C8000 0x00000269 +#define MSR_MTRRfix4K_D0000 0x0000026a +#define MSR_MTRRfix4K_D8000 0x0000026b +#define MSR_MTRRfix4K_E0000 0x0000026c +#define MSR_MTRRfix4K_E8000 0x0000026d +#define MSR_MTRRfix4K_F0000 0x0000026e +#define MSR_MTRRfix4K_F8000 0x0000026f +#define MSR_MTRRdefType 0x000002ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define MAX_INT_OVERRIDES 16 + +static inline void outl(int addr, int val) +{ + asm volatile ("outl %1, %w0" : : "d" (addr), "a" (val)); +} + +static inline void outw(int addr, int val) +{ + asm volatile ("outw %w1, %w0" : : "d" (addr), "a" (val)); +} + +static inline void outb(int addr, int val) +{ + asm volatile ("outb %b1, %w0" : : "d" (addr), "a" (val)); +} + +static inline uint32_t inl(int addr) +{ + uint32_t val; + asm volatile ("inl %w1, %0" : "=a" (val) : "d" (addr)); + return val; +} + +static inline uint16_t inw(int addr) +{ + uint16_t val; + asm volatile ("inw %w1, %w0" : "=a" (val) : "d" (addr)); + return val; +} + +static inline uint8_t inb(int addr) +{ + uint8_t val; + asm volatile ("inb %w1, %b0" : "=a" (val) : "d" (addr)); + return val; +} + +static inline void writel(void *addr, uint32_t val) +{ + *(volatile uint32_t *)addr = val; +} + +static inline void writew(void *addr, uint16_t val) +{ + *(volatile uint16_t *)addr = val; +} + +static inline void writeb(void *addr, uint8_t val) +{ + *(volatile uint8_t *)addr = val; +} + +static inline uint32_t readl(const void *addr) +{ + return *(volatile const uint32_t *)addr; +} + +static inline uint16_t readw(const void *addr) +{ + return *(volatile const uint16_t *)addr; +} + +static inline uint8_t readb(const void *addr) +{ + return *(volatile const uint8_t *)addr; +} + +static inline void putc(int c) +{ + outb(INFO_PORT, c); +} + +static uint64_t rdmsr(unsigned index) +{ + unsigned long long ret; + + asm ("rdmsr" : "=A"(ret) : "c"(index)); + return ret; +} + +static void wrmsr(unsigned index, uint64_t val) +{ + asm volatile ("wrmsr" : : "c"(index), "A"(val)); +} + +static inline int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +void *memset(void *d1, int val, size_t len) +{ + uint8_t *d = d1; + + while (len--) { + *d++ = val; + } + return d1; +} + +void *memcpy(void *d1, const void *s1, size_t len) +{ + uint8_t *d = d1; + const uint8_t *s = s1; + + while (len--) { + *d++ = *s++; + } + return d1; +} + +void *memmove(void *d1, const void *s1, size_t len) +{ + uint8_t *d = d1; + const uint8_t *s = s1; + + if (d <= s) { + while (len--) { + *d++ = *s++; + } + } else { + d += len; + s += len; + while (len--) { + *--d = *--s; + } + } + return d1; +} + +int memcmp(const void *s1, const void *s2, size_t len) +{ + const int8_t *p1 = s1; + const int8_t *p2 = s2; + + while (len--) { + int r = *p1++ - *p2++; + if(r) + return r; + } + + return 0; +} + +size_t strlen(const char *s) +{ + const char *s1; + for(s1 = s; *s1 != '\0'; s1++); + return s1 - s; +} + +/* from BSD ppp sources */ +int vsnprintf(char *buf, int buflen, const char *fmt, va_list args) +{ + int c, i, n; + int width, prec, fillch; + int base, len, neg; + unsigned long val = 0; + const char *f; + char *str, *buf0; + char num[32]; + static const char hexchars[] = "0123456789abcdef"; + + buf0 = buf; + --buflen; + while (buflen > 0) { + for (f = fmt; *f != '%' && *f != 0; ++f) + ; + if (f > fmt) { + len = f - fmt; + if (len > buflen) + len = buflen; + memcpy(buf, fmt, len); + buf += len; + buflen -= len; + fmt = f; + } + if (*fmt == 0) + break; + c = *++fmt; + width = prec = 0; + fillch = ' '; + if (c == '0') { + fillch = '0'; + c = *++fmt; + } + if (c == '*') { + width = va_arg(args, int); + c = *++fmt; + } else { + while (isdigit(c)) { + width = width * 10 + c - '0'; + c = *++fmt; + } + } + if (c == '.') { + c = *++fmt; + if (c == '*') { + prec = va_arg(args, int); + c = *++fmt; + } else { + while (isdigit(c)) { + prec = prec * 10 + c - '0'; + c = *++fmt; + } + } + } + /* modifiers */ + switch(c) { + case 'l': + c = *++fmt; + break; + default: + break; + } + str = 0; + base = 0; + neg = 0; + ++fmt; + switch (c) { + case 'd': + i = va_arg(args, int); + if (i < 0) { + neg = 1; + val = -i; + } else + val = i; + base = 10; + break; + case 'o': + val = va_arg(args, unsigned int); + base = 8; + break; + case 'x': + case 'X': + val = va_arg(args, unsigned int); + base = 16; + break; + case 'p': + val = (unsigned long) va_arg(args, void *); + base = 16; + neg = 2; + break; + case 's': + str = va_arg(args, char *); + break; + case 'c': + num[0] = va_arg(args, int); + num[1] = 0; + str = num; + break; + default: + *buf++ = '%'; + if (c != '%') + --fmt; /* so %z outputs %z etc. */ + --buflen; + continue; + } + if (base != 0) { + str = num + sizeof(num); + *--str = 0; + while (str > num + neg) { + *--str = hexchars[val % base]; + val = val / base; + if (--prec <= 0 && val == 0) + break; + } + switch (neg) { + case 1: + *--str = '-'; + break; + case 2: + *--str = 'x'; + *--str = '0'; + break; + } + len = num + sizeof(num) - 1 - str; + } else { + len = strlen(str); + if (prec > 0 && len > prec) + len = prec; + } + if (width > 0) { + if (width > buflen) + width = buflen; + if ((n = width - len) > 0) { + buflen -= n; + for (; n > 0; --n) + *buf++ = fillch; + } + } + if (len > buflen) + len = buflen; + memcpy(buf, str, len); + buf += len; + buflen -= len; + } + *buf = 0; + return buf - buf0; +} + +int snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} + +void bios_printf(int flags, const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + const char *s; + + if ((flags & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) + outb(PANIC_PORT2, 0x00); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + s = buf; + while (*s) + putc(*s++); + va_end(ap); +} + +void delay_ms(int n) +{ + int i, j; + for(i = 0; i < n; i++) { +#ifdef BX_QEMU + /* approximative ! */ + for(j = 0; j < 1000000; j++); +#else + { + int r1, r2; + j = 66; + r1 = inb(0x61) & 0x10; + do { + r2 = inb(0x61) & 0x10; + if (r1 != r2) { + j--; + r1 = r2; + } + } while (j > 0); + } +#endif + } +} + +uint16_t smp_cpus; +uint32_t cpuid_signature; +uint32_t cpuid_features; +uint32_t cpuid_ext_features; +unsigned long ram_size; +uint64_t ram_end; +#ifdef BX_QEMU +uint8_t irq0_override; +#endif +#ifdef BX_USE_EBDA_TABLES +unsigned long ebda_cur_addr; +#endif +int acpi_enabled; +uint32_t pm_io_base, smb_io_base; +int pm_sci_int; +unsigned long bios_table_cur_addr; +unsigned long bios_table_end_addr; + +void init_smp_msrs(void) +{ + *(uint32_t *)SMP_MSR_ADDR = 0; +} + +static inline uint64_t le64_to_cpu(uint64_t x) +{ + return x; +} + +void wrmsr_smp(uint32_t index, uint64_t val) +{ + static struct { uint32_t ecx, eax, edx; } *p = (void *)SMP_MSR_ADDR; + + wrmsr(index, val); + p->ecx = index; + p->eax = val; + p->edx = val >> 32; + ++p; + p->ecx = 0; +} + +#ifdef BX_QEMU + +int qemu_cfg_port; + +void qemu_cfg_select(int f) +{ + outw(QEMU_CFG_CTL_PORT, f); +} + +int qemu_cfg_port_probe() +{ + char *sig = "QEMU"; + int i; + + qemu_cfg_select(QEMU_CFG_SIGNATURE); + + for (i = 0; i < 4; i++) + if (inb(QEMU_CFG_DATA_PORT) != sig[i]) + return 0; + + return 1; +} + +void qemu_cfg_read(uint8_t *buf, int len) +{ + while (len--) + *(buf++) = inb(QEMU_CFG_DATA_PORT); +} + +static uint16_t acpi_additional_tables(void) +{ + uint16_t cnt; + + qemu_cfg_select(QEMU_CFG_ACPI_TABLES); + qemu_cfg_read((uint8_t*)&cnt, sizeof(cnt)); + + return cnt; +} + +static int acpi_load_table(int i, uint32_t addr, uint16_t *len) +{ + qemu_cfg_read((uint8_t*)len, sizeof(*len)); + + if (!*len) + return -1; + + qemu_cfg_read((uint8_t*)addr, *len); + return 0; +} + +static uint16_t smbios_entries(void) +{ + uint16_t cnt; + + qemu_cfg_select(QEMU_CFG_SMBIOS_ENTRIES); + qemu_cfg_read((uint8_t*)&cnt, sizeof(cnt)); + + return cnt; +} + +uint64_t qemu_cfg_get64 (void) +{ + uint64_t ret; + + qemu_cfg_read((uint8_t*)&ret, 8); + return le64_to_cpu(ret); +} +#endif + +#ifdef BX_QEMU +void irq0_override_probe(void) +{ + if(qemu_cfg_port) { + qemu_cfg_select(QEMU_CFG_IRQ0_OVERRIDE); + qemu_cfg_read(&irq0_override, 1); + } +} +#endif + +void cpu_probe(void) +{ + uint32_t eax, ebx, ecx, edx; + cpuid(1, eax, ebx, ecx, edx); + cpuid_signature = eax; + cpuid_features = edx; + cpuid_ext_features = ecx; +} + +static int cmos_readb(int addr) +{ + outb(0x70, addr); + return inb(0x71); +} + +void setup_mtrr(void) +{ + int i, vcnt, fix, wc; + uint32_t mtrr_cap; + union { + uint8_t valb[8]; + uint64_t val; + } u; + + if (!(cpuid_features & CPUID_MTRR)) + return; + + if (!(cpuid_features & CPUID_MSR)) + return; + + mtrr_cap = rdmsr(MSR_MTRRcap); + vcnt = mtrr_cap & 0xff; + fix = mtrr_cap & 0x100; + wc = mtrr_cap & 0x400; + if (!vcnt || !fix) + return; + + u.val = 0; + for (i = 0; i < 8; ++i) + if (ram_size >= 65536 * (i + 1)) + u.valb[i] = 6; + wrmsr_smp(MSR_MTRRfix64K_00000, u.val); + u.val = 0; + for (i = 0; i < 8; ++i) + if (ram_size >= 65536 * 8 + 16384 * (i + 1)) + u.valb[i] = 6; + wrmsr_smp(MSR_MTRRfix16K_80000, u.val); + wrmsr_smp(MSR_MTRRfix16K_A0000, 0); + wrmsr_smp(MSR_MTRRfix4K_C0000, 0); + wrmsr_smp(MSR_MTRRfix4K_C8000, 0); + wrmsr_smp(MSR_MTRRfix4K_D0000, 0); + wrmsr_smp(MSR_MTRRfix4K_D8000, 0); + wrmsr_smp(MSR_MTRRfix4K_E0000, 0); + wrmsr_smp(MSR_MTRRfix4K_E8000, 0); + wrmsr_smp(MSR_MTRRfix4K_F0000, 0); + wrmsr_smp(MSR_MTRRfix4K_F8000, 0); + /* Mark 3.5-4GB as UC, anything not specified defaults to WB */ + wrmsr_smp(MTRRphysBase_MSR(0), 0xe0000000ull | 0); + wrmsr_smp(MTRRphysMask_MSR(0), ~(0x20000000ull - 1) | 0x800); + wrmsr_smp(MSR_MTRRdefType, 0xc06); +} + +void ram_probe(void) +{ + if (cmos_readb(0x34) | cmos_readb(0x35)) + ram_size = (cmos_readb(0x34) | (cmos_readb(0x35) << 8)) * 65536 + + 16 * 1024 * 1024; + else + ram_size = (cmos_readb(0x17) | (cmos_readb(0x18) << 8)) * 1024; + + if (cmos_readb(0x5b) | cmos_readb(0x5c) | cmos_readb(0x5d)) + ram_end = (((uint64_t)cmos_readb(0x5b) << 16) | + ((uint64_t)cmos_readb(0x5c) << 24) | + ((uint64_t)cmos_readb(0x5d) << 32)) + (1ull << 32); + else + ram_end = ram_size; + + BX_INFO("end of ram=%ldMB\n", ram_end >> 20); + + BX_INFO("ram_size=0x%08lx\n", ram_size); +#ifdef BX_USE_EBDA_TABLES + ebda_cur_addr = ((*(uint16_t *)(0x40e)) << 4) + 0x380; + BX_INFO("ebda_cur_addr: 0x%08lx\n", ebda_cur_addr); +#endif +} + +/****************************************************/ +/* SMP probe */ + +extern uint8_t smp_ap_boot_code_start; +extern uint8_t smp_ap_boot_code_end; + +/* find the number of CPUs by launching a SIPI to them */ +void smp_probe(void) +{ + uint32_t val, sipi_vector; + + writew(&smp_cpus, 1); + if (cpuid_features & CPUID_APIC) { + + /* enable local APIC */ + val = readl(APIC_BASE + APIC_SVR); + val |= APIC_ENABLED; + writel(APIC_BASE + APIC_SVR, val); + + /* copy AP boot code */ + memcpy((void *)AP_BOOT_ADDR, &smp_ap_boot_code_start, + &smp_ap_boot_code_end - &smp_ap_boot_code_start); + + /* broadcast SIPI */ + writel(APIC_BASE + APIC_ICR_LOW, 0x000C4500); + sipi_vector = AP_BOOT_ADDR >> 12; + writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector); + +#ifndef BX_QEMU + delay_ms(10); +#else + while (cmos_readb(0x5f) + 1 != readw(&smp_cpus)) + ; +#endif + } + BX_INFO("Found %d cpu(s)\n", readw(&smp_cpus)); +} + +/****************************************************/ +/* PCI init */ + +#define PCI_ADDRESS_SPACE_MEM 0x00 +#define PCI_ADDRESS_SPACE_IO 0x01 +#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08 + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 + +#define PCI_DEVICES_MAX 64 + +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 + +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_APPLE 0x106b + +typedef struct PCIDevice { + int bus; + int devfn; +} PCIDevice; + +static uint32_t pci_bios_io_addr; +static uint32_t pci_bios_mem_addr; +static uint32_t pci_bios_bigmem_addr; +/* host irqs corresponding to PCI irqs A-D */ +static uint8_t pci_irqs[4] = { 10, 10, 11, 11 }; +static PCIDevice i440_pcidev; + +static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) +{ + outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc)); + outl(0xcfc, val); +} + +static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val) +{ + outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc)); + outw(0xcfc + (addr & 2), val); +} + +static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val) +{ + outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc)); + outb(0xcfc + (addr & 3), val); +} + +static uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) +{ + outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc)); + return inl(0xcfc); +} + +static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr) +{ + outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc)); + return inw(0xcfc + (addr & 2)); +} + +static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr) +{ + outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc)); + return inb(0xcfc + (addr & 3)); +} + +static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) +{ + uint16_t cmd; + uint32_t ofs, old_addr; + + if ( region_num == PCI_ROM_SLOT ) { + ofs = 0x30; + }else{ + ofs = 0x10 + region_num * 4; + } + + old_addr = pci_config_readl(d, ofs); + + pci_config_writel(d, ofs, addr); + BX_INFO("region %d: 0x%08x\n", region_num, addr); + + /* enable memory mappings */ + cmd = pci_config_readw(d, PCI_COMMAND); + if ( region_num == PCI_ROM_SLOT ) + cmd |= 2; + else if (old_addr & PCI_ADDRESS_SPACE_IO) + cmd |= 1; + else + cmd |= 2; + pci_config_writew(d, PCI_COMMAND, cmd); +} + +/* return the global irq number corresponding to a given device irq + pin. We could also use the bus number to have a more precise + mapping. */ +static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + int slot_addend; + slot_addend = (pci_dev->devfn >> 3) - 1; + return (irq_num + slot_addend) & 3; +} + +static void find_bios_table_area(void) +{ + unsigned long addr; + for(addr = 0xf0000; addr < 0x100000; addr += 16) { + if (*(uint32_t *)addr == 0xaafb4442) { + bios_table_cur_addr = addr + 8; + bios_table_end_addr = bios_table_cur_addr + *(uint32_t *)(addr + 4); + BX_INFO("bios_table_addr: 0x%08lx end=0x%08lx\n", + bios_table_cur_addr, bios_table_end_addr); + return; + } + } + return; +} + +static void bios_shadow_init(PCIDevice *d) +{ + int v; + + if (bios_table_cur_addr == 0) + return; + + /* remap the BIOS to shadow RAM an keep it read/write while we + are writing tables */ + v = pci_config_readb(d, 0x59); + v &= 0xcf; + pci_config_writeb(d, 0x59, v); + memcpy((void *)BIOS_TMP_STORAGE, (void *)0x000f0000, 0x10000); + v |= 0x30; + pci_config_writeb(d, 0x59, v); + memcpy((void *)0x000f0000, (void *)BIOS_TMP_STORAGE, 0x10000); + + i440_pcidev = *d; +} + +static void bios_lock_shadow_ram(void) +{ + PCIDevice *d = &i440_pcidev; + int v; + + wbinvd(); + v = pci_config_readb(d, 0x59); + v = (v & 0x0f) | (0x10); + pci_config_writeb(d, 0x59, v); +} + +static void pci_bios_init_bridges(PCIDevice *d) +{ + uint16_t vendor_id, device_id; + + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + + if (vendor_id == PCI_VENDOR_ID_INTEL && + (device_id == PCI_DEVICE_ID_INTEL_82371SB_0 || + device_id == PCI_DEVICE_ID_INTEL_82371AB_0)) { + int i, irq; + uint8_t elcr[2]; + + /* PIIX3/PIIX4 PCI to ISA bridge */ + + elcr[0] = 0x00; + elcr[1] = 0x00; + for(i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + /* activate irq remapping in PIIX */ + pci_config_writeb(d, 0x60 + i, irq); + } + outb(0x4d0, elcr[0]); + outb(0x4d1, elcr[1]); + BX_INFO("PIIX3/PIIX4 init: elcr=%02x %02x\n", + elcr[0], elcr[1]); + } else if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82441) { + /* i440 PCI bridge */ + bios_shadow_init(d); + } +} + +extern uint8_t smm_relocation_start, smm_relocation_end; +extern uint8_t smm_code_start, smm_code_end; + +#ifdef BX_USE_SMM +static void smm_init(PCIDevice *d) +{ + uint32_t value; + + /* check if SMM init is already done */ + value = pci_config_readl(d, 0x58); + if ((value & (1 << 25)) == 0) { + + /* enable the SMM memory window */ + pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x48); + + /* save original memory content */ + memcpy((void *)0xa8000, (void *)0x38000, 0x8000); + + /* copy the SMM relocation code */ + memcpy((void *)0x38000, &smm_relocation_start, + &smm_relocation_end - &smm_relocation_start); + + /* enable SMI generation when writing to the APMC register */ + pci_config_writel(d, 0x58, value | (1 << 25)); + + /* init APM status port */ + outb(0xb3, 0x01); + + /* raise an SMI interrupt */ + outb(0xb2, 0x00); + + /* wait until SMM code executed */ + while (inb(0xb3) != 0x00); + + /* restore original memory content */ + memcpy((void *)0x38000, (void *)0xa8000, 0x8000); + + /* copy the SMM code */ + memcpy((void *)0xa8000, &smm_code_start, + &smm_code_end - &smm_code_start); + wbinvd(); + + /* close the SMM memory window and enable normal SMM */ + pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x08); + } +} +#endif + +static void piix4_pm_enable(PCIDevice *d) +{ + /* PIIX4 Power Management device (for ACPI) */ + pci_config_writel(d, 0x40, PM_IO_BASE | 1); + pci_config_writeb(d, 0x80, 0x01); /* enable PM io space */ + pci_config_writel(d, 0x90, SMB_IO_BASE | 1); + pci_config_writeb(d, 0xd2, 0x09); /* enable SMBus io space */ +#ifdef BX_USE_SMM + smm_init(d); +#endif +} + +static void pci_bios_init_device(PCIDevice *d) +{ + int class; + uint32_t *paddr; + int i, pin, pic_irq, vendor_id, device_id; + + class = pci_config_readw(d, PCI_CLASS_DEVICE); + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + BX_INFO("PCI: bus=%d devfn=0x%02x: vendor_id=0x%04x device_id=0x%04x class=0x%04x\n", + d->bus, d->devfn, vendor_id, device_id, class); + switch(class) { + case 0x0101: /* Mass storage controller - IDE interface */ + if (vendor_id == PCI_VENDOR_ID_INTEL && + (device_id == PCI_DEVICE_ID_INTEL_82371SB_1 || + device_id == PCI_DEVICE_ID_INTEL_82371AB)) { + /* PIIX3/PIIX4 IDE */ + pci_config_writew(d, 0x40, 0x8000); // enable IDE0 + pci_config_writew(d, 0x42, 0x8000); // enable IDE1 + goto default_map; + } else { + /* IDE: we map it as in ISA mode */ + pci_set_io_region_addr(d, 0, 0x1f0); + pci_set_io_region_addr(d, 1, 0x3f4); + pci_set_io_region_addr(d, 2, 0x170); + pci_set_io_region_addr(d, 3, 0x374); + } + break; + case 0x0300: /* Display controller - VGA compatible controller */ + if (vendor_id != 0x1234) + goto default_map; + /* VGA: map frame buffer to default Bochs VBE address */ + pci_set_io_region_addr(d, 0, 0xE0000000); + break; + case 0x0800: /* Generic system peripheral - PIC */ + if (vendor_id == PCI_VENDOR_ID_IBM) { + /* IBM */ + if (device_id == 0x0046 || device_id == 0xFFFF) { + /* MPIC & MPIC2 */ + pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000); + } + } + break; + case 0xff00: + if (vendor_id == PCI_VENDOR_ID_APPLE && + (device_id == 0x0017 || device_id == 0x0022)) { + /* macio bridge */ + pci_set_io_region_addr(d, 0, 0x80800000); + } + break; + default: + default_map: + /* default memory mappings */ + for(i = 0; i < PCI_NUM_REGIONS; i++) { + int ofs; + uint32_t val, size ; + + if (i == PCI_ROM_SLOT) + ofs = 0x30; + else + ofs = 0x10 + i * 4; + pci_config_writel(d, ofs, 0xffffffff); + val = pci_config_readl(d, ofs); + if (val != 0) { + size = (~(val & ~0xf)) + 1; + if (val & PCI_ADDRESS_SPACE_IO) + paddr = &pci_bios_io_addr; + else if (size >= 0x04000000) + paddr = &pci_bios_bigmem_addr; + else + paddr = &pci_bios_mem_addr; + *paddr = (*paddr + size - 1) & ~(size - 1); + pci_set_io_region_addr(d, i, *paddr); + *paddr += size; + /* make memory address page aligned */ + if (!(val & PCI_ADDRESS_SPACE_IO)) + *paddr = (*paddr + 0xfff) & 0xfffff000; + } + } + break; + } + + /* map the interrupt */ + pin = pci_config_readb(d, PCI_INTERRUPT_PIN); + if (pin != 0) { + pin = pci_slot_get_pirq(d, pin - 1); + pic_irq = pci_irqs[pin]; + pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); + } + + if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82371AB_3) { + /* PIIX4 Power Management device (for ACPI) */ + + // acpi sci is hardwired to 9 + pci_config_writeb(d, PCI_INTERRUPT_LINE, 9); + + pm_io_base = PM_IO_BASE; + smb_io_base = SMB_IO_BASE; + pm_sci_int = pci_config_readb(d, PCI_INTERRUPT_LINE); + piix4_pm_enable(d); + acpi_enabled = 1; + } +} + +void pci_for_each_device(void (*init_func)(PCIDevice *d)) +{ + PCIDevice d1, *d = &d1; + int bus, devfn; + uint16_t vendor_id, device_id; + + for(bus = 0; bus < 1; bus++) { + for(devfn = 0; devfn < 256; devfn++) { + d->bus = bus; + d->devfn = devfn; + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + if (vendor_id != 0xffff || device_id != 0xffff) { + init_func(d); + } + } + } +} + +void pci_bios_init(void) +{ + pci_bios_io_addr = 0xc000; + pci_bios_mem_addr = 0xf0000000; + pci_bios_bigmem_addr = ram_size; + if (pci_bios_bigmem_addr < 0x90000000) + pci_bios_bigmem_addr = 0x90000000; + + pci_for_each_device(pci_bios_init_bridges); + + pci_for_each_device(pci_bios_init_device); +} + +/****************************************************/ +/* Multi Processor table init */ + +static void putb(uint8_t **pp, int val) +{ + uint8_t *q; + q = *pp; + *q++ = val; + *pp = q; +} + +static void putstr(uint8_t **pp, const char *str) +{ + uint8_t *q; + q = *pp; + while (*str) + *q++ = *str++; + *pp = q; +} + +static void putle16(uint8_t **pp, int val) +{ + uint8_t *q; + q = *pp; + *q++ = val; + *q++ = val >> 8; + *pp = q; +} + +static void putle32(uint8_t **pp, int val) +{ + uint8_t *q; + q = *pp; + *q++ = val; + *q++ = val >> 8; + *q++ = val >> 16; + *q++ = val >> 24; + *pp = q; +} + +static int mpf_checksum(const uint8_t *data, int len) +{ + int sum, i; + sum = 0; + for(i = 0; i < len; i++) + sum += data[i]; + return sum & 0xff; +} + +static unsigned long align(unsigned long addr, unsigned long v) +{ + return (addr + v - 1) & ~(v - 1); +} + +static void mptable_init(void) +{ + uint8_t *mp_config_table, *q, *float_pointer_struct; + int ioapic_id, i, len; + int mp_config_table_size; + +#ifdef BX_USE_EBDA_TABLES + mp_config_table = (uint8_t *)(ram_size - ACPI_DATA_SIZE - MPTABLE_MAX_SIZE); +#else + bios_table_cur_addr = align(bios_table_cur_addr, 16); + mp_config_table = (uint8_t *)bios_table_cur_addr; +#endif + q = mp_config_table; + putstr(&q, "PCMP"); /* "PCMP signature */ + putle16(&q, 0); /* table length (patched later) */ + putb(&q, 4); /* spec rev */ + putb(&q, 0); /* checksum (patched later) */ +#ifdef BX_QEMU + putstr(&q, "QEMUCPU "); /* OEM id */ +#else + putstr(&q, "BOCHSCPU"); +#endif + putstr(&q, "0.1 "); /* vendor id */ + putle32(&q, 0); /* OEM table ptr */ + putle16(&q, 0); /* OEM table size */ +#ifdef BX_QEMU + if (irq0_override) + putle16(&q, MAX_CPUS + 17); /* entry count */ + else + putle16(&q, MAX_CPUS + 18); /* entry count */ +#else + putle16(&q, MAX_CPUS + 18); /* entry count */ +#endif + putle32(&q, 0xfee00000); /* local APIC addr */ + putle16(&q, 0); /* ext table length */ + putb(&q, 0); /* ext table checksum */ + putb(&q, 0); /* reserved */ + + for(i = 0; i < MAX_CPUS ; i++) { + putb(&q, 0); /* entry type = processor */ + putb(&q, i); /* APIC id */ + putb(&q, 0x11); /* local APIC version number */ + if (i == 0) + putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */ + else if ( i < smp_cpus) + putb(&q, 1); /* cpu flags: enabled */ + else + putb(&q, 0); /* cpu flags: disabled */ + putb(&q, 0); /* cpu signature */ + putb(&q, 6); + putb(&q, 0); + putb(&q, 0); + putle16(&q, 0x201); /* feature flags */ + putle16(&q, 0); + + putle16(&q, 0); /* reserved */ + putle16(&q, 0); + putle16(&q, 0); + putle16(&q, 0); + } + + /* isa bus */ + putb(&q, 1); /* entry type = bus */ + putb(&q, 0); /* bus ID */ + putstr(&q, "ISA "); + + /* ioapic */ + ioapic_id = smp_cpus; + putb(&q, 2); /* entry type = I/O APIC */ + putb(&q, ioapic_id); /* apic ID */ + putb(&q, 0x11); /* I/O APIC version number */ + putb(&q, 1); /* enable */ + putle32(&q, 0xfec00000); /* I/O APIC addr */ + + /* irqs */ + for(i = 0; i < 16; i++) { +#ifdef BX_QEMU + /* One entry per ioapic interrupt destination. Destination 2 is covered + * by irq0->inti2 override (i == 0). Source IRQ 2 is unused + */ + if (irq0_override && i == 2) + continue; +#endif + putb(&q, 3); /* entry type = I/O interrupt */ + putb(&q, 0); /* interrupt type = vectored interrupt */ + putb(&q, 0); /* flags: po=0, el=0 */ + putb(&q, 0); + putb(&q, 0); /* source bus ID = ISA */ + putb(&q, i); /* source bus IRQ */ + putb(&q, ioapic_id); /* dest I/O APIC ID */ +#ifdef BX_QEMU + if (irq0_override && i == 0) + putb(&q, 2); /* dest I/O APIC interrupt in */ + else +#endif + putb(&q, i); /* dest I/O APIC interrupt in */ + } + /* patch length */ + len = q - mp_config_table; + mp_config_table[4] = len; + mp_config_table[5] = len >> 8; + + mp_config_table[7] = -mpf_checksum(mp_config_table, q - mp_config_table); + + mp_config_table_size = q - mp_config_table; + +#ifndef BX_USE_EBDA_TABLES + bios_table_cur_addr += mp_config_table_size; +#endif + + /* floating pointer structure */ +#ifdef BX_USE_EBDA_TABLES + ebda_cur_addr = align(ebda_cur_addr, 16); + float_pointer_struct = (uint8_t *)ebda_cur_addr; +#else + bios_table_cur_addr = align(bios_table_cur_addr, 16); + float_pointer_struct = (uint8_t *)bios_table_cur_addr; +#endif + q = float_pointer_struct; + putstr(&q, "_MP_"); + /* pointer to MP config table */ + putle32(&q, (unsigned long)mp_config_table); + + putb(&q, 1); /* length in 16 byte units */ + putb(&q, 4); /* MP spec revision */ + putb(&q, 0); /* checksum (patched later) */ + putb(&q, 0); /* MP feature byte 1 */ + + putb(&q, 0); + putb(&q, 0); + putb(&q, 0); + putb(&q, 0); + float_pointer_struct[10] = + -mpf_checksum(float_pointer_struct, q - float_pointer_struct); +#ifdef BX_USE_EBDA_TABLES + ebda_cur_addr += (q - float_pointer_struct); +#else + bios_table_cur_addr += (q - float_pointer_struct); +#endif + BX_INFO("MP table addr=0x%08lx MPC table addr=0x%08lx size=0x%x\n", + (unsigned long)float_pointer_struct, + (unsigned long)mp_config_table, + mp_config_table_size); +} + +/****************************************************/ +/* ACPI tables init */ + +/* Table structure from Linux kernel (the ACPI tables are under the + BSD license) */ + +/* + * All tables must be byte-packed to match the ACPI specification, since + * the tables are provided by the system BIOS. + */ + +#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \ + uint8_t signature [4]; /* ACPI signature (4 ASCII characters) */\ + uint32_t length; /* Length of table, in bytes, including header */\ + uint8_t revision; /* ACPI Specification minor version # */\ + uint8_t checksum; /* To make sum of entire table == 0 */\ + uint8_t oem_id [6]; /* OEM identification */\ + uint8_t oem_table_id [8]; /* OEM table identification */\ + uint32_t oem_revision; /* OEM revision number */\ + uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */\ + uint32_t asl_compiler_revision; /* ASL compiler revision number */ + + +struct acpi_table_header /* ACPI common table header */ +{ + ACPI_TABLE_HEADER_DEF +} __attribute__((__packed__)); + +struct rsdp_descriptor /* Root System Descriptor Pointer */ +{ + uint8_t signature [8]; /* ACPI signature, contains "RSD PTR " */ + uint8_t checksum; /* To make sum of struct == 0 */ + uint8_t oem_id [6]; /* OEM identification */ + uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */ + uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */ + uint32_t length; /* XSDT Length in bytes including hdr */ + uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */ + uint8_t extended_checksum; /* Checksum of entire table */ + uint8_t reserved [3]; /* Reserved field must be 0 */ +} __attribute__((__packed__)); + +#define MAX_RSDT_ENTRIES 100 + +/* + * ACPI 1.0 Root System Description Table (RSDT) + */ +struct rsdt_descriptor_rev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t table_offset_entry [MAX_RSDT_ENTRIES]; /* Array of pointers to other */ + /* ACPI tables */ +} __attribute__((__packed__)); + +/* + * ACPI 1.0 Firmware ACPI Control Structure (FACS) + */ +struct facs_descriptor_rev1 +{ + uint8_t signature[4]; /* ACPI Signature */ + uint32_t length; /* Length of structure, in bytes */ + uint32_t hardware_signature; /* Hardware configuration signature */ + uint32_t firmware_waking_vector; /* ACPI OS waking vector */ + uint32_t global_lock; /* Global Lock */ + uint32_t S4bios_f : 1; /* Indicates if S4BIOS support is present */ + uint32_t reserved1 : 31; /* Must be 0 */ + uint8_t resverved3 [40]; /* Reserved - must be zero */ +} __attribute__((__packed__)); + + +/* + * ACPI 1.0 Fixed ACPI Description Table (FADT) + */ +struct fadt_descriptor_rev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t firmware_ctrl; /* Physical address of FACS */ + uint32_t dsdt; /* Physical address of DSDT */ + uint8_t model; /* System Interrupt Model */ + uint8_t reserved1; /* Reserved */ + uint16_t sci_int; /* System vector of SCI interrupt */ + uint32_t smi_cmd; /* Port address of SMI command port */ + uint8_t acpi_enable; /* Value to write to smi_cmd to enable ACPI */ + uint8_t acpi_disable; /* Value to write to smi_cmd to disable ACPI */ + uint8_t S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ + uint8_t reserved2; /* Reserved - must be zero */ + uint32_t pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */ + uint32_t pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */ + uint32_t pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ + uint32_t pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ + uint32_t pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ + uint32_t pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ + uint32_t gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ + uint32_t gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ + uint8_t pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ + uint8_t pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ + uint8_t pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ + uint8_t pm_tmr_len; /* Byte Length of ports at pm_tm_blk */ + uint8_t gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ + uint8_t gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ + uint8_t gpe1_base; /* Offset in gpe model where gpe1 events start */ + uint8_t reserved3; /* Reserved */ + uint16_t plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ + uint16_t plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ + uint16_t flush_size; /* Size of area read to flush caches */ + uint16_t flush_stride; /* Stride used in flushing caches */ + uint8_t duty_offset; /* Bit location of duty cycle field in p_cnt reg */ + uint8_t duty_width; /* Bit width of duty cycle field in p_cnt reg */ + uint8_t day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ + uint8_t mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ + uint8_t century; /* Index to century in RTC CMOS RAM */ + uint8_t reserved4; /* Reserved */ + uint8_t reserved4a; /* Reserved */ + uint8_t reserved4b; /* Reserved */ +#if 0 + uint32_t wb_invd : 1; /* The wbinvd instruction works properly */ + uint32_t wb_invd_flush : 1; /* The wbinvd flushes but does not invalidate */ + uint32_t proc_c1 : 1; /* All processors support C1 state */ + uint32_t plvl2_up : 1; /* C2 state works on MP system */ + uint32_t pwr_button : 1; /* Power button is handled as a generic feature */ + uint32_t sleep_button : 1; /* Sleep button is handled as a generic feature, or not present */ + uint32_t fixed_rTC : 1; /* RTC wakeup stat not in fixed register space */ + uint32_t rtcs4 : 1; /* RTC wakeup stat not possible from S4 */ + uint32_t tmr_val_ext : 1; /* The tmr_val width is 32 bits (0 = 24 bits) */ + uint32_t reserved5 : 23; /* Reserved - must be zero */ +#else + uint32_t flags; +#endif +} __attribute__((__packed__)); + +/* + * MADT values and structures + */ + +/* Values for MADT PCATCompat */ + +#define DUAL_PIC 0 +#define MULTIPLE_APIC 1 + + +/* Master MADT */ + +struct multiple_apic_table +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t local_apic_address; /* Physical address of local APIC */ +#if 0 + uint32_t PCATcompat : 1; /* A one indicates system also has dual 8259s */ + uint32_t reserved1 : 31; +#else + uint32_t flags; +#endif +} __attribute__((__packed__)); + + +/* Values for Type in APIC sub-headers */ + +#define APIC_PROCESSOR 0 +#define APIC_IO 1 +#define APIC_XRUPT_OVERRIDE 2 +#define APIC_NMI 3 +#define APIC_LOCAL_NMI 4 +#define APIC_ADDRESS_OVERRIDE 5 +#define APIC_IO_SAPIC 6 +#define APIC_LOCAL_SAPIC 7 +#define APIC_XRUPT_SOURCE 8 +#define APIC_RESERVED 9 /* 9 and greater are reserved */ + +#define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\ + uint8_t type; \ + uint8_t length; + +/* + * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) + */ +/* Sub-structures for MADT */ + +struct madt_processor_apic +{ + ACPI_SUB_HEADER_DEF + uint8_t processor_id; /* ACPI processor id */ + uint8_t local_apic_id; /* Processor's local APIC id */ +#if 0 + uint32_t processor_enabled: 1; /* Processor is usable if set */ + uint32_t reserved2 : 31; /* Reserved, must be zero */ +#else + uint32_t flags; +#endif +} __attribute__((__packed__)); + +/* + * SRAT (NUMA topology description) table + */ + +#define SRAT_PROCESSOR 0 +#define SRAT_MEMORY 1 + +struct system_resource_affinity_table +{ + ACPI_TABLE_HEADER_DEF + uint32_t reserved1; + uint32_t reserved2[2]; +}; + +struct srat_processor_affinity +{ + ACPI_SUB_HEADER_DEF + uint8_t proximity_lo; + uint8_t local_apic_id; + uint32_t flags; + uint8_t local_sapic_eid; + uint8_t proximity_hi[3]; + uint32_t reserved; +}; + +struct srat_memory_affinity +{ + ACPI_SUB_HEADER_DEF + uint8_t proximity[4]; + uint16_t reserved1; + uint32_t base_addr_low,base_addr_high; + uint32_t length_low,length_high; + uint32_t reserved2; + uint32_t flags; + uint32_t reserved3[2]; +}; + +#ifdef BX_QEMU +/* + * * ACPI 2.0 Generic Address Space definition. + * */ +struct acpi_20_generic_address { + uint8_t address_space_id; + uint8_t register_bit_width; + uint8_t register_bit_offset; + uint8_t reserved; + uint64_t address; +} __attribute__((__packed__)); + +/* + * HPET Description Table + */ +struct acpi_20_hpet { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t timer_block_id; + struct acpi_20_generic_address addr; + uint8_t hpet_number; + uint16_t min_tick; + uint8_t page_protect; +} __attribute__((__packed__)); +#define ACPI_HPET_ADDRESS 0xFED00000UL +#endif + +struct madt_io_apic +{ + ACPI_SUB_HEADER_DEF + uint8_t io_apic_id; /* I/O APIC ID */ + uint8_t reserved; /* Reserved - must be zero */ + uint32_t address; /* APIC physical address */ + uint32_t interrupt; /* Global system interrupt where INTI + * lines start */ +} __attribute__((__packed__)); + +#ifdef BX_QEMU +struct madt_int_override +{ + ACPI_SUB_HEADER_DEF + uint8_t bus; /* Identifies ISA Bus */ + uint8_t source; /* Bus-relative interrupt source */ + uint32_t gsi; /* GSI that source will signal */ + uint16_t flags; /* MPS INTI flags */ +} __attribute__((__packed__)); +#endif + +#include "acpi-dsdt.hex" +#include "acpi-ssdt.hex" + +static inline uint16_t cpu_to_le16(uint16_t x) +{ + return x; +} + +static inline uint32_t cpu_to_le32(uint32_t x) +{ + return x; +} + +static int acpi_checksum(const uint8_t *data, int len) +{ + int sum, i; + sum = 0; + for(i = 0; i < len; i++) + sum += data[i]; + return (-sum) & 0xff; +} + +static void acpi_build_table_header(struct acpi_table_header *h, + char *sig, int len, uint8_t rev) +{ + memcpy(h->signature, sig, 4); + h->length = cpu_to_le32(len); + h->revision = rev; +#ifdef BX_QEMU + memcpy(h->oem_id, "QEMU ", 6); + memcpy(h->oem_table_id, "QEMU", 4); +#else + memcpy(h->oem_id, "BOCHS ", 6); + memcpy(h->oem_table_id, "BXPC", 4); +#endif + memcpy(h->oem_table_id + 4, sig, 4); + h->oem_revision = cpu_to_le32(1); +#ifdef BX_QEMU + memcpy(h->asl_compiler_id, "QEMU", 4); +#else + memcpy(h->asl_compiler_id, "BXPC", 4); +#endif + h->asl_compiler_revision = cpu_to_le32(1); + h->checksum = acpi_checksum((void *)h, len); +} + +static void acpi_build_srat_memory(struct srat_memory_affinity *numamem, + uint64_t base, uint64_t len, int node, int enabled) +{ + numamem->type = SRAT_MEMORY; + numamem->length = sizeof(*numamem); + memset (numamem->proximity, 0 ,4); + numamem->proximity[0] = node; + numamem->flags = cpu_to_le32(!!enabled); + numamem->base_addr_low = base & 0xFFFFFFFF; + numamem->base_addr_high = base >> 32; + numamem->length_low = len & 0xFFFFFFFF; + numamem->length_high = len >> 32; + return; +} + +/* base_addr must be a multiple of 4KB */ +void acpi_bios_init(void) +{ + struct rsdp_descriptor *rsdp; + struct rsdt_descriptor_rev1 *rsdt; + struct fadt_descriptor_rev1 *fadt; + struct facs_descriptor_rev1 *facs; + struct multiple_apic_table *madt; + uint8_t *dsdt, *ssdt; +#ifdef BX_QEMU + struct system_resource_affinity_table *srat; + struct acpi_20_hpet *hpet; + uint32_t hpet_addr; +#endif + uint32_t base_addr, rsdt_addr, fadt_addr, addr, facs_addr, dsdt_addr, ssdt_addr; + uint32_t acpi_tables_size, madt_addr, madt_size, rsdt_size, madt_end, rsdt_end; + uint32_t srat_addr,srat_size; + uint16_t i, external_tables; + int nb_numa_nodes; + int nb_rsdt_entries = 0; + + /* reserve memory space for tables */ +#ifdef BX_USE_EBDA_TABLES + ebda_cur_addr = align(ebda_cur_addr, 16); + rsdp = (void *)(ebda_cur_addr); + ebda_cur_addr += sizeof(*rsdp); +#else + bios_table_cur_addr = align(bios_table_cur_addr, 16); + rsdp = (void *)(bios_table_cur_addr); + bios_table_cur_addr += sizeof(*rsdp); +#endif + +#ifdef BX_QEMU + external_tables = acpi_additional_tables(); +#else + external_tables = 0; +#endif + + addr = base_addr = ram_size - ACPI_DATA_SIZE; + rsdt_addr = addr; + rsdt = (void *)(addr); + rsdt_size = sizeof(*rsdt); + addr += rsdt_size; + + fadt_addr = addr; + fadt = (void *)(addr); + addr += sizeof(*fadt); + + /* XXX: FACS should be in RAM */ + addr = (addr + 63) & ~63; /* 64 byte alignment for FACS */ + facs_addr = addr; + facs = (void *)(addr); + addr += sizeof(*facs); + + dsdt_addr = addr; + dsdt = (void *)(addr); + addr += sizeof(DSDTCode); + + ssdt_addr = addr; + ssdt = (void *)(addr); + addr += sizeof(SSDTCode); + +#ifdef BX_QEMU + qemu_cfg_select(QEMU_CFG_NUMA); + nb_numa_nodes = qemu_cfg_get64(); +#else + nb_numa_nodes = 0; +#endif + if (nb_numa_nodes > 0) { + addr = (addr + 7) & ~7; + srat_addr = addr; + srat_size = sizeof(*srat) + + sizeof(struct srat_processor_affinity) * smp_cpus + + sizeof(struct srat_memory_affinity) * (nb_numa_nodes + 2); + srat = (void *)(addr); + addr += srat_size; + } else { + srat_addr = addr; + srat = (void*)(addr); + srat_size = 0; + } + + addr = (addr + 7) & ~7; + madt_addr = addr; + madt_size = sizeof(*madt) + + sizeof(struct madt_processor_apic) * MAX_CPUS + +#ifdef BX_QEMU + sizeof(struct madt_io_apic) + sizeof(struct madt_int_override) * MAX_INT_OVERRIDES; +#else + sizeof(struct madt_io_apic); +#endif + madt = (void *)(addr); + addr += madt_size; + +#ifdef BX_QEMU + addr = (addr + 7) & ~7; + hpet_addr = addr; + hpet = (void *)(addr); + addr += sizeof(*hpet); +#endif + + /* RSDP */ + memset(rsdp, 0, sizeof(*rsdp)); + memcpy(rsdp->signature, "RSD PTR ", 8); +#ifdef BX_QEMU + memcpy(rsdp->oem_id, "QEMU ", 6); +#else + memcpy(rsdp->oem_id, "BOCHS ", 6); +#endif + rsdp->rsdt_physical_address = cpu_to_le32(rsdt_addr); + rsdp->checksum = acpi_checksum((void *)rsdp, 20); + + /* FADT */ + memset(fadt, 0, sizeof(*fadt)); + fadt->firmware_ctrl = cpu_to_le32(facs_addr); + fadt->dsdt = cpu_to_le32(dsdt_addr); + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(pm_sci_int); + fadt->smi_cmd = cpu_to_le32(SMI_CMD_IO_ADDR); + fadt->acpi_enable = 0xf1; + fadt->acpi_disable = 0xf0; + fadt->pm1a_evt_blk = cpu_to_le32(pm_io_base); + fadt->pm1a_cnt_blk = cpu_to_le32(pm_io_base + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(pm_io_base + 0x08); + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported + fadt->gpe0_blk = cpu_to_le32(0xafe0); + fadt->gpe0_blk_len = 4; + /* WBINVD + PROC_C1 + SLP_BUTTON + FIX_RTC */ + fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 6)); + acpi_build_table_header((struct acpi_table_header *)fadt, "FACP", + sizeof(*fadt), 1); + + /* FACS */ + memset(facs, 0, sizeof(*facs)); + memcpy(facs->signature, "FACS", 4); + facs->length = cpu_to_le32(sizeof(*facs)); + BX_INFO("Firmware waking vector %p\n", &facs->firmware_waking_vector); + + /* DSDT */ + memcpy(dsdt, DSDTCode, sizeof(DSDTCode)); + + /* SSDT */ + memcpy(ssdt, SSDTCode, sizeof(SSDTCode)); + + /* MADT */ + { + struct madt_processor_apic *apic; + struct madt_io_apic *io_apic; +#ifdef BX_QEMU + struct madt_int_override *int_override; +#endif + + memset(madt, 0, madt_size); + madt->local_apic_address = cpu_to_le32(0xfee00000); + madt->flags = cpu_to_le32(1); + *(uint32_t*)APIC_MADT_PTR = apic = (void *)(madt + 1); + for(i=0;i<MAX_CPUS;i++) { + apic->type = APIC_PROCESSOR; + apic->length = sizeof(*apic); + apic->processor_id = i; + apic->local_apic_id = i; + if (i < smp_cpus) + apic->flags = cpu_to_le32(1); + else + apic->flags = 0; + apic++; + } + io_apic = (void *)apic; + io_apic->type = APIC_IO; + io_apic->length = sizeof(*io_apic); + io_apic->io_apic_id = smp_cpus; + io_apic->address = cpu_to_le32(0xfec00000); + io_apic->interrupt = cpu_to_le32(0); + io_apic++; + int_override = (struct madt_int_override*)(io_apic); +#ifdef BX_QEMU + if (irq0_override) { + memset(int_override, 0, sizeof(*int_override)); + int_override->type = APIC_XRUPT_OVERRIDE; + int_override->length = sizeof(*int_override); + int_override->source = 0; + int_override->gsi = 2; + int_override->flags = 0; /* conforms to bus specifications */ + int_override++; + } +#endif + for (i = 0; i < 16; i++) { + if (PCI_ISA_IRQ_MASK & (1U << i)) { + memset(int_override, 0, sizeof(*int_override)); + int_override->type = APIC_XRUPT_OVERRIDE; + int_override->length = sizeof(*int_override); + int_override->source = i; + int_override->gsi = i; + int_override->flags = 0xd; /* active high, level triggered */ + } else { + /* No need for a INT source override structure. */ + continue; + } + int_override++; + } + madt_end = (uint32_t)int_override; + madt_size = madt_end - madt_addr; + acpi_build_table_header((struct acpi_table_header *)madt, + "APIC", madt_size, 1); + } + + memset(rsdt, 0, rsdt_size); +#ifdef BX_QEMU + /* SRAT */ + if (nb_numa_nodes > 0) { + struct srat_processor_affinity *core; + struct srat_memory_affinity *numamem; + int slots; + uint64_t mem_len, mem_base, next_base = 0, curnode; + + qemu_cfg_select(QEMU_CFG_NUMA); + qemu_cfg_get64(); + memset (srat, 0 , srat_size); + srat->reserved1=1; + + core = (void*)(srat + 1); + for (i = 0; i < smp_cpus; ++i) { + core->type = SRAT_PROCESSOR; + core->length = sizeof(*core); + core->local_apic_id = i; + curnode = qemu_cfg_get64(); + core->proximity_lo = curnode; + memset (core->proximity_hi, 0, 3); + core->local_sapic_eid = 0; + if (i < smp_cpus) + core->flags = cpu_to_le32(1); + else + core->flags = 0; + core++; + } + + /* the memory map is a bit tricky, it contains at least one hole + * from 640k-1M and possibly another one from 3.5G-4G. + */ + numamem = (void*)core; slots = 0; + acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); + next_base = 1024 * 1024; numamem++;slots++; + for (i = 1; i < nb_numa_nodes + 1; ++i) { + mem_base = next_base; + mem_len = qemu_cfg_get64(); + if (i == 1) mem_len -= 1024 * 1024; + next_base = mem_base + mem_len; + + /* Cut out the PCI hole */ + if (mem_base <= ram_size && next_base > ram_size) { + mem_len -= next_base - ram_size; + if (mem_len > 0) { + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + numamem++; slots++; + } + mem_base = 1ULL << 32; + mem_len = next_base - ram_size; + next_base += (1ULL << 32) - ram_size; + } + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + numamem++; slots++; + } + for (; slots < nb_numa_nodes + 2; slots++) { + acpi_build_srat_memory(numamem, 0, 0, 0, 0); + numamem++; + } + + acpi_build_table_header((struct acpi_table_header *)srat, + "SRAT", srat_size, 1); + } + + /* HPET */ + memset(hpet, 0, sizeof(*hpet)); + /* Note timer_block_id value must be kept in sync with value advertised by + * emulated hpet + */ + hpet->timer_block_id = cpu_to_le32(0x8086a201); + hpet->addr.address = cpu_to_le32(ACPI_HPET_ADDRESS); + acpi_build_table_header((struct acpi_table_header *)hpet, + "HPET", sizeof(*hpet), 1); + +#endif + + /* RSDT */ + rsdt->table_offset_entry[nb_rsdt_entries++] = cpu_to_le32(fadt_addr); + /* On real hardware the SSDT seems to come before the MADT (APIC) */ + rsdt->table_offset_entry[nb_rsdt_entries++] = cpu_to_le32(ssdt_addr); + rsdt->table_offset_entry[nb_rsdt_entries++] = cpu_to_le32(madt_addr); +#ifdef BX_QEMU + rsdt->table_offset_entry[nb_rsdt_entries++] = cpu_to_le32(hpet_addr); + if (nb_numa_nodes > 0) + rsdt->table_offset_entry[nb_rsdt_entries++] = cpu_to_le32(srat_addr); + acpi_additional_tables(); /* resets cfg to required entry */ + for(i = 0; i < external_tables; i++) { + uint16_t len; + if(acpi_load_table(i, addr, &len) < 0) + BX_PANIC("Failed to load ACPI table from QEMU\n"); + rsdt->table_offset_entry[nb_rsdt_entries++] = cpu_to_le32(addr); + addr += len; + if ((addr >= ram_size) || (nb_rsdt_entries > MAX_RSDT_ENTRIES)) + BX_PANIC("ACPI table overflow\n"); + } +#endif + rsdt_end = (uint32_t)(&rsdt->table_offset_entry[nb_rsdt_entries]); + rsdt_size = rsdt_end - rsdt_addr; + acpi_build_table_header((struct acpi_table_header *)rsdt, "RSDT", + rsdt_size, 1); + + acpi_tables_size = addr - base_addr; + + BX_INFO("ACPI tables: RSDP addr=0x%08lx ACPI DATA addr=0x%08lx size=0x%x\n", + (unsigned long)rsdp, + (unsigned long)rsdt, acpi_tables_size); + +} + +/* SMBIOS entry point -- must be written to a 16-bit aligned address + between 0xf0000 and 0xfffff. + */ +struct smbios_entry_point { + char anchor_string[4]; + uint8_t checksum; + uint8_t length; + uint8_t smbios_major_version; + uint8_t smbios_minor_version; + uint16_t max_structure_size; + uint8_t entry_point_revision; + uint8_t formatted_area[5]; + char intermediate_anchor_string[5]; + uint8_t intermediate_checksum; + uint16_t structure_table_length; + uint32_t structure_table_address; + uint16_t number_of_structures; + uint8_t smbios_bcd_revision; +} __attribute__((__packed__)); + +/* This goes at the beginning of every SMBIOS structure. */ +struct smbios_structure_header { + uint8_t type; + uint8_t length; + uint16_t handle; +} __attribute__((__packed__)); + +/* SMBIOS type 0 - BIOS Information */ +struct smbios_type_0 { + struct smbios_structure_header header; + uint8_t vendor_str; + uint8_t bios_version_str; + uint16_t bios_starting_address_segment; + uint8_t bios_release_date_str; + uint8_t bios_rom_size; + uint8_t bios_characteristics[8]; + uint8_t bios_characteristics_extension_bytes[2]; + uint8_t system_bios_major_release; + uint8_t system_bios_minor_release; + uint8_t embedded_controller_major_release; + uint8_t embedded_controller_minor_release; +} __attribute__((__packed__)); + +/* SMBIOS type 1 - System Information */ +struct smbios_type_1 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t product_name_str; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t uuid[16]; + uint8_t wake_up_type; + uint8_t sku_number_str; + uint8_t family_str; +} __attribute__((__packed__)); + +/* SMBIOS type 3 - System Enclosure (v2.3) */ +struct smbios_type_3 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t type; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t boot_up_state; + uint8_t power_supply_state; + uint8_t thermal_state; + uint8_t security_status; + uint32_t oem_defined; + uint8_t height; + uint8_t number_of_power_cords; + uint8_t contained_element_count; + // contained elements follow +} __attribute__((__packed__)); + +/* SMBIOS type 4 - Processor Information (v2.0) */ +struct smbios_type_4 { + struct smbios_structure_header header; + uint8_t socket_designation_str; + uint8_t processor_type; + uint8_t processor_family; + uint8_t processor_manufacturer_str; + uint32_t processor_id[2]; + uint8_t processor_version_str; + uint8_t voltage; + uint16_t external_clock; + uint16_t max_speed; + uint16_t current_speed; + uint8_t status; + uint8_t processor_upgrade; + uint16_t l1_cache_handle; + uint16_t l2_cache_handle; + uint16_t l3_cache_handle; +} __attribute__((__packed__)); + +/* SMBIOS type 16 - Physical Memory Array + * Associated with one type 17 (Memory Device). + */ +struct smbios_type_16 { + struct smbios_structure_header header; + uint8_t location; + uint8_t use; + uint8_t error_correction; + uint32_t maximum_capacity; + uint16_t memory_error_information_handle; + uint16_t number_of_memory_devices; +} __attribute__((__packed__)); + +/* SMBIOS type 17 - Memory Device + * Associated with one type 19 + */ +struct smbios_type_17 { + struct smbios_structure_header header; + uint16_t physical_memory_array_handle; + uint16_t memory_error_information_handle; + uint16_t total_width; + uint16_t data_width; + uint16_t size; + uint8_t form_factor; + uint8_t device_set; + uint8_t device_locator_str; + uint8_t bank_locator_str; + uint8_t memory_type; + uint16_t type_detail; +} __attribute__((__packed__)); + +/* SMBIOS type 19 - Memory Array Mapped Address */ +struct smbios_type_19 { + struct smbios_structure_header header; + uint32_t starting_address; + uint32_t ending_address; + uint16_t memory_array_handle; + uint8_t partition_width; +} __attribute__((__packed__)); + +/* SMBIOS type 20 - Memory Device Mapped Address */ +struct smbios_type_20 { + struct smbios_structure_header header; + uint32_t starting_address; + uint32_t ending_address; + uint16_t memory_device_handle; + uint16_t memory_array_mapped_address_handle; + uint8_t partition_row_position; + uint8_t interleave_position; + uint8_t interleaved_data_depth; +} __attribute__((__packed__)); + +/* SMBIOS type 32 - System Boot Information */ +struct smbios_type_32 { + struct smbios_structure_header header; + uint8_t reserved[6]; + uint8_t boot_status; +} __attribute__((__packed__)); + +/* SMBIOS type 127 -- End-of-table */ +struct smbios_type_127 { + struct smbios_structure_header header; +} __attribute__((__packed__)); + +static void +smbios_entry_point_init(void *start, + uint16_t max_structure_size, + uint16_t structure_table_length, + uint32_t structure_table_address, + uint16_t number_of_structures) +{ + uint8_t sum; + int i; + struct smbios_entry_point *ep = (struct smbios_entry_point *)start; + + memcpy(ep->anchor_string, "_SM_", 4); + ep->length = 0x1f; + ep->smbios_major_version = 2; + ep->smbios_minor_version = 4; + ep->max_structure_size = max_structure_size; + ep->entry_point_revision = 0; + memset(ep->formatted_area, 0, 5); + memcpy(ep->intermediate_anchor_string, "_DMI_", 5); + + ep->structure_table_length = structure_table_length; + ep->structure_table_address = structure_table_address; + ep->number_of_structures = number_of_structures; + ep->smbios_bcd_revision = 0x24; + + ep->checksum = 0; + ep->intermediate_checksum = 0; + + sum = 0; + for (i = 0; i < 0x10; i++) + sum += ((int8_t *)start)[i]; + ep->checksum = -sum; + + sum = 0; + for (i = 0x10; i < ep->length; i++) + sum += ((int8_t *)start)[i]; + ep->intermediate_checksum = -sum; + } + +struct smbios_header { + uint16_t length; + uint8_t type; +} __attribute__((__packed__)); + +struct smbios_field { + struct smbios_header header; + uint8_t type; + uint16_t offset; + uint8_t data[]; +} __attribute__((__packed__)); + +struct smbios_table { + struct smbios_header header; + uint8_t data[]; +} __attribute__((__packed__)); + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +static size_t +smbios_load_field(int type, size_t offset, void *addr) +{ +#ifdef BX_QEMU + int i; + + for (i = smbios_entries(); i > 0; i--) { + struct smbios_field field; + + qemu_cfg_read((uint8_t *)&field, sizeof(struct smbios_header)); + field.header.length -= sizeof(struct smbios_header); + + if (field.header.type != SMBIOS_FIELD_ENTRY) { + while (field.header.length--) + inb(QEMU_CFG_DATA_PORT); + continue; + } + + qemu_cfg_read((uint8_t *)&field.type, + sizeof(field) - sizeof(struct smbios_header)); + field.header.length -= sizeof(field) - sizeof(struct smbios_header); + + if (field.type != type || field.offset != offset) { + while (field.header.length--) + inb(QEMU_CFG_DATA_PORT); + continue; + } + + qemu_cfg_read(addr, field.header.length); + return (size_t)field.header.length; + } +#endif + return 0; +} + +#define load_str_field_with_default(type, field, def) do { \ + size = smbios_load_field(type, offsetof(struct smbios_type_##type, \ + field), end); \ + if (size > 0) { \ + end += size; \ + } else { \ + memcpy(end, def, sizeof(def)); \ + end += sizeof(def); \ + } \ + p->field = ++str_index; \ +} while (0) + +#define load_str_field_or_skip(type, field) do { \ + size = smbios_load_field(type, offsetof(struct smbios_type_##type, \ + field), end); \ + if (size > 0) { \ + end += size; \ + p->field = ++str_index; \ + } else { \ + p->field = 0; \ + } \ +} while (0) + +/* Type 0 -- BIOS Information */ +#define RELEASE_DATE_STR "01/01/2007" +static void * +smbios_init_type_0(void *start) +{ + struct smbios_type_0 *p = (struct smbios_type_0 *)start; + char *end = (char *)start + sizeof(struct smbios_type_0); + size_t size; + int str_index = 0; + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + load_str_field_with_default(0, vendor_str, BX_APPNAME); + load_str_field_with_default(0, bios_version_str, BX_APPNAME); + + p->bios_starting_address_segment = 0xe800; + + load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR); + + p->bios_rom_size = 0; /* FIXME */ + + memset(p->bios_characteristics, 0, 8); + p->bios_characteristics[0] = 0x08; /* BIOS characteristics not supported */ + p->bios_characteristics_extension_bytes[0] = 0; + p->bios_characteristics_extension_bytes[1] = 0; + + if (!smbios_load_field(0, offsetof(struct smbios_type_0, + system_bios_major_release), + &p->system_bios_major_release)) + p->system_bios_major_release = 1; + + if (!smbios_load_field(0, offsetof(struct smbios_type_0, + system_bios_minor_release), + &p->system_bios_minor_release)) + p->system_bios_minor_release = 0; + + p->embedded_controller_major_release = 0xff; + p->embedded_controller_minor_release = 0xff; + + *end = 0; + end++; + + return end; +} + +/* Type 1 -- System Information */ +static void * +smbios_init_type_1(void *start) +{ + struct smbios_type_1 *p = (struct smbios_type_1 *)start; + char *end = (char *)start + sizeof(struct smbios_type_1); + size_t size; + int str_index = 0; + + p->header.type = 1; + p->header.length = sizeof(struct smbios_type_1); + p->header.handle = 0x100; + + load_str_field_or_skip(1, manufacturer_str); + load_str_field_or_skip(1, product_name_str); + load_str_field_or_skip(1, version_str); + load_str_field_or_skip(1, serial_number_str); + + size = smbios_load_field(1, offsetof(struct smbios_type_1, + uuid), &p->uuid); + if (size == 0) + memset(p->uuid, 0, 16); + + p->wake_up_type = 0x06; /* power switch */ + + load_str_field_or_skip(1, sku_number_str); + load_str_field_or_skip(1, family_str); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 3 -- System Enclosure */ +static void * +smbios_init_type_3(void *start) +{ + struct smbios_type_3 *p = (struct smbios_type_3 *)start; + + p->header.type = 3; + p->header.length = sizeof(struct smbios_type_3); + p->header.handle = 0x300; + + p->manufacturer_str = 0; + p->type = 0x01; /* other */ + p->version_str = 0; + p->serial_number_str = 0; + p->asset_tag_number_str = 0; + p->boot_up_state = 0x03; /* safe */ + p->power_supply_state = 0x03; /* safe */ + p->thermal_state = 0x03; /* safe */ + p->security_status = 0x02; /* unknown */ + p->oem_defined = 0; + p->height = 0; + p->number_of_power_cords = 0; + p->contained_element_count = 0; + + start += sizeof(struct smbios_type_3); + *((uint16_t *)start) = 0; + + return start+2; +} + +/* Type 4 -- Processor Information */ +static void * +smbios_init_type_4(void *start, unsigned int cpu_number) +{ + struct smbios_type_4 *p = (struct smbios_type_4 *)start; + + p->header.type = 4; + p->header.length = sizeof(struct smbios_type_4); + p->header.handle = 0x400 + cpu_number; + + p->socket_designation_str = 1; + p->processor_type = 0x03; /* CPU */ + p->processor_family = 0x01; /* other */ + p->processor_manufacturer_str = 0; + + p->processor_id[0] = cpuid_signature; + p->processor_id[1] = cpuid_features; + + p->processor_version_str = 0; + p->voltage = 0; + p->external_clock = 0; + + p->max_speed = 0; /* unknown */ + p->current_speed = 0; /* unknown */ + + p->status = 0x41; /* socket populated, CPU enabled */ + p->processor_upgrade = 0x01; /* other */ + + p->l1_cache_handle = 0xffff; /* cache information structure not provided */ + p->l2_cache_handle = 0xffff; + p->l3_cache_handle = 0xffff; + + start += sizeof(struct smbios_type_4); + + memcpy((char *)start, "CPU " "\0" "" "\0" "", 7); + ((char *)start)[4] = cpu_number + '0'; + + return start+7; +} + +/* Type 16 -- Physical Memory Array */ +static void * +smbios_init_type_16(void *start, uint32_t memsize, int nr_mem_devs) +{ + struct smbios_type_16 *p = (struct smbios_type_16*)start; + + p->header.type = 16; + p->header.length = sizeof(struct smbios_type_16); + p->header.handle = 0x1000; + + p->location = 0x01; /* other */ + p->use = 0x03; /* system memory */ + p->error_correction = 0x01; /* other */ + p->maximum_capacity = memsize * 1024; + p->memory_error_information_handle = 0xfffe; /* none provided */ + p->number_of_memory_devices = nr_mem_devs; + + start += sizeof(struct smbios_type_16); + *((uint16_t *)start) = 0; + + return start + 2; +} + +/* Type 17 -- Memory Device */ +static void * +smbios_init_type_17(void *start, uint32_t memory_size_mb, int instance) +{ + struct smbios_type_17 *p = (struct smbios_type_17 *)start; + + p->header.type = 17; + p->header.length = sizeof(struct smbios_type_17); + p->header.handle = 0x1100 + instance; + + p->physical_memory_array_handle = 0x1000; + p->total_width = 64; + p->data_width = 64; +/* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */ + p->size = memory_size_mb; + p->form_factor = 0x09; /* DIMM */ + p->device_set = 0; + p->device_locator_str = 1; + p->bank_locator_str = 0; + p->memory_type = 0x07; /* RAM */ + p->type_detail = 0; + + start += sizeof(struct smbios_type_17); + snprintf(start, 8, "DIMM %d", instance); + start += strlen(start) + 1; + *((uint8_t *)start) = 0; + + return start+1; +} + +/* Type 19 -- Memory Array Mapped Address */ +static void * +smbios_init_type_19(void *start, uint32_t memory_size_mb, int instance) +{ + struct smbios_type_19 *p = (struct smbios_type_19 *)start; + + p->header.type = 19; + p->header.length = sizeof(struct smbios_type_19); + p->header.handle = 0x1300 + instance; + + p->starting_address = instance << 24; + p->ending_address = p->starting_address + (memory_size_mb << 10) - 1; + p->memory_array_handle = 0x1000; + p->partition_width = 1; + + start += sizeof(struct smbios_type_19); + *((uint16_t *)start) = 0; + + return start + 2; +} + +/* Type 20 -- Memory Device Mapped Address */ +static void * +smbios_init_type_20(void *start, uint32_t memory_size_mb, int instance) +{ + struct smbios_type_20 *p = (struct smbios_type_20 *)start; + + p->header.type = 20; + p->header.length = sizeof(struct smbios_type_20); + p->header.handle = 0x1400 + instance; + + p->starting_address = instance << 24; + p->ending_address = p->starting_address + (memory_size_mb << 10) - 1; + p->memory_device_handle = 0x1100 + instance; + p->memory_array_mapped_address_handle = 0x1300 + instance; + p->partition_row_position = 1; + p->interleave_position = 0; + p->interleaved_data_depth = 0; + + start += sizeof(struct smbios_type_20); + + *((uint16_t *)start) = 0; + return start+2; +} + +/* Type 32 -- System Boot Information */ +static void * +smbios_init_type_32(void *start) +{ + struct smbios_type_32 *p = (struct smbios_type_32 *)start; + + p->header.type = 32; + p->header.length = sizeof(struct smbios_type_32); + p->header.handle = 0x2000; + memset(p->reserved, 0, 6); + p->boot_status = 0; /* no errors detected */ + + start += sizeof(struct smbios_type_32); + *((uint16_t *)start) = 0; + + return start+2; +} + +/* Type 127 -- End of Table */ +static void * +smbios_init_type_127(void *start) +{ + struct smbios_type_127 *p = (struct smbios_type_127 *)start; + + p->header.type = 127; + p->header.length = sizeof(struct smbios_type_127); + p->header.handle = 0x7f00; + + start += sizeof(struct smbios_type_127); + *((uint16_t *)start) = 0; + + return start + 2; +} + +static int +smbios_load_external(int type, char **p, unsigned *nr_structs, + unsigned *max_struct_size) +{ +#ifdef BX_QEMU + static uint64_t used_bitmap[4] = { 0 }; + char *start = *p; + int i; + + /* Check if we've already reported these tables */ + if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f))) + return 1; + + /* Don't introduce spurious end markers */ + if (type == 127) + return 0; + + for (i = smbios_entries(); i > 0; i--) { + struct smbios_table table; + struct smbios_structure_header *header = (void *)*p; + int string; + + qemu_cfg_read((uint8_t *)&table, sizeof(struct smbios_header)); + table.header.length -= sizeof(struct smbios_header); + + if (table.header.type != SMBIOS_TABLE_ENTRY) { + while (table.header.length--) + inb(QEMU_CFG_DATA_PORT); + continue; + } + + qemu_cfg_read((uint8_t *)*p, sizeof(struct smbios_structure_header)); + table.header.length -= sizeof(struct smbios_structure_header); + + if (header->type != type) { + while (table.header.length--) + inb(QEMU_CFG_DATA_PORT); + continue; + } + + *p += sizeof(struct smbios_structure_header); + + /* Entries end with a double NULL char, if there's a string at + * the end (length is greater than formatted length), the string + * terminator provides the first NULL. */ + string = header->length < table.header.length + + sizeof(struct smbios_structure_header); + + /* Read the rest and terminate the entry */ + qemu_cfg_read((uint8_t *)*p, table.header.length); + *p += table.header.length; + *((uint8_t*)*p) = 0; + (*p)++; + if (!string) { + *((uint8_t*)*p) = 0; + (*p)++; + } + + (*nr_structs)++; + if (*p - (char *)header > *max_struct_size) + *max_struct_size = *p - (char *)header; + } + + if (start != *p) { + /* Mark that we've reported on this type */ + used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f)); + return 1; + } + +#endif /* !BX_QEMU */ + return 0; +} + +void smbios_init(void) +{ + unsigned cpu_num, nr_structs = 0, max_struct_size = 0; + char *start, *p, *q; + int memsize = (ram_end == ram_size) ? ram_size / (1024 * 1024) : + (ram_end - (1ull << 32) + ram_size) / (1024 * 1024); + int i, nr_mem_devs; + +#ifdef BX_USE_EBDA_TABLES + ebda_cur_addr = align(ebda_cur_addr, 16); + start = (void *)(ebda_cur_addr); +#else + bios_table_cur_addr = align(bios_table_cur_addr, 16); + start = (void *)(bios_table_cur_addr); +#endif + + p = (char *)start + sizeof(struct smbios_entry_point); + +#define add_struct(type, args...) do { \ + if (!smbios_load_external(type, &p, &nr_structs, &max_struct_size)) { \ + q = smbios_init_type_##type(args); \ + nr_structs++; \ + if ((q - p) > max_struct_size) \ + max_struct_size = q - p; \ + p = q; \ + } \ +} while (0) + + add_struct(0, p); + add_struct(1, p); + add_struct(3, p); + for (cpu_num = 1; cpu_num <= smp_cpus; cpu_num++) + add_struct(4, p, cpu_num); + + /* Each 'memory device' covers up to 16GB of address space. */ + nr_mem_devs = (memsize + 0x3fff) >> 14; + add_struct(16, p, memsize, nr_mem_devs); + for ( i = 0; i < nr_mem_devs; i++ ) + { + uint32_t dev_memsize = ((i == (nr_mem_devs - 1)) + ? (((memsize-1) & 0x3fff)+1) : 0x4000); + add_struct(17, p, dev_memsize, i); + add_struct(19, p, dev_memsize, i); + add_struct(20, p, dev_memsize, i); + } + + add_struct(32, p); + /* Add any remaining provided entries before the end marker */ + for (i = 0; i < 256; i++) + smbios_load_external(i, &p, &nr_structs, &max_struct_size); + add_struct(127, p); + +#undef add_struct + + smbios_entry_point_init( + start, max_struct_size, + (p - (char *)start) - sizeof(struct smbios_entry_point), + (uint32_t)(start + sizeof(struct smbios_entry_point)), + nr_structs); + +#ifdef BX_USE_EBDA_TABLES + ebda_cur_addr += (p - (char *)start); +#else + bios_table_cur_addr += (p - (char *)start); +#endif + + BX_INFO("SMBIOS table addr=0x%08lx\n", (unsigned long)start); +} + +static uint32_t find_resume_vector(void) +{ + unsigned long addr, start, end; + +#ifdef BX_USE_EBDA_TABLES + start = align(ebda_cur_addr, 16); + end = 0xa000 << 4; +#else + if (bios_table_cur_addr == 0) + return 0; + start = align(bios_table_cur_addr, 16); + end = bios_table_end_addr; +#endif + + for (addr = start; addr < end; addr += 16) { + if (!memcmp((void*)addr, "RSD PTR ", 8)) { + struct rsdp_descriptor *rsdp = (void*)addr; + struct rsdt_descriptor_rev1 *rsdt = (void*)rsdp->rsdt_physical_address; + struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[0]; + struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl; + return facs->firmware_waking_vector; + } + } + + return 0; +} + +static void find_440fx(PCIDevice *d) +{ + uint16_t vendor_id, device_id; + + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + + if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82441) + i440_pcidev = *d; +} + +static void reinit_piix4_pm(PCIDevice *d) +{ + uint16_t vendor_id, device_id; + + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + + if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82371AB_3) + piix4_pm_enable(d); +} + +void rombios32_init(uint32_t *s3_resume_vector, uint8_t *shutdown_flag) +{ + BX_INFO("Starting rombios32\n"); + BX_INFO("Shutdown flag %x\n", *shutdown_flag); + +#ifdef BX_QEMU + qemu_cfg_port = qemu_cfg_port_probe(); +#endif + + init_smp_msrs(); + + ram_probe(); + + cpu_probe(); + + setup_mtrr(); + + smp_probe(); + + find_bios_table_area(); + + if (*shutdown_flag == 0xfe) { + /* redirect bios read access to RAM */ + pci_for_each_device(find_440fx); + bios_lock_shadow_ram(); /* bios is already copied */ + *s3_resume_vector = find_resume_vector(); + if (!*s3_resume_vector) { + BX_INFO("This is S3 resume but wakeup vector is NULL\n"); + } else { + BX_INFO("S3 resume vector %p\n", *s3_resume_vector); + pci_for_each_device(reinit_piix4_pm); + } + return; + } + + pci_bios_init(); + + if (bios_table_cur_addr != 0) { + +#ifdef BX_QEMU + irq0_override_probe(); +#endif + mptable_init(); + + smbios_init(); + + if (acpi_enabled) + acpi_bios_init(); + + bios_lock_shadow_ram(); + + BX_INFO("bios_table_cur_addr: 0x%08lx\n", bios_table_cur_addr); + if (bios_table_cur_addr > bios_table_end_addr) + BX_PANIC("bios_table_end_addr overflow!\n"); +#ifdef BX_USE_EBDA_TABLES + BX_INFO("ebda_cur_addr: 0x%08lx\n", ebda_cur_addr); + if (ebda_cur_addr > 0xA0000) + BX_PANIC("ebda_cur_addr overflow!\n"); +#endif + } +} diff --git a/kvm/bios/rombios32.ld b/kvm/bios/rombios32.ld new file mode 100644 index 000000000..1fc99c38f --- /dev/null +++ b/kvm/bios/rombios32.ld @@ -0,0 +1,22 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start); +SECTIONS +{ + . = 0x000e0000; + .text : { *(.text) } + .rodata : { *(.rodata*) } + . = ALIGN(64); + fixup_start = .; + .fixup : { *(.fixup) } + fixup_end = .; + . = ALIGN(4096); + _end = . ; + .data 0x700 : AT (_end) { __data_start = .; *(.data); __data_end = .;} + .bss : { __bss_start = .; *(.bss) *(COMMON); __bss_end = .;} + /DISCARD/ : { *(.stab) + *(.stabstr) + *(.comment) + *(.note) + } +} diff --git a/kvm/bios/rombios32start.S b/kvm/bios/rombios32start.S new file mode 100644 index 000000000..c9bc045b9 --- /dev/null +++ b/kvm/bios/rombios32start.S @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: rombios32start.S,v 1.3 2006/10/02 06:29:38 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// 32 bit Bochs BIOS init code +// Copyright (C) 2006 Fabrice Bellard +// +// 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 "rombios.h" + +.globl _start +.globl smp_ap_boot_code_start +.globl smp_ap_boot_code_end +.global smm_relocation_start +.global smm_relocation_end +.global smm_code_start +.global smm_code_end + +_start: + /* clear bss section */ + xor %eax, %eax + mov $__bss_start, %edi + mov $__bss_end, %ecx + sub %edi, %ecx + rep stosb + + /* copy data section */ + mov $_end, %esi + mov $__data_start, %edi + mov $__data_end, %ecx + sub %edi, %ecx + rep movsb + + jmp rombios32_init + + .code16 +smp_ap_boot_code_start: + cli + xor %ax, %ax + mov %ax, %ds + + mov $SMP_MSR_ADDR, %ebx +11: + mov 0(%ebx), %ecx + test %ecx, %ecx + jz 12f + mov 4(%ebx), %eax + mov 8(%ebx), %edx + wrmsr + add $12, %ebx + jmp 11b +12: + + lock incw smp_cpus +1: + hlt + jmp 1b +smp_ap_boot_code_end: + +/* code to relocate SMBASE to 0xa0000 */ +smm_relocation_start: + mov $0x38000 + 0x7efc, %ebx + addr32 mov (%ebx), %al /* revision ID to see if x86_64 or x86 */ + cmp $0x64, %al + je 1f + mov $0x38000 + 0x7ef8, %ebx + jmp 2f +1: + mov $0x38000 + 0x7f00, %ebx +2: + movl $0xa0000, %eax + addr32 movl %eax, (%ebx) + /* indicate to the BIOS that the SMM code was executed */ + mov $0x00, %al + movw $0xb3, %dx + outb %al, %dx + rsm +smm_relocation_end: + +/* minimal SMM code to enable or disable ACPI */ +smm_code_start: + movw $0xb2, %dx + inb %dx, %al + cmp $0xf0, %al + jne 1f + + /* ACPI disable */ + mov $PM_IO_BASE + 0x04, %dx /* PMCNTRL */ + inw %dx, %ax + andw $~1, %ax + outw %ax, %dx + + jmp 2f + +1: + cmp $0xf1, %al + jne 2f + + /* ACPI enable */ + mov $PM_IO_BASE + 0x04, %dx /* PMCNTRL */ + inw %dx, %ax + orw $1, %ax + outw %ax, %dx + +2: + rsm +smm_code_end: diff --git a/kvm/bios/usage.cc b/kvm/bios/usage.cc new file mode 100644 index 000000000..8db95479c --- /dev/null +++ b/kvm/bios/usage.cc @@ -0,0 +1,99 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: usage.cc,v 1.4 2003/10/07 01:44:34 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + + +unsigned char bios[65536]; + + int +main(int argc, char *argv[]) +{ + int bios_file; + FILE * org_file; + unsigned org, last_org, offset; + int retval; + unsigned int to_read, index; + double elements, ratio; + + if (argc !=3 ) { + fprintf(stderr, "Usage: usage bios-file org-file\n"); + exit(1); + } + + bios_file = open(argv[1], O_RDONLY); + org_file = fopen(argv[2], "r"); + + if ( (bios_file<0) | (org_file==NULL) ) { + fprintf(stderr, "problems opening files.\n"); + exit(1); + } + + printf("files opened OK\n"); + + to_read = 65536; + index = 0; + while (to_read > 0) { + retval = read(bios_file, &bios[index], to_read); + if (retval <= 0) { + fprintf(stderr, "problem reading bios file\n"); + exit(1); + } + to_read -= retval; + index += retval; + } + printf("bios file read in OK\n"); + + last_org = 0; + + while (1) { + retval = fscanf(org_file, "0x%x\n", &org); + if (retval <= 0) break; + printf("%04x .. %04x ", last_org, org-1); + for (offset=org-1; offset>last_org; offset--) { + if (bios[offset] != 0) break; + } + if (offset > last_org) { + elements = (1.0 + double(offset) - double(last_org)); + } + else { + if (bios[last_org] == 0) + elements = 0.0; + else + elements = 1.0; + } + + ratio = elements / (double(org) - double(last_org)); + ratio *= 100.0; + printf("%6.2lf\n", ratio); + last_org = org; + } +} diff --git a/kvm/bios/vapic.S b/kvm/bios/vapic.S new file mode 100644 index 000000000..cf2a474d5 --- /dev/null +++ b/kvm/bios/vapic.S @@ -0,0 +1,294 @@ + .text + .code32 + .align 4096 + +vapic_size = 2*4096 + +.macro fixup delta=-4 +777: + .pushsection .fixup, "a" + .long 777b + \delta - vapic_base + .popsection +.endm + +.macro reenable_vtpr + out %al, $0x7e +.endm + +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 + +.align 4096 +/* + * 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) + */ +vapic: +. = . + vapic_size 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..73963e307 --- /dev/null +++ b/kvm/include/ia64/asm/kvm.h @@ -0,0 +1,303 @@ +#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 +#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 <asm/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_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_host.h b/kvm/include/ia64/asm/kvm_host.h new file mode 100644 index 000000000..fb4f87d88 --- /dev/null +++ b/kvm/include/ia64/asm/kvm_host.h @@ -0,0 +1,634 @@ +#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 +/* + * kvm_host.h: used for kvm module, and hold ia64-specific sections. + * + * Copyright (C) 2007, Intel Corporation. + * + * 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. + * + */ + +#ifndef __ASM_KVM_HOST_H +#define __ASM_KVM_HOST_H + +#define KVM_MEMORY_SLOTS 32 +/* memory slots that does not exposed to userspace */ +#define KVM_PRIVATE_MEM_SLOTS 4 + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + +/* define exit reasons from vmm to kvm*/ +#define EXIT_REASON_VM_PANIC 0 +#define EXIT_REASON_MMIO_INSTRUCTION 1 +#define EXIT_REASON_PAL_CALL 2 +#define EXIT_REASON_SAL_CALL 3 +#define EXIT_REASON_SWITCH_RR6 4 +#define EXIT_REASON_VM_DESTROY 5 +#define EXIT_REASON_EXTERNAL_INTERRUPT 6 +#define EXIT_REASON_IPI 7 +#define EXIT_REASON_PTC_G 8 +#define EXIT_REASON_DEBUG 20 + +/*Define vmm address space and vm data space.*/ +#define KVM_VMM_SIZE (__IA64_UL_CONST(16)<<20) +#define KVM_VMM_SHIFT 24 +#define KVM_VMM_BASE 0xD000000000000000 +#define VMM_SIZE (__IA64_UL_CONST(8)<<20) + +/* + * Define vm_buffer, used by PAL Services, base address. + * Note: vm_buffer is in the VMM-BLOCK, the size must be < 8M + */ +#define KVM_VM_BUFFER_BASE (KVM_VMM_BASE + VMM_SIZE) +#define KVM_VM_BUFFER_SIZE (__IA64_UL_CONST(8)<<20) + +/* + * kvm guest's data area looks as follow: + * + * +----------------------+ ------- KVM_VM_DATA_SIZE + * | vcpu[n]'s data | | ___________________KVM_STK_OFFSET + * | | | / | + * | .......... | | /vcpu's struct&stack | + * | .......... | | /---------------------|---- 0 + * | vcpu[5]'s data | | / vpd | + * | vcpu[4]'s data | |/-----------------------| + * | vcpu[3]'s data | / vtlb | + * | vcpu[2]'s data | /|------------------------| + * | vcpu[1]'s data |/ | vhpt | + * | vcpu[0]'s data |____________________________| + * +----------------------+ | + * | memory dirty log | | + * +----------------------+ | + * | vm's data struct | | + * +----------------------+ | + * | | | + * | | | + * | | | + * | | | + * | | | + * | | | + * | | | + * | vm's p2m table | | + * | | | + * | | | + * | | | | + * vm's data->| | | | + * +----------------------+ ------- 0 + * To support large memory, needs to increase the size of p2m. + * To support more vcpus, needs to ensure it has enough space to + * hold vcpus' data. + */ + +#define KVM_VM_DATA_SHIFT 26 +#define KVM_VM_DATA_SIZE (__IA64_UL_CONST(1) << KVM_VM_DATA_SHIFT) +#define KVM_VM_DATA_BASE (KVM_VMM_BASE + KVM_VM_DATA_SIZE) + +#define KVM_P2M_BASE KVM_VM_DATA_BASE +#define KVM_P2M_SIZE (__IA64_UL_CONST(24) << 20) + +#define VHPT_SHIFT 16 +#define VHPT_SIZE (__IA64_UL_CONST(1) << VHPT_SHIFT) +#define VHPT_NUM_ENTRIES (__IA64_UL_CONST(1) << (VHPT_SHIFT-5)) + +#define VTLB_SHIFT 16 +#define VTLB_SIZE (__IA64_UL_CONST(1) << VTLB_SHIFT) +#define VTLB_NUM_ENTRIES (1UL << (VHPT_SHIFT-5)) + +#define VPD_SHIFT 16 +#define VPD_SIZE (__IA64_UL_CONST(1) << VPD_SHIFT) + +#define VCPU_STRUCT_SHIFT 16 +#define VCPU_STRUCT_SIZE (__IA64_UL_CONST(1) << VCPU_STRUCT_SHIFT) + +/* + * This must match KVM_IA64_VCPU_STACK_{SHIFT,SIZE} arch/ia64/include/asm/kvm.h + */ +#define KVM_STK_SHIFT 16 +#define KVM_STK_OFFSET (__IA64_UL_CONST(1)<< KVM_STK_SHIFT) + +#define KVM_VM_STRUCT_SHIFT 19 +#define KVM_VM_STRUCT_SIZE (__IA64_UL_CONST(1) << KVM_VM_STRUCT_SHIFT) + +#define KVM_MEM_DIRY_LOG_SHIFT 19 +#define KVM_MEM_DIRTY_LOG_SIZE (__IA64_UL_CONST(1) << KVM_MEM_DIRY_LOG_SHIFT) + +#ifndef __ASSEMBLY__ + +/*Define the max vcpus and memory for Guests.*/ +#define KVM_MAX_VCPUS (KVM_VM_DATA_SIZE - KVM_P2M_SIZE - KVM_VM_STRUCT_SIZE -\ + KVM_MEM_DIRTY_LOG_SIZE) / sizeof(struct kvm_vcpu_data) +#define KVM_MAX_MEM_SIZE (KVM_P2M_SIZE >> 3 << PAGE_SHIFT) + +#define VMM_LOG_LEN 256 + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/kvm.h> +#include <linux/kvm_para.h> +#include <linux/kvm_types.h> + +#include <asm/pal.h> +#include <asm/sal.h> +#include <asm/page.h> + +struct kvm_vcpu_data { + char vcpu_vhpt[VHPT_SIZE]; + char vcpu_vtlb[VTLB_SIZE]; + char vcpu_vpd[VPD_SIZE]; + char vcpu_struct[VCPU_STRUCT_SIZE]; +}; + +struct kvm_vm_data { + char kvm_p2m[KVM_P2M_SIZE]; + char kvm_vm_struct[KVM_VM_STRUCT_SIZE]; + char kvm_mem_dirty_log[KVM_MEM_DIRTY_LOG_SIZE]; + struct kvm_vcpu_data vcpu_data[KVM_MAX_VCPUS]; +}; + +#define VCPU_BASE(n) (KVM_VM_DATA_BASE + \ + offsetof(struct kvm_vm_data, vcpu_data[n])) +#define KVM_VM_BASE (KVM_VM_DATA_BASE + \ + offsetof(struct kvm_vm_data, kvm_vm_struct)) +#define KVM_MEM_DIRTY_LOG_BASE KVM_VM_DATA_BASE + \ + offsetof(struct kvm_vm_data, kvm_mem_dirty_log) + +#define VHPT_BASE(n) (VCPU_BASE(n) + offsetof(struct kvm_vcpu_data, vcpu_vhpt)) +#define VTLB_BASE(n) (VCPU_BASE(n) + offsetof(struct kvm_vcpu_data, vcpu_vtlb)) +#define VPD_BASE(n) (VCPU_BASE(n) + offsetof(struct kvm_vcpu_data, vcpu_vpd)) +#define VCPU_STRUCT_BASE(n) (VCPU_BASE(n) + \ + offsetof(struct kvm_vcpu_data, vcpu_struct)) + +/*IO section definitions*/ +#define IOREQ_READ 1 +#define IOREQ_WRITE 0 + +#define STATE_IOREQ_NONE 0 +#define STATE_IOREQ_READY 1 +#define STATE_IOREQ_INPROCESS 2 +#define STATE_IORESP_READY 3 + +/*Guest Physical address layout.*/ +#define GPFN_MEM (0UL << 60) /* Guest pfn is normal mem */ +#define GPFN_FRAME_BUFFER (1UL << 60) /* VGA framebuffer */ +#define GPFN_LOW_MMIO (2UL << 60) /* Low MMIO range */ +#define GPFN_PIB (3UL << 60) /* PIB base */ +#define GPFN_IOSAPIC (4UL << 60) /* IOSAPIC base */ +#define GPFN_LEGACY_IO (5UL << 60) /* Legacy I/O base */ +#define GPFN_GFW (6UL << 60) /* Guest Firmware */ +#define GPFN_PHYS_MMIO (7UL << 60) /* Directed MMIO Range */ + +#define GPFN_IO_MASK (7UL << 60) /* Guest pfn is I/O type */ +#define GPFN_INV_MASK (1UL << 63) /* Guest pfn is invalid */ +#define INVALID_MFN (~0UL) +#define MEM_G (1UL << 30) +#define MEM_M (1UL << 20) +#define MMIO_START (3 * MEM_G) +#define MMIO_SIZE (512 * MEM_M) +#define VGA_IO_START 0xA0000UL +#define VGA_IO_SIZE 0x20000 +#define LEGACY_IO_START (MMIO_START + MMIO_SIZE) +#define LEGACY_IO_SIZE (64 * MEM_M) +#define IO_SAPIC_START 0xfec00000UL +#define IO_SAPIC_SIZE 0x100000 +#define PIB_START 0xfee00000UL +#define PIB_SIZE 0x200000 +#define GFW_START (4 * MEM_G - 16 * MEM_M) +#define GFW_SIZE (16 * MEM_M) + +/*Deliver mode, defined for ioapic.c*/ +#define dest_Fixed IOSAPIC_FIXED +#define dest_LowestPrio IOSAPIC_LOWEST_PRIORITY + +#define NMI_VECTOR 2 +#define ExtINT_VECTOR 0 +#define NULL_VECTOR (-1) +#define IA64_SPURIOUS_INT_VECTOR 0x0f + +#define VCPU_LID(v) (((u64)(v)->vcpu_id) << 24) + +/* + *Delivery mode + */ +#define SAPIC_DELIV_SHIFT 8 +#define SAPIC_FIXED 0x0 +#define SAPIC_LOWEST_PRIORITY 0x1 +#define SAPIC_PMI 0x2 +#define SAPIC_NMI 0x4 +#define SAPIC_INIT 0x5 +#define SAPIC_EXTINT 0x7 + +/* + * vcpu->requests bit members for arch + */ +#define KVM_REQ_PTC_G 32 +#define KVM_REQ_RESUME 33 + +#define KVM_PAGES_PER_HPAGE 1 + +struct kvm; +struct kvm_vcpu; + +struct kvm_mmio_req { + uint64_t addr; /* physical address */ + uint64_t size; /* size in bytes */ + uint64_t data; /* data (or paddr of data) */ + uint8_t state:4; + uint8_t dir:1; /* 1=read, 0=write */ +}; + +/*Pal data struct */ +struct kvm_pal_call{ + /*In area*/ + uint64_t gr28; + uint64_t gr29; + uint64_t gr30; + uint64_t gr31; + /*Out area*/ + struct ia64_pal_retval ret; +}; + +/* Sal data structure */ +struct kvm_sal_call{ + /*In area*/ + uint64_t in0; + uint64_t in1; + uint64_t in2; + uint64_t in3; + uint64_t in4; + uint64_t in5; + uint64_t in6; + uint64_t in7; + struct sal_ret_values ret; +}; + +/*Guest change rr6*/ +struct kvm_switch_rr6 { + uint64_t old_rr; + uint64_t new_rr; +}; + +union ia64_ipi_a{ + unsigned long val; + struct { + unsigned long rv : 3; + unsigned long ir : 1; + unsigned long eid : 8; + unsigned long id : 8; + unsigned long ib_base : 44; + }; +}; + +union ia64_ipi_d { + unsigned long val; + struct { + unsigned long vector : 8; + unsigned long dm : 3; + unsigned long ig : 53; + }; +}; + +/*ipi check exit data*/ +struct kvm_ipi_data{ + union ia64_ipi_a addr; + union ia64_ipi_d data; +}; + +/*global purge data*/ +struct kvm_ptc_g { + unsigned long vaddr; + unsigned long rr; + unsigned long ps; + struct kvm_vcpu *vcpu; +}; + +/*Exit control data */ +struct exit_ctl_data{ + uint32_t exit_reason; + uint32_t vm_status; + union { + struct kvm_mmio_req ioreq; + struct kvm_pal_call pal_data; + struct kvm_sal_call sal_data; + struct kvm_switch_rr6 rr_data; + struct kvm_ipi_data ipi_data; + struct kvm_ptc_g ptc_g_data; + } u; +}; + +union pte_flags { + unsigned long val; + struct { + unsigned long p : 1; /*0 */ + unsigned long : 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 : 2; /* 50-51 */ + unsigned long ed : 1; /* 52 */ + }; +}; + +union ia64_pta { + unsigned long val; + struct { + unsigned long ve : 1; + unsigned long reserved0 : 1; + unsigned long size : 6; + unsigned long vf : 1; + unsigned long reserved1 : 6; + unsigned long base : 49; + }; +}; + +struct thash_cb { + /* THASH base information */ + struct thash_data *hash; /* hash table pointer */ + union ia64_pta pta; + int num; +}; + +struct kvm_vcpu_stat { +}; + +struct kvm_vcpu_arch { + int launched; + int last_exit; + int last_run_cpu; + int vmm_tr_slot; + int vm_tr_slot; + int sn_rtc_tr_slot; + +#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 + int mp_state; + +#define MAX_PTC_G_NUM 3 + int ptc_g_count; + struct kvm_ptc_g ptc_g_data[MAX_PTC_G_NUM]; + + /*halt timer to wake up sleepy vcpus*/ + struct hrtimer hlt_timer; + long ht_active; + + struct kvm_lapic *apic; /* kernel irqchip context */ + struct vpd *vpd; + + /* Exit data for vmm_transition*/ + struct exit_ctl_data exit_data; + + cpumask_t cache_coherent_map; + + unsigned long vmm_rr; + unsigned long host_rr6; + unsigned long psbits[8]; + unsigned long cr_iipa; + unsigned long cr_isr; + unsigned long vsa_base; + unsigned long dirty_log_lock_pa; + unsigned long __gp; + /* 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; + /* purge all */ + unsigned long ptce_base; + unsigned long ptce_count[2]; + unsigned long ptce_stride[2]; + /* itc/itm */ + unsigned long last_itc; + long itc_offset; + unsigned long itc_check; + unsigned long timer_check; + unsigned int timer_pending; + unsigned int timer_fired; + + 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 */ + int mode_flags; + struct thash_cb vtlb; + struct thash_cb vhpt; + char irq_check; + char irq_new_pending; + + unsigned long opcode; + unsigned long cause; + char log_buf[VMM_LOG_LEN]; + union context host; + union context guest; +}; + +struct kvm_vm_stat { + u64 remote_tlb_flush; +}; + +struct kvm_sal_data { + unsigned long boot_ip; + unsigned long boot_gp; +}; + +struct kvm_arch { + spinlock_t dirty_log_lock; + + unsigned long vm_base; + unsigned long metaphysical_rr0; + unsigned long metaphysical_rr4; + unsigned long vmm_init_rr; + + int online_vcpus; + int is_sn2; + + struct kvm_ioapic *vioapic; + struct kvm_vm_stat stat; + struct kvm_sal_data rdv_sal_data; + + struct list_head assigned_dev_head; + struct iommu_domain *iommu_domain; + struct hlist_head irq_ack_notifier_list; + + unsigned long irq_sources_bitmap; + unsigned long irq_states[KVM_IOAPIC_NUM_PINS]; +}; + +union cpuid3_t { + u64 value; + struct { + u64 number : 8; + u64 revision : 8; + u64 model : 8; + u64 family : 8; + u64 archrev : 8; + u64 rv : 24; + }; +}; + +struct kvm_pt_regs { + /* The following registers are saved by SAVE_MIN: */ + unsigned long b6; /* scratch */ + unsigned long b7; /* scratch */ + + unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ + unsigned long ar_ssd; /* reserved for future use (scratch) */ + + unsigned long r8; /* scratch (return value register 0) */ + unsigned long r9; /* scratch (return value register 1) */ + unsigned long r10; /* scratch (return value register 2) */ + unsigned long r11; /* scratch (return value register 3) */ + + unsigned long cr_ipsr; /* interrupted task's psr */ + unsigned long cr_iip; /* interrupted task's instruction pointer */ + unsigned long cr_ifs; /* interrupted task's function state */ + + unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ + unsigned long ar_pfs; /* prev function state */ + unsigned long ar_rsc; /* RSE configuration */ + /* The following two are valid only if cr_ipsr.cpl > 0: */ + unsigned long ar_rnat; /* RSE NaT */ + unsigned long ar_bspstore; /* RSE bspstore */ + + unsigned long pr; /* 64 predicate registers (1 bit each) */ + unsigned long b0; /* return pointer (bp) */ + unsigned long loadrs; /* size of dirty partition << 16 */ + + unsigned long r1; /* the gp pointer */ + unsigned long r12; /* interrupted task's memory stack pointer */ + unsigned long r13; /* thread pointer */ + + unsigned long ar_fpsr; /* floating point status (preserved) */ + unsigned long r15; /* scratch */ + + /* The remaining registers are NOT saved for system calls. */ + unsigned long r14; /* scratch */ + unsigned long r2; /* scratch */ + unsigned long r3; /* scratch */ + unsigned long r16; /* scratch */ + unsigned long r17; /* scratch */ + unsigned long r18; /* scratch */ + unsigned long r19; /* scratch */ + unsigned long r20; /* scratch */ + unsigned long r21; /* scratch */ + unsigned long r22; /* scratch */ + unsigned long r23; /* scratch */ + unsigned long r24; /* scratch */ + unsigned long r25; /* scratch */ + unsigned long r26; /* scratch */ + unsigned long r27; /* scratch */ + unsigned long r28; /* scratch */ + unsigned long r29; /* scratch */ + unsigned long r30; /* scratch */ + unsigned long r31; /* scratch */ + unsigned long ar_ccv; /* compare/exchange value (scratch) */ + + /* + * Floating point registers that the kernel considers scratch: + */ + struct ia64_fpreg f6; /* scratch */ + struct ia64_fpreg f7; /* scratch */ + struct ia64_fpreg f8; /* scratch */ + struct ia64_fpreg f9; /* scratch */ + struct ia64_fpreg f10; /* scratch */ + struct ia64_fpreg f11; /* scratch */ + + unsigned long r4; /* preserved */ + unsigned long r5; /* preserved */ + unsigned long r6; /* preserved */ + unsigned long r7; /* preserved */ + unsigned long eml_unat; /* used for emulating instruction */ + unsigned long pad0; /* alignment pad */ +}; + +static inline struct kvm_pt_regs *vcpu_regs(struct kvm_vcpu *v) +{ + return (struct kvm_pt_regs *) ((unsigned long) v + KVM_STK_OFFSET) - 1; +} + +typedef int kvm_vmm_entry(void); +typedef void kvm_tramp_entry(union context *host, union context *guest); + +struct kvm_vmm_info{ + struct module *module; + kvm_vmm_entry *vmm_entry; + kvm_tramp_entry *tramp_entry; + unsigned long vmm_ivt; + unsigned long patch_mov_ar; + unsigned long patch_mov_ar_sn2; +}; + +int kvm_highest_pending_irq(struct kvm_vcpu *vcpu); +int kvm_emulate_halt(struct kvm_vcpu *vcpu); +int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run); +void kvm_sal_emul(struct kvm_vcpu *vcpu); + +#endif /* __ASSEMBLY__*/ + +#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..9931df99c --- /dev/null +++ b/kvm/include/ia64/asm/kvm_para.h @@ -0,0 +1,67 @@ +#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 +#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. + * + */ + +static inline unsigned int kvm_arch_para_features(void) +{ + return 0; +} + +#endif diff --git a/kvm/include/linux/kvm.h b/kvm/include/linux/kvm.h new file mode 100644 index 000000000..ff1025d4c --- /dev/null +++ b/kvm/include/linux/kvm.h @@ -0,0 +1,720 @@ +#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 +#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 <asm/types.h> + +#include <linux/ioctl.h> +#include <asm/kvm.h> + +#define KVM_API_VERSION 12 + +/* for KVM_TRACE_ENABLE */ +struct kvm_user_trace_setup { + __u32 buf_size; /* sub_buffer size of each per-cpu */ + __u32 buf_nr; /* the number of sub_buffers of each per-cpu */ +}; + +/* 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 + +/* 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; + + 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; + __u64 mask; /* psw upper half */ + __u64 addr; /* psw lower half */ + __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; + /* 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; +}; + +#define KVM_TRC_SHIFT 16 +/* + * kvm trace categories + */ +#define KVM_TRC_ENTRYEXIT (1 << KVM_TRC_SHIFT) +#define KVM_TRC_HANDLER (1 << (KVM_TRC_SHIFT + 1)) /* only 12 bits */ + +/* + * kvm trace action + */ +#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 + +/* This structure represents a single trace buffer record. */ +struct kvm_trace_rec { + /* variable rec_val + * is split into: + * bits 0 - 27 -> event id + * bits 28 -30 -> number of extra data args of size u32 + * bits 31 -> binary indicator for if tsc is in record + */ + __u32 rec_val; + __u32 pid; + __u32 vcpu_id; + union { + struct { + __u64 timestamp; + __u32 extra_u32[KVM_TRC_EXTRA_MAX]; + } __attribute__((packed)) timestamp; + struct { + __u32 extra_u32[KVM_TRC_EXTRA_MAX]; + } notimestamp; + } u; +}; + +#define TRACE_REC_EVENT_ID(val) \ + (0x0fffffff & (val)) +#define TRACE_REC_NUM_DATA_ARGS(val) \ + (0x70000000 & ((val) << 28)) +#define TRACE_REC_TCS(val) \ + (0x80000000 & ((val) << 31)) + +#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) +/* + * ioctls for kvm trace + */ +#define KVM_TRACE_ENABLE _IOW(KVMIO, 0x06, struct kvm_user_trace_setup) +#define KVM_TRACE_PAUSE _IO(KVMIO, 0x07) +#define KVM_TRACE_DISABLE _IO(KVMIO, 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_PIT2 33 +#define KVM_CAP_PIT_STATE2 35 +#define KVM_CAP_SET_IDENTITY_MAP_ADDR 37 + +#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 + +/* + * ioctls for VM fds + */ +#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region) +#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) +/* + * 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) +/* 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 _IOR(KVMIO, 0x70, \ + struct kvm_assigned_irq) +#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_CREATE_PIT2 _IOW(KVMIO, 0x77, struct kvm_pit_config) + +/* + * 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_DEBUG_GUEST +#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) + +/* + * Deprecated interfaces + */ +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_DEBUG_GUEST _IOW(KVMIO, 0x87, struct kvm_debug_guest) + +#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) +#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) + +#define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2) +#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2) + +#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) + +#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 512 +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 diff --git a/kvm/include/linux/kvm_host.h b/kvm/include/linux/kvm_host.h new file mode 100644 index 000000000..0c3c5b1c2 --- /dev/null +++ b/kvm/include/linux/kvm_host.h @@ -0,0 +1,567 @@ +#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 +#ifndef __KVM_HOST_H +#define __KVM_HOST_H + +/* + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include <linux/types.h> +#include <linux/hardirq.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/preempt.h> +#include <linux/marker.h> +#include <linux/msi.h> +#include <asm/signal.h> + +#include <linux/kvm.h> +#include <linux/kvm_para.h> + +#include <linux/kvm_types.h> + +#include <asm/kvm_host.h> + +/* + * vcpu->requests bit members + */ +#define KVM_REQ_TLB_FLUSH 0 +#define KVM_REQ_MIGRATE_TIMER 1 +#define KVM_REQ_REPORT_TPR_ACCESS 2 +#define KVM_REQ_MMU_RELOAD 3 +#define KVM_REQ_TRIPLE_FAULT 4 +#define KVM_REQ_PENDING_TIMER 5 +#define KVM_REQ_UNHALT 6 +#define KVM_REQ_MMU_SYNC 7 +#define KVM_REQ_KVMCLOCK_UPDATE 8 + +#define KVM_USERSPACE_IRQ_SOURCE_ID 0 + +struct kvm_vcpu; +extern struct kmem_cache *kvm_vcpu_cache; + +/* + * It would be nice to use something smarter than a linear search, TBD... + * Thankfully we dont expect many devices to register (famous last words :), + * so until then it will suffice. At least its abstracted so we can change + * in one place. + */ +struct kvm_io_bus { + int dev_count; +#define NR_IOBUS_DEVS 6 + struct kvm_io_device *devs[NR_IOBUS_DEVS]; +}; + +void kvm_io_bus_init(struct kvm_io_bus *bus); +void kvm_io_bus_destroy(struct kvm_io_bus *bus); +struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, + gpa_t addr, int len, int is_write); +void kvm_io_bus_register_dev(struct kvm_io_bus *bus, + struct kvm_io_device *dev); + +struct kvm_vcpu { + struct kvm *kvm; +#ifdef CONFIG_PREEMPT_NOTIFIERS + struct preempt_notifier preempt_notifier; +#endif + int vcpu_id; + struct mutex mutex; + int cpu; + struct kvm_run *run; + int guest_mode; + unsigned long requests; + unsigned long guest_debug; + int fpu_active; + int guest_fpu_loaded; + wait_queue_head_t wq; + int sigset_active; + sigset_t sigset; + struct kvm_vcpu_stat stat; + +#ifdef CONFIG_HAS_IOMEM + int mmio_needed; + int mmio_read_completed; + int mmio_is_write; + int mmio_size; + unsigned char mmio_data[8]; + gpa_t mmio_phys_addr; +#endif + + struct kvm_vcpu_arch arch; +}; + +struct kvm_memory_slot { + gfn_t base_gfn; + unsigned long npages; + unsigned long flags; + unsigned long *rmap; + unsigned long *dirty_bitmap; + struct { + unsigned long rmap_pde; + int write_count; + } *lpage_info; + unsigned long userspace_addr; + int user_alloc; +}; + +struct kvm_kernel_irq_routing_entry { + u32 gsi; + int (*set)(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int level); + union { + struct { + unsigned irqchip; + unsigned pin; + } irqchip; + struct msi_msg msi; + }; + struct list_head link; +}; + +struct kvm { + struct mutex lock; /* protects the vcpus array and APIC accesses */ + spinlock_t mmu_lock; + struct rw_semaphore slots_lock; + struct mm_struct *mm; /* userspace tied to this vm */ + int nmemslots; + struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS + + KVM_PRIVATE_MEM_SLOTS]; + struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; + struct list_head vm_list; + struct kvm_io_bus mmio_bus; + struct kvm_io_bus pio_bus; + struct kvm_vm_stat stat; + struct kvm_arch arch; + atomic_t users_count; +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + struct kvm_coalesced_mmio_dev *coalesced_mmio_dev; + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; +#endif + +#ifdef CONFIG_HAVE_KVM_IRQCHIP + struct list_head irq_routing; /* of kvm_kernel_irq_routing_entry */ + struct hlist_head mask_notifier_list; +#endif + +#ifdef KVM_ARCH_WANT_MMU_NOTIFIER + struct mmu_notifier mmu_notifier; + unsigned long mmu_notifier_seq; + long mmu_notifier_count; +#endif +}; + +/* The guest did something we don't support. */ +#define pr_unimpl(vcpu, fmt, ...) \ + do { \ + if (printk_ratelimit()) \ + printk(KERN_ERR "kvm: %i: cpu%i " fmt, \ + current->tgid, (vcpu)->vcpu_id , ## __VA_ARGS__); \ + } while (0) + +#define kvm_printf(kvm, fmt ...) printk(KERN_DEBUG fmt) +#define vcpu_printf(vcpu, fmt...) kvm_printf(vcpu->kvm, fmt) + +int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id); +void kvm_vcpu_uninit(struct kvm_vcpu *vcpu); + +void vcpu_load(struct kvm_vcpu *vcpu); +void vcpu_put(struct kvm_vcpu *vcpu); + +int kvm_init(void *opaque, unsigned int vcpu_size, + struct module *module); +void kvm_exit(void); + +void kvm_get_kvm(struct kvm *kvm); +void kvm_put_kvm(struct kvm *kvm); + +#define HPA_MSB ((sizeof(hpa_t) * 8) - 1) +#define HPA_ERR_MASK ((hpa_t)1 << HPA_MSB) +static inline int is_error_hpa(hpa_t hpa) { return hpa >> HPA_MSB; } +struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva); + +extern struct page *bad_page; +extern pfn_t bad_pfn; + +int is_error_page(struct page *page); +int is_error_pfn(pfn_t pfn); +int kvm_is_error_hva(unsigned long addr); +int kvm_set_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + int user_alloc); +int __kvm_set_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + int user_alloc); +int kvm_arch_set_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + struct kvm_memory_slot old, + int user_alloc); +void kvm_arch_flush_shadow(struct kvm *kvm); +gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn); +struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); +unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); +void kvm_release_page_clean(struct page *page); +void kvm_release_page_dirty(struct page *page); +void kvm_set_page_dirty(struct page *page); +void kvm_set_page_accessed(struct page *page); + +pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn); +void kvm_release_pfn_dirty(pfn_t); +void kvm_release_pfn_clean(pfn_t pfn); +void kvm_set_pfn_dirty(pfn_t pfn); +void kvm_set_pfn_accessed(pfn_t pfn); +void kvm_get_pfn(pfn_t pfn); + +int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, + int len); +int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data, + unsigned long len); +int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len); +int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data, + int offset, int len); +int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, + unsigned long len); +int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); +int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); +struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); +int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn); +void mark_page_dirty(struct kvm *kvm, gfn_t gfn); + +void kvm_vcpu_block(struct kvm_vcpu *vcpu); +void kvm_resched(struct kvm_vcpu *vcpu); +void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); +void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); +void kvm_flush_remote_tlbs(struct kvm *kvm); +void kvm_reload_remote_mmus(struct kvm *kvm); + +long kvm_arch_dev_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg); +long kvm_arch_vcpu_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg); +void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu); +void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu); + +int kvm_dev_ioctl_check_extension(long ext); + +int kvm_get_dirty_log(struct kvm *kvm, + struct kvm_dirty_log *log, int *is_dirty); +int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, + struct kvm_dirty_log *log); + +int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, + struct + kvm_userspace_memory_region *mem, + int user_alloc); +long kvm_arch_vm_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg); + +int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu); +int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu); + +int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr); + +int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); +int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); +int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs); +int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs); +int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state); +int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state); +int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg); +int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run); + +int kvm_arch_init(void *opaque); +void kvm_arch_exit(void); + +int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu); +void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu); + +void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu); +void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu); +void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu); +struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id); +int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu); +void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu); + +int kvm_arch_vcpu_reset(struct kvm_vcpu *vcpu); +void kvm_arch_hardware_enable(void *garbage); +void kvm_arch_hardware_disable(void *garbage); +int kvm_arch_hardware_setup(void); +void kvm_arch_hardware_unsetup(void); +void kvm_arch_check_processor_compat(void *rtn); +int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu); +int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu); + +void kvm_free_physmem(struct kvm *kvm); + +struct kvm *kvm_arch_create_vm(void); +void kvm_arch_destroy_vm(struct kvm *kvm); +void kvm_free_all_assigned_devices(struct kvm *kvm); +void kvm_arch_sync_events(struct kvm *kvm); + +int kvm_cpu_get_interrupt(struct kvm_vcpu *v); +int kvm_cpu_has_interrupt(struct kvm_vcpu *v); +int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); +void kvm_vcpu_kick(struct kvm_vcpu *vcpu); + +int kvm_is_mmio_pfn(pfn_t pfn); + +struct kvm_irq_ack_notifier { + struct hlist_node link; + unsigned gsi; + void (*irq_acked)(struct kvm_irq_ack_notifier *kian); +}; + +#define KVM_ASSIGNED_MSIX_PENDING 0x1 +struct kvm_guest_msix_entry { + u32 vector; + u16 entry; + u16 flags; +}; + +struct kvm_assigned_dev_kernel { + struct kvm_irq_ack_notifier ack_notifier; + struct work_struct interrupt_work; + struct list_head list; + int assigned_dev_id; + int host_busnr; + int host_devfn; + unsigned int entries_nr; + int host_irq; + bool host_irq_disabled; + struct msix_entry *host_msix_entries; + int guest_irq; + struct kvm_guest_msix_entry *guest_msix_entries; + unsigned long irq_requested_type; + int irq_source_id; + int flags; + struct pci_dev *dev; + struct kvm *kvm; +}; + +struct kvm_irq_mask_notifier { + void (*func)(struct kvm_irq_mask_notifier *kimn, bool masked); + int irq; + struct hlist_node link; +}; + +void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn); +void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn); +void kvm_fire_mask_notifiers(struct kvm *kvm, int irq, bool mask); + +#ifdef __KVM_HAVE_IOAPIC +void kvm_get_intr_delivery_bitmask(struct kvm_ioapic *ioapic, + union kvm_ioapic_redirect_entry *entry, + unsigned long *deliver_bitmask); +#endif +int kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level); +void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin); +void kvm_register_irq_ack_notifier(struct kvm *kvm, + struct kvm_irq_ack_notifier *kian); +void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian); +int kvm_request_irq_source_id(struct kvm *kvm); +void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); + +#ifdef CONFIG_IOMMU_API +int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, + unsigned long npages); +int kvm_iommu_map_guest(struct kvm *kvm); +int kvm_iommu_unmap_guest(struct kvm *kvm); +int kvm_assign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev); +int kvm_deassign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev); +#else /* CONFIG_IOMMU_API */ +static inline int kvm_iommu_map_pages(struct kvm *kvm, + gfn_t base_gfn, + unsigned long npages) +{ + return 0; +} + +static inline int kvm_iommu_map_guest(struct kvm *kvm) +{ + return -ENODEV; +} + +static inline int kvm_iommu_unmap_guest(struct kvm *kvm) +{ + return 0; +} + +static inline int kvm_assign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev) +{ + return 0; +} + +static inline int kvm_deassign_device(struct kvm *kvm, + struct kvm_assigned_dev_kernel *assigned_dev) +{ + return 0; +} +#endif /* CONFIG_IOMMU_API */ + +static inline void kvm_guest_enter(void) +{ + account_system_vtime(current); + current->flags |= PF_VCPU; +} + +static inline void kvm_guest_exit(void) +{ + account_system_vtime(current); + current->flags &= ~PF_VCPU; +} + +static inline int memslot_id(struct kvm *kvm, struct kvm_memory_slot *slot) +{ + return slot - kvm->memslots; +} + +static inline gpa_t gfn_to_gpa(gfn_t gfn) +{ + return (gpa_t)gfn << PAGE_SHIFT; +} + +static inline hpa_t pfn_to_hpa(pfn_t pfn) +{ + return (hpa_t)pfn << PAGE_SHIFT; +} + +static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu) +{ + set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests); +} + +enum kvm_stat_kind { + KVM_STAT_VM, + KVM_STAT_VCPU, +}; + +struct kvm_stats_debugfs_item { + const char *name; + int offset; + enum kvm_stat_kind kind; + struct dentry *dentry; +}; +extern struct kvm_stats_debugfs_item debugfs_entries[]; +extern struct dentry *kvm_debugfs_dir; + +#define KVMTRACE_5D(evt, vcpu, d1, d2, d3, d4, d5, name) \ + trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \ + vcpu, 5, d1, d2, d3, d4, d5) +#define KVMTRACE_4D(evt, vcpu, d1, d2, d3, d4, name) \ + trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \ + vcpu, 4, d1, d2, d3, d4, 0) +#define KVMTRACE_3D(evt, vcpu, d1, d2, d3, name) \ + trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \ + vcpu, 3, d1, d2, d3, 0, 0) +#define KVMTRACE_2D(evt, vcpu, d1, d2, name) \ + trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \ + vcpu, 2, d1, d2, 0, 0, 0) +#define KVMTRACE_1D(evt, vcpu, d1, name) \ + trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \ + vcpu, 1, d1, 0, 0, 0, 0) +#define KVMTRACE_0D(evt, vcpu, name) \ + trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \ + vcpu, 0, 0, 0, 0, 0, 0) + +#ifdef CONFIG_KVM_TRACE +int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg); +void kvm_trace_cleanup(void); +#else +static inline +int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg) +{ + return -EINVAL; +} +#define kvm_trace_cleanup() ((void)0) +#endif + +#ifdef KVM_ARCH_WANT_MMU_NOTIFIER +static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq) +{ + if (unlikely(vcpu->kvm->mmu_notifier_count)) + return 1; + /* + * Both reads happen under the mmu_lock and both values are + * modified under mmu_lock, so there's no need of smb_rmb() + * here in between, otherwise mmu_notifier_count should be + * read before mmu_notifier_seq, see + * mmu_notifier_invalidate_range_end write side. + */ + if (vcpu->kvm->mmu_notifier_seq != mmu_seq) + return 1; + return 0; +} +#endif + +#ifdef CONFIG_HAVE_KVM_IRQCHIP + +#define KVM_MAX_IRQ_ROUTES 1024 + +int kvm_setup_default_irq_routing(struct kvm *kvm); +int kvm_set_irq_routing(struct kvm *kvm, + const struct kvm_irq_routing_entry *entries, + unsigned nr, + unsigned flags); +void kvm_free_irq_routing(struct kvm *kvm); + +#else + +static inline void kvm_free_irq_routing(struct kvm *kvm) {} + +#endif + +#endif diff --git a/kvm/include/linux/kvm_para.h b/kvm/include/linux/kvm_para.h new file mode 100644 index 000000000..54ebbd544 --- /dev/null +++ b/kvm/include/linux/kvm_para.h @@ -0,0 +1,80 @@ +#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 +#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_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/linux/kvm_types.h b/kvm/include/linux/kvm_types.h new file mode 100644 index 000000000..c65f89e69 --- /dev/null +++ b/kvm/include/linux/kvm_types.h @@ -0,0 +1,110 @@ +#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 +/* + * 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. + * + * 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. + * + */ + +#ifndef __KVM_TYPES_H__ +#define __KVM_TYPES_H__ + +#include <asm/types.h> + +/* + * Address types: + * + * gva - guest virtual address + * gpa - guest physical address + * gfn - guest frame number + * hva - host virtual address + * hpa - host physical address + * hfn - host frame number + */ + +typedef unsigned long gva_t; +typedef u64 gpa_t; +typedef unsigned long gfn_t; + +typedef unsigned long hva_t; +typedef u64 hpa_t; +typedef unsigned long hfn_t; + +typedef hfn_t pfn_t; + +union kvm_ioapic_redirect_entry { + 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; +}; + +struct kvm_lapic_irq { + u32 vector; + u32 delivery_mode; + u32 dest_mode; + u32 level; + u32 trig_mode; + u32 shorthand; + u32 dest_id; +}; + +#endif /* __KVM_TYPES_H__ */ diff --git a/kvm/include/powerpc/asm/kvm.h b/kvm/include/powerpc/asm/kvm.h new file mode 100644 index 000000000..c4f1ed181 --- /dev/null +++ b/kvm/include/powerpc/asm/kvm.h @@ -0,0 +1,102 @@ +#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 +/* + * 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_44x.h b/kvm/include/powerpc/asm/kvm_44x.h new file mode 100644 index 000000000..956f252c5 --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_44x.h @@ -0,0 +1,108 @@ +#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 +/* + * 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 __ASM_44X_H__ +#define __ASM_44X_H__ + +#include <linux/kvm_host.h> + +#define PPC44x_TLB_SIZE 64 + +/* If the guest is expecting it, this can be as large as we like; we'd just + * need to find some way of advertising it. */ +#define KVM44x_GUEST_TLB_SIZE 64 + +struct kvmppc_44x_tlbe { + u32 tid; /* Only the low 8 bits are used. */ + u32 word0; + u32 word1; + u32 word2; +}; + +struct kvmppc_44x_shadow_ref { + struct page *page; + u16 gtlb_index; + u8 writeable; + u8 tid; +}; + +struct kvmppc_vcpu_44x { + /* Unmodified copy of the guest's TLB. */ + struct kvmppc_44x_tlbe guest_tlb[KVM44x_GUEST_TLB_SIZE]; + + /* References to guest pages in the hardware TLB. */ + struct kvmppc_44x_shadow_ref shadow_refs[PPC44x_TLB_SIZE]; + + /* State of the shadow TLB at guest context switch time. */ + struct kvmppc_44x_tlbe shadow_tlb[PPC44x_TLB_SIZE]; + u8 shadow_tlb_mod[PPC44x_TLB_SIZE]; + + struct kvm_vcpu vcpu; +}; + +static inline struct kvmppc_vcpu_44x *to_44x(struct kvm_vcpu *vcpu) +{ + return container_of(vcpu, struct kvmppc_vcpu_44x, vcpu); +} + +void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 new_pid); +void kvmppc_44x_tlb_put(struct kvm_vcpu *vcpu); +void kvmppc_44x_tlb_load(struct kvm_vcpu *vcpu); + +#endif /* __ASM_44X_H__ */ diff --git a/kvm/include/powerpc/asm/kvm_asm.h b/kvm/include/powerpc/asm/kvm_asm.h new file mode 100644 index 000000000..84ff858ee --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_asm.h @@ -0,0 +1,100 @@ +#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 +/* + * 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_ASM_H__ +#define __POWERPC_KVM_ASM_H__ + +/* IVPR must be 64KiB-aligned. */ +#define VCPU_SIZE_ORDER 4 +#define VCPU_SIZE_LOG (VCPU_SIZE_ORDER + 12) +#define VCPU_TLB_PGSZ PPC44x_TLB_64K +#define VCPU_SIZE_BYTES (1<<VCPU_SIZE_LOG) + +#define BOOKE_INTERRUPT_CRITICAL 0 +#define BOOKE_INTERRUPT_MACHINE_CHECK 1 +#define BOOKE_INTERRUPT_DATA_STORAGE 2 +#define BOOKE_INTERRUPT_INST_STORAGE 3 +#define BOOKE_INTERRUPT_EXTERNAL 4 +#define BOOKE_INTERRUPT_ALIGNMENT 5 +#define BOOKE_INTERRUPT_PROGRAM 6 +#define BOOKE_INTERRUPT_FP_UNAVAIL 7 +#define BOOKE_INTERRUPT_SYSCALL 8 +#define BOOKE_INTERRUPT_AP_UNAVAIL 9 +#define BOOKE_INTERRUPT_DECREMENTER 10 +#define BOOKE_INTERRUPT_FIT 11 +#define BOOKE_INTERRUPT_WATCHDOG 12 +#define BOOKE_INTERRUPT_DTLB_MISS 13 +#define BOOKE_INTERRUPT_ITLB_MISS 14 +#define BOOKE_INTERRUPT_DEBUG 15 + +/* E500 */ +#define BOOKE_INTERRUPT_SPE_UNAVAIL 32 +#define BOOKE_INTERRUPT_SPE_FP_DATA 33 +#define BOOKE_INTERRUPT_SPE_FP_ROUND 34 +#define BOOKE_INTERRUPT_PERFORMANCE_MONITOR 35 + +#define RESUME_FLAG_NV (1<<0) /* Reload guest nonvolatile state? */ +#define RESUME_FLAG_HOST (1<<1) /* Resume host? */ + +#define RESUME_GUEST 0 +#define RESUME_GUEST_NV RESUME_FLAG_NV +#define RESUME_HOST RESUME_FLAG_HOST +#define RESUME_HOST_NV (RESUME_FLAG_HOST|RESUME_FLAG_NV) + +#endif /* __POWERPC_KVM_ASM_H__ */ diff --git a/kvm/include/powerpc/asm/kvm_e500.h b/kvm/include/powerpc/asm/kvm_e500.h new file mode 100644 index 000000000..094b45381 --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_e500.h @@ -0,0 +1,107 @@ +#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 +/* + * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Yu Liu, <yu.liu@freescale.com> + * + * Description: + * This file is derived from arch/powerpc/include/asm/kvm_44x.h, + * by Hollis Blanchard <hollisb@us.ibm.com>. + * + * 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. + */ + +#ifndef __ASM_KVM_E500_H__ +#define __ASM_KVM_E500_H__ + +#include <linux/kvm_host.h> + +#define BOOKE_INTERRUPT_SIZE 36 + +#define E500_PID_NUM 3 +#define E500_TLB_NUM 2 + +struct tlbe{ + u32 mas1; + u32 mas2; + u32 mas3; + u32 mas7; +}; + +struct kvmppc_vcpu_e500 { + /* Unmodified copy of the guest's TLB. */ + struct tlbe *guest_tlb[E500_TLB_NUM]; + /* TLB that's actually used when the guest is running. */ + struct tlbe *shadow_tlb[E500_TLB_NUM]; + /* Pages which are referenced in the shadow TLB. */ + struct page **shadow_pages[E500_TLB_NUM]; + + unsigned int guest_tlb_size[E500_TLB_NUM]; + unsigned int shadow_tlb_size[E500_TLB_NUM]; + unsigned int guest_tlb_nv[E500_TLB_NUM]; + + u32 host_pid[E500_PID_NUM]; + u32 pid[E500_PID_NUM]; + + u32 mas0; + u32 mas1; + u32 mas2; + u32 mas3; + u32 mas4; + u32 mas5; + u32 mas6; + u32 mas7; + u32 l1csr1; + u32 hid0; + u32 hid1; + + struct kvm_vcpu vcpu; +}; + +static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) +{ + return container_of(vcpu, struct kvmppc_vcpu_e500, vcpu); +} + +#endif /* __ASM_KVM_E500_H__ */ diff --git a/kvm/include/powerpc/asm/kvm_host.h b/kvm/include/powerpc/asm/kvm_host.h new file mode 100644 index 000000000..e69fb50ac --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_host.h @@ -0,0 +1,232 @@ +#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 +/* + * 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 __POWERPC_KVM_HOST_H__ +#define __POWERPC_KVM_HOST_H__ + +#include <linux/mutex.h> +#include <linux/timer.h> +#include <linux/types.h> +#include <linux/kvm_types.h> +#include <asm/kvm_asm.h> + +#define KVM_MAX_VCPUS 1 +#define KVM_MEMORY_SLOTS 32 +/* memory slots that does not exposed to userspace */ +#define KVM_PRIVATE_MEM_SLOTS 4 + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + +/* We don't currently support large pages. */ +#define KVM_PAGES_PER_HPAGE (1<<31) + +struct kvm; +struct kvm_run; +struct kvm_vcpu; + +struct kvm_vm_stat { + u32 remote_tlb_flush; +}; + +struct kvm_vcpu_stat { + u32 sum_exits; + u32 mmio_exits; + u32 dcr_exits; + u32 signal_exits; + u32 light_exits; + /* Account for special types of light exits: */ + u32 itlb_real_miss_exits; + u32 itlb_virt_miss_exits; + u32 dtlb_real_miss_exits; + u32 dtlb_virt_miss_exits; + u32 syscall_exits; + u32 isi_exits; + u32 dsi_exits; + u32 emulated_inst_exits; + u32 dec_exits; + u32 ext_intr_exits; + u32 halt_wakeup; +}; + +enum kvm_exit_types { + MMIO_EXITS, + DCR_EXITS, + SIGNAL_EXITS, + ITLB_REAL_MISS_EXITS, + ITLB_VIRT_MISS_EXITS, + DTLB_REAL_MISS_EXITS, + DTLB_VIRT_MISS_EXITS, + SYSCALL_EXITS, + ISI_EXITS, + DSI_EXITS, + EMULATED_INST_EXITS, + EMULATED_MTMSRWE_EXITS, + EMULATED_WRTEE_EXITS, + EMULATED_MTSPR_EXITS, + EMULATED_MFSPR_EXITS, + EMULATED_MTMSR_EXITS, + EMULATED_MFMSR_EXITS, + EMULATED_TLBSX_EXITS, + EMULATED_TLBWE_EXITS, + EMULATED_RFI_EXITS, + DEC_EXITS, + EXT_INTR_EXITS, + HALT_WAKEUP, + USR_PR_INST, + FP_UNAVAIL, + DEBUG_EXITS, + TIMEINGUEST, + __NUMBER_OF_KVM_EXIT_TYPES +}; + +/* allow access to big endian 32bit upper/lower parts and 64bit var */ +struct kvmppc_exit_timing { + union { + u64 tv64; + struct { + u32 tbu, tbl; + } tv32; + }; +}; + +struct kvm_arch { +}; + +struct kvm_vcpu_arch { + u32 host_stack; + u32 host_pid; + + u64 fpr[32]; + ulong gpr[32]; + + ulong pc; + u32 cr; + ulong ctr; + ulong lr; + ulong xer; + + ulong msr; + u32 mmucr; + ulong sprg0; + ulong sprg1; + ulong sprg2; + ulong sprg3; + ulong sprg4; + ulong sprg5; + ulong sprg6; + ulong sprg7; + ulong srr0; + ulong srr1; + ulong csrr0; + ulong csrr1; + ulong dsrr0; + ulong dsrr1; + ulong dear; + ulong esr; + u32 dec; + u32 decar; + u32 tbl; + u32 tbu; + u32 tcr; + u32 tsr; + u32 ivor[64]; + ulong ivpr; + u32 pir; + + u32 shadow_pid; + u32 pid; + u32 swap_pid; + + u32 pvr; + u32 ccr0; + u32 ccr1; + u32 dbcr0; + u32 dbcr1; + u32 dbsr; + +#ifdef CONFIG_KVM_EXIT_TIMING + struct kvmppc_exit_timing timing_exit; + struct kvmppc_exit_timing timing_last_enter; + u32 last_exit_type; + u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES]; + u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES]; + u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES]; + u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES]; + u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES]; + u64 timing_last_exit; + struct dentry *debugfs_exit_timing; +#endif + + u32 last_inst; + ulong fault_dear; + ulong fault_esr; + gpa_t paddr_accessed; + + u8 io_gpr; /* GPR used as IO source/target */ + u8 mmio_is_bigendian; + u8 dcr_needed; + u8 dcr_is_write; + + u32 cpr0_cfgaddr; /* holds the last set cpr0_cfgaddr */ + + struct timer_list dec_timer; + unsigned long pending_exceptions; +}; + +#endif /* __POWERPC_KVM_HOST_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..eb598a6f4 --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_para.h @@ -0,0 +1,77 @@ +#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 +/* + * 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/powerpc/asm/kvm_ppc.h b/kvm/include/powerpc/asm/kvm_ppc.h new file mode 100644 index 000000000..93915b32c --- /dev/null +++ b/kvm/include/powerpc/asm/kvm_ppc.h @@ -0,0 +1,137 @@ +#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 +/* + * 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_PPC_H__ +#define __POWERPC_KVM_PPC_H__ + +/* This file exists just so we can dereference kvm_vcpu, avoiding nested header + * dependencies. */ + +#include <linux/mutex.h> +#include <linux/timer.h> +#include <linux/types.h> +#include <linux/kvm_types.h> +#include <linux/kvm_host.h> + +enum emulation_result { + EMULATE_DONE, /* no further processing */ + EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */ + EMULATE_DO_DCR, /* kvm_run filled with DCR request */ + EMULATE_FAIL, /* can't emulate this instruction */ +}; + +extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu); +extern char kvmppc_handlers_start[]; +extern unsigned long kvmppc_handler_len; + +extern void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu); +extern int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu, + unsigned int rt, unsigned int bytes, + int is_bigendian); +extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu, + u32 val, unsigned int bytes, int is_bigendian); + +extern int kvmppc_emulate_instruction(struct kvm_run *run, + struct kvm_vcpu *vcpu); +extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu); +extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu); + +/* Core-specific hooks */ + +extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, + unsigned int gtlb_idx); +extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode); +extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid); +extern void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu); +extern int kvmppc_mmu_dtlb_index(struct kvm_vcpu *vcpu, gva_t eaddr); +extern int kvmppc_mmu_itlb_index(struct kvm_vcpu *vcpu, gva_t eaddr); +extern gpa_t kvmppc_mmu_xlate(struct kvm_vcpu *vcpu, unsigned int gtlb_index, + gva_t eaddr); +extern void kvmppc_mmu_dtlb_miss(struct kvm_vcpu *vcpu); +extern void kvmppc_mmu_itlb_miss(struct kvm_vcpu *vcpu); + +extern struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, + unsigned int id); +extern void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu); +extern int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu); +extern int kvmppc_core_check_processor_compat(void); +extern int kvmppc_core_vcpu_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr); + +extern void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu); +extern void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu); + +extern void kvmppc_core_deliver_interrupts(struct kvm_vcpu *vcpu); +extern int kvmppc_core_pending_dec(struct kvm_vcpu *vcpu); +extern void kvmppc_core_queue_program(struct kvm_vcpu *vcpu); +extern void kvmppc_core_queue_dec(struct kvm_vcpu *vcpu); +extern void kvmppc_core_queue_external(struct kvm_vcpu *vcpu, + struct kvm_interrupt *irq); + +extern int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, + unsigned int op, int *advance); +extern int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs); +extern int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt); + +extern int kvmppc_booke_init(void); +extern void kvmppc_booke_exit(void); + +extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu); + +#endif /* __POWERPC_KVM_PPC_H__ */ diff --git a/kvm/include/x86/asm/kvm.h b/kvm/include/x86/asm/kvm.h new file mode 100644 index 000000000..411063ca4 --- /dev/null +++ b/kvm/include/x86/asm/kvm.h @@ -0,0 +1,292 @@ +#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 +#ifndef _ASM_X86_KVM_H +#define _ASM_X86_KVM_H + +/* + * KVM x86 specific structures and definitions + * + */ + +#include <asm/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 + +/* 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 + +/* 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]; +}; +#endif /* _ASM_X86_KVM_H */ diff --git a/kvm/include/x86/asm/kvm_host.h b/kvm/include/x86/asm/kvm_host.h new file mode 100644 index 000000000..07e105848 --- /dev/null +++ b/kvm/include/x86/asm/kvm_host.h @@ -0,0 +1,835 @@ +#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 +/* + * Kernel-based Virtual Machine driver for Linux + * + * This header defines architecture specific interfaces, x86 version + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _ASM_X86_KVM_HOST_H +#define _ASM_X86_KVM_HOST_H + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/mmu_notifier.h> + +#include <linux/kvm.h> +#include <linux/kvm_para.h> +#include <linux/kvm_types.h> + +#include <asm/pvclock-abi.h> +#include <asm/desc.h> +#include <asm/mtrr.h> +#include <asm/msr-index.h> + +#define KVM_MAX_VCPUS 16 +#define KVM_MEMORY_SLOTS 32 +/* memory slots that does not exposed to userspace */ +#define KVM_PRIVATE_MEM_SLOTS 4 + +#define KVM_PIO_PAGE_OFFSET 1 +#define KVM_COALESCED_MMIO_PAGE_OFFSET 2 + +#define CR3_PAE_RESERVED_BITS ((X86_CR3_PWT | X86_CR3_PCD) - 1) +#define CR3_NONPAE_RESERVED_BITS ((PAGE_SIZE-1) & ~(X86_CR3_PWT | X86_CR3_PCD)) +#define CR3_L_MODE_RESERVED_BITS (CR3_NONPAE_RESERVED_BITS | \ + 0xFFFFFF0000000000ULL) + +#define KVM_GUEST_CR0_MASK \ + (X86_CR0_PG | X86_CR0_PE | X86_CR0_WP | X86_CR0_NE \ + | X86_CR0_NW | X86_CR0_CD) +#define KVM_VM_CR0_ALWAYS_ON \ + (X86_CR0_PG | X86_CR0_PE | X86_CR0_WP | X86_CR0_NE | X86_CR0_TS \ + | X86_CR0_MP) +#define KVM_GUEST_CR4_MASK \ + (X86_CR4_VME | X86_CR4_PSE | X86_CR4_PAE | X86_CR4_PGE | X86_CR4_VMXE) +#define KVM_PMODE_VM_CR4_ALWAYS_ON (X86_CR4_PAE | X86_CR4_VMXE) +#define KVM_RMODE_VM_CR4_ALWAYS_ON (X86_CR4_VME | X86_CR4_PAE | X86_CR4_VMXE) + +#define INVALID_PAGE (~(hpa_t)0) +#define UNMAPPED_GVA (~(gpa_t)0) + +/* shadow tables are PAE even on non-PAE hosts */ +#define KVM_HPAGE_SHIFT 21 +#define KVM_HPAGE_SIZE (1UL << KVM_HPAGE_SHIFT) +#define KVM_HPAGE_MASK (~(KVM_HPAGE_SIZE - 1)) + +#define KVM_PAGES_PER_HPAGE (KVM_HPAGE_SIZE / PAGE_SIZE) + +#define DE_VECTOR 0 +#define DB_VECTOR 1 +#define BP_VECTOR 3 +#define OF_VECTOR 4 +#define BR_VECTOR 5 +#define UD_VECTOR 6 +#define NM_VECTOR 7 +#define DF_VECTOR 8 +#define TS_VECTOR 10 +#define NP_VECTOR 11 +#define SS_VECTOR 12 +#define GP_VECTOR 13 +#define PF_VECTOR 14 +#define MF_VECTOR 16 +#define MC_VECTOR 18 + +#define SELECTOR_TI_MASK (1 << 2) +#define SELECTOR_RPL_MASK 0x03 + +#define IOPL_SHIFT 12 + +#define KVM_ALIAS_SLOTS 4 + +#define KVM_PERMILLE_MMU_PAGES 20 +#define KVM_MIN_ALLOC_MMU_PAGES 64 +#define KVM_MMU_HASH_SHIFT 10 +#define KVM_NUM_MMU_PAGES (1 << KVM_MMU_HASH_SHIFT) +#define KVM_MIN_FREE_MMU_PAGES 5 +#define KVM_REFILL_PAGES 25 +#define KVM_MAX_CPUID_ENTRIES 40 +#define KVM_NR_FIXED_MTRR_REGION 88 +#define KVM_NR_VAR_MTRR 8 + +extern spinlock_t kvm_lock; +extern struct list_head vm_list; + +struct kvm_vcpu; +struct kvm; + +enum kvm_reg { + VCPU_REGS_RAX = 0, + VCPU_REGS_RCX = 1, + VCPU_REGS_RDX = 2, + VCPU_REGS_RBX = 3, + VCPU_REGS_RSP = 4, + VCPU_REGS_RBP = 5, + VCPU_REGS_RSI = 6, + VCPU_REGS_RDI = 7, +#ifdef CONFIG_X86_64 + VCPU_REGS_R8 = 8, + VCPU_REGS_R9 = 9, + VCPU_REGS_R10 = 10, + VCPU_REGS_R11 = 11, + VCPU_REGS_R12 = 12, + VCPU_REGS_R13 = 13, + VCPU_REGS_R14 = 14, + VCPU_REGS_R15 = 15, +#endif + VCPU_REGS_RIP, + NR_VCPU_REGS +}; + +enum { + VCPU_SREG_ES, + VCPU_SREG_CS, + VCPU_SREG_SS, + VCPU_SREG_DS, + VCPU_SREG_FS, + VCPU_SREG_GS, + VCPU_SREG_TR, + VCPU_SREG_LDTR, +}; + +#include <asm/kvm_x86_emulate.h> + +#define KVM_NR_MEM_OBJS 40 + +#define KVM_NR_DB_REGS 4 + +#define DR6_BD (1 << 13) +#define DR6_BS (1 << 14) +#define DR6_FIXED_1 0xffff0ff0 +#define DR6_VOLATILE 0x0000e00f + +#define DR7_BP_EN_MASK 0x000000ff +#define DR7_GE (1 << 9) +#define DR7_GD (1 << 13) +#define DR7_FIXED_1 0x00000400 +#define DR7_VOLATILE 0xffff23ff + +/* + * We don't want allocation failures within the mmu code, so we preallocate + * enough memory for a single page fault in a cache. + */ +struct kvm_mmu_memory_cache { + int nobjs; + void *objects[KVM_NR_MEM_OBJS]; +}; + +#define NR_PTE_CHAIN_ENTRIES 5 + +struct kvm_pte_chain { + u64 *parent_ptes[NR_PTE_CHAIN_ENTRIES]; + struct hlist_node link; +}; + +/* + * kvm_mmu_page_role, below, is defined as: + * + * bits 0:3 - total guest paging levels (2-4, or zero for real mode) + * bits 4:7 - page table level for this shadow (1-4) + * bits 8:9 - page table quadrant for 2-level guests + * bit 16 - direct mapping of virtual to physical mapping at gfn + * used for real mode and two-dimensional paging + * bits 17:19 - common access permissions for all ptes in this shadow page + */ +union kvm_mmu_page_role { + unsigned word; + struct { + unsigned glevels:4; + unsigned level:4; + unsigned quadrant:2; + unsigned pad_for_nice_hex_output:6; + unsigned direct:1; + unsigned access:3; + unsigned invalid:1; + unsigned cr4_pge:1; + unsigned nxe:1; + }; +}; + +struct kvm_mmu_page { + struct list_head link; + struct hlist_node hash_link; + + struct list_head oos_link; + + /* + * The following two entries are used to key the shadow page in the + * hash table. + */ + gfn_t gfn; + union kvm_mmu_page_role role; + + u64 *spt; + /* hold the gfn of each spte inside spt */ + gfn_t *gfns; + /* + * One bit set per slot which has memory + * in this shadow page. + */ + DECLARE_BITMAP(slot_bitmap, KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS); + int multimapped; /* More than one parent_pte? */ + int root_count; /* Currently serving as active root */ + bool unsync; + unsigned int unsync_children; + union { + u64 *parent_pte; /* !multimapped */ + struct hlist_head parent_ptes; /* multimapped, kvm_pte_chain */ + }; + DECLARE_BITMAP(unsync_child_bitmap, 512); +}; + +struct kvm_pv_mmu_op_buffer { + void *ptr; + unsigned len; + unsigned processed; + char buf[512] __aligned(sizeof(long)); +}; + +struct kvm_pio_request { + unsigned long count; + int cur_count; + gva_t guest_gva; + int in; + int port; + int size; + int string; + int down; + int rep; +}; + +/* + * x86 supports 3 paging modes (4-level 64-bit, 3-level 64-bit, and 2-level + * 32-bit). The kvm_mmu structure abstracts the details of the current mmu + * mode. + */ +struct kvm_mmu { + void (*new_cr3)(struct kvm_vcpu *vcpu); + int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err); + void (*free)(struct kvm_vcpu *vcpu); + gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva); + void (*prefetch_page)(struct kvm_vcpu *vcpu, + struct kvm_mmu_page *page); + int (*sync_page)(struct kvm_vcpu *vcpu, + struct kvm_mmu_page *sp); + void (*invlpg)(struct kvm_vcpu *vcpu, gva_t gva); + hpa_t root_hpa; + int root_level; + int shadow_root_level; + union kvm_mmu_page_role base_role; + + u64 *pae_root; + u64 rsvd_bits_mask[2][4]; +}; + +struct kvm_vcpu_arch { + u64 host_tsc; + int interrupt_window_open; + unsigned long irq_summary; /* bit vector: 1 per word in irq_pending */ + DECLARE_BITMAP(irq_pending, KVM_NR_INTERRUPTS); + /* + * rip and regs accesses must go through + * kvm_{register,rip}_{read,write} functions. + */ + unsigned long regs[NR_VCPU_REGS]; + u32 regs_avail; + u32 regs_dirty; + + unsigned long cr0; + unsigned long cr2; + unsigned long cr3; + unsigned long cr4; + unsigned long cr8; + u32 hflags; + u64 pdptrs[4]; /* pae */ + u64 shadow_efer; + u64 apic_base; + struct kvm_lapic *apic; /* kernel irqchip context */ + int32_t apic_arb_prio; + int mp_state; + int sipi_vector; + u64 ia32_misc_enable_msr; + bool tpr_access_reporting; + + struct kvm_mmu mmu; + /* only needed in kvm_pv_mmu_op() path, but it's hot so + * put it here to avoid allocation */ + struct kvm_pv_mmu_op_buffer mmu_op_buffer; + + struct kvm_mmu_memory_cache mmu_pte_chain_cache; + struct kvm_mmu_memory_cache mmu_rmap_desc_cache; + struct kvm_mmu_memory_cache mmu_page_cache; + struct kvm_mmu_memory_cache mmu_page_header_cache; + + gfn_t last_pt_write_gfn; + int last_pt_write_count; + u64 *last_pte_updated; + gfn_t last_pte_gfn; + + struct { + gfn_t gfn; /* presumed gfn during guest pte update */ + pfn_t pfn; /* pfn corresponding to that gfn */ + int largepage; + unsigned long mmu_seq; + } update_pte; + + struct i387_fxsave_struct host_fx_image; + struct i387_fxsave_struct guest_fx_image; + + gva_t mmio_fault_cr2; + struct kvm_pio_request pio; + void *pio_data; + + struct kvm_queued_exception { + bool pending; + bool has_error_code; + u8 nr; + u32 error_code; + } exception; + + struct kvm_queued_interrupt { + bool pending; + u8 nr; + } interrupt; + + struct { + int active; + u8 save_iopl; + struct kvm_save_segment { + u16 selector; + unsigned long base; + u32 limit; + u32 ar; + } tr, es, ds, fs, gs; + } rmode; + int halt_request; /* real mode on Intel only */ + + int cpuid_nent; + struct kvm_cpuid_entry2 cpuid_entries[KVM_MAX_CPUID_ENTRIES]; + /* emulate context */ + + struct x86_emulate_ctxt emulate_ctxt; + + gpa_t time; + struct pvclock_vcpu_time_info hv_clock; + unsigned int hv_clock_tsc_khz; + unsigned int time_offset; + struct page *time_page; + + bool nmi_pending; + bool nmi_injected; + bool nmi_window_open; + + struct mtrr_state_type mtrr_state; + u32 pat; + + int switch_db_regs; + unsigned long host_db[KVM_NR_DB_REGS]; + unsigned long host_dr6; + unsigned long host_dr7; + unsigned long db[KVM_NR_DB_REGS]; + unsigned long dr6; + unsigned long dr7; + unsigned long eff_db[KVM_NR_DB_REGS]; +}; + +struct kvm_mem_alias { + gfn_t base_gfn; + unsigned long npages; + gfn_t target_gfn; +}; + +struct kvm_arch{ + int naliases; + struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS]; + + unsigned int n_free_mmu_pages; + unsigned int n_requested_mmu_pages; + unsigned int n_alloc_mmu_pages; + struct hlist_head mmu_page_hash[KVM_NUM_MMU_PAGES]; + /* + * Hash table of struct kvm_mmu_page. + */ + struct list_head active_mmu_pages; + struct list_head assigned_dev_head; + struct iommu_domain *iommu_domain; + struct kvm_pic *vpic; + struct kvm_ioapic *vioapic; + struct kvm_pit *vpit; + struct hlist_head irq_ack_notifier_list; + int vapics_in_nmi_mode; + + unsigned int tss_addr; + struct page *apic_access_page; + + gpa_t wall_clock; + + struct page *ept_identity_pagetable; + bool ept_identity_pagetable_done; + + unsigned long irq_sources_bitmap; + unsigned long irq_states[KVM_IOAPIC_NUM_PINS]; + u64 vm_init_tsc; +}; + +struct kvm_vm_stat { + u32 mmu_shadow_zapped; + u32 mmu_pte_write; + u32 mmu_pte_updated; + u32 mmu_pde_zapped; + u32 mmu_flooded; + u32 mmu_recycled; + u32 mmu_cache_miss; + u32 mmu_unsync; + u32 remote_tlb_flush; + u32 lpages; +}; + +struct kvm_vcpu_stat { + u32 pf_fixed; + u32 pf_guest; + u32 tlb_flush; + u32 invlpg; + + u32 exits; + u32 io_exits; + u32 mmio_exits; + u32 signal_exits; + u32 irq_window_exits; + u32 nmi_window_exits; + u32 halt_exits; + u32 halt_wakeup; + u32 request_irq_exits; + u32 request_nmi_exits; + u32 irq_exits; + u32 host_state_reload; + u32 efer_reload; + u32 fpu_reload; + u32 insn_emulation; + u32 insn_emulation_fail; + u32 hypercalls; + u32 irq_injections; + u32 nmi_injections; +}; + +struct descriptor_table { + u16 limit; + unsigned long base; +} __attribute__((packed)); + +struct kvm_x86_ops { + int (*cpu_has_kvm_support)(void); /* __init */ + int (*disabled_by_bios)(void); /* __init */ + void (*hardware_enable)(void *dummy); /* __init */ + void (*hardware_disable)(void *dummy); + void (*check_processor_compatibility)(void *rtn); + int (*hardware_setup)(void); /* __init */ + void (*hardware_unsetup)(void); /* __exit */ + bool (*cpu_has_accelerated_tpr)(void); + + /* Create, but do not attach this VCPU */ + struct kvm_vcpu *(*vcpu_create)(struct kvm *kvm, unsigned id); + void (*vcpu_free)(struct kvm_vcpu *vcpu); + int (*vcpu_reset)(struct kvm_vcpu *vcpu); + + void (*prepare_guest_switch)(struct kvm_vcpu *vcpu); + void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu); + void (*vcpu_put)(struct kvm_vcpu *vcpu); + + int (*set_guest_debug)(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg); + int (*get_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata); + int (*set_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 data); + u64 (*get_segment_base)(struct kvm_vcpu *vcpu, int seg); + void (*get_segment)(struct kvm_vcpu *vcpu, + struct kvm_segment *var, int seg); + int (*get_cpl)(struct kvm_vcpu *vcpu); + void (*set_segment)(struct kvm_vcpu *vcpu, + struct kvm_segment *var, int seg); + void (*get_cs_db_l_bits)(struct kvm_vcpu *vcpu, int *db, int *l); + void (*decache_cr4_guest_bits)(struct kvm_vcpu *vcpu); + void (*set_cr0)(struct kvm_vcpu *vcpu, unsigned long cr0); + void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long cr3); + void (*set_cr4)(struct kvm_vcpu *vcpu, unsigned long cr4); + void (*set_efer)(struct kvm_vcpu *vcpu, u64 efer); + void (*get_idt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt); + void (*set_idt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt); + void (*get_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt); + void (*set_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt); + unsigned long (*get_dr)(struct kvm_vcpu *vcpu, int dr); + void (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value, + int *exception); + void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg); + unsigned long (*get_rflags)(struct kvm_vcpu *vcpu); + void (*set_rflags)(struct kvm_vcpu *vcpu, unsigned long rflags); + + void (*tlb_flush)(struct kvm_vcpu *vcpu); + + void (*run)(struct kvm_vcpu *vcpu, struct kvm_run *run); + int (*handle_exit)(struct kvm_run *run, struct kvm_vcpu *vcpu); + void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); + void (*patch_hypercall)(struct kvm_vcpu *vcpu, + unsigned char *hypercall_addr); + int (*get_irq)(struct kvm_vcpu *vcpu); + void (*set_irq)(struct kvm_vcpu *vcpu, int vec); + void (*queue_exception)(struct kvm_vcpu *vcpu, unsigned nr, + bool has_error_code, u32 error_code); + bool (*exception_injected)(struct kvm_vcpu *vcpu); + void (*inject_pending_irq)(struct kvm_vcpu *vcpu); + void (*inject_pending_vectors)(struct kvm_vcpu *vcpu, + struct kvm_run *run); + int (*interrupt_allowed)(struct kvm_vcpu *vcpu); + int (*set_tss_addr)(struct kvm *kvm, unsigned int addr); + int (*get_tdp_level)(void); + int (*get_mt_mask_shift)(void); +}; + +extern struct kvm_x86_ops *kvm_x86_ops; + +int kvm_mmu_module_init(void); +void kvm_mmu_module_exit(void); + +void kvm_mmu_destroy(struct kvm_vcpu *vcpu); +int kvm_mmu_create(struct kvm_vcpu *vcpu); +int kvm_mmu_setup(struct kvm_vcpu *vcpu); +void kvm_mmu_set_nonpresent_ptes(u64 trap_pte, u64 notrap_pte); +void kvm_mmu_set_base_ptes(u64 base_pte); +void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask, + u64 dirty_mask, u64 nx_mask, u64 x_mask, u64 mt_mask); + +int kvm_mmu_reset_context(struct kvm_vcpu *vcpu); +void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot); +void kvm_mmu_zap_all(struct kvm *kvm); +unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm); +void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int kvm_nr_mmu_pages); + +int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3); + +int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, + const void *val, int bytes); +int kvm_pv_mmu_op(struct kvm_vcpu *vcpu, unsigned long bytes, + gpa_t addr, unsigned long *ret); + +extern bool tdp_enabled; + +enum emulation_result { + EMULATE_DONE, /* no further processing */ + EMULATE_DO_MMIO, /* kvm_run filled with mmio request */ + EMULATE_FAIL, /* can't emulate this instruction */ +}; + +#define EMULTYPE_NO_DECODE (1 << 0) +#define EMULTYPE_TRAP_UD (1 << 1) +#define EMULTYPE_SKIP (1 << 2) +int emulate_instruction(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long cr2, u16 error_code, int emulation_type); +void kvm_report_emulation_failure(struct kvm_vcpu *cvpu, const char *context); +void realmode_lgdt(struct kvm_vcpu *vcpu, u16 size, unsigned long address); +void realmode_lidt(struct kvm_vcpu *vcpu, u16 size, unsigned long address); +void realmode_lmsw(struct kvm_vcpu *vcpu, unsigned long msw, + unsigned long *rflags); + +unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr); +void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long value, + unsigned long *rflags); +void kvm_enable_efer_bits(u64); +int kvm_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *data); +int kvm_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data); + +struct x86_emulate_ctxt; + +int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, + int size, unsigned port); +int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, + int size, unsigned long count, int down, + gva_t address, int rep, unsigned port); +void kvm_emulate_cpuid(struct kvm_vcpu *vcpu); +int kvm_emulate_halt(struct kvm_vcpu *vcpu); +int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address); +int emulate_clts(struct kvm_vcpu *vcpu); +int emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr, + unsigned long *dest); +int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr, + unsigned long value); + +void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); +int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, + int type_bits, int seg); + +int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason); + +void kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0); +void kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3); +void kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4); +void kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8); +unsigned long kvm_get_cr8(struct kvm_vcpu *vcpu); +void kvm_lmsw(struct kvm_vcpu *vcpu, unsigned long msw); +void kvm_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l); + +int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata); +int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data); + +void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); +void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); +void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long cr2, + u32 error_code); + +int kvm_pic_set_irq(void *opaque, int irq, int level); + +void kvm_inject_nmi(struct kvm_vcpu *vcpu); + +void fx_init(struct kvm_vcpu *vcpu); + +int emulator_write_emulated(unsigned long addr, + const void *val, + unsigned int bytes, + struct kvm_vcpu *vcpu); + +unsigned long segment_base(u16 selector); + +void kvm_mmu_flush_tlb(struct kvm_vcpu *vcpu); +void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, + const u8 *new, int bytes, + bool guest_initiated); +int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva); +void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu); +int kvm_mmu_load(struct kvm_vcpu *vcpu); +void kvm_mmu_unload(struct kvm_vcpu *vcpu); +void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu); + +int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); + +int kvm_fix_hypercall(struct kvm_vcpu *vcpu); + +int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code); +void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva); + +void kvm_enable_tdp(void); +void kvm_disable_tdp(void); + +int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3); +int complete_pio(struct kvm_vcpu *vcpu); + +struct kvm_memory_slot *gfn_to_memslot_unaliased(struct kvm *kvm, gfn_t gfn); + +static inline struct kvm_mmu_page *page_header(hpa_t shadow_page) +{ + struct page *page = pfn_to_page(shadow_page >> PAGE_SHIFT); + + return (struct kvm_mmu_page *)page_private(page); +} + +static inline u16 kvm_read_fs(void) +{ + u16 seg; + asm("mov %%fs, %0" : "=g"(seg)); + return seg; +} + +static inline u16 kvm_read_gs(void) +{ + u16 seg; + asm("mov %%gs, %0" : "=g"(seg)); + return seg; +} + +static inline u16 kvm_read_ldt(void) +{ + u16 ldt; + asm("sldt %0" : "=g"(ldt)); + return ldt; +} + +static inline void kvm_load_fs(u16 sel) +{ + asm("mov %0, %%fs" : : "rm"(sel)); +} + +static inline void kvm_load_gs(u16 sel) +{ + asm("mov %0, %%gs" : : "rm"(sel)); +} + +static inline void kvm_load_ldt(u16 sel) +{ + asm("lldt %0" : : "rm"(sel)); +} + +static inline void kvm_get_idt(struct descriptor_table *table) +{ + asm("sidt %0" : "=m"(*table)); +} + +static inline void kvm_get_gdt(struct descriptor_table *table) +{ + asm("sgdt %0" : "=m"(*table)); +} + +static inline unsigned long kvm_read_tr_base(void) +{ + u16 tr; + asm("str %0" : "=g"(tr)); + return segment_base(tr); +} + +#ifdef CONFIG_X86_64 +static inline unsigned long read_msr(unsigned long msr) +{ + u64 value; + + rdmsrl(msr, value); + return value; +} +#endif + +static inline void kvm_fx_save(struct i387_fxsave_struct *image) +{ + asm("fxsave (%0)":: "r" (image)); +} + +static inline void kvm_fx_restore(struct i387_fxsave_struct *image) +{ + asm("fxrstor (%0)":: "r" (image)); +} + +static inline void kvm_fx_finit(void) +{ + asm("finit"); +} + +static inline u32 get_rdx_init_val(void) +{ + return 0x600; /* P6 family */ +} + +static inline void kvm_inject_gp(struct kvm_vcpu *vcpu, u32 error_code) +{ + kvm_queue_exception_e(vcpu, GP_VECTOR, error_code); +} + +#define MSR_IA32_TIME_STAMP_COUNTER 0x010 + +#define TSS_IOPB_BASE_OFFSET 0x66 +#define TSS_BASE_SIZE 0x68 +#define TSS_IOPB_SIZE (65536 / 8) +#define TSS_REDIRECTION_SIZE (256 / 8) +#define RMODE_TSS_SIZE \ + (TSS_BASE_SIZE + TSS_REDIRECTION_SIZE + TSS_IOPB_SIZE + 1) + +enum { + TASK_SWITCH_CALL = 0, + TASK_SWITCH_IRET = 1, + TASK_SWITCH_JMP = 2, + TASK_SWITCH_GATE = 3, +}; + +#define HF_GIF_MASK (1 << 0) +#define HF_HIF_MASK (1 << 1) +#define HF_VINTR_MASK (1 << 2) + +/* + * Hardware virtualization extension instructions may fault if a + * reboot turns off virtualization while processes are running. + * Trap the fault and ignore the instruction if that happens. + */ +asmlinkage void kvm_handle_fault_on_reboot(void); + +#define __kvm_handle_fault_on_reboot(insn) \ + "666: " insn "\n\t" \ + ".pushsection .fixup, \"ax\" \n" \ + "667: \n\t" \ + __ASM_SIZE(push) " $666b \n\t" \ + "jmp kvm_handle_fault_on_reboot \n\t" \ + ".popsection \n\t" \ + ".pushsection __ex_table, \"a\" \n\t" \ + _ASM_PTR " 666b, 667b \n\t" \ + ".popsection" + +#define KVM_ARCH_WANT_MMU_NOTIFIER +int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); +int kvm_age_hva(struct kvm *kvm, unsigned long hva); +int cpuid_maxphyaddr(struct kvm_vcpu *vcpu); + +#endif /* _ASM_X86_KVM_HOST_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..7a30a52c0 --- /dev/null +++ b/kvm/include/x86/asm/kvm_para.h @@ -0,0 +1,187 @@ +#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 +#ifndef _ASM_X86_KVM_PARA_H +#define _ASM_X86_KVM_PARA_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/include/x86/asm/kvm_x86_emulate.h b/kvm/include/x86/asm/kvm_x86_emulate.h new file mode 100644 index 000000000..17686747d --- /dev/null +++ b/kvm/include/x86/asm/kvm_x86_emulate.h @@ -0,0 +1,221 @@ +#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 +/****************************************************************************** + * x86_emulate.h + * + * Generic x86 (32-bit and 64-bit) instruction decoder and emulator. + * + * Copyright (c) 2005 Keir Fraser + * + * From: xen-unstable 10676:af9809f51f81a3c43f276f00c81a52ef558afda4 + */ + +#ifndef _ASM_X86_KVM_X86_EMULATE_H +#define _ASM_X86_KVM_X86_EMULATE_H + +struct x86_emulate_ctxt; + +/* + * x86_emulate_ops: + * + * These operations represent the instruction emulator's interface to memory. + * There are two categories of operation: those that act on ordinary memory + * regions (*_std), and those that act on memory regions known to require + * special treatment or emulation (*_emulated). + * + * The emulator assumes that an instruction accesses only one 'emulated memory' + * location, that this location is the given linear faulting address (cr2), and + * that this is one of the instruction's data operands. Instruction fetches and + * stack operations are assumed never to access emulated memory. The emulator + * automatically deduces which operand of a string-move operation is accessing + * emulated memory, and assumes that the other operand accesses normal memory. + * + * NOTES: + * 1. The emulator isn't very smart about emulated vs. standard memory. + * 'Emulated memory' access addresses should be checked for sanity. + * 'Normal memory' accesses may fault, and the caller must arrange to + * detect and handle reentrancy into the emulator via recursive faults. + * Accesses may be unaligned and may cross page boundaries. + * 2. If the access fails (cannot emulate, or a standard access faults) then + * it is up to the memop to propagate the fault to the guest VM via + * some out-of-band mechanism, unknown to the emulator. The memop signals + * failure by returning X86EMUL_PROPAGATE_FAULT to the emulator, which will + * then immediately bail. + * 3. Valid access sizes are 1, 2, 4 and 8 bytes. On x86/32 systems only + * cmpxchg8b_emulated need support 8-byte accesses. + * 4. The emulator cannot handle 64-bit mode emulation on an x86/32 system. + */ +/* Access completed successfully: continue emulation as normal. */ +#define X86EMUL_CONTINUE 0 +/* Access is unhandleable: bail from emulation and return error to caller. */ +#define X86EMUL_UNHANDLEABLE 1 +/* Terminate emulation but return success to the caller. */ +#define X86EMUL_PROPAGATE_FAULT 2 /* propagate a generated fault to guest */ +#define X86EMUL_RETRY_INSTR 2 /* retry the instruction for some reason */ +#define X86EMUL_CMPXCHG_FAILED 2 /* cmpxchg did not see expected value */ +struct x86_emulate_ops { + /* + * read_std: Read bytes of standard (non-emulated/special) memory. + * Used for instruction fetch, stack operations, and others. + * @addr: [IN ] Linear address from which to read. + * @val: [OUT] Value read from memory, zero-extended to 'u_long'. + * @bytes: [IN ] Number of bytes to read from memory. + */ + int (*read_std)(unsigned long addr, void *val, + unsigned int bytes, struct kvm_vcpu *vcpu); + + /* + * read_emulated: Read bytes from emulated/special memory area. + * @addr: [IN ] Linear address from which to read. + * @val: [OUT] Value read from memory, zero-extended to 'u_long'. + * @bytes: [IN ] Number of bytes to read from memory. + */ + int (*read_emulated)(unsigned long addr, + void *val, + unsigned int bytes, + struct kvm_vcpu *vcpu); + + /* + * write_emulated: Read bytes from emulated/special memory area. + * @addr: [IN ] Linear address to which to write. + * @val: [IN ] Value to write to memory (low-order bytes used as + * required). + * @bytes: [IN ] Number of bytes to write to memory. + */ + int (*write_emulated)(unsigned long addr, + const void *val, + unsigned int bytes, + struct kvm_vcpu *vcpu); + + /* + * cmpxchg_emulated: Emulate an atomic (LOCKed) CMPXCHG operation on an + * emulated/special memory area. + * @addr: [IN ] Linear address to access. + * @old: [IN ] Value expected to be current at @addr. + * @new: [IN ] Value to write to @addr. + * @bytes: [IN ] Number of bytes to access using CMPXCHG. + */ + int (*cmpxchg_emulated)(unsigned long addr, + const void *old, + const void *new, + unsigned int bytes, + struct kvm_vcpu *vcpu); + +}; + +/* Type, address-of, and value of an instruction's operand. */ +struct operand { + enum { OP_REG, OP_MEM, OP_IMM, OP_NONE } type; + unsigned int bytes; + unsigned long val, orig_val, *ptr; +}; + +struct fetch_cache { + u8 data[15]; + unsigned long start; + unsigned long end; +}; + +struct decode_cache { + u8 twobyte; + u8 b; + u8 lock_prefix; + u8 rep_prefix; + u8 op_bytes; + u8 ad_bytes; + u8 rex_prefix; + struct operand src; + struct operand src2; + struct operand dst; + bool has_seg_override; + u8 seg_override; + unsigned int d; + unsigned long regs[NR_VCPU_REGS]; + unsigned long eip; + /* modrm */ + u8 modrm; + u8 modrm_mod; + u8 modrm_reg; + u8 modrm_rm; + u8 use_modrm_ea; + bool rip_relative; + unsigned long modrm_ea; + void *modrm_ptr; + unsigned long modrm_val; + struct fetch_cache fetch; +}; + +struct x86_emulate_ctxt { + /* Register state before/after emulation. */ + struct kvm_vcpu *vcpu; + + unsigned long eflags; + /* Emulated execution mode, represented by an X86EMUL_MODE value. */ + int mode; + u32 cs_base; + + /* decode cache */ + struct decode_cache decode; +}; + +/* Repeat String Operation Prefix */ +#define REPE_PREFIX 1 +#define REPNE_PREFIX 2 + +/* Execution mode, passed to the emulator. */ +#define X86EMUL_MODE_REAL 0 /* Real mode. */ +#define X86EMUL_MODE_PROT16 2 /* 16-bit protected mode. */ +#define X86EMUL_MODE_PROT32 4 /* 32-bit protected mode. */ +#define X86EMUL_MODE_PROT64 8 /* 64-bit (long) mode. */ + +/* Host execution mode. */ +#if defined(CONFIG_X86_32) +#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT32 +#elif defined(CONFIG_X86_64) +#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT64 +#endif + +int x86_decode_insn(struct x86_emulate_ctxt *ctxt, + struct x86_emulate_ops *ops); +int x86_emulate_insn(struct x86_emulate_ctxt *ctxt, + struct x86_emulate_ops *ops); + +#endif /* _ASM_X86_KVM_X86_EMULATE_H */ diff --git a/kvm/include/x86/asm/svm.h b/kvm/include/x86/asm/svm.h new file mode 100644 index 000000000..5a34c949f --- /dev/null +++ b/kvm/include/x86/asm/svm.h @@ -0,0 +1,365 @@ +#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 +#ifndef __SVM_H +#define __SVM_H + +enum { + INTERCEPT_INTR, + INTERCEPT_NMI, + INTERCEPT_SMI, + INTERCEPT_INIT, + INTERCEPT_VINTR, + INTERCEPT_SELECTIVE_CR0, + INTERCEPT_STORE_IDTR, + INTERCEPT_STORE_GDTR, + INTERCEPT_STORE_LDTR, + INTERCEPT_STORE_TR, + INTERCEPT_LOAD_IDTR, + INTERCEPT_LOAD_GDTR, + INTERCEPT_LOAD_LDTR, + INTERCEPT_LOAD_TR, + INTERCEPT_RDTSC, + INTERCEPT_RDPMC, + INTERCEPT_PUSHF, + INTERCEPT_POPF, + INTERCEPT_CPUID, + INTERCEPT_RSM, + INTERCEPT_IRET, + INTERCEPT_INTn, + INTERCEPT_INVD, + INTERCEPT_PAUSE, + INTERCEPT_HLT, + INTERCEPT_INVLPG, + INTERCEPT_INVLPGA, + INTERCEPT_IOIO_PROT, + INTERCEPT_MSR_PROT, + INTERCEPT_TASK_SWITCH, + INTERCEPT_FERR_FREEZE, + INTERCEPT_SHUTDOWN, + INTERCEPT_VMRUN, + INTERCEPT_VMMCALL, + INTERCEPT_VMLOAD, + INTERCEPT_VMSAVE, + INTERCEPT_STGI, + INTERCEPT_CLGI, + INTERCEPT_SKINIT, + INTERCEPT_RDTSCP, + INTERCEPT_ICEBP, + INTERCEPT_WBINVD, + INTERCEPT_MONITOR, + INTERCEPT_MWAIT, + INTERCEPT_MWAIT_COND, +}; + + +struct __attribute__ ((__packed__)) vmcb_control_area { + u16 intercept_cr_read; + u16 intercept_cr_write; + u16 intercept_dr_read; + u16 intercept_dr_write; + u32 intercept_exceptions; + u64 intercept; + u8 reserved_1[44]; + u64 iopm_base_pa; + u64 msrpm_base_pa; + u64 tsc_offset; + u32 asid; + u8 tlb_ctl; + u8 reserved_2[3]; + u32 int_ctl; + u32 int_vector; + u32 int_state; + u8 reserved_3[4]; + u32 exit_code; + u32 exit_code_hi; + u64 exit_info_1; + u64 exit_info_2; + u32 exit_int_info; + u32 exit_int_info_err; + u64 nested_ctl; + u8 reserved_4[16]; + u32 event_inj; + u32 event_inj_err; + u64 nested_cr3; + u64 lbr_ctl; + u8 reserved_5[832]; +}; + + +#define TLB_CONTROL_DO_NOTHING 0 +#define TLB_CONTROL_FLUSH_ALL_ASID 1 + +#define V_TPR_MASK 0x0f + +#define V_IRQ_SHIFT 8 +#define V_IRQ_MASK (1 << V_IRQ_SHIFT) + +#define V_INTR_PRIO_SHIFT 16 +#define V_INTR_PRIO_MASK (0x0f << V_INTR_PRIO_SHIFT) + +#define V_IGN_TPR_SHIFT 20 +#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT) + +#define V_INTR_MASKING_SHIFT 24 +#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT) + +#define SVM_INTERRUPT_SHADOW_MASK 1 + +#define SVM_IOIO_STR_SHIFT 2 +#define SVM_IOIO_REP_SHIFT 3 +#define SVM_IOIO_SIZE_SHIFT 4 +#define SVM_IOIO_ASIZE_SHIFT 7 + +#define SVM_IOIO_TYPE_MASK 1 +#define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT) +#define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT) +#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT) +#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT) + +struct __attribute__ ((__packed__)) vmcb_seg { + u16 selector; + u16 attrib; + u32 limit; + u64 base; +}; + +struct __attribute__ ((__packed__)) vmcb_save_area { + struct vmcb_seg es; + struct vmcb_seg cs; + struct vmcb_seg ss; + struct vmcb_seg ds; + struct vmcb_seg fs; + struct vmcb_seg gs; + struct vmcb_seg gdtr; + struct vmcb_seg ldtr; + struct vmcb_seg idtr; + struct vmcb_seg tr; + u8 reserved_1[43]; + u8 cpl; + u8 reserved_2[4]; + u64 efer; + u8 reserved_3[112]; + u64 cr4; + u64 cr3; + u64 cr0; + u64 dr7; + u64 dr6; + u64 rflags; + u64 rip; + u8 reserved_4[88]; + u64 rsp; + u8 reserved_5[24]; + u64 rax; + u64 star; + u64 lstar; + u64 cstar; + u64 sfmask; + u64 kernel_gs_base; + u64 sysenter_cs; + u64 sysenter_esp; + u64 sysenter_eip; + u64 cr2; + u8 reserved_6[32]; + u64 g_pat; + u64 dbgctl; + u64 br_from; + u64 br_to; + u64 last_excp_from; + u64 last_excp_to; +}; + +struct __attribute__ ((__packed__)) vmcb { + struct vmcb_control_area control; + struct vmcb_save_area save; +}; + +#define SVM_CPUID_FEATURE_SHIFT 2 +#define SVM_CPUID_FUNC 0x8000000a + +#define SVM_VM_CR_SVM_DISABLE 4 + +#define SVM_SELECTOR_S_SHIFT 4 +#define SVM_SELECTOR_DPL_SHIFT 5 +#define SVM_SELECTOR_P_SHIFT 7 +#define SVM_SELECTOR_AVL_SHIFT 8 +#define SVM_SELECTOR_L_SHIFT 9 +#define SVM_SELECTOR_DB_SHIFT 10 +#define SVM_SELECTOR_G_SHIFT 11 + +#define SVM_SELECTOR_TYPE_MASK (0xf) +#define SVM_SELECTOR_S_MASK (1 << SVM_SELECTOR_S_SHIFT) +#define SVM_SELECTOR_DPL_MASK (3 << SVM_SELECTOR_DPL_SHIFT) +#define SVM_SELECTOR_P_MASK (1 << SVM_SELECTOR_P_SHIFT) +#define SVM_SELECTOR_AVL_MASK (1 << SVM_SELECTOR_AVL_SHIFT) +#define SVM_SELECTOR_L_MASK (1 << SVM_SELECTOR_L_SHIFT) +#define SVM_SELECTOR_DB_MASK (1 << SVM_SELECTOR_DB_SHIFT) +#define SVM_SELECTOR_G_MASK (1 << SVM_SELECTOR_G_SHIFT) + +#define SVM_SELECTOR_WRITE_MASK (1 << 1) +#define SVM_SELECTOR_READ_MASK SVM_SELECTOR_WRITE_MASK +#define SVM_SELECTOR_CODE_MASK (1 << 3) + +#define INTERCEPT_CR0_MASK 1 +#define INTERCEPT_CR3_MASK (1 << 3) +#define INTERCEPT_CR4_MASK (1 << 4) +#define INTERCEPT_CR8_MASK (1 << 8) + +#define INTERCEPT_DR0_MASK 1 +#define INTERCEPT_DR1_MASK (1 << 1) +#define INTERCEPT_DR2_MASK (1 << 2) +#define INTERCEPT_DR3_MASK (1 << 3) +#define INTERCEPT_DR4_MASK (1 << 4) +#define INTERCEPT_DR5_MASK (1 << 5) +#define INTERCEPT_DR6_MASK (1 << 6) +#define INTERCEPT_DR7_MASK (1 << 7) + +#define SVM_EVTINJ_VEC_MASK 0xff + +#define SVM_EVTINJ_TYPE_SHIFT 8 +#define SVM_EVTINJ_TYPE_MASK (7 << SVM_EVTINJ_TYPE_SHIFT) + +#define SVM_EVTINJ_TYPE_INTR (0 << SVM_EVTINJ_TYPE_SHIFT) +#define SVM_EVTINJ_TYPE_NMI (2 << SVM_EVTINJ_TYPE_SHIFT) +#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT) +#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT) + +#define SVM_EVTINJ_VALID (1 << 31) +#define SVM_EVTINJ_VALID_ERR (1 << 11) + +#define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK +#define SVM_EXITINTINFO_TYPE_MASK SVM_EVTINJ_TYPE_MASK + +#define SVM_EXITINTINFO_TYPE_INTR SVM_EVTINJ_TYPE_INTR +#define SVM_EXITINTINFO_TYPE_NMI SVM_EVTINJ_TYPE_NMI +#define SVM_EXITINTINFO_TYPE_EXEPT SVM_EVTINJ_TYPE_EXEPT +#define SVM_EXITINTINFO_TYPE_SOFT SVM_EVTINJ_TYPE_SOFT + +#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID +#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR + +#define SVM_EXITINFOSHIFT_TS_REASON_IRET 36 +#define SVM_EXITINFOSHIFT_TS_REASON_JMP 38 + +#define SVM_EXIT_READ_CR0 0x000 +#define SVM_EXIT_READ_CR3 0x003 +#define SVM_EXIT_READ_CR4 0x004 +#define SVM_EXIT_READ_CR8 0x008 +#define SVM_EXIT_WRITE_CR0 0x010 +#define SVM_EXIT_WRITE_CR3 0x013 +#define SVM_EXIT_WRITE_CR4 0x014 +#define SVM_EXIT_WRITE_CR8 0x018 +#define SVM_EXIT_READ_DR0 0x020 +#define SVM_EXIT_READ_DR1 0x021 +#define SVM_EXIT_READ_DR2 0x022 +#define SVM_EXIT_READ_DR3 0x023 +#define SVM_EXIT_READ_DR4 0x024 +#define SVM_EXIT_READ_DR5 0x025 +#define SVM_EXIT_READ_DR6 0x026 +#define SVM_EXIT_READ_DR7 0x027 +#define SVM_EXIT_WRITE_DR0 0x030 +#define SVM_EXIT_WRITE_DR1 0x031 +#define SVM_EXIT_WRITE_DR2 0x032 +#define SVM_EXIT_WRITE_DR3 0x033 +#define SVM_EXIT_WRITE_DR4 0x034 +#define SVM_EXIT_WRITE_DR5 0x035 +#define SVM_EXIT_WRITE_DR6 0x036 +#define SVM_EXIT_WRITE_DR7 0x037 +#define SVM_EXIT_EXCP_BASE 0x040 +#define SVM_EXIT_INTR 0x060 +#define SVM_EXIT_NMI 0x061 +#define SVM_EXIT_SMI 0x062 +#define SVM_EXIT_INIT 0x063 +#define SVM_EXIT_VINTR 0x064 +#define SVM_EXIT_CR0_SEL_WRITE 0x065 +#define SVM_EXIT_IDTR_READ 0x066 +#define SVM_EXIT_GDTR_READ 0x067 +#define SVM_EXIT_LDTR_READ 0x068 +#define SVM_EXIT_TR_READ 0x069 +#define SVM_EXIT_IDTR_WRITE 0x06a +#define SVM_EXIT_GDTR_WRITE 0x06b +#define SVM_EXIT_LDTR_WRITE 0x06c +#define SVM_EXIT_TR_WRITE 0x06d +#define SVM_EXIT_RDTSC 0x06e +#define SVM_EXIT_RDPMC 0x06f +#define SVM_EXIT_PUSHF 0x070 +#define SVM_EXIT_POPF 0x071 +#define SVM_EXIT_CPUID 0x072 +#define SVM_EXIT_RSM 0x073 +#define SVM_EXIT_IRET 0x074 +#define SVM_EXIT_SWINT 0x075 +#define SVM_EXIT_INVD 0x076 +#define SVM_EXIT_PAUSE 0x077 +#define SVM_EXIT_HLT 0x078 +#define SVM_EXIT_INVLPG 0x079 +#define SVM_EXIT_INVLPGA 0x07a +#define SVM_EXIT_IOIO 0x07b +#define SVM_EXIT_MSR 0x07c +#define SVM_EXIT_TASK_SWITCH 0x07d +#define SVM_EXIT_FERR_FREEZE 0x07e +#define SVM_EXIT_SHUTDOWN 0x07f +#define SVM_EXIT_VMRUN 0x080 +#define SVM_EXIT_VMMCALL 0x081 +#define SVM_EXIT_VMLOAD 0x082 +#define SVM_EXIT_VMSAVE 0x083 +#define SVM_EXIT_STGI 0x084 +#define SVM_EXIT_CLGI 0x085 +#define SVM_EXIT_SKINIT 0x086 +#define SVM_EXIT_RDTSCP 0x087 +#define SVM_EXIT_ICEBP 0x088 +#define SVM_EXIT_WBINVD 0x089 +#define SVM_EXIT_MONITOR 0x08a +#define SVM_EXIT_MWAIT 0x08b +#define SVM_EXIT_MWAIT_COND 0x08c +#define SVM_EXIT_NPF 0x400 + +#define SVM_EXIT_ERR -1 + +#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */ + +#define SVM_VMLOAD ".byte 0x0f, 0x01, 0xda" +#define SVM_VMRUN ".byte 0x0f, 0x01, 0xd8" +#define SVM_VMSAVE ".byte 0x0f, 0x01, 0xdb" +#define SVM_CLGI ".byte 0x0f, 0x01, 0xdd" +#define SVM_STGI ".byte 0x0f, 0x01, 0xdc" +#define SVM_INVLPGA ".byte 0x0f, 0x01, 0xdf" + +#endif + diff --git a/kvm/include/x86/asm/virtext.h b/kvm/include/x86/asm/virtext.h new file mode 100644 index 000000000..e6f17fb45 --- /dev/null +++ b/kvm/include/x86/asm/virtext.h @@ -0,0 +1,172 @@ +#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 +/* CPU virtualization extensions handling + * + * This should carry the code for handling CPU virtualization extensions + * that needs to live in the kernel core. + * + * Author: Eduardo Habkost <ehabkost@redhat.com> + * + * Copyright (C) 2008, Red Hat Inc. + * + * Contains code from KVM, Copyright (C) 2006 Qumranet, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#ifndef _ASM_X86_VIRTEX_H +#define _ASM_X86_VIRTEX_H + +#include <asm/processor.h> +#include <asm/system.h> + +#include <asm/vmx.h> +#include <asm/svm.h> + +/* + * VMX functions: + */ + +static inline int cpu_has_vmx(void) +{ + unsigned long ecx = cpuid_ecx(1); + return test_bit(5, &ecx); /* CPUID.1:ECX.VMX[bit 5] -> VT */ +} + + +/** Disable VMX on the current CPU + * + * vmxoff causes a undefined-opcode exception if vmxon was not run + * on the CPU previously. Only call this function if you know VMX + * is enabled. + */ +static inline void cpu_vmxoff(void) +{ + asm volatile (ASM_VMX_VMXOFF : : : "cc"); + write_cr4(read_cr4() & ~X86_CR4_VMXE); +} + +static inline int cpu_vmx_enabled(void) +{ + return read_cr4() & X86_CR4_VMXE; +} + +/** Disable VMX if it is enabled on the current CPU + * + * You shouldn't call this if cpu_has_vmx() returns 0. + */ +static inline void __cpu_emergency_vmxoff(void) +{ + if (cpu_vmx_enabled()) + cpu_vmxoff(); +} + +/** Disable VMX if it is supported and enabled on the current CPU + */ +static inline void cpu_emergency_vmxoff(void) +{ + if (cpu_has_vmx()) + __cpu_emergency_vmxoff(); +} + + + + +/* + * SVM functions: + */ + +/** Check if the CPU has SVM support + * + * You can use the 'msg' arg to get a message describing the problem, + * if the function returns zero. Simply pass NULL if you are not interested + * on the messages; gcc should take care of not generating code for + * the messages on this case. + */ +static inline int cpu_has_svm(const char **msg) +{ + uint32_t eax, ebx, ecx, edx; + + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) { + if (msg) + *msg = "not amd"; + return 0; + } + + cpuid(0x80000000, &eax, &ebx, &ecx, &edx); + if (eax < SVM_CPUID_FUNC) { + if (msg) + *msg = "can't execute cpuid_8000000a"; + return 0; + } + + cpuid(0x80000001, &eax, &ebx, &ecx, &edx); + if (!(ecx & (1 << SVM_CPUID_FEATURE_SHIFT))) { + if (msg) + *msg = "svm not available"; + return 0; + } + return 1; +} + + +/** Disable SVM on the current CPU + * + * You should call this only if cpu_has_svm() returned true. + */ +static inline void cpu_svm_disable(void) +{ + uint64_t efer; + + wrmsrl(MSR_VM_HSAVE_PA, 0); + rdmsrl(MSR_EFER, efer); + wrmsrl(MSR_EFER, efer & ~EFER_SVME); +} + +/** Makes sure SVM is disabled, if it is supported on the CPU + */ +static inline void cpu_emergency_svm_disable(void) +{ + if (cpu_has_svm(NULL)) + cpu_svm_disable(); +} + +#endif /* _ASM_X86_VIRTEX_H */ diff --git a/kvm/include/x86/asm/vmx.h b/kvm/include/x86/asm/vmx.h new file mode 100644 index 000000000..df8d4f963 --- /dev/null +++ b/kvm/include/x86/asm/vmx.h @@ -0,0 +1,423 @@ +#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 +#ifndef VMX_H +#define VMX_H + +/* + * vmx.h: VMX Architecture related definitions + * Copyright (c) 2004, 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. + * + * A few random additions are: + * Copyright (C) 2006 Qumranet + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * + */ + +/* + * Definitions of Primary Processor-Based VM-Execution Controls. + */ +#define CPU_BASED_VIRTUAL_INTR_PENDING 0x00000004 +#define CPU_BASED_USE_TSC_OFFSETING 0x00000008 +#define CPU_BASED_HLT_EXITING 0x00000080 +#define CPU_BASED_INVLPG_EXITING 0x00000200 +#define CPU_BASED_MWAIT_EXITING 0x00000400 +#define CPU_BASED_RDPMC_EXITING 0x00000800 +#define CPU_BASED_RDTSC_EXITING 0x00001000 +#define CPU_BASED_CR3_LOAD_EXITING 0x00008000 +#define CPU_BASED_CR3_STORE_EXITING 0x00010000 +#define CPU_BASED_CR8_LOAD_EXITING 0x00080000 +#define CPU_BASED_CR8_STORE_EXITING 0x00100000 +#define CPU_BASED_TPR_SHADOW 0x00200000 +#define CPU_BASED_VIRTUAL_NMI_PENDING 0x00400000 +#define CPU_BASED_MOV_DR_EXITING 0x00800000 +#define CPU_BASED_UNCOND_IO_EXITING 0x01000000 +#define CPU_BASED_USE_IO_BITMAPS 0x02000000 +#define CPU_BASED_USE_MSR_BITMAPS 0x10000000 +#define CPU_BASED_MONITOR_EXITING 0x20000000 +#define CPU_BASED_PAUSE_EXITING 0x40000000 +#define CPU_BASED_ACTIVATE_SECONDARY_CONTROLS 0x80000000 +/* + * Definitions of Secondary Processor-Based VM-Execution Controls. + */ +#define SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES 0x00000001 +#define SECONDARY_EXEC_ENABLE_EPT 0x00000002 +#define SECONDARY_EXEC_ENABLE_VPID 0x00000020 +#define SECONDARY_EXEC_WBINVD_EXITING 0x00000040 + + +#define PIN_BASED_EXT_INTR_MASK 0x00000001 +#define PIN_BASED_NMI_EXITING 0x00000008 +#define PIN_BASED_VIRTUAL_NMIS 0x00000020 + +#define VM_EXIT_HOST_ADDR_SPACE_SIZE 0x00000200 +#define VM_EXIT_ACK_INTR_ON_EXIT 0x00008000 +#define VM_EXIT_SAVE_IA32_PAT 0x00040000 +#define VM_EXIT_LOAD_IA32_PAT 0x00080000 + +#define VM_ENTRY_IA32E_MODE 0x00000200 +#define VM_ENTRY_SMM 0x00000400 +#define VM_ENTRY_DEACT_DUAL_MONITOR 0x00000800 +#define VM_ENTRY_LOAD_IA32_PAT 0x00004000 + +/* VMCS Encodings */ +enum vmcs_field { + VIRTUAL_PROCESSOR_ID = 0x00000000, + GUEST_ES_SELECTOR = 0x00000800, + GUEST_CS_SELECTOR = 0x00000802, + GUEST_SS_SELECTOR = 0x00000804, + GUEST_DS_SELECTOR = 0x00000806, + GUEST_FS_SELECTOR = 0x00000808, + GUEST_GS_SELECTOR = 0x0000080a, + GUEST_LDTR_SELECTOR = 0x0000080c, + GUEST_TR_SELECTOR = 0x0000080e, + HOST_ES_SELECTOR = 0x00000c00, + HOST_CS_SELECTOR = 0x00000c02, + HOST_SS_SELECTOR = 0x00000c04, + HOST_DS_SELECTOR = 0x00000c06, + HOST_FS_SELECTOR = 0x00000c08, + HOST_GS_SELECTOR = 0x00000c0a, + HOST_TR_SELECTOR = 0x00000c0c, + IO_BITMAP_A = 0x00002000, + IO_BITMAP_A_HIGH = 0x00002001, + IO_BITMAP_B = 0x00002002, + IO_BITMAP_B_HIGH = 0x00002003, + MSR_BITMAP = 0x00002004, + MSR_BITMAP_HIGH = 0x00002005, + VM_EXIT_MSR_STORE_ADDR = 0x00002006, + VM_EXIT_MSR_STORE_ADDR_HIGH = 0x00002007, + VM_EXIT_MSR_LOAD_ADDR = 0x00002008, + VM_EXIT_MSR_LOAD_ADDR_HIGH = 0x00002009, + VM_ENTRY_MSR_LOAD_ADDR = 0x0000200a, + VM_ENTRY_MSR_LOAD_ADDR_HIGH = 0x0000200b, + TSC_OFFSET = 0x00002010, + TSC_OFFSET_HIGH = 0x00002011, + VIRTUAL_APIC_PAGE_ADDR = 0x00002012, + VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013, + APIC_ACCESS_ADDR = 0x00002014, + APIC_ACCESS_ADDR_HIGH = 0x00002015, + EPT_POINTER = 0x0000201a, + EPT_POINTER_HIGH = 0x0000201b, + GUEST_PHYSICAL_ADDRESS = 0x00002400, + GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, + VMCS_LINK_POINTER = 0x00002800, + VMCS_LINK_POINTER_HIGH = 0x00002801, + GUEST_IA32_DEBUGCTL = 0x00002802, + GUEST_IA32_DEBUGCTL_HIGH = 0x00002803, + GUEST_IA32_PAT = 0x00002804, + GUEST_IA32_PAT_HIGH = 0x00002805, + GUEST_PDPTR0 = 0x0000280a, + GUEST_PDPTR0_HIGH = 0x0000280b, + GUEST_PDPTR1 = 0x0000280c, + GUEST_PDPTR1_HIGH = 0x0000280d, + GUEST_PDPTR2 = 0x0000280e, + GUEST_PDPTR2_HIGH = 0x0000280f, + GUEST_PDPTR3 = 0x00002810, + GUEST_PDPTR3_HIGH = 0x00002811, + HOST_IA32_PAT = 0x00002c00, + HOST_IA32_PAT_HIGH = 0x00002c01, + PIN_BASED_VM_EXEC_CONTROL = 0x00004000, + CPU_BASED_VM_EXEC_CONTROL = 0x00004002, + EXCEPTION_BITMAP = 0x00004004, + PAGE_FAULT_ERROR_CODE_MASK = 0x00004006, + PAGE_FAULT_ERROR_CODE_MATCH = 0x00004008, + CR3_TARGET_COUNT = 0x0000400a, + VM_EXIT_CONTROLS = 0x0000400c, + VM_EXIT_MSR_STORE_COUNT = 0x0000400e, + VM_EXIT_MSR_LOAD_COUNT = 0x00004010, + VM_ENTRY_CONTROLS = 0x00004012, + VM_ENTRY_MSR_LOAD_COUNT = 0x00004014, + VM_ENTRY_INTR_INFO_FIELD = 0x00004016, + VM_ENTRY_EXCEPTION_ERROR_CODE = 0x00004018, + VM_ENTRY_INSTRUCTION_LEN = 0x0000401a, + TPR_THRESHOLD = 0x0000401c, + SECONDARY_VM_EXEC_CONTROL = 0x0000401e, + VM_INSTRUCTION_ERROR = 0x00004400, + VM_EXIT_REASON = 0x00004402, + VM_EXIT_INTR_INFO = 0x00004404, + VM_EXIT_INTR_ERROR_CODE = 0x00004406, + IDT_VECTORING_INFO_FIELD = 0x00004408, + IDT_VECTORING_ERROR_CODE = 0x0000440a, + VM_EXIT_INSTRUCTION_LEN = 0x0000440c, + VMX_INSTRUCTION_INFO = 0x0000440e, + GUEST_ES_LIMIT = 0x00004800, + GUEST_CS_LIMIT = 0x00004802, + GUEST_SS_LIMIT = 0x00004804, + GUEST_DS_LIMIT = 0x00004806, + GUEST_FS_LIMIT = 0x00004808, + GUEST_GS_LIMIT = 0x0000480a, + GUEST_LDTR_LIMIT = 0x0000480c, + GUEST_TR_LIMIT = 0x0000480e, + GUEST_GDTR_LIMIT = 0x00004810, + GUEST_IDTR_LIMIT = 0x00004812, + GUEST_ES_AR_BYTES = 0x00004814, + GUEST_CS_AR_BYTES = 0x00004816, + GUEST_SS_AR_BYTES = 0x00004818, + GUEST_DS_AR_BYTES = 0x0000481a, + GUEST_FS_AR_BYTES = 0x0000481c, + GUEST_GS_AR_BYTES = 0x0000481e, + GUEST_LDTR_AR_BYTES = 0x00004820, + GUEST_TR_AR_BYTES = 0x00004822, + GUEST_INTERRUPTIBILITY_INFO = 0x00004824, + GUEST_ACTIVITY_STATE = 0X00004826, + GUEST_SYSENTER_CS = 0x0000482A, + HOST_IA32_SYSENTER_CS = 0x00004c00, + CR0_GUEST_HOST_MASK = 0x00006000, + CR4_GUEST_HOST_MASK = 0x00006002, + CR0_READ_SHADOW = 0x00006004, + CR4_READ_SHADOW = 0x00006006, + CR3_TARGET_VALUE0 = 0x00006008, + CR3_TARGET_VALUE1 = 0x0000600a, + CR3_TARGET_VALUE2 = 0x0000600c, + CR3_TARGET_VALUE3 = 0x0000600e, + EXIT_QUALIFICATION = 0x00006400, + GUEST_LINEAR_ADDRESS = 0x0000640a, + GUEST_CR0 = 0x00006800, + GUEST_CR3 = 0x00006802, + GUEST_CR4 = 0x00006804, + GUEST_ES_BASE = 0x00006806, + GUEST_CS_BASE = 0x00006808, + GUEST_SS_BASE = 0x0000680a, + GUEST_DS_BASE = 0x0000680c, + GUEST_FS_BASE = 0x0000680e, + GUEST_GS_BASE = 0x00006810, + GUEST_LDTR_BASE = 0x00006812, + GUEST_TR_BASE = 0x00006814, + GUEST_GDTR_BASE = 0x00006816, + GUEST_IDTR_BASE = 0x00006818, + GUEST_DR7 = 0x0000681a, + GUEST_RSP = 0x0000681c, + GUEST_RIP = 0x0000681e, + GUEST_RFLAGS = 0x00006820, + GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822, + GUEST_SYSENTER_ESP = 0x00006824, + GUEST_SYSENTER_EIP = 0x00006826, + HOST_CR0 = 0x00006c00, + HOST_CR3 = 0x00006c02, + HOST_CR4 = 0x00006c04, + HOST_FS_BASE = 0x00006c06, + HOST_GS_BASE = 0x00006c08, + HOST_TR_BASE = 0x00006c0a, + HOST_GDTR_BASE = 0x00006c0c, + HOST_IDTR_BASE = 0x00006c0e, + HOST_IA32_SYSENTER_ESP = 0x00006c10, + HOST_IA32_SYSENTER_EIP = 0x00006c12, + HOST_RSP = 0x00006c14, + HOST_RIP = 0x00006c16, +}; + +#define VMX_EXIT_REASONS_FAILED_VMENTRY 0x80000000 + +#define EXIT_REASON_EXCEPTION_NMI 0 +#define EXIT_REASON_EXTERNAL_INTERRUPT 1 +#define EXIT_REASON_TRIPLE_FAULT 2 + +#define EXIT_REASON_PENDING_INTERRUPT 7 +#define EXIT_REASON_NMI_WINDOW 8 +#define EXIT_REASON_TASK_SWITCH 9 +#define EXIT_REASON_CPUID 10 +#define EXIT_REASON_HLT 12 +#define EXIT_REASON_INVLPG 14 +#define EXIT_REASON_RDPMC 15 +#define EXIT_REASON_RDTSC 16 +#define EXIT_REASON_VMCALL 18 +#define EXIT_REASON_VMCLEAR 19 +#define EXIT_REASON_VMLAUNCH 20 +#define EXIT_REASON_VMPTRLD 21 +#define EXIT_REASON_VMPTRST 22 +#define EXIT_REASON_VMREAD 23 +#define EXIT_REASON_VMRESUME 24 +#define EXIT_REASON_VMWRITE 25 +#define EXIT_REASON_VMOFF 26 +#define EXIT_REASON_VMON 27 +#define EXIT_REASON_CR_ACCESS 28 +#define EXIT_REASON_DR_ACCESS 29 +#define EXIT_REASON_IO_INSTRUCTION 30 +#define EXIT_REASON_MSR_READ 31 +#define EXIT_REASON_MSR_WRITE 32 +#define EXIT_REASON_MWAIT_INSTRUCTION 36 +#define EXIT_REASON_TPR_BELOW_THRESHOLD 43 +#define EXIT_REASON_APIC_ACCESS 44 +#define EXIT_REASON_EPT_VIOLATION 48 +#define EXIT_REASON_EPT_MISCONFIG 49 +#define EXIT_REASON_WBINVD 54 + +/* + * Interruption-information format + */ +#define INTR_INFO_VECTOR_MASK 0xff /* 7:0 */ +#define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */ +#define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */ +#define INTR_INFO_UNBLOCK_NMI 0x1000 /* 12 */ +#define INTR_INFO_VALID_MASK 0x80000000 /* 31 */ +#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000 + +#define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK +#define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK +#define VECTORING_INFO_DELIVER_CODE_MASK INTR_INFO_DELIVER_CODE_MASK +#define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK + +#define INTR_TYPE_EXT_INTR (0 << 8) /* external interrupt */ +#define INTR_TYPE_NMI_INTR (2 << 8) /* NMI */ +#define INTR_TYPE_HARD_EXCEPTION (3 << 8) /* processor exception */ +#define INTR_TYPE_SOFT_INTR (4 << 8) /* software interrupt */ +#define INTR_TYPE_SOFT_EXCEPTION (6 << 8) /* software exception */ + +/* GUEST_INTERRUPTIBILITY_INFO flags. */ +#define GUEST_INTR_STATE_STI 0x00000001 +#define GUEST_INTR_STATE_MOV_SS 0x00000002 +#define GUEST_INTR_STATE_SMI 0x00000004 +#define GUEST_INTR_STATE_NMI 0x00000008 + +/* + * Exit Qualifications for MOV for Control Register Access + */ +#define CONTROL_REG_ACCESS_NUM 0x7 /* 2:0, number of control reg.*/ +#define CONTROL_REG_ACCESS_TYPE 0x30 /* 5:4, access type */ +#define CONTROL_REG_ACCESS_REG 0xf00 /* 10:8, general purpose reg. */ +#define LMSW_SOURCE_DATA_SHIFT 16 +#define LMSW_SOURCE_DATA (0xFFFF << LMSW_SOURCE_DATA_SHIFT) /* 16:31 lmsw source */ +#define REG_EAX (0 << 8) +#define REG_ECX (1 << 8) +#define REG_EDX (2 << 8) +#define REG_EBX (3 << 8) +#define REG_ESP (4 << 8) +#define REG_EBP (5 << 8) +#define REG_ESI (6 << 8) +#define REG_EDI (7 << 8) +#define REG_R8 (8 << 8) +#define REG_R9 (9 << 8) +#define REG_R10 (10 << 8) +#define REG_R11 (11 << 8) +#define REG_R12 (12 << 8) +#define REG_R13 (13 << 8) +#define REG_R14 (14 << 8) +#define REG_R15 (15 << 8) + +/* + * Exit Qualifications for MOV for Debug Register Access + */ +#define DEBUG_REG_ACCESS_NUM 0x7 /* 2:0, number of debug reg. */ +#define DEBUG_REG_ACCESS_TYPE 0x10 /* 4, direction of access */ +#define TYPE_MOV_TO_DR (0 << 4) +#define TYPE_MOV_FROM_DR (1 << 4) +#define DEBUG_REG_ACCESS_REG(eq) (((eq) >> 8) & 0xf) /* 11:8, general purpose reg. */ + + +/* segment AR */ +#define SEGMENT_AR_L_MASK (1 << 13) + +#define AR_TYPE_ACCESSES_MASK 1 +#define AR_TYPE_READABLE_MASK (1 << 1) +#define AR_TYPE_WRITEABLE_MASK (1 << 2) +#define AR_TYPE_CODE_MASK (1 << 3) +#define AR_TYPE_MASK 0x0f +#define AR_TYPE_BUSY_64_TSS 11 +#define AR_TYPE_BUSY_32_TSS 11 +#define AR_TYPE_BUSY_16_TSS 3 +#define AR_TYPE_LDT 2 + +#define AR_UNUSABLE_MASK (1 << 16) +#define AR_S_MASK (1 << 4) +#define AR_P_MASK (1 << 7) +#define AR_L_MASK (1 << 13) +#define AR_DB_MASK (1 << 14) +#define AR_G_MASK (1 << 15) +#define AR_DPL_SHIFT 5 +#define AR_DPL(ar) (((ar) >> AR_DPL_SHIFT) & 3) + +#define AR_RESERVD_MASK 0xfffe0f00 + +#define TSS_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 0) +#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 1) +#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 2) + +#define VMX_NR_VPIDS (1 << 16) +#define VMX_VPID_EXTENT_SINGLE_CONTEXT 1 +#define VMX_VPID_EXTENT_ALL_CONTEXT 2 + +#define VMX_EPT_EXTENT_INDIVIDUAL_ADDR 0 +#define VMX_EPT_EXTENT_CONTEXT 1 +#define VMX_EPT_EXTENT_GLOBAL 2 +#define VMX_EPT_EXTENT_INDIVIDUAL_BIT (1ull << 24) +#define VMX_EPT_EXTENT_CONTEXT_BIT (1ull << 25) +#define VMX_EPT_EXTENT_GLOBAL_BIT (1ull << 26) +#define VMX_EPT_DEFAULT_GAW 3 +#define VMX_EPT_MAX_GAW 0x4 +#define VMX_EPT_MT_EPTE_SHIFT 3 +#define VMX_EPT_GAW_EPTP_SHIFT 3 +#define VMX_EPT_DEFAULT_MT 0x6ull +#define VMX_EPT_READABLE_MASK 0x1ull +#define VMX_EPT_WRITABLE_MASK 0x2ull +#define VMX_EPT_EXECUTABLE_MASK 0x4ull +#define VMX_EPT_IGMT_BIT (1ull << 6) + +#define VMX_EPT_IDENTITY_PAGETABLE_ADDR 0xfffbc000ul + + +#define ASM_VMX_VMCLEAR_RAX ".byte 0x66, 0x0f, 0xc7, 0x30" +#define ASM_VMX_VMLAUNCH ".byte 0x0f, 0x01, 0xc2" +#define ASM_VMX_VMRESUME ".byte 0x0f, 0x01, 0xc3" +#define ASM_VMX_VMPTRLD_RAX ".byte 0x0f, 0xc7, 0x30" +#define ASM_VMX_VMREAD_RDX_RAX ".byte 0x0f, 0x78, 0xd0" +#define ASM_VMX_VMWRITE_RAX_RDX ".byte 0x0f, 0x79, 0xd0" +#define ASM_VMX_VMWRITE_RSP_RDX ".byte 0x0f, 0x79, 0xd4" +#define ASM_VMX_VMXOFF ".byte 0x0f, 0x01, 0xc4" +#define ASM_VMX_VMXON_RAX ".byte 0xf3, 0x0f, 0xc7, 0x30" +#define ASM_VMX_INVEPT ".byte 0x66, 0x0f, 0x38, 0x80, 0x08" +#define ASM_VMX_INVVPID ".byte 0x66, 0x0f, 0x38, 0x81, 0x08" + + + +#endif 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..3b1dccff2 --- /dev/null +++ b/kvm/scripts/make-release @@ -0,0 +1,60 @@ +#!/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" + +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..3bf8801b7 --- /dev/null +++ b/kvm/scripts/qemu-ifup @@ -0,0 +1,5 @@ +#!/bin/sh + +switch=$(/sbin/ip route list | awk '/^default / { print $NF }') +/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/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..d9fbf17f4 --- /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..2f59bb5a4 --- /dev/null +++ b/kvm/user/config-x86-common.mak @@ -0,0 +1,68 @@ +#This is a make file with common rules for both x86 & x86-64 + +CFLAGS += -I../include/x86 + +all: kvmctl kvmtrace test_cases + +kvmctl_objs= main.o iotable.o ../libkvm/libkvm.a +balloon_ctl: balloon_ctl.o + +cflatobjs += \ + test/lib/x86/io.o \ + test/lib/x86/smp.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)/irq.flat: $(TEST_DIR)/print.o + +$(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)/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)/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 diff --git a/kvm/user/config-x86_64.mak b/kvm/user/config-x86_64.mak new file mode 100644 index 000000000..d88f54c7f --- /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)/irq.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..61f10573a --- /dev/null +++ b/kvm/user/flat.lds @@ -0,0 +1,17 @@ +OUTPUT_FORMAT(binary) + +SECTIONS +{ + . = 1M; + 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..1f96cb80e --- /dev/null +++ b/kvm/user/test/lib/libcflat.h @@ -0,0 +1,36 @@ +/* + * 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> + +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/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/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..92ebada77 --- /dev/null +++ b/kvm/user/test/lib/x86/smp.c @@ -0,0 +1,150 @@ + +#include <libcflat.h> +#include "smp.h" +#include "fake-apic.h" + +#define IPI_VECTOR 0x20 + +static int apic_read(int reg) +{ + unsigned short port = APIC_BASE + reg; + unsigned v; + + asm volatile ("in %1, %0" : "=a"(v) : "d"(port)); + return v; +} + +static void apic_write(int reg, unsigned v) +{ + unsigned short port = APIC_BASE + reg; + + asm volatile ("out %0, %1" : : "a"(v), "d"(port)); +} + +static int apic_get_cpu_count() +{ + return apic_read(APIC_REG_NCPU); +} + +static int apic_get_id() +{ + return apic_read(APIC_REG_ID); +} + +static void apic_set_ipi_vector(int vector) +{ + apic_write(APIC_REG_IPI_VECTOR, vector); +} + +static void apic_send_ipi(int cpu) +{ + apic_write(APIC_REG_SEND_IPI, cpu); +} + +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); + 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 apic_get_cpu_count(); +} + +int smp_id(void) +{ + return apic_get_id(); +} + +void on_cpu(int cpu, void (*function)(void *data), void *data) +{ + spin_lock(&ipi_lock); + if (cpu == apic_get_id()) + function(data); + else { + ipi_function = function; + ipi_data = data; + apic_send_ipi(cpu); + while (!ipi_done) + ; + ipi_done = 0; + } + spin_unlock(&ipi_lock); +} + +static void (*smp_main_func)(void); +static volatile int smp_main_running; + +asm ("smp_init_entry: \n" + "incl smp_main_running \n" + "sti \n" + "call *smp_main_func"); + +void smp_init(void (*smp_main)(void)) +{ + int i; + void smp_init_entry(void); + void ipi_entry(void); + + apic_set_ipi_vector(IPI_VECTOR); + set_ipi_descriptor(smp_init_entry); + smp_main_func = smp_main; + for (i = 1; i < cpu_count(); ++i) { + apic_send_ipi(i); + while (smp_main_running < i) + ; + } + set_ipi_descriptor(ipi_entry); +} diff --git a/kvm/user/test/lib/x86/smp.h b/kvm/user/test/lib/x86/smp.h new file mode 100644 index 000000000..bcf76a357 --- /dev/null +++ b/kvm/user/test/lib/x86/smp.h @@ -0,0 +1,16 @@ +#ifndef __SMP_H +#define __SMP_H + +struct spinlock { + int v; +}; + +void smp_init(void (*smp_main)(void)); + +int cpu_count(void); +int smp_id(void); +void on_cpu(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..272a4ef45 --- /dev/null +++ b/kvm/user/test/x86/access.c @@ -0,0 +1,604 @@ + +#include "libcflat.h" +#include "smp.h" + +#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"); + smp_init((void(*)(void))ac_test_run); + 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..77946159d --- /dev/null +++ b/kvm/user/test/x86/apic.c @@ -0,0 +1,351 @@ +#include "libcflat.h" +#include "apic.h" +#include "vm.h" + +static void *g_apic; +static void *g_ioapic; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned u32; +typedef unsigned long ulong; + +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 rflags; + ulong cs; + ulong rip; + ulong func; + ulong regs[sizeof(ulong)*2]; +} 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 report(const char *msg, int pass) +{ + ++g_tests; + printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); + if (!pass) + ++g_fail; +} + +static u32 apic_read(unsigned reg) +{ + return *(volatile u32 *)(g_apic + reg); +} + +static void apic_write(unsigned reg, u32 val) +{ + *(volatile u32 *)(g_apic + reg) = val; +} + +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); +} + +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_write(APIC_ICR, + APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec); + asm volatile ("nop"); + report("self ipi", ipi_count == 1); +} + +static void ioapic_write_reg(unsigned reg, u32 value) +{ + *(volatile u32 *)g_ioapic = reg; + *(volatile u32 *)(g_ioapic + 0x10) = value; +} + +typedef 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; +} ioapic_redir_entry_t; + +static 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]); +} + +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); +} + +static void enable_apic(void) +{ + apic_write(0xf0, 0x1ff); /* spurious vector register */ +} + +int main() +{ + setup_vm(); + + g_apic = vmap(0xfee00000, 0x1000); + g_ioapic = vmap(0xfec00000, 0x1000); + + test_lapic_existence(); + + enable_apic(); + 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/apic.h b/kvm/user/test/x86/apic.h new file mode 100644 index 000000000..c061e3d4a --- /dev/null +++ b/kvm/user/test/x86/apic.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/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..69a626261 --- /dev/null +++ b/kvm/user/test/x86/cstart.S @@ -0,0 +1,10 @@ + + +.bss + +.section .init + 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..432a3dc87 --- /dev/null +++ b/kvm/user/test/x86/cstart64.S @@ -0,0 +1,168 @@ + +#include "fake-apic.h" + +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 + .quad 0x1e7 | (i << 21) + i = i + 1 + .endr + +.align 4096 +ptl3: + .quad ptl2 + 7 + +.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 + 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_init_ipi: + call prepare_64 + jmpl $8, $ap_start64 + +.code64 +ap_start64: + call load_tss + sti + nop + +1: hlt + jmp 1b + +start64: + call load_tss + call smp_init + call main + mov %eax, %edi + call exit + +load_tss: + mov $0, %eax + mov %ax, %ss + mov $(APIC_BASE + APIC_REG_ID), %dx + in %dx, %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: + lea boot_idt + ipi_vector * 8, %rdi + mov $smp_init_ipi, %eax + mov %ax, (%rdi) + mov %cs, %ax + mov %ax, 2(%rdi) + movw $0x8e00, 4(%rdi) + shr $16, %eax + mov %ax, 6(%rdi) + + mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx + mov $ipi_vector, %eax + out %eax, %dx + + mov $(APIC_BASE + APIC_REG_NCPU), %dx + in %dx, %eax + mov %eax, %ecx + mov $1, %esi +smp_loop: + cmp %esi, %ecx + je smp_init_done + + mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx + mov %esi, %eax + out %eax, %dx + + inc %esi + jmp smp_loop +smp_init_done: + ret 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/irq.S b/kvm/user/test/x86/irq.S new file mode 100644 index 000000000..0425db6f8 --- /dev/null +++ b/kvm/user/test/x86/irq.S @@ -0,0 +1,118 @@ +// irq test program. assumes outb $irq, $0xff generates an interrupt $irq. + +#include "print.h" + +.text + PRINT "irq test" + mov $stack_top, %rsp + + call setup_gdt + + mov %ds, %ax + mov %ax, %ds // check ds descriptor is okay + + mov $irq_handler, %rdx + mov $0x20, %eax + call setup_idt_entry + + lidt idt_descriptor + + PRINT "software interrupt" + int $0x20 + + sti + nop + + PRINT "injecting interrupt with interrupts enabled" + + mov $0x20, %al + outb %al, $0xff // inject interrupt + + nop + nop + nop + PRINT "after injection" + + cli + + PRINT "injecting interrupt with interrupts disabled" + + mov $0x20, %al + outb %al, $0xff // inject interrupt + + // no interrupt here (disabled) + nop + nop + PRINT "enabling interrupts" + nop + nop + sti + out %al, $0x80 // blocked by sti + // interrupt here + out %al, $0x80 + + PRINT "after injection" + nop + nop + + hlt + +irq_handler: + PRINT "interrupt handler" + iretq + +setup_idt_entry: // %rax: irq %rdx: handler + shl $4, %rax + mov %dx, idt(%rax) + shr $16, %rdx + mov %cs, 2+idt(%rax) + mov %dx, 6+idt(%rax) + shr $16, %rdx + mov %edx, 8+idt(%rax) + movw $0x8e00, 4+idt(%rax) + ret + +setup_gdt: + mov $0, %eax + mov %cs, %ax + andl $~7, %eax + movl $0xffff, gdt(%rax) + movl $0xaf9b00, 4+gdt(%rax) + + mov $0, %eax + mov %ds, %ax + andl $~7, %eax + movl $0xffff, gdt(%rax) + movl $0x8f9300, 4+gdt(%rax) + + lgdt gdt_descriptor + ret + +.data + +.align 16 + +idt: + . = . + 256 * 16 + +idt_descriptor: + .word . - idt - 1 + .quad idt + +.align 8 + +gdt: + . = . + 256 * 8 + +gdt_descriptor: + .word . - gdt - 1 + .quad gdt + + +.align 4096 +stack_base: + . = . + 4096 +stack_top: + + + 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..755b5d18c --- /dev/null +++ b/kvm/user/test/x86/realmode.c @@ -0,0 +1,518 @@ +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 ("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_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 start(void) +{ + test_null(); + + test_shld(); + 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); +} + +asm( + ".data \n\t" + ". = . + 4096 \n\t" + "stacktop: \n\t" + ".text \n\t" + "init: \n\t" + "xor %ax, %ax \n\t" + "mov %ax, %ds \n\t" + "mov %ax, %es \n\t" + "mov %ax, %ss \n\t" + "mov $0x4000, %cx \n\t" + "xor %esi, %esi \n\t" + "mov %esi, %edi \n\t" + "rep/addr32/cs/movsl \n\t" + "mov $stacktop, %sp\n\t" + "ljmp $0, $start \n\t" + ".pushsection .boot, \"ax\" \n\t" + "ljmp $0xf000, $init \n\t" + ".popsection" + ); diff --git a/kvm/user/test/x86/realmode.lds b/kvm/user/test/x86/realmode.lds new file mode 100644 index 000000000..c9cdd7d55 --- /dev/null +++ b/kvm/user/test/x86/realmode.lds @@ -0,0 +1,16 @@ +OUTPUT_FORMAT(binary) + +SECTIONS +{ + . = 0; + stext = .; + .text : { *(.init) *(.text) } + . = ALIGN(4K); + .data : { *(.data) *(.rodata*) } + . = ALIGN(16); + .bss : { *(.bss) } + . = 0xfff0; + .boot : { *(.boot) } + 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..7b1ba498b --- /dev/null +++ b/kvm/user/test/x86/smptest.c @@ -0,0 +1,31 @@ +#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()); +} + +static void smp_main(void) +{ + printf("smp main %d\n", smp_id()); + while (1) + asm volatile ("hlt" : : : "memory"); +} + +int main() +{ + int ncpus; + int i; + + smp_init(smp_main); + 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..204b1fd99 --- /dev/null +++ b/kvm/user/test/x86/tsc.c @@ -0,0 +1,40 @@ +#include "libcflat.h" + +typedef unsigned long long u64; + +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..03c73546b --- /dev/null +++ b/kvm/user/test/x86/vm.c @@ -0,0 +1,268 @@ + +#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; + + 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..981d6c142 --- /dev/null +++ b/kvm/user/test/x86/vmexit.c @@ -0,0 +1,39 @@ + +#include "libcflat.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; +} + +#define N (1 << 22) + +#ifdef __x86_64__ +# define R "r" +#else +# define R "e" +#endif + +int main() +{ + int i; + unsigned long long t1, t2; + + t1 = rdtsc(); + for (i = 0; i < N; ++i) + asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx" + : : : "eax", "ecx", "edx"); + t2 = rdtsc(); + printf("vmexit latency: %d\n", (int)((t2 - t1) / N)); + 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 +}; @@ -44,6 +44,9 @@ #include "migration.h" #include "kvm.h" #include "acl.h" +#include "exec-all.h" + +#include "qemu-kvm.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -374,6 +377,7 @@ static void do_info_cpus(Monitor *mon) #endif if (env->halted) monitor_printf(mon, " (halted)"); + monitor_printf(mon," thread_id=%d", env->thread_id); monitor_printf(mon, "\n"); } } @@ -384,6 +388,23 @@ static void do_cpu_set(Monitor *mon, int index) monitor_printf(mon, "Invalid CPU index\n"); } +static void do_cpu_set_nr(Monitor *mon, int value, const char *status) +{ + int state; + + 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); @@ -1421,7 +1442,7 @@ static void do_info_kqemu(Monitor *mon) static void do_info_kvm(Monitor *mon) { -#ifdef CONFIG_KVM +#if defined(USE_KVM) || defined(CONFIG_KVM) monitor_printf(mon, "kvm support: "); if (kvm_enabled()) monitor_printf(mon, "enabled\n"); @@ -1556,7 +1577,10 @@ static void do_inject_nmi(Monitor *mon, int cpu_index) 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; } } @@ -110,6 +110,7 @@ #define memalign(align, size) malloc(size) #endif +// FIXME: #include "qemu-kvm.h" #include "qemu-common.h" #include "net.h" #include "monitor.h" @@ -434,7 +435,7 @@ int qemu_can_send_packet(VLANClientState *sender) } static int -qemu_deliver_packet(VLANClientState *sender, const uint8_t *buf, int size) +qemu_deliver_packet(VLANClientState *sender, const uint8_t *buf, int size, int raw) { VLANClientState *vc; int ret = -1; @@ -453,7 +454,11 @@ qemu_deliver_packet(VLANClientState *sender, const uint8_t *buf, int size) continue; } - len = vc->receive(vc, buf, size); + if (raw && vc->receive_raw) { + len = vc->receive_raw(vc, buf, size); + } else { + len = vc->receive(vc, buf, size); + } ret = (ret >= 0) ? ret : len; } @@ -484,7 +489,8 @@ void qemu_flush_queued_packets(VLANClientState *vc) packet = TAILQ_FIRST(&vc->vlan->send_queue); TAILQ_REMOVE(&vc->vlan->send_queue, packet, entry); - ret = qemu_deliver_packet(packet->sender, packet->data, packet->size); + ret = qemu_deliver_packet(packet->sender, packet->data, + packet->size, packet->raw); if (ret == 0 && packet->sent_cb != NULL) { TAILQ_INSERT_HEAD(&vc->vlan->send_queue, packet, entry); break; @@ -498,7 +504,7 @@ void qemu_flush_queued_packets(VLANClientState *vc) } static void qemu_enqueue_packet(VLANClientState *sender, - const uint8_t *buf, int size, + const uint8_t *buf, int size, int raw, NetPacketSent *sent_cb) { VLANPacket *packet; @@ -506,15 +512,16 @@ static void qemu_enqueue_packet(VLANClientState *sender, packet = qemu_malloc(sizeof(VLANPacket) + size); packet->sender = sender; packet->size = size; + packet->raw = raw; packet->sent_cb = sent_cb; memcpy(packet->data, buf, size); TAILQ_INSERT_TAIL(&sender->vlan->send_queue, packet, entry); } -ssize_t qemu_send_packet_async(VLANClientState *sender, - const uint8_t *buf, int size, - NetPacketSent *sent_cb) +static ssize_t qemu_send_packet_async2(VLANClientState *sender, + const uint8_t *buf, int size, int raw, + NetPacketSent *sent_cb) { int ret; @@ -528,13 +535,13 @@ ssize_t qemu_send_packet_async(VLANClientState *sender, #endif if (sender->vlan->delivering) { - qemu_enqueue_packet(sender, buf, size, NULL); + qemu_enqueue_packet(sender, buf, size, raw, NULL); return size; } - ret = qemu_deliver_packet(sender, buf, size); + ret = qemu_deliver_packet(sender, buf, size, raw); if (ret == 0 && sent_cb != NULL) { - qemu_enqueue_packet(sender, buf, size, sent_cb); + qemu_enqueue_packet(sender, buf, size, raw, sent_cb); return 0; } @@ -543,9 +550,21 @@ ssize_t qemu_send_packet_async(VLANClientState *sender, return ret; } -void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size) +ssize_t qemu_send_packet_async(VLANClientState *sender, + const uint8_t *buf, int size, + NetPacketSent *sent_cb) +{ + return qemu_send_packet_async2(sender, buf, size, 0, sent_cb); +} + +ssize_t qemu_send_packet(VLANClientState *sender, const uint8_t *buf, int size) { - qemu_send_packet_async(vc, buf, size, NULL); + return qemu_send_packet_async2(sender, buf, size, 0, NULL); +} + +ssize_t qemu_send_packet_raw(VLANClientState *sender, const uint8_t *buf, int size) +{ + return qemu_send_packet_async2(sender, buf, size, 1, NULL); } static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov, @@ -624,6 +643,7 @@ static ssize_t qemu_enqueue_packet_iov(VLANClientState *sender, packet->sender = sender; packet->sent_cb = sent_cb; packet->size = 0; + packet->raw = 0; for (i = 0; i < iovcnt; i++) { size_t len = iov[i].iov_len; @@ -1264,16 +1284,38 @@ void do_info_usernet(Monitor *mon) #endif /* CONFIG_SLIRP */ -#if !defined(_WIN32) +#ifdef _WIN32 + +int tap_has_vnet_hdr(void *opaque) +{ + return 0; +} + +void tap_using_vnet_hdr(void *opaque, int using_vnet_hdr) +{ +} + +#else /* !defined(_WIN32) */ + +/* Maximum GSO packet size (64k) plus plenty of room for + * the ethernet and virtio_net headers + */ +#define TAP_BUFSIZE (4096 + 65536) + +#ifdef IFF_VNET_HDR +#include <linux/virtio_net.h> +#endif typedef struct TAPState { VLANClientState *vc; int fd; char down_script[1024]; char down_script_arg[128]; - uint8_t buf[4096]; + uint8_t buf[TAP_BUFSIZE]; unsigned int read_poll : 1; unsigned int write_poll : 1; + unsigned int has_vnet_hdr : 1; + unsigned int using_vnet_hdr : 1; } TAPState; static int launch_script(const char *setup_script, const char *ifname, int fd); @@ -1332,14 +1374,48 @@ static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov, static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size) { + struct iovec iov[2]; + int i = 0; + +#ifdef IFF_VNET_HDR TAPState *s = vc->opaque; - ssize_t len; + struct virtio_net_hdr hdr = { 0, }; - do { - len = write(s->fd, buf, size); - } while (len == -1 && (errno == EINTR || errno == EAGAIN)); + if (s->has_vnet_hdr && !s->using_vnet_hdr) { + iov[i].iov_base = &hdr; + iov[i].iov_len = sizeof(hdr); + i++; + } +#endif - return len; + iov[i].iov_base = (char *) buf; + iov[i].iov_len = size; + i++; + + return tap_receive_iov(vc, iov, i); +} + +static ssize_t tap_receive_raw(VLANClientState *vc, const uint8_t *buf, size_t size) +{ + struct iovec iov[2]; + int i = 0; + +#ifdef IFF_VNET_HDR + TAPState *s = vc->opaque; + struct virtio_net_hdr hdr = { 0, }; + + if (s->has_vnet_hdr && s->using_vnet_hdr) { + iov[i].iov_base = &hdr; + iov[i].iov_len = sizeof(hdr); + i++; + } +#endif + + iov[i].iov_base = (char *) buf; + iov[i].iov_len = size; + i++; + + return tap_receive_iov(vc, iov, i); } static int tap_can_send(void *opaque) @@ -1379,12 +1455,21 @@ static void tap_send(void *opaque) int size; do { + uint8_t *buf = s->buf; + size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); if (size <= 0) { break; } - size = qemu_send_packet_async(s->vc, s->buf, size, tap_send_completed); +#ifdef IFF_VNET_HDR + if (s->has_vnet_hdr && !s->using_vnet_hdr) { + buf += sizeof(struct virtio_net_hdr); + size -= sizeof(struct virtio_net_hdr); + } +#endif + + size = qemu_send_packet_async(s->vc, buf, size, tap_send_completed); if (size == 0) { tap_read_poll(s, 0); } @@ -1425,6 +1510,70 @@ static void tap_set_sndbuf(TAPState *s, const char *sndbuf_str, Monitor *mon) } #endif /* TUNSETSNDBUF */ +int tap_has_vnet_hdr(void *opaque) +{ + VLANClientState *vc = opaque; + TAPState *s = vc->opaque; + + if (vc->receive != tap_receive) + return 0; + + return s ? s->has_vnet_hdr : 0; +} + +void tap_using_vnet_hdr(void *opaque, int using_vnet_hdr) +{ + VLANClientState *vc = opaque; + TAPState *s = vc->opaque; + + if (vc->receive != tap_receive) + return; + + if (!s || !s->has_vnet_hdr) + return; + + s->using_vnet_hdr = using_vnet_hdr != 0; +} + +static int tap_probe_vnet_hdr(int fd) +{ +#if defined(TUNGETIFF) && defined(IFF_VNET_HDR) + struct ifreq ifr; + + if (ioctl(fd, TUNGETIFF, &ifr) != 0) { + fprintf(stderr, "TUNGETIFF ioctl() failed: %s\n", strerror(errno)); + return 0; + } + + return ifr.ifr_flags & IFF_VNET_HDR; +#else + return 0; +#endif +} + +#ifdef TUNSETOFFLOAD +static void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, + int ecn) +{ + TAPState *s = vc->opaque; + unsigned int offload = 0; + + if (csum) { + offload |= TUN_F_CSUM; + if (tso4) + offload |= TUN_F_TSO4; + if (tso6) + offload |= TUN_F_TSO6; + if ((tso4 || tso6) && ecn) + offload |= TUN_F_TSO_ECN; + } + + if (ioctl(s->fd, TUNSETOFFLOAD, offload) != 0) + fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", + strerror(errno)); +} +#endif /* TUNSETOFFLOAD */ + static void tap_cleanup(VLANClientState *vc) { TAPState *s = vc->opaque; @@ -1445,14 +1594,21 @@ static void tap_cleanup(VLANClientState *vc) static TAPState *net_tap_fd_init(VLANState *vlan, const char *model, const char *name, - int fd) + int fd, + int vnet_hdr) { TAPState *s; s = qemu_mallocz(sizeof(TAPState)); s->fd = fd; + s->has_vnet_hdr = vnet_hdr != 0; s->vc = qemu_new_vlan_client(vlan, model, name, NULL, tap_receive, tap_receive_iov, tap_cleanup, s); + s->vc->receive_raw = tap_receive_raw; +#ifdef TUNSETOFFLOAD + s->vc->set_offload = tap_set_offload; + tap_set_offload(s->vc, 0, 0, 0, 0); +#endif tap_read_poll(s, 1); snprintf(s->vc->info_str, sizeof(s->vc->info_str), "fd=%d", fd); return s; @@ -1601,7 +1757,7 @@ static int tap_alloc(char *dev, size_t dev_size) return tap_fd; } -static int tap_open(char *ifname, int ifname_size) +static int tap_open(char *ifname, int ifname_size, int *vnet_hdr) { char dev[10]=""; int fd; @@ -1620,7 +1776,7 @@ static int tap_open(char *ifname, int ifname_size) return -1; } #else -static int tap_open(char *ifname, int ifname_size) +static int tap_open(char *ifname, int ifname_size, int *vnet_hdr) { struct ifreq ifr; int fd, ret; @@ -1632,6 +1788,19 @@ static int tap_open(char *ifname, int ifname_size) } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + +#if defined(TUNGETFEATURES) && defined(IFF_VNET_HDR) + { + unsigned int features; + + if (ioctl(fd, TUNGETFEATURES, &features) == 0 && + features & IFF_VNET_HDR) { + *vnet_hdr = 1; + ifr.ifr_flags |= IFF_VNET_HDR; + } + } +#endif + if (ifname[0] != '\0') pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname); else @@ -1698,13 +1867,15 @@ static TAPState *net_tap_init(VLANState *vlan, const char *model, { TAPState *s; int fd; + int vnet_hdr; char ifname[128]; if (ifname1 != NULL) pstrcpy(ifname, sizeof(ifname), ifname1); else ifname[0] = '\0'; - TFR(fd = tap_open(ifname, sizeof(ifname))); + vnet_hdr = 0; + TFR(fd = tap_open(ifname, sizeof(ifname), &vnet_hdr)); if (fd < 0) return NULL; @@ -1714,7 +1885,7 @@ static TAPState *net_tap_init(VLANState *vlan, const char *model, launch_script(setup_script, ifname, fd)) { return NULL; } - s = net_tap_fd_init(vlan, model, name, fd); + s = net_tap_fd_init(vlan, model, name, fd, vnet_hdr); snprintf(s->vc->info_str, sizeof(s->vc->info_str), "ifname=%s,script=%s,downscript=%s", ifname, setup_script, down_script); @@ -2673,7 +2844,7 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } fcntl(fd, F_SETFL, O_NONBLOCK); - s = net_tap_fd_init(vlan, device, name, fd); + s = net_tap_fd_init(vlan, device, name, fd, tap_probe_vnet_hdr(fd)); if (!s) { close(fd); } @@ -13,9 +13,11 @@ typedef ssize_t (NetReceive)(VLANClientState *, const uint8_t *, size_t); typedef ssize_t (NetReceiveIOV)(VLANClientState *, const struct iovec *, int); typedef void (NetCleanup) (VLANClientState *); typedef void (LinkStatusChanged)(VLANClientState *); +typedef void (SetOffload)(VLANClientState *, int, int, int, int); struct VLANClientState { NetReceive *receive; + NetReceive *receive_raw; NetReceiveIOV *receive_iov; /* Packets may still be sent if this returns zero. It's used to rate-limit the slirp code. */ @@ -23,6 +25,7 @@ struct VLANClientState { NetCleanup *cleanup; LinkStatusChanged *link_status_changed; int link_down; + SetOffload *set_offload; void *opaque; struct VLANClientState *next; struct VLANState *vlan; @@ -39,6 +42,7 @@ struct VLANPacket { TAILQ_ENTRY(VLANPacket) entry; VLANClientState *sender; int size; + int raw; NetPacketSent *sent_cb; uint8_t data[0]; }; @@ -68,7 +72,8 @@ ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt); ssize_t qemu_sendv_packet_async(VLANClientState *vc, const struct iovec *iov, int iovcnt, NetPacketSent *sent_cb); -void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size); +ssize_t qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size); +ssize_t qemu_send_packet_raw(VLANClientState *vc, const uint8_t *buf, int size); ssize_t qemu_send_packet_async(VLANClientState *vc, const uint8_t *buf, int size, NetPacketSent *sent_cb); void qemu_purge_queued_packets(VLANClientState *vc); @@ -84,6 +89,9 @@ void do_set_link(Monitor *mon, const char *name, const char *up_or_down); void do_info_usernet(Monitor *mon); +int tap_has_vnet_hdr(void *opaque); +void tap_using_vnet_hdr(void *opaque, int using_vnet_hdr); + /* NIC info */ #define MAX_NICS 8 @@ -224,7 +224,11 @@ void *qemu_vmalloc(size_t size) if (kqemu_allowed) return kqemu_vmalloc(size); #endif +#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/bios.bin b/pc-bios/bios.bin Binary files differindex e4323c050..dff17ce17 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin 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 e7ad4b198..d3dd87089 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -30,7 +30,7 @@ ifeq ($(call cc-option-yn,-fno-stack-protector),y) CFLAGS += -fno-stack-protector endif -build-all: multiboot.bin +build-all: multiboot.bin extboot.bin %.o: %.S $(CC) $(CFLAGS) -o $@ -c $< 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/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/qemu-common.h b/qemu-common.h index 6a15f8934..d478b47d3 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -193,6 +193,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-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..b8a2587c2 --- /dev/null +++ b/qemu-kvm-ia64.c @@ -0,0 +1,147 @@ +#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_qemu_init_env(CPUState *cenv) +{ + return 0; +} + +int kvm_arch_halt(void *opaque, 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; +} + +void kvm_arch_update_regs_for_sipi(CPUState *env) +{ +} + +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..492dbc543 --- /dev/null +++ b/qemu-kvm-x86.c @@ -0,0 +1,1688 @@ +/* + * 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, 0xfffbd000); + 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, unsigned long 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, 0xfffbc000); + 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; + + return 0; +} + +#ifdef KVM_EXIT_TPR_ACCESS + +static int kvm_handle_tpr_access(kvm_vcpu_context_t vcpu) +{ + struct kvm_run *run = vcpu->run; + kvm_tpr_access_report(cpu_single_env, + run->tpr_access.rip, + run->tpr_access.is_write); + return 0; +} + + +int kvm_enable_vapic(kvm_vcpu_context_t vcpu, uint64_t vapic) +{ + int r; + struct kvm_vapic_addr va = { + .vapic_addr = vapic, + }; + + r = ioctl(vcpu->fd, KVM_SET_VAPIC_ADDR, &va); + if (r == -1) { + r = -errno; + perror("kvm_enable_vapic"); + return r; + } + return 0; +} + +#endif + +int kvm_arch_run(kvm_vcpu_context_t vcpu) +{ + int r = 0; + struct kvm_run *run = vcpu->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(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 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(kvm_vcpu_context_t vcpu, struct kvm_lapic_state *s) +{ + int r; + if (!kvm_irqchip_in_kernel(vcpu->kvm)) + return 0; + r = ioctl(vcpu->fd, KVM_GET_LAPIC, s); + if (r == -1) { + r = -errno; + perror("kvm_get_lapic"); + } + return r; +} + +int kvm_set_lapic(kvm_vcpu_context_t vcpu, struct kvm_lapic_state *s) +{ + int r; + if (!kvm_irqchip_in_kernel(vcpu->kvm)) + return 0; + r = ioctl(vcpu->fd, 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) +{ + 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(kvm_vcpu_context_t vcpu) +{ +#define SHOW_CODE_LEN 50 + int fd = vcpu->fd; + 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; + kvm_context_t kvm = vcpu->kvm; + + 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_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; + + 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(kvm_vcpu_context_t vcpu, struct kvm_msr_entry *msrs, int n) +{ + struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); + int r, e; + + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = ioctl(vcpu->fd, KVM_GET_MSRS, kmsrs); + e = errno; + memcpy(msrs, kmsrs->entries, n * sizeof *msrs); + free(kmsrs); + errno = e; + return r; +} + +int kvm_set_msrs(kvm_vcpu_context_t vcpu, struct kvm_msr_entry *msrs, int n) +{ + struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); + int r, e; + + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = ioctl(vcpu->fd, KVM_SET_MSRS, kmsrs); + e = errno; + free(kmsrs); + errno = e; + 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(kvm_vcpu_context_t vcpu, uint64_t *mcg_cap) +{ +#ifdef KVM_CAP_MCE + return ioctl(vcpu->fd, KVM_X86_SETUP_MCE, mcg_cap); +#else + return -ENOSYS; +#endif +} + +int kvm_set_mce(kvm_vcpu_context_t vcpu, struct kvm_x86_mce *m) +{ +#ifdef KVM_CAP_MCE + return ioctl(vcpu->fd, 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(kvm_vcpu_context_t vcpu) +{ + int fd = vcpu->fd; + 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_vcpu_context_t vcpu) +{ + return vcpu->run->apic_base; +} + +void kvm_set_cr8(kvm_vcpu_context_t vcpu, uint64_t cr8) +{ + vcpu->run->cr8 = cr8; +} + +__u64 kvm_get_cr8(kvm_vcpu_context_t vcpu) +{ + return vcpu->run->cr8; +} + +int kvm_setup_cpuid(kvm_vcpu_context_t vcpu, 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 = ioctl(vcpu->fd, KVM_SET_CPUID, cpuid); + + free(cpuid); + return r; +} + +int kvm_setup_cpuid2(kvm_vcpu_context_t vcpu, 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 = ioctl(vcpu->fd, 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 = 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(kvm_vcpu_context_t vcpu, 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; + r = ioctl(vcpu->fd, 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_vcpu_context_t vcpu) +{ + return tpr_access_reporting(vcpu, 1); +} + +int kvm_disable_tpr_access_reporting(kvm_vcpu_context_t vcpu) +{ + return tpr_access_reporting(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 = 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); +} + +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; + } + + 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; + default: + printf("Warning unknown msr index 0x%x\n", entry->index); + return 1; + } + return 0; +} + +#ifdef TARGET_X86_64 +#define MSR_COUNT 9 +#else +#define MSR_COUNT 5 +#endif + +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[MSR_COUNT]; + 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->kvm_cpu_state.vcpu_ctx, ®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->kvm_cpu_state.vcpu_ctx, &fpu); + + memcpy(sregs.interrupt_bitmap, env->interrupt_bitmap, sizeof(sregs.interrupt_bitmap)); + + 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->kvm_cpu_state.vcpu_ctx, &sregs); + + /* msrs */ + n = 0; + 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 + + rc = kvm_set_msrs(env->kvm_cpu_state.vcpu_ctx, 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->kvm_cpu_state.vcpu_ctx, &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->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_save_regs(CPUState *env) +{ + struct kvm_regs regs; + struct kvm_fpu fpu; + struct kvm_sregs sregs; + struct kvm_msr_entry msrs[MSR_COUNT]; + uint32_t hflags; + uint32_t i, n, rc; + + kvm_get_regs(env->kvm_cpu_state.vcpu_ctx, ®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->kvm_cpu_state.vcpu_ctx, &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->kvm_cpu_state.vcpu_ctx, &sregs); + + memcpy(env->interrupt_bitmap, sregs.interrupt_bitmap, sizeof(env->interrupt_bitmap)); + + 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; + 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 + rc = kvm_get_msrs(env->kvm_cpu_state.vcpu_ctx, 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; + } + } +} + +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_qemu_init_env(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); + + +#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->kvm_cpu_state.vcpu_ctx, 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->kvm_cpu_state.vcpu_ctx, &mcg_cap)) + perror("kvm_setup_mce FAILED"); + else + cenv->mcg_cap = mcg_cap; + } + } +#endif + + return 0; +} + +int kvm_arch_halt(void *opaque, kvm_vcpu_context_t vcpu) +{ + CPUState *env = cpu_single_env; + + if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) && + !(env->interrupt_request & CPU_INTERRUPT_NMI)) { + env->halted = 1; + } + return 1; +} + +void kvm_arch_pre_kvm_run(void *opaque, CPUState *env) +{ + if (!kvm_irqchip_in_kernel(kvm_context)) + kvm_set_cr8(env->kvm_cpu_state.vcpu_ctx, cpu_get_apic_tpr(env)); +} + +void kvm_arch_post_kvm_run(void *opaque, CPUState *env) +{ + cpu_single_env = env; + + env->eflags = kvm_get_interrupt_flag(env->kvm_cpu_state.vcpu_ctx) + ? env->eflags | IF_MASK : env->eflags & ~IF_MASK; + + cpu_set_apic_tpr(env, kvm_get_cr8(env->kvm_cpu_state.vcpu_ctx)); + cpu_set_apic_base(env, kvm_get_apic_base(env->kvm_cpu_state.vcpu_ctx)); +} + +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->kvm_cpu_state.vcpu_ctx) && + (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->kvm_cpu_state.vcpu_ctx, 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->kvm_cpu_state.vcpu_ctx); + if (r < 0) + printf("cpu %d fail inject NMI\n", env->cpu_index); +} +#endif /* KVM_CAP_USER_NMI */ + +void kvm_arch_update_regs_for_sipi(CPUState *env) +{ + SegmentCache cs = env->segs[R_CS]; + + kvm_arch_save_regs(env); + env->segs[R_CS] = cs; + env->eip = 0; + kvm_arch_load_regs(env); +} + +void kvm_arch_cpu_reset(CPUState *env) +{ + kvm_arch_load_regs(env); + if (!cpu_is_bsp(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; + } + } +} + +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 + +void kvm_arch_do_ioperm(void *_data) +{ + struct ioperm_data *data = _data; + ioperm(data->start_port, data->num, data->turn_on); +} + +/* + * 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) +{ + kvm_arch_save_regs(env); + if (env->interrupt_request & CPU_INTERRUPT_INIT) + do_cpu_init(env); + if (env->interrupt_request & CPU_INTERRUPT_SIPI) + do_cpu_sipi(env); + kvm_arch_load_regs(env); +} diff --git a/qemu-kvm.c b/qemu-kvm.c new file mode 100644 index 000000000..32dce4aab --- /dev/null +++ b/qemu-kvm.c @@ -0,0 +1,2621 @@ +/* + * 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 <signal.h> + +#define false 0 +#define true 1 + +#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; + +/* The list of ioperm_data */ +static LIST_HEAD(, ioperm_data) ioperm_head; + +//#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(void *opaque, void *data, + struct kvm_debug_exit_arch *arch_info) +{ + int handle = kvm_arch_debug(arch_info); + CPUState *env = data; + + if (handle) { + kvm_debug_cpu_requested = env; + env->stopped = 1; + } + return handle; +} +#endif + +int kvm_mmio_read(void *opaque, uint64_t addr, uint8_t *data, int len) +{ + cpu_physical_memory_rw(addr, data, len, 0); + return 0; +} + +int kvm_mmio_write(void *opaque, uint64_t addr, uint8_t *data, int len) +{ + cpu_physical_memory_rw(addr, data, len, 1); + return 0; +} + +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 %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 = 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 + TAILQ_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; +} + +kvm_vcpu_context_t kvm_create_vcpu(CPUState *env, int id) +{ + long mmap_size; + int r; + kvm_vcpu_context_t vcpu_ctx = qemu_malloc(sizeof(struct kvm_vcpu_context)); + kvm_context_t kvm = kvm_context; + + vcpu_ctx->kvm = kvm; + vcpu_ctx->id = id; + + r = kvm_vm_ioctl(kvm_state, KVM_CREATE_VCPU, id); + if (r < 0) { + fprintf(stderr, "kvm_create_vcpu: %m\n"); + goto err; + } + vcpu_ctx->fd = r; + + 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; + } + vcpu_ctx->run = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, + vcpu_ctx->fd, 0); + if (vcpu_ctx->run == MAP_FAILED) { + fprintf(stderr, "mmap vcpu area: %m\n"); + goto err_fd; + } + return vcpu_ctx; +err_fd: + close(vcpu_ctx->fd); +err: + free(vcpu_ctx); + return NULL; +} + +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 +} + +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)(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 = 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 %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 = 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((slots[i].len / 4096 + 7) / 8 + 2); + 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_io(kvm_vcpu_context_t vcpu) +{ + struct kvm_run *run = vcpu->run; + kvm_context_t kvm = vcpu->kvm; + uint16_t addr = run->io.port; + 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: + *(uint8_t *)p = cpu_inb(kvm->opaque, addr); + break; + case 2: + *(uint16_t *)p = cpu_inw(kvm->opaque, addr); + break; + case 4: + *(uint32_t *)p = cpu_inl(kvm->opaque, addr); + 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: + cpu_outb(kvm->opaque, addr, *(uint8_t *)p); + break; + case 2: + cpu_outw(kvm->opaque, addr, *(uint16_t *)p); + break; + case 4: + cpu_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_vcpu_context_t vcpu, void *env) +{ +#ifdef KVM_CAP_SET_GUEST_DEBUG + struct kvm_run *run = vcpu->run; + kvm_context_t kvm = vcpu->kvm; + + return kvm_debug(kvm->opaque, env, &run->debug.arch); +#else + return 0; +#endif +} + +int kvm_get_regs(kvm_vcpu_context_t vcpu, struct kvm_regs *regs) +{ + return ioctl(vcpu->fd, KVM_GET_REGS, regs); +} + +int kvm_set_regs(kvm_vcpu_context_t vcpu, struct kvm_regs *regs) +{ + return ioctl(vcpu->fd, KVM_SET_REGS, regs); +} + +int kvm_get_fpu(kvm_vcpu_context_t vcpu, struct kvm_fpu *fpu) +{ + return ioctl(vcpu->fd, KVM_GET_FPU, fpu); +} + +int kvm_set_fpu(kvm_vcpu_context_t vcpu, struct kvm_fpu *fpu) +{ + return ioctl(vcpu->fd, KVM_SET_FPU, fpu); +} + +int kvm_get_sregs(kvm_vcpu_context_t vcpu, struct kvm_sregs *sregs) +{ + return ioctl(vcpu->fd, KVM_GET_SREGS, sregs); +} + +int kvm_set_sregs(kvm_vcpu_context_t vcpu, struct kvm_sregs *sregs) +{ + return ioctl(vcpu->fd, KVM_SET_SREGS, sregs); +} + +#ifdef KVM_CAP_MP_STATE +int kvm_get_mpstate(kvm_vcpu_context_t vcpu, struct kvm_mp_state *mp_state) +{ + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); + if (r > 0) + return ioctl(vcpu->fd, KVM_GET_MP_STATE, mp_state); + return -ENOSYS; +} + +int kvm_set_mpstate(kvm_vcpu_context_t vcpu, struct kvm_mp_state *mp_state) +{ + int r; + + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); + if (r > 0) + return ioctl(vcpu->fd, KVM_SET_MP_STATE, mp_state); + return -ENOSYS; +} +#endif + +static int handle_mmio(kvm_vcpu_context_t vcpu) +{ + unsigned long addr = vcpu->run->mmio.phys_addr; + kvm_context_t kvm = vcpu->kvm; + struct kvm_run *kvm_run = vcpu->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; + + if (kvm_run->mmio.is_write) + return kvm_mmio_write(kvm->opaque, addr, data, + kvm_run->mmio.len); + else + return kvm_mmio_read(kvm->opaque, addr, data, + kvm_run->mmio.len); +} + +int handle_io_window(kvm_context_t kvm) +{ + return 1; +} + +int handle_halt(kvm_vcpu_context_t vcpu) +{ + return kvm_arch_halt(vcpu->kvm->opaque, vcpu); +} + +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_kvm_run(kvm->opaque, env); +} + +int pre_kvm_run(kvm_context_t kvm, CPUState *env) +{ + kvm_arch_pre_kvm_run(kvm->opaque, env); + + pthread_mutex_unlock(&qemu_mutex); + return 0; +} + +int kvm_get_interrupt_flag(kvm_vcpu_context_t vcpu) +{ + return vcpu->run->if_flag; +} + +int kvm_is_ready_for_interrupt_injection(kvm_vcpu_context_t vcpu) +{ + return vcpu->run->ready_for_interrupt_injection; +} + +int kvm_run(kvm_vcpu_context_t vcpu, void *env) +{ + int r; + int fd = vcpu->fd; + struct kvm_run *run = vcpu->run; + kvm_context_t kvm = vcpu->kvm; + +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->coalesced_mmio) { + struct kvm_coalesced_mmio_ring *ring = (void *)run + + kvm->coalesced_mmio * PAGE_SIZE; + while (ring->first != ring->last) { + kvm_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: + 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(vcpu); + kvm_show_code(vcpu); + abort(); + break; + case KVM_EXIT_IO: + r = handle_io(vcpu); + break; + case KVM_EXIT_DEBUG: + r = handle_debug(vcpu, env); + break; + case KVM_EXIT_MMIO: + r = handle_mmio(vcpu); + break; + case KVM_EXIT_HLT: + r = handle_halt(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_s390_handle_intercept(kvm, vcpu, + run); + break; + case KVM_EXIT_S390_RESET: + r = kvm_s390_handle_reset(kvm, vcpu, run); + break; +#endif + default: + if (kvm_arch_run(vcpu)) { + fprintf(stderr, "unhandled vm exit: 0x%x\n", + run->exit_reason); + kvm_show_regs(vcpu); + abort(); + } + break; + } + } +more: + if (!r) + goto again; + return r; +} + +int kvm_inject_irq(kvm_vcpu_context_t vcpu, unsigned irq) +{ + struct kvm_interrupt intr; + + intr.irq = irq; + return ioctl(vcpu->fd, KVM_INTERRUPT, &intr); +} + +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_set_guest_debug(kvm_vcpu_context_t vcpu, struct kvm_guest_debug *dbg) +{ + return ioctl(vcpu->fd, KVM_SET_GUEST_DEBUG, dbg); +} +#endif + +int kvm_set_signal_mask(kvm_vcpu_context_t vcpu, const sigset_t *sigset) +{ + struct kvm_signal_mask *sigmask; + int r; + + if (!sigset) { + r = ioctl(vcpu->fd, KVM_SET_SIGNAL_MASK, NULL); + if (r == -1) + r = -errno; + return r; + } + sigmask = qemu_malloc(sizeof(*sigmask) + sizeof(*sigset)); + + sigmask->len = 8; + memcpy(sigmask->sigset, sigset, sizeof(*sigset)); + r = ioctl(vcpu->fd, 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(void) +{ + int r = 0; +#ifdef KVM_CAP_SYNC_MMU + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU); +#endif + return r; +} + +int kvm_inject_nmi(kvm_vcpu_context_t vcpu) +{ +#ifdef KVM_CAP_USER_NMI + return ioctl(vcpu->fd, 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 = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO); + if (r > 0) { + kvm->coalesced_mmio = r; + return 0; + } +#endif + return r; +} + +int kvm_coalesce_mmio_region(target_phys_addr_t addr, ram_addr_t size) +{ +#ifdef KVM_CAP_COALESCED_MMIO + kvm_context_t kvm = kvm_context; + struct kvm_coalesced_mmio_zone zone; + int r; + + if (kvm->coalesced_mmio) { + + zone.addr = addr; + zone.size = size; + + r = kvm_vm_ioctl(kvm_state, KVM_REGISTER_COALESCED_MMIO, &zone); + if (r < 0) { + perror("kvm_register_coalesced_mmio_zone"); + return r; + } + return 0; + } +#endif + return -ENOSYS; +} + +int kvm_uncoalesce_mmio_region(target_phys_addr_t addr, ram_addr_t size) +{ +#ifdef KVM_CAP_COALESCED_MMIO + kvm_context_t kvm = kvm_context; + struct kvm_coalesced_mmio_zone zone; + int r; + + if (kvm->coalesced_mmio) { + + zone.addr = addr; + zone.size = size; + + r = kvm_vm_ioctl(kvm_state, KVM_UNREGISTER_COALESCED_MMIO, &zone); + if (r < 0) { + perror("kvm_unregister_coalesced_mmio_zone"); + return r; + } + 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) +{ + 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; + static const struct timespec ts = { + .tv_sec = 0, + .tv_nsec = 100000, + }; + + pthread_cond_timedwait(cond, &qemu_mutex, &ts); + cpu_single_env = env; +} + +static void sig_ipi_handler(int n) +{ +} + +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); +} + +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) + on_vcpu(env, kvm_do_load_mpstate, env); +} + +static void kvm_do_save_mpstate(void *_env) +{ + CPUState *env = _env; + + kvm_arch_save_mpstate(env); + env->halted = (env->mp_state == KVM_MP_STATE_HALTED); +} + +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->kvm_cpu_state.vcpu_ctx, 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_main_loop_wait(CPUState *env, int timeout) +{ + struct timespec ts; + int r, e; + siginfo_t siginfo; + sigset_t waitset; + + pthread_mutex_unlock(&qemu_mutex); + + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + + 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); + } + + 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); + + kvm_set_signal_mask(env->kvm_cpu_state.vcpu_ctx, &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) +{ + setup_kernel_sigmask(env); + + pthread_mutex_lock(&qemu_mutex); + + kvm_qemu_init_env(env); +#ifdef TARGET_I386 + kvm_tpr_vcpu_start(env); +#endif + + cpu_single_env = env; + kvm_arch_load_regs(env); + + while (1) { + int run_cpu = !is_cpu_stopped(env); + if (run_cpu && !kvm_irqchip_in_kernel(kvm_context)) { + process_irqchip_events(env); + run_cpu = !env->halted; + } + if (run_cpu) { + kvm_main_loop_wait(env, 0); + kvm_cpu_exec(env); + } 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; + struct ioperm_data *data = NULL; + + current_env = env; + env->thread_id = kvm_get_thread_id(); + sigfillset(&signals); + sigprocmask(SIG_BLOCK, &signals, NULL); + env->kvm_cpu_state.vcpu_ctx = kvm_create_vcpu(env, env->cpu_index); + +#ifdef USE_KVM_DEVICE_ASSIGNMENT + /* do ioperm for io ports of assigned devices */ + LIST_FOREACH(data, &ioperm_head, entries) + on_vcpu(env, kvm_arch_do_ioperm, data); +#endif + + /* signal VCPU creation */ + pthread_mutex_lock(&qemu_mutex); + 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); + pthread_mutex_unlock(&qemu_mutex); + + 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) +{ +#ifdef TARGET_I386 + kvm_tpr_opt_setup(); +#endif + qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL); + + signal(SIG_IPI, sig_ipi_handler); + 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_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); + 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_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() +{ + 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_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; +} + +int kvm_qemu_check_extension(int ext) +{ + return kvm_check_extension(kvm_state, ext); +} + +int kvm_qemu_init_env(CPUState *cenv) +{ + return kvm_arch_qemu_init_env(cenv); +} + +#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; + + dbg_data->err = kvm_set_guest_debug(cpu_single_env->kvm_cpu_state.vcpu_ctx, + &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); + if (kvm_dirty_bitmap == NULL) { + perror("Failed to allocate dirty pages bitmap"); + r=-1; + } + else { + 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); + } +} + +int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap) +{ + unsigned int bsize = BITMAP_SIZE(phys_ram_size); + unsigned int brsize = BITMAP_SIZE(ram_size); + unsigned int extra_pages = (phys_ram_size - ram_size) / TARGET_PAGE_SIZE; + unsigned int extra_bytes = (extra_pages +7)/8; + unsigned int hole_start = BITMAP_SIZE(0xa0000); + unsigned int hole_end = BITMAP_SIZE(0xc0000); + + memset(bitmap, 0xFF, brsize + extra_bytes); + memset(bitmap + hole_start, 0, hole_end - hole_start); + memset(bitmap + brsize + extra_bytes, 0, bsize - brsize - extra_bytes); + + return 0; +} + +#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; +} + +#ifdef USE_KVM_DEVICE_ASSIGNMENT +void kvm_add_ioperm_data(struct ioperm_data *data) +{ + LIST_INSERT_HEAD(&ioperm_head, data, entries); +} + +void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num) +{ + struct ioperm_data *data; + + data = LIST_FIRST(&ioperm_head); + while (data) { + struct ioperm_data *next = LIST_NEXT(data, entries); + + if (data->start_port == start_port && data->num == num) { + LIST_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; +}; + +static void kvm_do_inject_x86_mce(void *_data) +{ + struct kvm_x86_mce_data *data = _data; + int r; + + r = kvm_set_mce(data->env->kvm_cpu_state.vcpu_ctx, data->mce); + if (r < 0) + perror("kvm_set_mce FAILED"); +} +#endif + +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc) +{ +#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, + }; + + on_vcpu(cenv, kvm_do_inject_x86_mce, &data); +#endif +} +#endif diff --git a/qemu-kvm.h b/qemu-kvm.h new file mode 100644 index 000000000..eb48ff8f7 --- /dev/null +++ b/qemu-kvm.h @@ -0,0 +1,1216 @@ +/* + * 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 + +#include "cpu.h" + +#include <signal.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; + /// 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; +}; + +struct kvm_vcpu_context +{ + int fd; + struct kvm_run *run; + struct kvm_context *kvm; + uint32_t id; +}; + +typedef struct kvm_context *kvm_context_t; +typedef struct kvm_vcpu_context *kvm_vcpu_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(kvm_vcpu_context_t vcpu); + + +void kvm_show_code(kvm_vcpu_context_t vcpu); + +int handle_halt(kvm_vcpu_context_t vcpu); +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 handle_debug(kvm_vcpu_context_t vcpu, void *env); +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(kvm_vcpu_context_t, struct kvm_msr_entry *msrs, int n); +int kvm_set_msrs(kvm_vcpu_context_t, 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(kvm_vcpu_context_t vcpu, uint64_t *mcg_cap); +struct kvm_x86_mce; +int kvm_set_mce(kvm_vcpu_context_t vcpu, struct kvm_x86_mce *mce); +#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 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 + */ +kvm_vcpu_context_t kvm_create_vcpu(CPUState *env, int id); + +/*! + * \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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t vcpu, struct kvm_sregs *regs); + +#ifdef KVM_CAP_MP_STATE +/*! + * * \brief Read VCPU MP state + * + */ +int kvm_get_mpstate(kvm_vcpu_context_t vcpu, struct kvm_mp_state *mp_state); + +/*! + * * \brief Write VCPU MP state + * + */ +int kvm_set_mpstate(kvm_vcpu_context_t vcpu, struct kvm_mp_state *mp_state); +/*! + * * \brief Reset VCPU MP state + * + */ +static inline int kvm_reset_mpstate(kvm_vcpu_context_t vcpu) +{ + struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED}; + return kvm_set_mpstate(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_vcpu_context_t vcpu, unsigned irq); + +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_set_guest_debug(kvm_vcpu_context_t, 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t vcpu, 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(kvm_vcpu_context_t 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*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); + +#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_vcpu_context_t 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_vcpu_context_t 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_vcpu_context_t vcpu); + +#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 + */ +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc); + +/*! + * \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(kvm_vcpu_context_t 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_vcpu_context_t vcpu); + +int kvm_enable_vapic(kvm_vcpu_context_t 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 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; } +static inline void kvm_inject_x86_mce( + CPUState *cenv, int bank,uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc) { } + + +extern int kvm_allowed; + +#endif /* !CONFIG_KVM */ + + +int kvm_main_loop(void); +int kvm_qemu_init(void); +int kvm_init_ap(void); +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); +int kvm_qemu_init_env(CPUState *env); +int kvm_qemu_check_extension(int ext); +void kvm_apic_init(CPUState *env); +/* called from vcpu initialization */ +void qemu_kvm_load_lapic(CPUState *env); + +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); +int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap); + +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); +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); + +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_qemu_init_env(CPUState *cenv); +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); +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_update_regs_for_sipi(CPUState *env); +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; + TAILQ_ENTRY(kvm_sw_breakpoint) entry; +}; + +TAILQ_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); + +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); + +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); + +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 USE_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)) +#define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8) + +#ifdef CONFIG_KVM +#include "sys-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; + LIST_ENTRY(ioperm_data) entries; +}; + +void qemu_kvm_cpu_stop(CPUState *env); +int kvm_arch_halt(void *opaque, kvm_vcpu_context_t vcpu); +int handle_tpr_access(void *opaque, kvm_vcpu_context_t vcpu, + uint64_t rip, int is_write); +int kvm_has_sync_mmu(void); + +#define kvm_enabled() (kvm_allowed) +#define qemu_kvm_irqchip_in_kernel() kvm_irqchip_in_kernel(kvm_context) +#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_irqchip_in_kernel() (0) +#define qemu_kvm_pit_in_kernel() (0) +#define qemu_kvm_has_gsi_routing() (0) +#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 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 + +void kvm_mutex_unlock(void); +void kvm_mutex_lock(void); + +static inline void qemu_mutex_unlock_iothread(void) +{ + if (kvm_enabled()) + kvm_mutex_unlock(); +} + +static inline void qemu_mutex_lock_iothread(void) +{ + if (kvm_enabled()) + kvm_mutex_lock(); +} + +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; } + +static inline void kvm_arch_get_registers(CPUState *env) +{ + kvm_save_registers(env); + kvm_save_mpstate(env); +} + +static inline void kvm_arch_put_registers(CPUState *env) +{ + kvm_load_registers(env); + kvm_load_mpstate(env); +} + +static inline void cpu_synchronize_state(CPUState *env, int modified) +{ + if (kvm_enabled()) { + if (modified) + kvm_arch_put_registers(env); + else + kvm_arch_get_registers(env); + } +} + +uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, + int reg); + + +static inline int kvm_set_migration_log(int enable) +{ + return kvm_physical_memory_set_dirty_tracking(enable); +} + +#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; +#ifdef KVM_CAP_SET_GUEST_DEBUG + struct kvm_sw_breakpoint_head kvm_sw_breakpoints; +#endif + 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_check_extension(KVMState *s, unsigned int ext); + +#endif + +#endif diff --git a/qemu-lock.h b/qemu-lock.h index 49e220375..029fae00c 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 11bdb2c2d..0dc2ad7d2 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -511,7 +511,7 @@ Add drive to PCI storage controller. ETEXI #if defined(TARGET_I386) - { "pci_add", "sss?", pci_device_hot_add, "auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" }, + { "pci_add", "sss?", pci_device_hot_add, "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]", "hot-add PCI device" }, #endif STEXI @item pci_add @@ -646,6 +646,13 @@ Close the file descriptor previously assigned to @var{fdname} using the used by another monitor command. ETEXI + { "cpu_set", "is", do_cpu_set_nr, + "cpu [online|offline]", "change cpu state" }, +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 a58287cbf..3038acddf 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -93,6 +93,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]\n" + " [,boot=on|off]\n" " use 'file' as a drive image\n") STEXI @item -drive @var{option}[,@var{option}[,@var{option}[,...]]] @@ -1661,3 +1662,34 @@ DEF("semihosting", 0, QEMU_OPTION_semihosting, DEF("old-param", 0, QEMU_OPTION_old_param, "-old-param old param mode\n") #endif + +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 @@ -42,6 +42,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); @@ -118,6 +119,7 @@ extern int vga_interface_type; extern int graphic_width; extern int graphic_height; extern int graphic_depth; +extern uint8_t irq0override; extern DisplayType display_type; extern const char *keyboard_layout; extern int win2k_install_hack; @@ -133,6 +135,7 @@ extern int no_quit; extern int semihosting_enabled; extern int old_param; extern int boot_menu; +extern long hpagesize; #ifdef CONFIG_KQEMU extern int kqemu_allowed; @@ -184,6 +187,7 @@ typedef struct DriveInfo { extern int nb_drives; extern DriveInfo drives_table[MAX_DRIVES+1]; +extern int extboot_drive; extern int drive_get_index(BlockInterfaceType type, int bus, int unit); extern int drive_get_max_bus(BlockInterfaceType type); @@ -207,6 +211,7 @@ extern int drive_add(const char *file, const char *fmt, ...); extern int drive_init(struct drive_opt *arg, int snapshot, void *machine); /* acpi */ +void qemu_system_cpu_hot_add(int cpu, int state); typedef void (*qemu_system_device_hot_add_t)(int pcibus, int slot, int state); void qemu_system_device_hot_add_register(qemu_system_device_hot_add_t callback); void qemu_system_device_hot_add(int pcibus, int slot, int state); diff --git a/target-i386/fake-exec.c b/target-i386/fake-exec.c new file mode 100644 index 000000000..737286dce --- /dev/null +++ b/target-i386/fake-exec.c @@ -0,0 +1,54 @@ +/* + * 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 flush_icache_range(unsigned long start, unsigned long stop) +{ +} + +void optimize_flags_init(void) +{ +} diff --git a/target-i386/helper.c b/target-i386/helper.c index dd898852c..4785ff0d7 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[] = { @@ -1504,6 +1506,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); + return; + } + if (bank >= bank_num || !(status & MCI_STATUS_VAL)) return; @@ -1568,7 +1575,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__ @@ -1709,8 +1716,29 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = env->cpuid_ext2_features; if (kvm_enabled()) { - /* Nested SVM not yet supported in KVM */ - *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); @@ -1837,8 +1865,6 @@ CPUX86State *cpu_x86_init(const char *cpu_model) kqemu_init(env); #endif - qemu_init_vcpu(env); - return env; } diff --git a/target-i386/kvm.c b/target-i386/kvm.c index cab9fccae..cfa5b80d5 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -24,6 +24,7 @@ #include "cpu.h" #include "gdbstub.h" +#ifdef KVM_UPSTREAM //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -477,6 +478,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); @@ -610,6 +612,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; @@ -653,6 +656,9 @@ static int kvm_get_msrs(CPUState *env) case MSR_IA32_TSC: env->tsc = msrs[i].data; break; + case MSR_VM_HSAVE_PA: + env->vm_hsave = msrs[i].data; + break; } } @@ -957,3 +963,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 8bf13cce8..ca32a92a0 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -6,6 +6,7 @@ #include "exec-all.h" #include "kvm.h" +#include "qemu-kvm.h" static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) { @@ -32,7 +33,10 @@ void cpu_save(QEMUFile *f, void *opaque) int32_t pending_irq; int i, bit; - cpu_synchronize_state(env, 0); + if (kvm_enabled()) { + kvm_save_registers(env); + kvm_arch_save_mpstate(env); + } for(i = 0; i < CPU_NB_REGS; i++) qemu_put_betls(f, &env->regs[i]); @@ -119,7 +123,6 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_be64s(f, &env->kernelgsbase); #endif qemu_put_be32s(f, &env->smbase); - qemu_put_be64s(f, &env->pat); qemu_put_be32s(f, &env->hflags2); @@ -161,7 +164,7 @@ void cpu_save(QEMUFile *f, void *opaque) /* MCE */ qemu_put_be64s(f, &env->mcg_cap); - if (env->mcg_cap) { + if (env->mcg_cap && !kvm_enabled()) { qemu_put_be64s(f, &env->mcg_status); qemu_put_be64s(f, &env->mcg_ctl); for (i = 0; i < (env->mcg_cap & 0xff); i++) { @@ -364,7 +367,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) if (version_id >= 10) { qemu_get_be64s(f, &env->mcg_cap); - if (env->mcg_cap) { + if (env->mcg_cap && !kvm_enabled()) { qemu_get_be64s(f, &env->mcg_status); qemu_get_be64s(f, &env->mcg_ctl); for (i = 0; i < (env->mcg_cap & 0xff); i++) { @@ -381,5 +384,26 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->hflags = hflags; tlb_flush(env, 1); cpu_synchronize_state(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; + if (version_id < 9) { + for (i = 0; i < sizeof(env->interrupt_bitmap)/8; i++) { + qemu_get_be64s(f, &env->interrupt_bitmap[i]); + } + qemu_get_be64s(f, &env->tsc); + kvm_load_registers(env); + kvm_load_tsc(env); + if (version_id >= 5) { + qemu_get_be32s(f, &env->mp_state); + kvm_arch_load_mpstate(env); + } + } else { + kvm_load_registers(env); + kvm_load_tsc(env); + kvm_arch_load_mpstate(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 69c1d5807..d697bd286 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) */ @@ -1591,4 +1592,11 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, *flags = env->hflags; } +/* 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 b7162dfe5..6ffe9ce1c 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 99ba3eb2a..ec8e19794 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) { @@ -161,6 +161,8 @@ int main(int argc, char **argv) #include "kvm.h" #include "balloon.h" #include "qemu-option.h" +#include "qemu-kvm.h" +#include "hw/device-assignment.h" #include "disas.h" @@ -187,6 +189,7 @@ const char *bios_name = NULL; to store the VM snapshots */ DriveInfo drives_table[MAX_DRIVES+1]; int nb_drives; +int extboot_drive = -1; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; static DisplayState *display_state; DisplayType display_type = DT_DEFAULT; @@ -223,10 +226,14 @@ 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; const char *vnc_display; int acpi_enabled = 1; +#ifdef TARGET_I386 int no_hpet = 0; +#endif int virtio_balloon = 1; const char *virtio_balloon_devaddr; int fd_bootchk = 1; @@ -234,6 +241,7 @@ int no_reboot = 0; int no_shutdown = 0; int cursor_hide = 1; int graphic_rotate = 0; +uint8_t irq0override = 1; #ifndef _WIN32 int daemonize = 0; #endif @@ -242,6 +250,12 @@ int watchdog_action = WDT_RESET; 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 @@ -252,6 +266,7 @@ unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; #endif int nb_drives_opt; +const char *nvram = NULL; struct drive_opt drives_opt[MAX_DRIVES]; int boot_menu; @@ -2017,6 +2032,7 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) "media", "snapshot", "file", "cache", "format", "serial", "werror", "addr", + "boot", NULL }; if (check_params(buf, sizeof(buf), params, str) < 0) { @@ -2202,6 +2218,19 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) } } + if (get_param_value(buf, sizeof(buf), "boot", str)) { + if (!strcmp(buf, "on")) { + if (extboot_drive != -1) { + fprintf(stderr, "qemu: two bootable drives specified\n"); + return -1; + } + extboot_drive = nb_drives; + } else if (strcmp(buf, "off")) { + fprintf(stderr, "qemu: '%s' invalid boot option\n", str); + return -1; + } + } + if (arg->file == NULL) get_param_value(file, sizeof(file), "file", str); else @@ -2812,6 +2841,7 @@ int qemu_set_fd_handler2(int fd, ioh->opaque = opaque; ioh->deleted = 0; } + qemu_notify_event(); return 0; } @@ -2938,6 +2968,8 @@ static int ram_load_v1(QEMUFile *f, void *opaque) if (qemu_get_be32(f) != last_ram_offset) return -EINVAL; for(i = 0; i < last_ram_offset; i+= TARGET_PAGE_SIZE) { + if (kvm_enabled() && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */ + continue; ret = ram_get_page(f, qemu_get_ram_ptr(i), TARGET_PAGE_SIZE); if (ret) return ret; @@ -3024,6 +3056,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; @@ -3153,6 +3194,8 @@ static int ram_load_dead(QEMUFile *f, void *opaque) if (ram_decompress_open(s, f) < 0) return -EINVAL; for(i = 0; i < last_ram_offset; i+= BDRV_HASH_BLOCK_SIZE) { + if (kvm_enabled() && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */ + continue; if (ram_decompress_buf(s, buf, 1) < 0) { fprintf(stderr, "Error while reading ram block header\n"); goto error; @@ -3467,6 +3510,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; @@ -3551,6 +3601,9 @@ void qemu_system_reset_request(void) } else { reset_requested = 1; } + if (cpu_single_env) { + cpu_single_env->stopped = 1; + } qemu_notify_event(); } @@ -3697,6 +3750,10 @@ void qemu_notify_event(void) { CPUState *env = cpu_single_env; + if (kvm_enabled()) { + qemu_kvm_notify_work(); + return; + } if (env) { cpu_exit(env); #ifdef USE_KQEMU @@ -3706,8 +3763,10 @@ void qemu_notify_event(void) } } +#ifdef KVM_UPSTREAM #define qemu_mutex_lock_iothread() do { } while (0) #define qemu_mutex_unlock_iothread() do { } while (0) +#endif void vm_stop(int reason) { @@ -4127,6 +4186,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); @@ -4328,6 +4389,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); @@ -4933,6 +5000,7 @@ int main(int argc, char **argv, char **envp) } usb_devices_index = 0; + assigned_devices_index = 0; nb_net_clients = 0; nb_bt_opts = 0; @@ -5437,13 +5505,45 @@ int main(int argc, char **argv, char **envp) break; #endif #ifdef CONFIG_KVM +#ifdef KVM_UPSTREAM case QEMU_OPTION_enable_kvm: kvm_allowed = 1; #ifdef CONFIG_KQEMU kqemu_allowed = 0; #endif +#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; break; @@ -5515,6 +5615,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); { @@ -5609,6 +5723,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: @@ -5714,6 +5831,20 @@ int main(int argc, char **argv, char **envp) } #endif + if (kvm_enabled()) { + int ret; + + ret = kvm_init(smp_cpus); + if (ret < 0) { +#if defined(KVM_UPSTREAM) || defined(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; + } + } + #ifdef CONFIG_KQEMU if (smp_cpus > 1) kqemu_allowed = 0; @@ -5885,16 +6016,6 @@ int main(int argc, char **argv, char **envp) } } - if (kvm_enabled()) { - int ret; - - ret = kvm_init(smp_cpus); - if (ret < 0) { - fprintf(stderr, "failed to initialize KVM\n"); - exit(1); - } - } - if (monitor_device) { monitor_hd = qemu_chr_open("monitor", monitor_device, NULL); if (!monitor_hd) { |